Skip to content

fix(waylib): keep shortcut-consumed keys out of keyboard enter - #1454

Open
gugullll wants to merge 1 commit into
linuxdeepin:masterfrom
gugullll:fix/altTab
Open

gugullll wants to merge 1 commit into
linuxdeepin:masterfrom
gugullll:fix/altTab

Conversation

@gugullll

@gugullll gugullll commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

A key matching a registered shortcut is consumed by shortcut handling and never reaches the client. Exclude such keys from wl_keyboard.enter and swallow their release unconditionally, so the client never learns the key was held and never receives an orphan release.

命中已注册快捷键的按键会被快捷键处理消费,不会下发给客户端。
因此 enter 的按键集合需剔除这类按键,并无条件吞掉其 release;
客户端既不会误判该键按下,也不会收到孤立 release。

Log: 修复 Alt+Tab 切换后客户端持续收到 Tab 的问题
Influence: Alt+Tab 等快捷键切换焦点时,客户端不再从 enter 得知被快捷键消费的 按键,且这些按键的 release 无条件镜像吞掉,不会出现孤立 release。

Summary by Sourcery

Keep keys consumed by shortcut handling out of client keyboard state during focus transitions.

Bug Fixes:

  • Prevent shortcut-consumed key presses from being advertised during keyboard focus entry and swallow their releases to avoid stuck keys and orphan release events reaching clients.

Enhancements:

  • Track consumed shortcut keys across focus changes and reset the tracking state when the seat is destroyed.

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: gugullll

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Sep 30, 2026

Copy link
Copy Markdown

Reviewer's Guide

Prevents shortcut-consumed keys such as Alt+Tab from being advertised to the newly focused client during wl_keyboard.enter, while swallowing their later releases to avoid stuck keys and orphan key-release events.

Sequence diagram for shortcut-consumed key focus switching

sequenceDiagram
    participant Keyboard
    participant Helper
    participant ShortcutManager
    participant WSeat
    participant Client

    Keyboard->>Helper: KeyPress(nativeVirtualKey)
    Helper->>WSeat: suppressKey(evdevKeycode)
    Helper->>ShortcutManager: dispatchKeyEvent(kevent)
    ShortcutManager->>WSeat: setKeyboardFocusWindow(newWindow)
    WSeat->>WSeat: filterSuppressedKeycodes(keycodes, suppressedKeycodes, visibleKeycodes)
    WSeat->>Client: wl_keyboard.enter(visibleKeycodes)
    Keyboard->>Helper: KeyRelease(nativeVirtualKey)
    Helper->>WSeat: isKeySuppressed(evdevKeycode)
    Helper->>WSeat: unsuppressKey(evdevKeycode)
    Helper-->>Keyboard: swallow release
Loading

File-Level Changes

Change Details Files
Track shortcut-consumed key presses and unconditionally swallow their releases.
  • Record keycodes before shortcut dispatch so synchronous focus changes see the suppression state.
  • Drop releases for suppressed keys even if the shortcut combination no longer matches.
  • Remove suppression when a press is not consumed or after a consumed release.
src/seat/helper.cpp
Filter suppressed keys from keyboard focus-enter notifications.
  • Maintain a seat-level set of suppressed evdev keycodes.
  • Filter the key list passed to wl_keyboard.enter.
  • Clear suppression state when the seat is destroyed.
waylib/src/server/kernel/wseat.cpp
waylib/src/server/kernel/wseat.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/seat/helper.cpp" line_range="2823-2826" />
<code_context>
+        // the shortcut table (e.g. a modifier was released first). The client
+        // never saw that key pressed, so forwarding its release would hand it an
+        // orphan release.
+        const uint32_t evdevKeycode = kevent->nativeVirtualKey();
+        if (kevent->type() == QEvent::KeyRelease && seat->isKeySuppressed(evdevKeycode)) {
+            seat->unsuppressKey(evdevKeycode);
+            return true;
+        }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** The synthetic auto-repeat release generated by `WSeatPrivate` clears suppression for a shortcut-consumed key even though the physical key is still held. Subsequent focus changes can therefore include that still-held key in `wl_keyboard.enter`, recreating the stuck-key/orphan-state bug.

**Triggers:** When a consumed shortcut key is held long enough for the seat's auto-repeat timer to generate its press/release pair.

