X11: Sync key press/release with window focus#1296
X11: Sync key press/release with window focus#1296murarth merged 4 commits intorust-windowing:masterfrom
Conversation
* When a window loses focus, key release events are issued for all pressed keys * When a window gains focus, key press events are issued for all pressed keys * Adds `is_synthetic` field to `WindowEvent` variant `KeyboardInput` to indicate that these events are synthetic. * Adds `is_synthetic: false` to `WindowEvent::KeyboardInput` events issued on all other platforms
There was a problem hiding this comment.
So, under i3wm there are some unfortunate focus issues that make this operate in an unexpected manner.
It's hard to spot, but becomes more obvious while testing this PR as it leads to the synthetic key events to get repeated.
For context, the window is on workspace 5 and my terminal on workspace 4. Logo-5 moves your focus to workspace 5, Logo-4 moves it to workspace 4.
More precisely, when Logo-<num> is pressed, ALL windows lose focus, the cursor is moved onto the appropriate workstation and the window that will become focused is highlighted.
The window that will become focused, however, only gains it's focus when the <num> key is released. Releasing the Logo key has no effect.
Here we have the window in focus. We hold Logo then 4. We receive two focus lost events. Upon releasing the 4 we receive a third. This is likely just i3wm being janky and reporting multiple focus lost events to the client that just lost focus- once when it sends it to all clients, and twice for some odd reason to the specific client that lost focus.
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167648, tv_nsec: 411933429 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Key(KeyboardInput { scancode: 125, state: Pressed, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } }) }
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: ModifiersChanged { modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 125, state: Pressed, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: false } }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167653, tv_nsec: 272226669 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Key(KeyboardInput { scancode: 5, state: Pressed, virtual_keycode: Some(Key4), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }) }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 5, state: Released, virtual_keycode: Some(Key4), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: true } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 125, state: Released, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: true } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: Focused(false) }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167654, tv_nsec: 194180513 }, requested_resume: None })
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 5, state: Released, virtual_keycode: Some(Key4), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: true } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 125, state: Released, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: true } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: Focused(false) }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167654, tv_nsec: 201562658 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Key(KeyboardInput { scancode: 5, state: Released, virtual_keycode: Some(Key4), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }) }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 125, state: Released, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: true } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: Focused(false) }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: CursorLeft { device_id: DeviceId(X(DeviceId(2))) } }
EventsCleared
However, consider the following: when the window is regaining focus, we receive two focus gained events- one before the <num> key is released and one after. The first focus event actually arrives before the window has gained focus! I tested the behavior out in some other applications: firefox seams to recognize that they have not gained focus, but riot-desktop, keepassxc, file-roller, and xfce4-terminal do not.
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167884, tv_nsec: 50759782 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Key(KeyboardInput { scancode: 125, state: Pressed, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }) }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167886, tv_nsec: 682480255 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Key(KeyboardInput { scancode: 6, state: Pressed, virtual_keycode: Some(Key5), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }) }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167889, tv_nsec: 258764883 }, requested_resume: None })
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167889, tv_nsec: 267693397 }, requested_resume: None })
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: Focused(true) }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: CursorMoved { device_id: DeviceId(X(DeviceId(2))), position: LogicalPosition { x: -919.3846153846155, y: 488.3076923076923 }, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 6, state: Pressed, virtual_keycode: Some(Key5), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: true } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 125, state: Pressed, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: true } }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167889, tv_nsec: 270401893 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Key(KeyboardInput { scancode: 6, state: Released, virtual_keycode: Some(Key5), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }) }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: Focused(true) }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: CursorMoved { device_id: DeviceId(X(DeviceId(2))), position: LogicalPosition { x: -919.3846153846155, y: 488.3076923076923 }, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 125, state: Pressed, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }, is_synthetic: true } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: CursorEntered { device_id: DeviceId(X(DeviceId(2))) } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: CursorMoved { device_id: DeviceId(X(DeviceId(2))), position: LogicalPosition { x: 884.3076923076924, y: 475.3846153846154 }, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: CursorMoved { device_id: DeviceId(X(DeviceId(2))), position: LogicalPosition { x: 884.3076923076924, y: 475.3846153846154 }, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: AxisMotion { device_id: DeviceId(X(DeviceId(2))), axis: 0, value: 2880.0 } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: AxisMotion { device_id: DeviceId(X(DeviceId(2))), axis: 1, value: 539.0 } }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2167894, tv_nsec: 276126253 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Key(KeyboardInput { scancode: 125, state: Released, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: true } }) }
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: ModifiersChanged { modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } }
WindowEvent { window_id: WindowId(X(WindowId(71303169))), event: KeyboardInput { device_id: DeviceId(X(DeviceId(3))), input: KeyboardInput { scancode: 125, state: Released, virtual_keycode: None, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } }, is_synthetic: false } }
EventsCleared
Anyways, as it stands, these synthetic errors are getting duplicated multiple times. I'll leave it up to you to decide if that's okay.
|
@goddessfreya If there are no further issues with this PR, can I get an explicit approval? |
rust-windowing generates synthetic key press and release events to try and work around issues with keys being pressed and held while window focus is lost, see rust-windowing/winit#1296 However the Ubuntu workspace keyboard shortcut (and others) is causing synthetic Pressed events, without the corresponding Release event. Ignoring synthetic key pressed events, in addition to the existing behaviour of releasing all keys when window focus is lost (feenkcom/gtoolkit#423) resolves the issue. Fixes: feenkcom/gtoolkit#1178
Metacello new
baseline: 'GToolkit';
repository: 'github://feenkcom/gtoolkit:v0.7.1088/src';
load
All commits (inlcuding upstream repositories) since last build:
feenkcom/gtoolkit-glutin@55656e by Alistair Grant
Ignore synthetic key Pressed events
rust-windowing generates synthetic key press and release events to try and work around issues with keys being pressed and held while window focus is lost, see rust-windowing/winit#1296
However the Ubuntu workspace keyboard shortcut (and others) is causing synthetic Pressed events, without the corresponding Release event.
Ignoring synthetic key pressed events, in addition to the existing behaviour of releasing all keys when window focus is lost (#423) resolves the issue.
Fixes: #1178
is_syntheticfield toWindowEventvariantKeyboardInputto indicate that these events are synthetic.
is_synthetic: falsetoWindowEvent::KeyboardInputevents issuedon all other platforms
Addresses #1272 (but doesn't close, as that issue affects other platforms).
cargo fmthas been run on this branchcargo docbuilds successfullyCHANGELOG.mdif knowledge of this change could be valuable to users