Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X11: Sync key press/release with window focus #1296

Merged
merged 4 commits into from
Dec 7, 2019

Conversation

murarth
Copy link
Contributor

@murarth murarth commented Nov 30, 2019

  • 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

Addresses #1272 (but doesn't close, as that issue affects other platforms).

  • Tested on all platforms changed
  • Compilation warnings were addressed
  • cargo fmt has been run on this branch
  • cargo doc builds successfully
  • Added an entry to CHANGELOG.md if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality
  • Updated feature matrix, if new features were added or implemented

* 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
Copy link
Contributor

@goddessfreya goddessfreya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

src/platform_impl/linux/x11/event_processor.rs Outdated Show resolved Hide resolved
src/platform_impl/linux/x11/util/keys.rs Outdated Show resolved Hide resolved
@murarth
Copy link
Contributor Author

murarth commented Dec 7, 2019

@goddessfreya If there are no further issues with this PR, can I get an explicit approval?

@murarth murarth merged commit 35505a3 into rust-windowing:master Dec 7, 2019
@murarth murarth deleted the x11-focus-keys branch December 7, 2019 22:51
akgrant43 added a commit to feenkcom/gtoolkit-glutin that referenced this pull request Jun 20, 2020
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
georgeganea pushed a commit to feenkcom/gtoolkit that referenced this pull request Jun 20, 2020
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C - waiting on maintainer A maintainer must review this code DS - x11
Development

Successfully merging this pull request may close these issues.

2 participants