**Suggested fix:** Do not treat auto-repeat releases as the physical release that clears suppression; clear the entry only on the real key release, or track physical key state separately.
</issue_to_address>

### Comment 2
<location path="src/seat/helper.cpp" line_range="2818-2826" />
<code_context>
             }
         }

+        // A release for a key whose press was consumed by a registered shortcut
+        // must be swallowed too, no matter whether the combination still matches
+        // the shortcut table (e.g. a modifier was released first). The client
+        // never saw that key pressed, so forwarding its release would hand it an
+        // orphan release.
+        const uint32_t evdevKeycode = kevent->nativeVirtualKey();
+        if (kevent->type() == QEvent::KeyRelease && seat->isKeySuppressed(evdevKeycode)) {
+            seat->unsuppressKey(evdevKeycode);
+            return true;
+        }
+
</code_context>
<issue_to_address>
**issue (broader_impact):** The early Meta/Super handling returns before `suppressKey` runs, so a shortcut using Super/Meta does not mark that modifier as consumed. If the shortcut switches focus, the modifier remains in the keyboard enter key set even though its press was consumed, and its release can be swallowed as an orphan-prevention release.

**Triggers:** When a registered shortcut uses `Qt::Key_Meta`, `Qt::Key_Super_L`, or `Qt::Key_Super_R` and synchronously changes keyboard focus.

**Suggested fix:** Integrate the Meta/Super path with the suppression bookkeeping, marking the modifier press before any shortcut-triggered focus change and clearing it on the corresponding physical release.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/seat/helper.cpp
Comment on lines +2823 to +2826
const uint32_t evdevKeycode = kevent->nativeVirtualKey();
if (kevent->type() == QEvent::KeyRelease && seat->isKeySuppressed(evdevKeycode)) {
seat->unsuppressKey(evdevKeycode);
return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): The synthetic auto-repeat release generated by WSeatPrivate clears suppression for a shortcut-consumed key even though the physical key is still held. Subsequent focus changes can therefore include that still-held key in wl_keyboard.enter, recreating the stuck-key/orphan-state bug.

Triggers: When a consumed shortcut key is held long enough for the seat's auto-repeat timer to generate its press/release pair.

Suggested fix: Do not treat auto-repeat releases as the physical release that clears suppression; clear the entry only on the real key release, or track physical key state separately.

Comment thread src/seat/helper.cpp
Comment on lines +2818 to +2826
// A release for a key whose press was consumed by a registered shortcut
// must be swallowed too, no matter whether the combination still matches
// the shortcut table (e.g. a modifier was released first). The client
// never saw that key pressed, so forwarding its release would hand it an
// orphan release.
const uint32_t evdevKeycode = kevent->nativeVirtualKey();
if (kevent->type() == QEvent::KeyRelease && seat->isKeySuppressed(evdevKeycode)) {
seat->unsuppressKey(evdevKeycode);
return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (broader_impact): The early Meta/Super handling returns before suppressKey runs, so a shortcut using Super/Meta does not mark that modifier as consumed. If the shortcut switches focus, the modifier remains in the keyboard enter key set even though its press was consumed, and its release can be swallowed as an orphan-prevention release.

Triggers: When a registered shortcut uses Qt::Key_Meta, Qt::Key_Super_L, or Qt::Key_Super_R and synchronously changes keyboard focus.

Suggested fix: Integrate the Meta/Super path with the suppression bookkeeping, marking the modifier press before any shortcut-triggered focus change and clearing it on the corresponding physical release.

A key matching a registered shortcut is consumed by shortcut handling and
never reaches the client. Exclude such keys from wl_keyboard.enter and
swallow their release unconditionally, so the client never learns the key
was held and never receives an orphan release.

命中已注册快捷键的按键会被快捷键处理消费,不会下发给客户端。
因此 enter 的按键集合需剔除这类按键,并无条件吞掉其 release;
客户端既不会误判该键按下,也不会收到孤立 release。

Log: 修复 Alt+Tab 切换后客户端持续收到 Tab 的问题
Influence: Alt+Tab 等快捷键切换焦点时,客户端不再从 enter 得知被快捷键消费的
按键,且这些按键的 release 无条件镜像吞掉,不会出现孤立 release。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants