Skip to content

fix: restore grab-resilient X11 shortcuts - #127

Open
yixinshark wants to merge 1 commit into
linuxdeepin:masterfrom
yixinshark:fix/x11-grab-resilient-shortcuts
Open

fix: restore grab-resilient X11 shortcuts#127
yixinshark wants to merge 1 commit into
linuxdeepin:masterfrom
yixinshark:fix/x11-grab-resilient-shortcuts

Conversation

@yixinshark

@yixinshark yixinshark commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restore X RECORD handling for legacy grab-resilient screenshot, screen recorder, and standalone modifier shortcuts
  • retain XGrabKey/XCB and XI2 fallback paths while preventing duplicate activation across event streams
  • add lifecycle, fallback, active-grab, policy, and parallel test coverage

Test plan

  • shortcut CTest suite: 9/9 passed
  • parallel CTest -j9: passed 5 consecutive runs
  • clean DISPLAY=:0 dpkg-buildpackage -b -us -uc: passed
  • runtime X11 validation with taskbar/desktop context menus: Win, CapsLock, and screenshot shortcuts passed

Pms: BUG-372643

Summary by Sourcery

Restore grab-resilient X11 shortcut handling while keeping XInput2 as a fallback path and preventing duplicate activations across event streams.

Bug Fixes:

  • Re-enable legacy grab-resilient shortcuts (e.g. screenshots, screen recorder, standalone modifiers) on X11, including when other clients hold keyboard grabs.
  • Prevent duplicate shortcut activation by coordinating between X RECORD and XInput2/XGrabKey event streams.
  • Ensure CapsLock and NumLock modifiers are correctly recognized and handled in X11 shortcut processing.

Enhancements:

  • Introduce an X11 shortcut policy module to explicitly mark legacy grab-resilient shortcuts and route them through the appropriate transport.
  • Extend the modifier key monitor to support both X RECORD and XInput2 transports with dynamic fallback and improved lifecycle management.
  • Improve X11 shortcut handler state tracking for presses, repeats, and releases across both XCB and RECORD inputs.

Build:

  • Link the shortcut plugin and tests against the XTest (XTST) library and wire up new X11-related test binaries in CMake.

Tests:

  • Add unit and integration tests for X11 shortcut policy, RECORD-based modifier monitoring, and grab-resilient shortcut behavior, including lifecycle, fallback, and parallel execution coverage.

Restore the legacy X RECORD path for screenshot, screen recorder, and standalone modifier shortcuts while another X11 client owns an active keyboard grab.
Keep passive XGrabKey and XI2 fallback paths available, isolate RECORD lifecycle and keymap connections, and deduplicate XCB/RECORD events.
Add policy, RECORD lifecycle, fallback, active-grab, and parallel test coverage.

恢复旧版 X RECORD 路径,使截图、录屏和独立修饰键快捷键在其他 X11 客户端持有主动键盘 grab 时仍可使用。
保留被动 XGrabKey 和 XI2 回退路径,隔离 RECORD 生命周期与键盘映射连接,并对 XCB/RECORD 事件去重。
增加策略、RECORD 生命周期、回退、主动 grab 和并行测试覆盖。

Log: restore grab-resilient X11 shortcuts
Pms: BUG-372643
Change-Id: I9498c98fe60a953447beedda08e4e916ac493247

@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.

Sorry @yixinshark, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: yixinshark

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 Aug 5, 2026

Copy link
Copy Markdown

Reviewer's Guide

Restores X RECORD-based grab-resilient X11 shortcut handling with a dual-transport (RECORD/XI2) modifier monitor, integrates it with X11KeyHandler to avoid duplicate activations across RECORD and XCB streams, adds explicit policy for legacy grab-resilient shortcuts, and introduces targeted X11 integration and unit tests plus build dependencies for RECORD/XTest.

Sequence diagram for grab-resilient shortcut activation via X RECORD

sequenceDiagram
    participant X11Server
    participant ModifierKeyMonitor
    participant X11KeyHandler
    participant X11ShortcutPolicy

    X11KeyHandler->>ModifierKeyMonitor: ModifierKeyMonitor(parent, allowRecord=true)
    ModifierKeyMonitor->>ModifierKeyMonitor: initializeRecord()
    alt RECORD available
        ModifierKeyMonitor->>ModifierKeyMonitor: m_transport = Record
    else RECORD unavailable
        ModifierKeyMonitor->>ModifierKeyMonitor: fallbackToXInput2()
    end

    X11KeyHandler->>X11ShortcutPolicy: isLegacyGrabResilientShortcut(shortcutId)
    X11ShortcutPolicy-->>X11KeyHandler: true
    X11KeyHandler->>X11KeyHandler: m_recordShortcutIds.insert(shortcutId)

    X11Server-->>ModifierKeyMonitor: RECORD key event
    ModifierKeyMonitor-->>X11KeyHandler: keyEventRecorded(pressed, keycode, state, time)

    X11KeyHandler->>X11KeyHandler: onRecordedKeyEvent(pressed=true,...)
    alt shortcutId in m_recordShortcutIds
        X11KeyHandler->>X11KeyHandler: m_recordPressedBindings.insert(keycode, shortcutId)
        X11KeyHandler->>X11KeyHandler: activate(shortcutId, Press)
    else non-legacy or claimed by XCB
        X11KeyHandler-->>X11KeyHandler: ignore to avoid duplicate
    end

    X11Server-->>X11KeyHandler: XCB KeyPress
    X11KeyHandler->>X11KeyHandler: handleKeyPress()
    alt key already in m_recordPressedBindings or m_recordObservedPresses
        X11KeyHandler-->>X11KeyHandler: ignore XCB event
    else
        X11KeyHandler->>X11KeyHandler: activate(shortcutId, Press)
    end

    X11Server-->>ModifierKeyMonitor: RECORD KeyRelease
    ModifierKeyMonitor-->>X11KeyHandler: keyEventRecorded(pressed=false,...)
    X11KeyHandler->>X11KeyHandler: onRecordedKeyEvent(pressed=false,...)
    X11KeyHandler->>X11KeyHandler: flushRecordedPendingReleases()
    X11KeyHandler->>X11KeyHandler: activate(shortcutId, Release)
Loading

File-Level Changes

Change Details Files
Introduce dual-transport X11 modifier key monitor with X RECORD primary path and XI2 fallback, including lifecycle, keymap refresh, and failure handling.
  • Add X RECORD-based transport to ModifierKeyMonitor with control/data X11 connections, context creation, enable/disable and state machine for RECORD epochs.
  • Retain and refactor existing XInput2 raw event path as a fallback transport, with clearer logging, root window discovery, and error handling.
  • Expose runtime state helpers (isRunning, supportsGrabResilientEvents, refreshKeyboardMapping) and manage resources/cleanup across both transports.
  • Implement RECORD data callback handling, translating X11 events into modifier state updates and a new keyEventRecorded signal while emitting modifierKeyReleased as before.
  • Extend modifier key detection to include CapsLock and NumLock and ensure key symbol usage is guarded against null key symbol tables.
src/plugin-qt/shortcut/src/backend/x11/modifierkeymonitor.cpp
src/plugin-qt/shortcut/src/backend/x11/modifierkeymonitor.h
Integrate X RECORD stream with X11KeyHandler so legacy grab-resilient shortcuts fire exactly once without duplication with XGrabKey/XCB handling.
  • Track which shortcuts are designated legacy grab-resilient via a new X11ShortcutPolicy helper and register them for RECORD-based handling when supported.
  • Wire ModifierKeyMonitor::keyEventRecorded into X11KeyHandler, maintaining parallel pressed/release maps and observed-press timestamps for the RECORD stream.
  • Implement conflict resolution between XCB and RECORD streams so only one stream owns a given key sequence, handling press, repeat, and release ordering and RECORD restart windows.
  • Extend logical modifier handling to cover CapsLock/NumLock and refresh the modifier monitor’s keymap when X11 keymap changes.
  • Ensure unregister and capture paths clear both XCB and RECORD tracking maps to avoid stale state and spurious activations.
src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.cpp
src/plugin-qt/shortcut/src/backend/x11/x11keyhandler.h
src/plugin-qt/shortcut/src/backend/x11/x11shortcutpolicy.cpp
src/plugin-qt/shortcut/src/backend/x11/x11shortcutpolicy.h
Add X11 test coverage for shortcut policy, RECORD-based modifier monitoring, and grab-resilient shortcut behavior, plus required build wiring and dependencies.
  • Introduce unit tests for X11ShortcutPolicy to validate the set of legacy grab-resilient shortcut IDs.
  • Add integration tests for ModifierKeyMonitor verifying RECORD availability, XI2 fallback behavior, keymap refresh behavior, and capture boundary semantics under active X11 grabs using XTest.
  • Add integration tests for grab-resilient shortcuts ensuring legacy shortcuts activate during active keyboard grabs and that XCB fallback during RECORD restarts still produces exactly one activation.
  • Wire new tests into the shortcut CTest suite, including executable targets, include paths, library links (X11, XCB, XInput, XTest/XTST, QtTest), and define a resource lock so X11 grab-based tests don’t contend in parallel runs.
  • Update the main shortcut plugin CMake to link against XTST and include its headers for both library and debug builds.
src/plugin-qt/shortcut/tests/CMakeLists.txt
src/plugin-qt/shortcut/tests/tst_x11recordmonitor.cpp
src/plugin-qt/shortcut/tests/tst_x11grabresilientshortcuts.cpp
src/plugin-qt/shortcut/tests/tst_x11shortcutpolicy.cpp
src/plugin-qt/shortcut/CMakeLists.txt

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

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:98分

■ 【总体评价】

代码完美实现了X11 RECORD扩展的双通道去重与状态同步,解决了键盘抓取场景下快捷键失效的复杂问题
逻辑严密且测试覆盖充分,仅因极微小的析构冗余操作扣除2分

■ 【详细分析】

  • 1.语法逻辑(基本正确)✓

modifierkeymonitor.cppinitializeRecord函数中,若XRecordQueryVersionxcb_connect失败,函数会提前返回false,此时部分已分配的资源(如m_controlDisplay)未在函数内部立即释放。但由于构造函数在initializeRecord返回false后必定调用cleanupRecordResources(),形成了安全的兜底清理,不存在内存泄漏。在x11keyhandler.cpponRecordedKeyEvent中,通过比对m_xcbObservedPresses的时间戳与当前事件时间戳,精准解决了RECORD重启期间XCB通道抢占序列的所有权归属问题,状态机转换严密无漏洞。
建议:在initializeRecord失败路径的返回前添加注释,明确指出资源清理由调用方cleanupRecordResources接管,以提升可读性

  • 2.代码质量(优秀)✓

新增的X11ShortcutPolicy模块采用静态QSet实现白名单匹配,符合单一职责原则,将策略与底层事件分发完全解耦。变量命名极具自解释性,如m_recordPendingReleasesm_xcbObservedPresses清晰表达了双通道去重的业务语义。注释质量极高,例如在enableContext中解释了为何在进入Xlib前必须修改状态,在handleRecordedData中说明了为何在EndOfData时必须销毁并重建数据连接。测试用例设计专业,特别是利用set_tests_properties(PROPERTIES RESOURCE_LOCK x11_keyboard_grab)解决了并行测试中的键盘抓取互斥问题。
建议:无

  • 3.代码性能(高效)✓

isLegacyGrabResilientShortcut使用static const QSet避免了每次快捷键注册时的哈希表构造开销。onRecordedKeyEvent中大量使用QMap::constFind进行O(log n)的查找,对于系统级快捷键的极小数据集而言开销趋近于零。flushRecordedPendingReleases采用先拷贝Key列表再清空原Map再遍历的策略,在保证异常安全的同时没有引入额外的内存分配负担。X RECORD协议本身虽然会引入全局限流,但仅针对白名单内的7个遗留快捷键启用,对整体系统性能影响微乎其微。
建议:无

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
代码在处理XRecordFromServer类别数据时,严格通过offset + 32 <= byteCount进行边界检查,彻底杜绝了越界读取风险。Lambda回调中通过reinterpret_cast转换closure指针后进行了空指针校验。析构函数中严格按照依赖关系依次禁用RECORD上下文、断开XCB连接、关闭Display,防止了悬空指针回调。X RECORD虽然具备监听全局输入的能力,但此为系统快捷键守护进程的合法权限需求,不属于代码引入的越权漏洞。
建议:无

■ 【改进建议代码示例】

// modifierkeymonitor.cpp:在 initializeRecord 失败路径增加明确的所有权交接注释
bool ModifierKeyMonitor::initializeRecord()
{
    m_controlDisplay = XOpenDisplay(nullptr);
    if (!m_controlDisplay) {
        qCWarning(logShortcut) << "ModifierMonitor: failed to connect to X server for RECORD";
        return false;
    }

    int major = 0;
    int minor = 0;
    if (!XRecordQueryVersion(m_controlDisplay, &major, &minor)) {
        qCWarning(logShortcut) << "ModifierMonitor: X RECORD extension is unavailable";
        // Resource ownership of m_controlDisplay is transferred to and cleaned up by cleanupRecordResources()
        return false;
    }

    m_keyConnection = xcb_connect(nullptr, nullptr);
    if (!m_keyConnection || xcb_connection_has_error(m_keyConnection)) {
        qCWarning(logShortcut) << "ModifierMonitor: failed to open keymap connection";
        // Resource ownership of m_controlDisplay is transferred to and cleaned up by cleanupRecordResources()
        return false;
    }
    m_keySymbols = xcb_key_symbols_alloc(m_keyConnection);
    if (!m_keySymbols) {
        qCWarning(logShortcut) << "ModifierMonitor: failed to initialize RECORD key symbols";
        return false;
    }

    if (!createContext())
        return false;

    qCInfo(logShortcut) << "ModifierMonitor: X RECORD" << major << minor << "available";
    return true;
}

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