Compare commits

...

2 Commits

Author SHA1 Message Date
Thorsten Ball
3baff65c6b linux/x11: Ignore auto-generated KeyRelease events
This is inspired by [GLFW's X11 input
handling](b35641f4a3/src/x11_window.c (L1321-L1349)):

Based on a heuristic we detect whether a `KeyRelease` event was
auto-generated and if so, we drop it. That essentially halves the amount
of events we have to process when someone keeps a key pressed.
2024-07-03 15:02:24 +02:00
Thorsten Ball
8db36d562f linux/x11: refactor handling of x11 events 2024-07-03 14:54:13 +02:00

View File

@@ -285,70 +285,9 @@ impl X11Client {
calloop::Interest::READ,
calloop::Mode::Level,
),
{
let xcb_connection = xcb_connection.clone();
move |_readiness, _, client| {
let mut events = Vec::new();
let mut windows_to_refresh = HashSet::new();
while let Some(event) = xcb_connection.poll_for_event()? {
if let Event::Expose(event) = event {
windows_to_refresh.insert(event.window);
} else {
events.push(event);
}
}
for window in windows_to_refresh.into_iter() {
if let Some(window) = client.get_window(window) {
window.refresh();
}
}
for event in events.into_iter() {
let mut state = client.0.borrow_mut();
if state.ximc.is_none() || state.xim_handler.is_none() {
drop(state);
client.handle_event(event);
continue;
}
let mut ximc = state.ximc.take().unwrap();
let mut xim_handler = state.xim_handler.take().unwrap();
let xim_connected = xim_handler.connected;
drop(state);
let xim_filtered = match ximc.filter_event(&event, &mut xim_handler) {
Ok(handled) => handled,
Err(err) => {
log::error!("XIMClientError: {}", err);
false
}
};
let xim_callback_event = xim_handler.last_callback_event.take();
let mut state = client.0.borrow_mut();
state.ximc = Some(ximc);
state.xim_handler = Some(xim_handler);
drop(state);
if let Some(event) = xim_callback_event {
client.handle_xim_callback_event(event);
}
if xim_filtered {
continue;
}
if xim_connected {
client.xim_handle_event(event);
} else {
client.handle_event(event);
}
}
Ok(calloop::PostAction::Continue)
}
|_readiness, _, client| {
client.consume_x11_events();
Ok(calloop::PostAction::Continue)
},
)
.expect("Failed to initialize x11 event source");
@@ -468,6 +407,109 @@ impl X11Client {
.map(|window_reference| window_reference.window.clone())
}
fn consume_x11_events(&self) {
let xcb_connection = self.0.borrow_mut().xcb_connection.clone();
let mut events = Vec::new();
let mut windows_to_refresh = HashSet::new();
let mut last_key_release: Option<Event> = None;
loop {
match xcb_connection.poll_for_event() {
Ok(Some(event)) => {
if let Event::Expose(expose_event) = event {
windows_to_refresh.insert(expose_event.window);
} else {
match event {
Event::KeyRelease(_) => {
last_key_release = Some(event);
}
Event::KeyPress(key_press) => {
if let Some(Event::KeyRelease(key_release)) =
last_key_release.take()
{
// We ignore that last KeyRelease if it's too close to this KeyPress,
// suggesting that it's auto-generated by X11 as a key-repeat event.
if key_release.detail != key_press.detail
|| key_press.time.wrapping_sub(key_release.time) > 20
{
events.push(Event::KeyRelease(key_release));
}
}
events.push(Event::KeyPress(key_press));
}
_ => {
if let Some(release_event) = last_key_release.take() {
events.push(release_event);
}
events.push(event);
}
}
}
}
Ok(None) => {
// Add any remaining stored KeyRelease event
if let Some(release_event) = last_key_release.take() {
events.push(release_event);
}
break;
}
Err(error) => {
log::warn!("error polling xcb connection for new events: {}", error);
break;
}
}
}
for window in windows_to_refresh.into_iter() {
if let Some(window) = self.get_window(window) {
window.refresh();
}
}
for event in events.into_iter() {
let mut state = self.0.borrow_mut();
if state.ximc.is_none() || state.xim_handler.is_none() {
drop(state);
self.handle_event(event);
continue;
}
let mut ximc = state.ximc.take().unwrap();
let mut xim_handler = state.xim_handler.take().unwrap();
let xim_connected = xim_handler.connected;
drop(state);
let xim_filtered = match ximc.filter_event(&event, &mut xim_handler) {
Ok(handled) => handled,
Err(err) => {
log::error!("XIMClientError: {}", err);
false
}
};
let xim_callback_event = xim_handler.last_callback_event.take();
let mut state = self.0.borrow_mut();
state.ximc = Some(ximc);
state.xim_handler = Some(xim_handler);
drop(state);
if let Some(event) = xim_callback_event {
self.handle_xim_callback_event(event);
}
if xim_filtered {
continue;
}
if xim_connected {
self.xim_handle_event(event);
} else {
self.handle_event(event);
}
}
}
fn handle_event(&self, event: Event) -> Option<()> {
match event {
Event::ClientMessage(event) => {