Skip to content

feat: add move_xembed_window protocol for later use#414

Merged
BLumia merged 2 commits intolinuxdeepin:masterfrom
BLumia:protocol-move-xembed-window
Jan 20, 2026
Merged

feat: add move_xembed_window protocol for later use#414
BLumia merged 2 commits intolinuxdeepin:masterfrom
BLumia:protocol-move-xembed-window

Conversation

@BLumia
Copy link
Member

@BLumia BLumia commented Jan 15, 2026

Log:

Summary by Sourcery

Introduce a Wayland protocol and client integration for moving XEmbed windows via the plugin manager, preparing Wayland-specific handling in the application tray.

New Features:

  • Add a move_xembed_window request and plugin_manager_callback_v1 interface to the plugin manager Wayland protocol for repositioning XEmbed windows.
  • Expose a moveXembedWindow API in PluginManagerIntegration with a corresponding PluginManagerCallback object for asynchronous completion handling.

Enhancements:

  • Begin wiring Wayland-specific XEmbed window movement in the application tray handler, gated by XDG_SESSION_TYPE detection.

@BLumia BLumia requested a review from tsic404 January 15, 2026 11:11
@sourcery-ai
Copy link

sourcery-ai bot commented Jan 15, 2026

Reviewer's Guide

Adds a new Wayland protocol request and callback interface to move XEmbed windows via the plugin manager, exposes this as an asynchronous callback-based API in PluginManagerIntegration, and wires in initial Wayland-specific handling in the Xembed protocol handler (currently with a TODO).

Sequence diagram for move_xembed_window request and callback

sequenceDiagram
    actor User
    participant XembedProtocolHandler
    participant EmbedPlugin
    participant PluginManagerIntegration
    participant WaylandCompositor
    participant PluginManagerCallback

    User->>XembedProtocolHandler: triggers input on tray icon
    XembedProtocolHandler->>XembedProtocolHandler: detect Wayland session
    XembedProtocolHandler->>EmbedPlugin: get(windowHandle)
    EmbedPlugin-->>XembedProtocolHandler: plugin_id, item_key
    XembedProtocolHandler->>PluginManagerIntegration: moveXembedWindow(xembedWinId, plugin_id, item_key)
    PluginManagerIntegration->>PluginManagerIntegration: create PluginManagerCallback instance
    PluginManagerIntegration->>WaylandCompositor: plugin_manager_v1.move_xembed_window(xembed_winid, plugin_id, item_key, callback)
    WaylandCompositor-->>PluginManagerCallback: plugin_manager_callback_v1.done
    PluginManagerCallback->>PluginManagerCallback: plugin_manager_callback_v1_done()
    PluginManagerCallback-->>PluginManagerCallback: emit done()
Loading

Class diagram for PluginManagerIntegration and PluginManagerCallback changes

classDiagram
    class PluginManagerIntegration {
        +void requestMessage(QString plugin_id, QString item_key, QString msg)
        +PluginManagerCallback* moveXembedWindow(uint32_t xembedWinId, QString pluginId, QString itemKey)
        +void plugin_manager_v1_position_changed(uint32_t dock_position)
        +bool tryCreatePopupForSubWindow(QWindow* window)
        -uint32_t m_dockPosition
        -uint32_t m_dockColorType
        -static PluginManagerIntegration* s_instance
    }

    class PluginManagerCallback {
        +PluginManagerCallback()
        +~PluginManagerCallback()
        +signal void done()
        +void plugin_manager_callback_v1_done()
    }

    class QObject
    class QtWaylandClient_QWaylandShellIntegrationTemplate_PluginManagerIntegration
    class QtWayland_plugin_manager_v1
    class QtWayland_plugin_manager_callback_v1

    PluginManagerIntegration ..|> QtWaylandClient_QWaylandShellIntegrationTemplate_PluginManagerIntegration
    PluginManagerIntegration ..|> QtWayland_plugin_manager_v1

    PluginManagerCallback ..|> QObject
    PluginManagerCallback ..|> QtWayland_plugin_manager_callback_v1

    PluginManagerIntegration --> PluginManagerCallback : creates
Loading

File-Level Changes

Change Details Files
Extend plugin-manager Wayland protocol with a move_xembed_window request and generic async callback interface.
  • Add move_xembed_window request to plugin_manager_v1 interface, including xembed_winid, plugin_id, item_key, and a callback new_id argument.
  • Introduce plugin_manager_callback_v1 interface with destroy request and done event for signaling async completion.
src/protocol/plugin-manager-v1.xml
Expose a moveXembedWindow API in PluginManagerIntegration backed by the new Wayland protocol callback interface.
  • Add moveXembedWindow method that creates a PluginManagerCallback and calls move_xembed_window on the Wayland protocol object.
  • Introduce PluginManagerCallback class implementing QtWayland::plugin_manager_callback_v1 and emitting a Qt signal on done.
  • Declare a static PluginManagerIntegration *s_instance member for potential singleton-style access.
src/tray-wayland-integration/pluginmanagerintegration.cpp
src/tray-wayland-integration/pluginmanagerintegration_p.h
Prepare XembedProtocolHandler for using the new move_xembed_window request under Wayland sessions.
  • Include plugin.h to access Plugin::EmbedPlugin.
  • Add Wayland-specific branch in updateEmbedWindowPosForGetInputEvent that looks up the EmbedPlugin for the current window and will use move_xembed_window instead of direct X11 moves (currently left as a TODO).
  • Retain existing X11 window move behavior for non-Wayland sessions.
plugins/application-tray/xembedprotocolhandler.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 2 issues, and left some high level feedback:

  • The new moveXembedWindow helper allocates a PluginManagerCallback but never passes a callback object to the Wayland move_xembed_window request (which expects a new_id), so the callback will never be wired up to protocol events; consider using the generated request overload that takes a plugin_manager_callback_v1 and binding it to the PluginManagerCallback instance.
  • Ownership and lifetime of PluginManagerCallback are unclear: moveXembedWindow returns a raw pointer that is never parented or deleted, which risks a leak; consider giving it a QObject parent (e.g., the caller or PluginManagerIntegration) or returning a smart pointer.
  • The Wayland branch in XembedProtocolHandler::updateEmbedWindowPosForGetInputEvent currently contains only a TODO and does not use the new protocol/methods, leaving Wayland behavior effectively unchanged; if this is intended to be usable now, wire it up to PluginManagerIntegration::moveXembedWindow with the retrieved plugin_id and item_key.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `moveXembedWindow` helper allocates a `PluginManagerCallback` but never passes a `callback` object to the Wayland `move_xembed_window` request (which expects a `new_id`), so the callback will never be wired up to protocol events; consider using the generated request overload that takes a `plugin_manager_callback_v1` and binding it to the `PluginManagerCallback` instance.
- Ownership and lifetime of `PluginManagerCallback` are unclear: `moveXembedWindow` returns a raw pointer that is never parented or deleted, which risks a leak; consider giving it a QObject parent (e.g., the caller or `PluginManagerIntegration`) or returning a smart pointer.
- The Wayland branch in `XembedProtocolHandler::updateEmbedWindowPosForGetInputEvent` currently contains only a TODO and does not use the new protocol/methods, leaving Wayland behavior effectively unchanged; if this is intended to be usable now, wire it up to `PluginManagerIntegration::moveXembedWindow` with the retrieved `plugin_id` and `item_key`.

## Individual Comments

### Comment 1
<location> `src/tray-wayland-integration/pluginmanagerintegration.cpp:51-54` </location>
<code_context>
     request_message(plugin_id, item_key, msg);
 }

+PluginManagerCallback *PluginManagerIntegration::moveXembedWindow(uint32_t xembedWinId, const QString &pluginId, const QString &itemKey)
+{
+    auto callback = new PluginManagerCallback();
+    move_xembed_window(xembedWinId, pluginId, itemKey);
+    return callback;
+}
</code_context>

<issue_to_address>
**issue (bug_risk):** The Wayland callback object is never wired to the `move_xembed_window` request, so it will never receive `done` events.

Here you allocate a `PluginManagerCallback` but never pass it to the Wayland `move_xembed_window` request, so its `plugin_manager_callback_v1_done()` will never be called. You should use the generated API overload that accepts a `plugin_manager_callback_v1 *` (or equivalent) and construct `PluginManagerCallback` with the proxy returned from `move_xembed_window`, for example:

```cpp
auto callback = new PluginManagerCallback(this, move_xembed_window(xembedWinId, pluginId, itemKey));
return callback;
```

Adjust the exact construction to match your generated bindings, ensuring the `new_id` callback proxy is bound to `PluginManagerCallback`.
</issue_to_address>

### Comment 2
<location> `src/tray-wayland-integration/pluginmanagerintegration_p.h:48-52` </location>
<code_context>
+    static PluginManagerIntegration *s_instance;
 };
+
+class PluginManagerCallback : public QObject, public QtWayland::plugin_manager_callback_v1
+{
+    Q_OBJECT
+public:
+    PluginManagerCallback();
+    ~PluginManagerCallback();
+
</code_context>

<issue_to_address>
**issue (bug_risk):** `PluginManagerCallback`'s construction doesn’t initialize the underlying Wayland proxy, which can break the generated base class expectations.

The generated `QtWayland::plugin_manager_callback_v1` is meant to be constructed with a valid `wl_registry`/`wl_proxy` (or via the `new_id` factory), not default-constructed. With the current empty ctor, the base may hold a null/invalid proxy and `plugin_manager_callback_v1_done()` could be called on an unbound object.

To avoid this, consider:
- Removing the public default constructor, and
- Adding a constructor that takes the handle returned for the `callback` `new_id` of `move_xembed_window` (e.g. a `struct ::wl_proxy *` or forwarding to the appropriate base-class ctor),
so the instance is always bound to the correct Wayland object.
</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.

@deepin-bot
Copy link

deepin-bot bot commented Jan 15, 2026

TAG Bot

New tag: 2.0.22
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #415

@BLumia BLumia force-pushed the protocol-move-xembed-window branch from b90a156 to 058940d Compare January 16, 2026 08:16
@BLumia BLumia force-pushed the protocol-move-xembed-window branch from 1c2d742 to 8bccff2 Compare January 19, 2026 07:15
@deepin-ci-robot
Copy link

deepin pr auto review

这段代码主要是为了在 Wayland 会话下实现 XEmbed 窗口的移动功能,以替代 X11 下直接移动窗口的方式。以下是对这段代码的详细审查和改进建议:

1. 语法逻辑审查

问题 1:潜在的资源泄漏
pluginmanagerintegration.cpp 中:

struct ::wl_callback *PluginManagerIntegration::moveXembedWindow(uint32_t xembedWinId, const QString &pluginId, const QString &itemKey)
{
    auto callback = move_xembed_window(xembedWinId, pluginId, itemKey);
    wl_callback_add_listener(callback, &xembedWindowMovedListener, this);
    return callback;
}

如果 move_xembed_window 返回 nullptr(例如 Wayland 协议错误),wl_callback_add_listener 将导致崩溃。
建议:添加空指针检查。

问题 2:超时处理后的状态
xembedprotocolhandler.cpp 中:

if (timer.isActive()) {
    timer.stop();
} else {
    qDebug() << "requestMoveXembedWindow() timeout";
}

超时后,代码仅打印日志,但未处理窗口可能处于错误位置的状态。
建议:添加回退机制或错误处理逻辑。

问题 3:静态实例未初始化
pluginmanagerintegration_p.h 中声明了 static PluginManagerIntegration *s_instance;,但未在任何地方初始化或使用。
建议:如果不需要,请删除该声明;如果需要,请实现单例模式。


2. 代码质量审查

问题 1:魔法数字

timer.start(3000); // 3秒超时

建议:将超时时间定义为常量,例如:

static constexpr int MOVE_WINDOW_TIMEOUT_MS = 3000;
timer.start(MOVE_WINDOW_TIMEOUT_MS);

问题 2:日志级别
使用 qDebug() 打印超时信息可能不够显眼。
建议:使用 qWarning()qCritical()

qWarning() << "requestMoveXembedWindow() timeout after" << MOVE_WINDOW_TIMEOUT_MS << "ms";

问题 3:代码重复
pluginsurface.cpp 中,lambda 表达式与 requestMessage 的实现类似。
建议:考虑统一处理这类请求,减少重复代码。


3. 代码性能审查

问题 1:阻塞主线程

QEventLoop loop;
// ...
loop.exec();

使用 QEventLoop::exec() 会阻塞主线程,可能导致界面卡顿。
建议:考虑使用异步回调或信号槽机制替代。

问题 2:频繁的环境变量检查

if (qgetenv("XDG_SESSION_TYPE") == "wayland") {

如果该函数被频繁调用,每次检查环境变量会有性能开销。
建议:在类初始化时缓存会话类型:

bool XembedProtocolHandler::isWaylandSession() const {
    static const bool isWayland = (qgetenv("XDG_SESSION_TYPE") == "wayland");
    return isWayland;
}

4. 代码安全审查

问题 1:空指针解引用风险

auto plugin = Plugin::EmbedPlugin::get(window()->windowHandle());

如果 window()windowHandle() 返回 nullptr,会导致崩溃。
建议:添加空指针检查:

auto handle = window() ? window()->windowHandle() : nullptr;
if (!handle) {
    qWarning() << "Failed to get window handle";
    return QPoint();
}
auto plugin = Plugin::EmbedPlugin::get(handle);
if (!plugin) {
    qWarning() << "Failed to get EmbedPlugin";
    return QPoint();
}

问题 2:Wayland 协议安全性
新增的 move_xembed_window 协议请求未对参数进行验证。
建议:在协议实现中添加参数验证逻辑,防止恶意或错误的参数导致问题。


5. 改进后的代码示例

以下是部分改进后的代码示例:

xembedprotocolhandler.cpp

QPoint XembedProtocolHandler::updateEmbedWindowPosForGetInputEvent()
{
    static constexpr int MOVE_WINDOW_TIMEOUT_MS = 3000;
    static const bool isWayland = (qgetenv("XDG_SESSION_TYPE") == "wayland");

    if (isWayland) {
        auto handle = window() ? window()->windowHandle() : nullptr;
        if (!handle) {
            qWarning() << "Failed to get window handle";
            return QPoint();
        }

        auto plugin = Plugin::EmbedPlugin::get(handle);
        if (!plugin) {
            qWarning() << "Failed to get EmbedPlugin";
            return QPoint();
        }

        QEventLoop loop;
        connect(plugin, &Plugin::EmbedPlugin::xembedWindowMoved, &loop, &QEventLoop::quit);
        QTimer timer;
        timer.setSingleShot(true);
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        timer.start(MOVE_WINDOW_TIMEOUT_MS);

        plugin->requestMoveXembedWindow(m_containerWid);
        loop.exec();

        if (!timer.isActive()) {
            qWarning() << "requestMoveXembedWindow() timeout after" << MOVE_WINDOW_TIMEOUT_MS << "ms";
            // 添加回退逻辑
        }
    } else {
        QPoint p = UTIL->getMousePos();
        UTIL->moveX11Window(m_containerWid, p.x(), p.y());
    }

    UTIL->setX11WindowInputShape(m_containerWid, QSize(1, 1));
    return QPoint();
}

pluginmanagerintegration.cpp

struct ::wl_callback *PluginManagerIntegration::moveXembedWindow(uint32_t xembedWinId, const QString &pluginId, const QString &itemKey)
{
    auto callback = move_xembed_window(xembedWinId, pluginId, itemKey);
    if (!callback) {
        qWarning() << "Failed to move xembed window: move_xembed_window returned nullptr";
        return nullptr;
    }
    wl_callback_add_listener(callback, &xembedWindowMovedListener, this);
    return callback;
}

总结

这段代码在功能上实现了 Wayland 下 XEmbed 窗口的移动,但在错误处理、性能优化和安全性方面还有改进空间。建议优先处理空指针检查和超时后的状态恢复问题,以提高代码的健壮性。

@BLumia BLumia requested a review from tsic404 January 19, 2026 08:02
@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BLumia, tsic404

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

@BLumia BLumia merged commit be31f54 into linuxdeepin:master Jan 20, 2026
10 checks passed
@BLumia BLumia deleted the protocol-move-xembed-window branch January 20, 2026 06:57
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.

3 participants