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

Phantom key presses on window focus change (alt+tab, etc) #13299

Closed
hut opened this issue May 9, 2024 · 3 comments · Fixed by #13696
Closed

Phantom key presses on window focus change (alt+tab, etc) #13299

hut opened this issue May 9, 2024 · 3 comments · Fixed by #13696
Labels
A-Input Player input via keyboard, mouse, gamepad, and more C-Bug An unexpected or incorrect behavior O-Linux Specific to the Linux desktop operating system

Comments

@hut
Copy link
Contributor

hut commented May 9, 2024

Bevy version

0.13.2

Relevant system information

ArchLinux with i3wm window manager alongside KDE using X11

What you did

I press Alt+Tab to change focus to my bevy application.

What went wrong

I expected this to change focus to my application, and nothing else.

What happend: It caused a key press event of the TAB key and triggered the application action associated with the TAB key.

This happens with any key that is used for focus switching. For example, if I use my window manager's "alt+w" key binding to switch to the workspace of the game, it registers as a key press of the "w" key, which causes my character to unintentionally move forward.

Worse yet, those keys get stuck in a "permanently pressed" state, triggering the actions forever, until I press the respective key once again.

This is driving me crazy as I switch focus back/forth between my bevy application a lot.

Additional information / Cause

This is caused by bevy erroneously interpreting winit's "synthetic" key presses as real key presses.

If I add a check for is_synthetic in bevy's winit KeyboardInput event handler:

WindowEvent::KeyboardInput { ref event, is_synthetic, .. } => {
    if !is_synthetic {
        if event.state.is_pressed() {
            if let Some(char) = &event.text {
                let char = char.clone();
                app.send_event(ReceivedCharacter { window, char });
            }
        }
        app.send_event(converters::convert_keyboard_input(event, window));
    }
}

then the phantom key press events disappear in my application.

This is likely not the full solution though. While the phantom key press events are a clear bug, the phantom key release events might serve a purpose and possibly should be exposed. See rust-windowing/winit#3543 (comment)

If you decide to not filter out synthetic keys, please at least expose the fact that they are synthetic to the user so we can filter them out ourselves. This would be suboptimal solution, but better than nothing.

If I can help in any way with solving this, don't hesitate to ask.

Thank you.

P.S. here's an analogous fix in another UI framework: lapce/floem#387

@hut hut added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels May 9, 2024
@james7132 james7132 added A-Input Player input via keyboard, mouse, gamepad, and more O-Linux Specific to the Linux desktop operating system and removed S-Needs-Triage This issue needs to be labelled labels May 12, 2024
@hut
Copy link
Contributor Author

hut commented May 21, 2024

Another analogous PR in the UI framework egui: emilk/egui#4513

I would happily make a PR to fix this in bevy. Just say the word.

@SpecificProtagonist
Copy link
Contributor

SpecificProtagonist commented Jun 4, 2024

Thanks for your investigation! A PR would be great. Ignoring synthetic events should fix #12273 as well (EDIT: only if #12878 doesn't get merged) (see also #12372).

There are multiple related issues around how keys behave when focus is lost/gained – behavior differs between platforms (and keys). Documenting that would be great, but doesn't need to happen in this PR.

@alice-i-cecile
Copy link
Member

Seconding the request for a PR :) Just make sure to branch on top of #13678, which is the adopted form of the PR linked above.

hut added a commit to hut/bevy that referenced this issue Jun 5, 2024
On Linux/X11, changing focus into a winit window will produce winit
KeyboardInput events with a "is_synthetic=true" flag that are not
intended to be used. Bevy erroneously passes them on to the user,
resulting in phantom key presses.

This patch properly filters them out.

For example, pressing Alt+Tab to focus a bevy winit window results in a
permanently stuck Tab key until the user presses Tab once again to
produce a winit KeyboardInput release event.

The Tab key press event that causes this problem is "synthetic", should
not be used according to the winit devs, and simply ignoring it fixes
this problem.

Reference: https://docs.rs/winit/0.30.0/winit/event/enum.WindowEvent.html#variant.KeyboardInput.field.is_synthetic
Relevant discussion: rust-windowing/winit#3543

Synthetic key **releases** are still evaluated though, as they are
essential for correct release key handling. For example, if the user
binds the key combination Alt+1 to the action "move the window to
workspace 1", places the bevy game in workspace 2, focuses the game and
presses Alt+1, then the key release event for the "1" key will be
synthetic. If we would filter out all synthetic keys, the bevy game
would think that the 1 key remains pressed forever, until the user
manually presses+releases the key again inside bevy.
hut added a commit to hut/bevy that referenced this issue Jun 5, 2024
On Linux/X11, changing focus into a winit window will produce winit
KeyboardInput events with a "is_synthetic=true" flag that are not
intended to be used. Bevy erroneously passes them on to the user,
resulting in phantom key presses.

This patch properly filters them out.

For example, pressing Alt+Tab to focus a bevy winit window results in a
permanently stuck Tab key until the user presses Tab once again to
produce a winit KeyboardInput release event.

The Tab key press event that causes this problem is "synthetic", should
not be used according to the winit devs, and simply ignoring it fixes
this problem.

Reference: https://docs.rs/winit/0.30.0/winit/event/enum.WindowEvent.html#variant.KeyboardInput.field.is_synthetic
Relevant discussion: rust-windowing/winit#3543

Synthetic key **releases** are still evaluated though, as they are
essential for correct release key handling. For example, if the user
binds the key combination Alt+1 to the action "move the window to
workspace 1", places the bevy game in workspace 2, focuses the game and
presses Alt+1, then the key release event for the "1" key will be
synthetic. If we would filter out all synthetic keys, the bevy game
would think that the 1 key remains pressed forever, until the user
manually presses+releases the key again inside bevy.
github-merge-queue bot pushed a commit that referenced this issue Jun 17, 2024
# Objective

Fixes #13299

On Linux/X11, changing focus into a winit window will produce winit
KeyboardInput events with a "is_synthetic=true" flag that are not
intended to be used. Bevy erroneously passes them on to the user,
resulting in phantom key presses.

## Solution

This patch properly filters out winit KeyboardInput events with
"is_synthetic=true".

For example, pressing Alt+Tab to focus a bevy winit window results in a
permanently stuck Tab key until the user presses Tab once again to
produce a winit KeyboardInput release event. The Tab key press event
that causes this problem is "synthetic", should not be used according to
the winit devs, and simply ignoring it fixes this problem.

Synthetic key **releases** are still evaluated though, as they are
essential for correct release key handling. For example, if the user
binds the key combination Alt+1 to the action "move the window to
workspace 1", places the bevy game in workspace 2, focuses the game and
presses Alt+1, then the key release event for the "1" key will be
synthetic. If we would filter out all synthetic keys, the bevy game
would think that the 1 key remains pressed forever, until the user
manually presses+releases the key again inside bevy.

Reference:
https://docs.rs/winit/0.30.0/winit/event/enum.WindowEvent.html#variant.KeyboardInput.field.is_synthetic
Relevant discussion: rust-windowing/winit#3543

## Testing

Tested with the "keyboard_input_events" example. Entering/exiting the
window with various keys, as well as changing its workspace, produces
the correct press/release events.
mockersf pushed a commit that referenced this issue Jun 19, 2024
# Objective

Fixes #13299

On Linux/X11, changing focus into a winit window will produce winit
KeyboardInput events with a "is_synthetic=true" flag that are not
intended to be used. Bevy erroneously passes them on to the user,
resulting in phantom key presses.

## Solution

This patch properly filters out winit KeyboardInput events with
"is_synthetic=true".

For example, pressing Alt+Tab to focus a bevy winit window results in a
permanently stuck Tab key until the user presses Tab once again to
produce a winit KeyboardInput release event. The Tab key press event
that causes this problem is "synthetic", should not be used according to
the winit devs, and simply ignoring it fixes this problem.

Synthetic key **releases** are still evaluated though, as they are
essential for correct release key handling. For example, if the user
binds the key combination Alt+1 to the action "move the window to
workspace 1", places the bevy game in workspace 2, focuses the game and
presses Alt+1, then the key release event for the "1" key will be
synthetic. If we would filter out all synthetic keys, the bevy game
would think that the 1 key remains pressed forever, until the user
manually presses+releases the key again inside bevy.

Reference:
https://docs.rs/winit/0.30.0/winit/event/enum.WindowEvent.html#variant.KeyboardInput.field.is_synthetic
Relevant discussion: rust-windowing/winit#3543

## Testing

Tested with the "keyboard_input_events" example. Entering/exiting the
window with various keys, as well as changing its workspace, produces
the correct press/release events.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Input Player input via keyboard, mouse, gamepad, and more C-Bug An unexpected or incorrect behavior O-Linux Specific to the Linux desktop operating system
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants