Skip to content

sync: from linuxdeepin/dde-session-shell#489

Merged
yixinshark merged 1 commit intomasterfrom
sync-pr-58-nosync
Feb 26, 2026
Merged

sync: from linuxdeepin/dde-session-shell#489
yixinshark merged 1 commit intomasterfrom
sync-pr-58-nosync

Conversation

@deepin-ci-robot
Copy link
Contributor

@deepin-ci-robot deepin-ci-robot commented Feb 26, 2026

Synchronize source files from linuxdeepin/dde-session-shell.

Source-pull-request: linuxdeepin/dde-session-shell#58

Summary by Sourcery

Improve fullscreen background and multi-screen lock frame positioning correctness and diagnostics, especially under X11.

Bug Fixes:

  • Address mismatches between Qt geometry and actual X11 window positions for fullscreen background and lock frames by validating and correcting geometry via XCB.

Enhancements:

  • Add X11/XCB integration and detailed logging for fullscreen background geometry updates and screen binding.
  • Add XCB-based verification and richer logging for multi-screen lock frame placement to aid debugging of positioning issues.

Synchronize source files from linuxdeepin/dde-session-shell.

Source-pull-request: linuxdeepin/dde-session-shell#58
@deepin-ci-robot
Copy link
Contributor Author

deepin pr auto review

这份代码 diff 主要针对多屏管理和全屏背景窗口在 X11/Wayland 混合环境下的位置同步问题进行了修复和增强。代码通过引入 XCB 直接查询 X Server 的窗口几何信息,并增加了详细的日志记录和强制刷新逻辑。

以下是对该 diff 的详细审查意见:

1. 语法与逻辑

  • 头文件包含与宏定义:

    • 问题: 在 multiscreenmanager.cppfullscreenbackground.cpp 中,使用了 #ifndef ENABLE_DSS_SNIPE 来区分 QX11Info 和 Qt 6 的新 NativeInterface。
    • 意见: 逻辑正确。这兼容了 Qt 5(使用 QX11Info)和 Qt 6(使用 QNativeInterface::QX11Application)。
    • 建议: 确保项目构建系统中 ENABLE_DSS_SNIPE 宏定义的一致性。
  • XCB 连接获取:

    • 问题: 代码中在 checkLockFrameLocation 和定时器回调中多次获取 xcb_connection_t
    • 意见: 逻辑正确,增加了对 Wayland 的判断(platformName().startsWith("wayland")),避免了在 Wayland 下调用 X11 接口。
    • 建议: 考虑到性能,如果调用频繁,可以将获取 connection 的逻辑封装成一个辅助函数,减少重复代码。
  • 坐标系转换逻辑:

    • 问题: 代码注释提到 "X11 下窗口位置不乘 DPR(与 RandR 物理坐标一致),窗口大小乘 DPR"。代码中 expectedPhysical 的计算逻辑是:位置用 screen->geometry()(逻辑坐标),宽高乘以 dpr
    • 审查: 这里存在一个潜在的逻辑矛盾或混淆。
      • Qt 的 QScreen::geometry() 通常返回的是逻辑像素坐标。
      • xcb_get_geometry 返回的是物理像素
      • 如果 X Server 的 RandR 坐标系是物理坐标(通常在高分屏下确实是),那么 expectedPhysicalxy 也应该乘以 dpr,或者 xcbGeometryxy 应该除以 dpr 再进行比较。
      • 当前代码中 expectedPhysical 的构造方式是:x, y 直接取逻辑值,width, height 取物理值。这意味着代码假设 xcb_get_geometry 返回的 x, y 是逻辑坐标,而 width, height 是物理坐标。
    • 建议: 请务必确认 X Server 在当前环境下的行为。通常 X11 的窗口位置也是物理像素。如果 xcb_get_geometry 返回的是物理坐标,那么 expectedPhysical 的计算方式应该修正为:
      QRect expectedPhysical(qRound(screen->geometry().x() * dpr),
                             qRound(screen->geometry().y() * dpr),
                             qRound(screen->geometry().width() * dpr),
                             qRound(screen->geometry().height() * dpr));
      如果注释中的假设是正确的(即位置是逻辑坐标,大小是物理坐标),则保持现状,但建议在注释中更详细地解释这种特殊行为的原因,或者引用相关文档。

2. 代码质量

  • 日志记录:

    • 优点: 增加了大量的 qCInfoqCWarning,这对于调试多屏显示问题非常有帮助。
    • 建议:
      • FullScreenBackground::setddeGeometry 中,日志非常详细,但在高频调用下可能会产生大量 I/O。建议在发布版本中通过宏或日志级别控制这些输出的开关。
      • checkLockFrameLocation 中,如果 reply 为空,打印了警告,这是好的做法。
  • 资源释放:

    • 优点: 使用了 free(reply) 释放 xcb_get_geometry_reply 返回的内存,防止内存泄漏。这点做得很好。
  • 魔法数字:

    • 问题: m_resetGeometryTimer->start(200); 中的 200ms 是硬编码的。
    • 建议: 建议将其定义为类常量或宏,例如 static const int GEOMETRY_RESET_INTERVAL = 200;,便于维护。

3. 代码性能

  • 同步 XCB 请求:

    • 问题: xcb_get_geometry_reply 是一个同步请求(它会阻塞等待 X Server 的响应)。
    • 影响: 在 UI 线程(通常是主线程)中执行同步 XCB 调用可能会导致界面卡顿,尤其是在网络延迟较高的 X 远程显示场景下。
    • 建议:
      • 如果这是一个定时器(m_resetGeometryTimer)触发的操作,频率较低(200ms),通常是可以接受的。
      • 如果 checkLockFrameLocation 被频繁调用(例如屏幕热插拔时),需要谨慎。
      • 优化方案:可以使用 XCB 的异步机制(cookie + 回调),但这会增加代码复杂度。鉴于这是为了修复显示 bug,目前的同步方案在保证正确性上更稳妥。
  • 重复计算:

    • 问题: 在 FullScreenBackground 的定时器 lambda 中,每次都重新获取 xcb_connection_t
    • 建议: 虽然开销不大,但可以优化。

4. 代码安全

  • 空指针检查:

    • 优点: 代码中对 screen, frame, windowHandle(), reply 等指针进行了非空检查,避免了空指针解引用崩溃。
    • 细节: 在 fullscreenbackground.cpp 中,if (!m_model->isUseWayland() && windowHandle()) 检查了 Wayland 和 windowHandle,随后才获取 connection,逻辑严密。
  • 强转安全:

    • 问题: static_cast<xcb_window_t>(frame->winId())
    • 意见: winId() 返回 WId(通常是 quint32unsigned long),强制转换为 xcb_window_t(通常是 uint32_t)在 X11 平台下是安全的。
  • 强制刷新逻辑的副作用:

    • 代码片段:
      setGeometry(0, 0, 0, 0);
      setGeometry(m_geometryRect);
    • 风险: 这种 "先设为0再设回目标值" 的 Hack 手法虽然能绕过 Qt 的几何缓存优化,强制发送 XCB ConfigureRequest 事件,但可能会导致屏幕闪烁。
    • 建议: 确认这是在检测到不匹配时的最后手段,且仅在必要时执行。目前的逻辑是在 xcbGeometry != expectedPhysical 时执行,条件判断是合理的。

总结与改进建议

这段代码有效地解决了 X11 环境下 Qt 窗口几何信息与 X Server 实际状态不一致的问题,通过引入 XCB 底层查询和强制刷新机制,增强了系统的鲁棒性。

主要改进建议:

  1. 验证 DPR 坐标转换逻辑: 重点关注 expectedPhysical 的计算。强烈建议测试在高分屏(DPR=2 或更高)下的表现,确认 xcb_get_geometry 返回的 x, y 究竟是逻辑坐标还是物理坐标。如果是物理坐标,必须修正 expectedPhysical 的计算公式,将 x, y 也乘以 dpr
  2. 封装 XCB Connection 获取: 将获取 xcb_connection_t 的代码提取为私有辅助函数 getXcbConnection(),减少重复代码。
  3. 常量化魔法数字: 将定时器间隔 200 定义为具名常量。
  4. 异步调用考量: 虽然目前同步调用可接受,但如果后续出现性能问题,应考虑异步化 XCB 请求。

修正后的坐标计算逻辑示例(假设 XCB 返回物理坐标):

// 假设 xcb_get_geometry 返回的是物理像素坐标
QRect xcbGeometry(reply->x, reply->y, reply->width, reply->height);
const qreal dpr = frame->devicePixelRatioF();

// 将 Qt 的逻辑几何转换为物理几何进行对比
QRect expectedPhysical(qRound(screen->geometry().x() * dpr),
                       qRound(screen->geometry().y() * dpr),
                       qRound(screen->geometry().width() * dpr),
                       qRound(screen->geometry().height() * dpr));

如果实际情况确实是注释所说的“位置不乘DPR”,则请保留原样,但务必加上充分的单元测试或集成测试来覆盖多屏、高分屏场景。

@deepin-ci-robot
Copy link
Contributor Author

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: deepin-ci-robot, 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

@yixinshark yixinshark merged commit 90db72d into master Feb 26, 2026
24 of 26 checks passed
@sourcery-ai
Copy link

sourcery-ai bot commented Feb 26, 2026

Reviewer's Guide

Adds XCB/X11-based validation and detailed logging around fullscreen background and lock frame geometries to detect and correct X11/Qt geometry mismatches, ensuring lock screens bind to the correct screen and have accurate positions/sizes, especially on multi‑screen X11 setups.

Sequence diagram for geometry reset and XCB validation in FullScreenBackground

sequenceDiagram
    participant Timer as ResetGeometryTimer
    participant FullScreenBackground
    participant QtWindow as QWindow_handle
    participant QtScreen as QScreen
    participant X11Conn as XCB_Connection
    participant XServer

    Timer->>FullScreenBackground: timeout()
    activate FullScreenBackground
    FullScreenBackground->>FullScreenBackground: currentGeometry = geometry()
    FullScreenBackground->>FullScreenBackground: compare currentGeometry vs m_geometryRect
    alt Qt_geometry_mismatch
        FullScreenBackground->>FullScreenBackground: qCWarning(DDE_SHELL, geometry mismatch)
        FullScreenBackground->>FullScreenBackground: setGeometry(m_geometryRect)
    end

    FullScreenBackground->>FullScreenBackground: if !m_model->isUseWayland() && windowHandle()
    alt X11_backend
        FullScreenBackground->>QtWindow: windowHandle()
        QtWindow-->>FullScreenBackground: QWindow*
        FullScreenBackground->>X11Conn: get X11 connection (QX11Info or QX11Application)
        alt connection_available
            X11Conn-->>FullScreenBackground: xcb_connection_t*
            FullScreenBackground->>X11Conn: xcb_get_geometry(winId())
            X11Conn->>XServer: query window geometry
            XServer-->>X11Conn: xcb_get_geometry_reply
            X11Conn-->>FullScreenBackground: reply(x, y, width, height)
            FullScreenBackground->>FullScreenBackground: build xcbGeometry from reply
            FullScreenBackground->>FullScreenBackground: dpr = devicePixelRatioF()
            FullScreenBackground->>FullScreenBackground: expectedPhysical from m_geometryRect and dpr
            alt XCB_geometry_mismatch
                FullScreenBackground->>FullScreenBackground: qCWarning(DDE_SHELL, XCB mismatch)
                FullScreenBackground->>QtWindow: windowHandle()->screen()
                QtWindow-->>FullScreenBackground: currentScreen
                alt screen_mismatch_and_m_screen_not_null
                    FullScreenBackground->>QtWindow: setScreen(m_screen)
                end
                FullScreenBackground->>FullScreenBackground: setGeometry(0,0,0,0)
                FullScreenBackground->>FullScreenBackground: setGeometry(m_geometryRect)
            else XCB_geometry_match
                FullScreenBackground->>FullScreenBackground: qCInfo(DDE_SHELL, position match)
            end
        else no_connection
            FullScreenBackground->>FullScreenBackground: skip XCB validation
        end
    else Wayland_backend
        FullScreenBackground->>FullScreenBackground: skip XCB validation
    end
    deactivate FullScreenBackground
Loading

Sequence diagram for MultiScreenManager lock frame XCB validation

sequenceDiagram
    participant MSM as MultiScreenManager
    participant QtApp as QGuiApplication
    participant X11Conn as XCB_Connection
    participant Screen as QScreen
    participant Frame as LockFrame_QWidget
    participant XServer

    MSM->>MSM: checkLockFrameLocation()
    MSM->>QtApp: platformName()
    QtApp-->>MSM: platformName
    alt not_Wayland
        MSM->>X11Conn: get X11 connection (QX11Info or QX11Application)
        X11Conn-->>MSM: xcb_connection_t*
    else Wayland
        MSM->>MSM: connection = nullptr
    end

    MSM->>MSM: for each screen in m_frames.keys()
    loop each_screen
        MSM->>MSM: frame = m_frames.value(screen)
        MSM->>MSM: qCInfo(DDE_SHELL, screen and Qt frame geometry)
        alt connection_and_frame_available
            MSM->>X11Conn: xcb_get_geometry(frame->winId())
            X11Conn->>XServer: query window geometry
            XServer-->>X11Conn: xcb_get_geometry_reply
            X11Conn-->>MSM: reply(x, y, width, height)
            MSM->>MSM: xcbGeometry from reply
            MSM->>Frame: devicePixelRatioF()
            Frame-->>MSM: dpr
            MSM->>Screen: geometry()
            Screen-->>MSM: screenGeometry
            MSM->>MSM: expectedPhysical from screenGeometry and dpr
            MSM->>MSM: compare xcbGeometry vs expectedPhysical
            alt geometry_match
                MSM->>MSM: qCInfo(DDE_SHELL, XCB match)
            else geometry_mismatch
                MSM->>MSM: qCWarning(DDE_SHELL, POSITION MISMATCH DETECTED)
            end
        else no_connection_or_frame
            MSM->>MSM: skip XCB validation for this screen
        end
    end
Loading

Updated class diagram for FullScreenBackground and MultiScreenManager geometry handling

classDiagram
    class SessionBaseModel
    class QScreen
    class QWidget
    class QTimer
    class QRect

    class FullScreenBackground {
        - SessionBaseModel* m_model
        - QPointer~QScreen~ m_screen
        - QTimer* m_resetGeometryTimer
        - QRect m_geometryRect
        + FullScreenBackground(SessionBaseModel* model, QWidget* parent)
        + updateGeometry() void
        + setddeGeometry(const QRect& rect) void
    }

    class MultiScreenManager {
        - QMap~QScreen*, QWidget*~ m_frames
        - bool m_isCopyMode
        + MultiScreenManager(QObject* parent)
        + onScreenAdded(QPointer~QScreen~ screen) void
        + checkLockFrameLocation() void
    }

    class QWindow
    class QGuiApplication

    FullScreenBackground --|> QWidget
    MultiScreenManager ..> FullScreenBackground : manages_lock_frames
    MultiScreenManager --> QScreen : uses
    MultiScreenManager --> QWidget : uses_as_frame
    FullScreenBackground --> QScreen : binds_to
    FullScreenBackground --> QTimer : uses_resetGeometryTimer
    FullScreenBackground --> QRect : stores_target_geometry
    MultiScreenManager --> QRect : uses_screen_geometry
    FullScreenBackground --> QWindow : uses_windowHandle
    MultiScreenManager --> QGuiApplication : queries_platform_and_X11_connection
    FullScreenBackground --> SessionBaseModel : reads_backend_type_and_state
Loading

File-Level Changes

Change Details Files
Add XCB/X11 geometry verification and corrective behavior for the fullscreen background widget on X11.
  • Include XCB and conditional X11 headers based on ENABLE_DSS_SNIPE to access the raw X11 connection.
  • Extend the reset-geometry timer callback to query the window’s real X11 geometry via xcb_get_geometry, compare it with the expected DPR-scaled geometry, and log or correct mismatches.
  • When a geometry mismatch is detected, optionally rebind the QWindow to the stored QScreen and force geometry updates by resizing to 0,0 then restoring the target rect.
  • Add richer diagnostic logging around geometry updates, including screen name, windowHandle screen, DPR, and target/current rects in updateGeometry and setddeGeometry.
src/widgets/fullscreenbackground.cpp
Improve lock frame multi-screen management diagnostics and add XCB-based geometry checks for lock frames on X11.
  • Include XCB and conditional X11 headers to allow querying window geometry on X11 in the multi-screen manager.
  • Log detailed information when screens are added, including screen name, geometry, and current frame count.
  • Enhance checkLockFrameLocation to log per-screen/frame geometry and, on X11, query XCB geometry for each frame and compare it to DPR-scaled screen geometry.
  • Log explicit warnings when an XCB/Qt geometry mismatch is detected for a frame, including actual and expected physical geometry, DPR, and the screen name.
src/global_util/multiscreenmanager.cpp

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

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 5 issues, and left some high level feedback:

  • The XCB geometry inspection logic is duplicated between FullScreenBackground and MultiScreenManager; consider extracting a shared helper to query/check X11 window geometry so future fixes stay in one place.
  • Several new logging statements dereference windowHandle()->screen()->name() without verifying screen() is non-null; adding a null-check (or conditional logging) would avoid potential crashes when no screen is associated.
  • The newly added qCInfo/qCWarning logs in geometry timers and periodic checks will run very frequently and may spam logs in normal operation; consider downgrading some of these to qCDebug or gating them behind a verbosity flag.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The XCB geometry inspection logic is duplicated between `FullScreenBackground` and `MultiScreenManager`; consider extracting a shared helper to query/check X11 window geometry so future fixes stay in one place.
- Several new logging statements dereference `windowHandle()->screen()->name()` without verifying `screen()` is non-null; adding a null-check (or conditional logging) would avoid potential crashes when no screen is associated.
- The newly added `qCInfo`/`qCWarning` logs in geometry timers and periodic checks will run very frequently and may spam logs in normal operation; consider downgrading some of these to `qCDebug` or gating them behind a verbosity flag.

## Individual Comments

### Comment 1
<location path="src/widgets/fullscreenbackground.cpp" line_range="508-509" />
<code_context>
     if (!m_screen.isNull()) {
-        if(m_model->isUseWayland())
+        // X11 下也需要将 QWindow 关联到正确的 QScreen,否则 Qt 可能将窗口归属到错误的 screen
+        if (windowHandle() && windowHandle()->screen() != m_screen) {
+            qCInfo(DDE_SHELL) << "bindWindowToScreen, windowHandle screen:" << windowHandle()->screen()->name()
+                              << ", target screen:" << m_screen->name() << ", this:" << this;
             windowHandle()->setScreen(m_screen);
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against null QScreen when logging windowHandle()->screen()->name().

`windowHandle()->screen()` may be null (e.g. during screen changes or early initialization), so calling `.name()` directly risks a null dereference in this logging path. Capture the screen in a local pointer and check it before accessing `name()`, for example:

```cpp
if (auto *win = windowHandle()) {
    QScreen *curScreen = win->screen();
    qCInfo(DDE_SHELL) << "bindWindowToScreen, windowHandle screen:"
                      << (curScreen ? curScreen->name() : "null")
                      << ", target screen:" << m_screen->name() << ", this:" << this;
    if (curScreen != m_screen)
        win->setScreen(m_screen);
}
```
</issue_to_address>

### Comment 2
<location path="src/widgets/fullscreenbackground.cpp" line_range="799" />
<code_context>
+                      << ", target rect:" << rect
+                      << ", current geometry:" << geometry()
+                      << ", screen:" << (m_screen ? m_screen->name() : "null")
+                      << ", windowHandle screen:" << (windowHandle() ? windowHandle()->screen()->name() : "null")
+                      << ", dpr:" << devicePixelRatioF();
     setGeometry(rect);
</code_context>
<issue_to_address>
**issue (bug_risk):** Also guard windowHandle()->screen() in logging to prevent crashes when screen is null.

The current ternary only guards `windowHandle()`, not `windowHandle()->screen()`. If a window exists but has no screen yet, `screen()->name()` will dereference null and crash in this logging code. Please also guard the screen pointer, e.g. via a local `QScreen *winScreen` and logging `winScreen ? winScreen->name() : "null"`.
</issue_to_address>

### Comment 3
<location path="src/global_util/multiscreenmanager.cpp" line_range="271-272" />
<code_context>
+    }
+
     for (QScreen *screen : m_frames.keys()) {
         if (screen) {
-            qCInfo(DDE_SHELL) << "Check lock frame location, screen:" << screen << ", location:" << screen->geometry()
-                       << ", lockframe:" << m_frames.value(screen) << ", location:" << m_frames.value(screen)->geometry();
+            QWidget *frame = m_frames.value(screen);
+            qCInfo(DDE_SHELL) << "Check lock frame location, screen:" << screen
+                              << ", screen name:" << screen->name()
</code_context>
<issue_to_address>
**issue (bug_risk):** Check that the frame pointer is non-null before dereferencing it.

In this loop you only check `screen`, but then dereference `frame` (e.g. `frame->geometry()`) without ensuring it’s non-null. If `m_frames` can contain `nullptr`, this can crash. Please add a null check for `frame` (e.g. `if (!frame) continue;` or `if (screen && frame)` before use).
</issue_to_address>

### Comment 4
<location path="src/widgets/fullscreenbackground.cpp" line_range="91" />
<code_context>
+
+        // 通过 XCB 检查窗口在 X Server 中的实际位置
+        // 注意:X11 坐标系中,窗口位置不乘 DPR(与 X RandR 一致),窗口大小乘 DPR
+        if (!m_model->isUseWayland() && windowHandle()) {
+            xcb_connection_t *connection = nullptr;
+#ifndef ENABLE_DSS_SNIPE
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the X11/XCB connection and geometry reconciliation logic into helper functions and centralizing the repeated logging to keep the timer callback and geometry-related methods simpler and easier to read.

You can keep the new behavior but reduce complexity by extracting the XCB/X11 handling and geometry reconciliation into small helpers, and by centralizing the verbose logging.

### 1. Extract X11 connection acquisition

The timer lambda and possibly other files duplicate the `#ifndef ENABLE_DSS_SNIPE` + `QX11Info` vs `QNativeInterface::QX11Application` logic. Move that into a small helper:

```cpp
// In a suitable .cpp/.h (e.g. fullscreenbackground.cpp or a small X11 helper)
static xcb_connection_t *x11Connection()
{
    xcb_connection_t *connection = nullptr;
#ifndef ENABLE_DSS_SNIPE
    connection = QX11Info::connection();
#else
    auto *x11App = qGuiApp->nativeInterface<QNativeInterface::QX11Application>();
    if (x11App)
        connection = x11App->connection();
#endif
    return connection;
}
```

Then use it in the timer:

```cpp
if (!m_model->isUseWayland() && windowHandle()) {
    if (auto *connection = x11Connection()) {
        auto cookie = xcb_get_geometry(connection, static_cast<xcb_window_t>(winId()));
        auto *reply = xcb_get_geometry_reply(connection, cookie, nullptr);
        // ...
    }
}
```

This removes preprocessor noise from the lambda and avoids duplication in other places.

### 2. Extract XCB geometry reconciliation

The DPR handling, XCB `get_geometry`, and resize-to-0 workaround can be moved out of the lambda into a dedicated method. That keeps the timer callback focused on “check geometry + fix if needed”.

```cpp
// In FullScreenBackground
void FullScreenBackground::ensureXcbGeometryMatches()
{
    if (m_model->isUseWayland() || !windowHandle())
        return;

    xcb_connection_t *connection = x11Connection();
    if (!connection)
        return;

    auto cookie = xcb_get_geometry(connection, static_cast<xcb_window_t>(winId()));
    auto *reply = xcb_get_geometry_reply(connection, cookie, nullptr);
    if (!reply)
        return;

    const QRect xcbGeometry(reply->x, reply->y, reply->width, reply->height);
    free(reply);

    const qreal dpr = devicePixelRatioF();
    const QRect expectedPhysical(
        m_geometryRect.x(),
        m_geometryRect.y(),
        qRound(m_geometryRect.width() * dpr),
        qRound(m_geometryRect.height() * dpr));

    if (xcbGeometry == expectedPhysical) {
        qCInfo(DDE_SHELL) << "XCB position match, no need to set geometry";
        return;
    }

    qCWarning(DDE_SHELL) << "XCB position mismatch! XCB geometry(physical):" << xcbGeometry
                         << ", expected(physical):" << expectedPhysical
                         << ", target(logical):" << m_geometryRect
                         << ", dpr:" << dpr
                         << ", this:" << this
                         << ", screen:" << (m_screen ? m_screen->name() : "null");

    if (!m_screen.isNull() && windowHandle()->screen() != m_screen)
        windowHandle()->setScreen(m_screen);

    setGeometry(0, 0, 0, 0);
    setGeometry(m_geometryRect);
}
```

Timer becomes much clearer:

```cpp
connect(m_resetGeometryTimer, &QTimer::timeout, this, [this] {
    const auto &currentGeometry = geometry();
    if (currentGeometry != m_geometryRect) {
        qCWarning(DDE_SHELL) << "Geometry mismatch detected! Qt cached geometry:" << currentGeometry
                             << ", target geometry:" << m_geometryRect << ", this:" << this;
        setGeometry(m_geometryRect);
    }

    ensureXcbGeometryMatches();
});
```

### 3. Centralize repetitive logging

`setddeGeometry`, `updateGeometry`, and the timer lambda all log similar information about the frame, screen, and window handle. You can reduce repetition and visual noise by introducing a small logging helper:

```cpp
void FullScreenBackground::logGeometryInfo(const char *prefix, const QRect &target) const
{
    qCInfo(DDE_SHELL) << prefix
                      << "this:" << this
                      << "target:" << target
                      << "current geometry:" << geometry()
                      << "screen:" << (m_screen ? m_screen->name() : "null")
                      << "windowHandle screen:" << (windowHandle() ? windowHandle()->screen()->name() : "null")
                      << "dpr:" << devicePixelRatioF();
}
```

Use it where logging is currently verbose:

```cpp
void FullScreenBackground::setddeGeometry(const QRect &rect)
{
    logGeometryInfo("setddeGeometry", rect);
    setGeometry(rect);
    m_geometryRect = rect;
    m_resetGeometryTimer->start(200);
    QTimer::singleShot(400 * 5, m_resetGeometryTimer, &QTimer::stop);
}
```

And in `updateGeometry` / timer as needed, with short prefixes. This keeps all current information but makes the control flow easier to read and maintain.
</issue_to_address>

### Comment 5
<location path="src/global_util/multiscreenmanager.cpp" line_range="257" />
<code_context>

 void MultiScreenManager::checkLockFrameLocation()
 {
+    xcb_connection_t *connection = nullptr;
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the XCB connection retrieval and geometry-checking logic into reusable helper functions so `checkLockFrameLocation` stays focused on high-level behavior.

You can keep all the new checks/logging but reduce complexity by extracting the XCB-specific and geometry logic into small helpers, and centralizing XCB connection acquisition (also avoids duplication with `fullscreenbackground.cpp`).

### 1. Centralize XCB connection retrieval

Create a small helper (in a shared `.cpp` or anonymous namespace) and use it both here and in `fullscreenbackground.cpp`:

```cpp
// e.g. in a common helper .cpp, or at top of this .cpp in an anonymous namespace
static xcb_connection_t *getXcbConnection()
{
    if (QGuiApplication::platformName().startsWith("wayland", Qt::CaseInsensitive))
        return nullptr;

#ifndef ENABLE_DSS_SNIPE
    return QX11Info::connection();
#else
    auto *x11App = qGuiApp->nativeInterface<QNativeInterface::QX11Application>();
    return x11App ? x11App->connection() : nullptr;
#endif
}
```

Then `checkLockFrameLocation` becomes simpler:

```cpp
void MultiScreenManager::checkLockFrameLocation()
{
    xcb_connection_t *connection = getXcbConnection();

    for (QScreen *screen : m_frames.keys()) {
        if (!screen)
            continue;

        QWidget *frame = m_frames.value(screen);
        qCInfo(DDE_SHELL) << "Check lock frame location, screen:" << screen
                          << ", screen name:" << screen->name()
                          << ", screen geometry:" << screen->geometry()
                          << ", lockframe:" << frame
                          << ", Qt frame geometry:" << frame->geometry();

        if (connection && frame)
            checkAndLogXcbGeometry(connection, screen, frame);
    }
}
```

### 2. Extract geometry calculation + logging

Move the DPR/geometry math and logging into a focused helper:

```cpp
static void checkAndLogXcbGeometry(xcb_connection_t *connection, QScreen *screen, QWidget *frame)
{
    auto cookie = xcb_get_geometry(connection, static_cast<xcb_window_t>(frame->winId()));
    auto *reply = xcb_get_geometry_reply(connection, cookie, nullptr);
    if (!reply) {
        qCWarning(DDE_SHELL) << "  Failed to get XCB geometry for frame:" << frame;
        return;
    }

    QRect xcbGeometry(reply->x, reply->y, reply->width, reply->height);
    free(reply);

    const qreal dpr = frame->devicePixelRatioF();
    const QRect screenGeo = screen->geometry();
    QRect expectedPhysical(screenGeo.x(), screenGeo.y(),
                           qRound(screenGeo.width() * dpr),
                           qRound(screenGeo.height() * dpr));

    const bool positionMatch = (xcbGeometry == expectedPhysical);

    qCInfo(DDE_SHELL) << "  XCB actual geometry(physical):" << xcbGeometry
                      << ", expected(physical):" << expectedPhysical
                      << ", dpr:" << dpr
                      << ", match:" << positionMatch;

    if (!positionMatch) {
        qCWarning(DDE_SHELL) << "  *** POSITION MISMATCH DETECTED! ***"
                             << "XCB geometry:" << xcbGeometry
                             << "expected:" << expectedPhysical
                             << "for screen" << screen->name();
    }
}
```

This keeps all current behavior and logging, but makes `checkLockFrameLocation` read as high-level intent while encapsulating platform-specific and geometry details.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +508 to +509
if (windowHandle() && windowHandle()->screen() != m_screen) {
qCInfo(DDE_SHELL) << "bindWindowToScreen, windowHandle screen:" << windowHandle()->screen()->name()
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Guard against null QScreen when logging windowHandle()->screen()->name().

windowHandle()->screen() may be null (e.g. during screen changes or early initialization), so calling .name() directly risks a null dereference in this logging path. Capture the screen in a local pointer and check it before accessing name(), for example:

if (auto *win = windowHandle()) {
    QScreen *curScreen = win->screen();
    qCInfo(DDE_SHELL) << "bindWindowToScreen, windowHandle screen:"
                      << (curScreen ? curScreen->name() : "null")
                      << ", target screen:" << m_screen->name() << ", this:" << this;
    if (curScreen != m_screen)
        win->setScreen(m_screen);
}

<< ", target rect:" << rect
<< ", current geometry:" << geometry()
<< ", screen:" << (m_screen ? m_screen->name() : "null")
<< ", windowHandle screen:" << (windowHandle() ? windowHandle()->screen()->name() : "null")
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Also guard windowHandle()->screen() in logging to prevent crashes when screen is null.

The current ternary only guards windowHandle(), not windowHandle()->screen(). If a window exists but has no screen yet, screen()->name() will dereference null and crash in this logging code. Please also guard the screen pointer, e.g. via a local QScreen *winScreen and logging winScreen ? winScreen->name() : "null".

Comment on lines 271 to +272
if (screen) {
qCInfo(DDE_SHELL) << "Check lock frame location, screen:" << screen << ", location:" << screen->geometry()
<< ", lockframe:" << m_frames.value(screen) << ", location:" << m_frames.value(screen)->geometry();
QWidget *frame = m_frames.value(screen);
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Check that the frame pointer is non-null before dereferencing it.

In this loop you only check screen, but then dereference frame (e.g. frame->geometry()) without ensuring it’s non-null. If m_frames can contain nullptr, this can crash. Please add a null check for frame (e.g. if (!frame) continue; or if (screen && frame) before use).


// 通过 XCB 检查窗口在 X Server 中的实际位置
// 注意:X11 坐标系中,窗口位置不乘 DPR(与 X RandR 一致),窗口大小乘 DPR
if (!m_model->isUseWayland() && windowHandle()) {
Copy link

Choose a reason for hiding this comment

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

issue (complexity): Consider extracting the X11/XCB connection and geometry reconciliation logic into helper functions and centralizing the repeated logging to keep the timer callback and geometry-related methods simpler and easier to read.

You can keep the new behavior but reduce complexity by extracting the XCB/X11 handling and geometry reconciliation into small helpers, and by centralizing the verbose logging.

1. Extract X11 connection acquisition

The timer lambda and possibly other files duplicate the #ifndef ENABLE_DSS_SNIPE + QX11Info vs QNativeInterface::QX11Application logic. Move that into a small helper:

// In a suitable .cpp/.h (e.g. fullscreenbackground.cpp or a small X11 helper)
static xcb_connection_t *x11Connection()
{
    xcb_connection_t *connection = nullptr;
#ifndef ENABLE_DSS_SNIPE
    connection = QX11Info::connection();
#else
    auto *x11App = qGuiApp->nativeInterface<QNativeInterface::QX11Application>();
    if (x11App)
        connection = x11App->connection();
#endif
    return connection;
}

Then use it in the timer:

if (!m_model->isUseWayland() && windowHandle()) {
    if (auto *connection = x11Connection()) {
        auto cookie = xcb_get_geometry(connection, static_cast<xcb_window_t>(winId()));
        auto *reply = xcb_get_geometry_reply(connection, cookie, nullptr);
        // ...
    }
}

This removes preprocessor noise from the lambda and avoids duplication in other places.

2. Extract XCB geometry reconciliation

The DPR handling, XCB get_geometry, and resize-to-0 workaround can be moved out of the lambda into a dedicated method. That keeps the timer callback focused on “check geometry + fix if needed”.

// In FullScreenBackground
void FullScreenBackground::ensureXcbGeometryMatches()
{
    if (m_model->isUseWayland() || !windowHandle())
        return;

    xcb_connection_t *connection = x11Connection();
    if (!connection)
        return;

    auto cookie = xcb_get_geometry(connection, static_cast<xcb_window_t>(winId()));
    auto *reply = xcb_get_geometry_reply(connection, cookie, nullptr);
    if (!reply)
        return;

    const QRect xcbGeometry(reply->x, reply->y, reply->width, reply->height);
    free(reply);

    const qreal dpr = devicePixelRatioF();
    const QRect expectedPhysical(
        m_geometryRect.x(),
        m_geometryRect.y(),
        qRound(m_geometryRect.width() * dpr),
        qRound(m_geometryRect.height() * dpr));

    if (xcbGeometry == expectedPhysical) {
        qCInfo(DDE_SHELL) << "XCB position match, no need to set geometry";
        return;
    }

    qCWarning(DDE_SHELL) << "XCB position mismatch! XCB geometry(physical):" << xcbGeometry
                         << ", expected(physical):" << expectedPhysical
                         << ", target(logical):" << m_geometryRect
                         << ", dpr:" << dpr
                         << ", this:" << this
                         << ", screen:" << (m_screen ? m_screen->name() : "null");

    if (!m_screen.isNull() && windowHandle()->screen() != m_screen)
        windowHandle()->setScreen(m_screen);

    setGeometry(0, 0, 0, 0);
    setGeometry(m_geometryRect);
}

Timer becomes much clearer:

connect(m_resetGeometryTimer, &QTimer::timeout, this, [this] {
    const auto &currentGeometry = geometry();
    if (currentGeometry != m_geometryRect) {
        qCWarning(DDE_SHELL) << "Geometry mismatch detected! Qt cached geometry:" << currentGeometry
                             << ", target geometry:" << m_geometryRect << ", this:" << this;
        setGeometry(m_geometryRect);
    }

    ensureXcbGeometryMatches();
});

3. Centralize repetitive logging

setddeGeometry, updateGeometry, and the timer lambda all log similar information about the frame, screen, and window handle. You can reduce repetition and visual noise by introducing a small logging helper:

void FullScreenBackground::logGeometryInfo(const char *prefix, const QRect &target) const
{
    qCInfo(DDE_SHELL) << prefix
                      << "this:" << this
                      << "target:" << target
                      << "current geometry:" << geometry()
                      << "screen:" << (m_screen ? m_screen->name() : "null")
                      << "windowHandle screen:" << (windowHandle() ? windowHandle()->screen()->name() : "null")
                      << "dpr:" << devicePixelRatioF();
}

Use it where logging is currently verbose:

void FullScreenBackground::setddeGeometry(const QRect &rect)
{
    logGeometryInfo("setddeGeometry", rect);
    setGeometry(rect);
    m_geometryRect = rect;
    m_resetGeometryTimer->start(200);
    QTimer::singleShot(400 * 5, m_resetGeometryTimer, &QTimer::stop);
}

And in updateGeometry / timer as needed, with short prefixes. This keeps all current information but makes the control flow easier to read and maintain.

@@ -244,10 +256,53 @@ void MultiScreenManager::onDisplayModeChanged(const QString &)

void MultiScreenManager::checkLockFrameLocation()
Copy link

Choose a reason for hiding this comment

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

issue (complexity): Consider extracting the XCB connection retrieval and geometry-checking logic into reusable helper functions so checkLockFrameLocation stays focused on high-level behavior.

You can keep all the new checks/logging but reduce complexity by extracting the XCB-specific and geometry logic into small helpers, and centralizing XCB connection acquisition (also avoids duplication with fullscreenbackground.cpp).

1. Centralize XCB connection retrieval

Create a small helper (in a shared .cpp or anonymous namespace) and use it both here and in fullscreenbackground.cpp:

// e.g. in a common helper .cpp, or at top of this .cpp in an anonymous namespace
static xcb_connection_t *getXcbConnection()
{
    if (QGuiApplication::platformName().startsWith("wayland", Qt::CaseInsensitive))
        return nullptr;

#ifndef ENABLE_DSS_SNIPE
    return QX11Info::connection();
#else
    auto *x11App = qGuiApp->nativeInterface<QNativeInterface::QX11Application>();
    return x11App ? x11App->connection() : nullptr;
#endif
}

Then checkLockFrameLocation becomes simpler:

void MultiScreenManager::checkLockFrameLocation()
{
    xcb_connection_t *connection = getXcbConnection();

    for (QScreen *screen : m_frames.keys()) {
        if (!screen)
            continue;

        QWidget *frame = m_frames.value(screen);
        qCInfo(DDE_SHELL) << "Check lock frame location, screen:" << screen
                          << ", screen name:" << screen->name()
                          << ", screen geometry:" << screen->geometry()
                          << ", lockframe:" << frame
                          << ", Qt frame geometry:" << frame->geometry();

        if (connection && frame)
            checkAndLogXcbGeometry(connection, screen, frame);
    }
}

2. Extract geometry calculation + logging

Move the DPR/geometry math and logging into a focused helper:

static void checkAndLogXcbGeometry(xcb_connection_t *connection, QScreen *screen, QWidget *frame)
{
    auto cookie = xcb_get_geometry(connection, static_cast<xcb_window_t>(frame->winId()));
    auto *reply = xcb_get_geometry_reply(connection, cookie, nullptr);
    if (!reply) {
        qCWarning(DDE_SHELL) << "  Failed to get XCB geometry for frame:" << frame;
        return;
    }

    QRect xcbGeometry(reply->x, reply->y, reply->width, reply->height);
    free(reply);

    const qreal dpr = frame->devicePixelRatioF();
    const QRect screenGeo = screen->geometry();
    QRect expectedPhysical(screenGeo.x(), screenGeo.y(),
                           qRound(screenGeo.width() * dpr),
                           qRound(screenGeo.height() * dpr));

    const bool positionMatch = (xcbGeometry == expectedPhysical);

    qCInfo(DDE_SHELL) << "  XCB actual geometry(physical):" << xcbGeometry
                      << ", expected(physical):" << expectedPhysical
                      << ", dpr:" << dpr
                      << ", match:" << positionMatch;

    if (!positionMatch) {
        qCWarning(DDE_SHELL) << "  *** POSITION MISMATCH DETECTED! ***"
                             << "XCB geometry:" << xcbGeometry
                             << "expected:" << expectedPhysical
                             << "for screen" << screen->name();
    }
}

This keeps all current behavior and logging, but makes checkLockFrameLocation read as high-level intent while encapsulating platform-specific and geometry details.

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