diff --git a/panels/dock/DockCompositor.qml b/panels/dock/DockCompositor.qml index d9d75ff5f..730cdfe40 100644 --- a/panels/dock/DockCompositor.qml +++ b/panels/dock/DockCompositor.qml @@ -20,6 +20,10 @@ Item { property alias dockSize: pluginManager.dockSize property var moveXEmbedWindowHandler: null + function notifyXEmbedWindowMoveResult(wid, success) { + pluginManager.notifyXEmbedWindowMoveResult(wid, success) + } + property ListModel trayPluginSurfaces: ListModel {} property ListModel quickPluginSurfaces: ListModel {} property ListModel fixedPluginSurfaces: ListModel {} @@ -115,11 +119,15 @@ Item { dockCompositor.popupClosed() } - onMoveXEmbedWindowRequested: (wid, pluginId, itemKey, dx, dy) => { - console.log("move xembed window requested:", wid, pluginId, itemKey, dx, dy) + onMoveXEmbedWindowRequested: (wid, pluginId, itemKey, dx, dy, anchorWindow) => { + console.log("move xembed window requested:", wid, pluginId, itemKey, dx, dy, "anchorWindow:", anchorWindow) if (typeof dockCompositor.moveXEmbedWindowHandler === 'function') { - var success = dockCompositor.moveXEmbedWindowHandler(wid, dx, dy) - pluginManager.notifyXEmbedWindowMoveResult(wid, success) + var sent = dockCompositor.moveXEmbedWindowHandler(wid, dx, dy, anchorWindow) + if (!sent) { + // Pre-check failed (e.g. manager not ready) — no wl_callback registered + pluginManager.notifyXEmbedWindowMoveResult(wid, false) + } + // If sent, result arrives asynchronously via xembedWindowMoveResult signal } else { console.warn("moveXEmbedWindowHandler not available") pluginManager.notifyXEmbedWindowMoveResult(wid, false) diff --git a/panels/dock/dockhelper.h b/panels/dock/dockhelper.h index 38f889b6e..0f2f50d12 100644 --- a/panels/dock/dockhelper.h +++ b/panels/dock/dockhelper.h @@ -8,6 +8,7 @@ #include "dockpanel.h" #include +#include namespace dock { class DockWakeUpArea; @@ -22,8 +23,8 @@ class DockHelper : public QObject void enterScreen(QScreen *screen); void leaveScreen(); - // Move XEmbed window relative to dock surface (no-op on X11) - virtual bool moveXEmbedWindow(uint32_t wid, double dx, double dy) { return false; } + // Move XEmbed window relative to anchor window's surface (no-op on X11) + virtual bool moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow = nullptr) { return false; } Q_SIGNALS: void isWindowOverlapChanged(bool overlap); diff --git a/panels/dock/dockpanel.cpp b/panels/dock/dockpanel.cpp index c672686aa..0a47b765f 100644 --- a/panels/dock/dockpanel.cpp +++ b/panels/dock/dockpanel.cpp @@ -162,6 +162,8 @@ bool DockPanel::init() auto platformName = QGuiApplication::platformName(); if (QStringLiteral("wayland") == platformName) { m_helper = new WaylandDockHelper(this); + connect(static_cast(m_helper), &WaylandDockHelper::xembedWindowMoveResult, + this, &DockPanel::xembedWindowMoveResult); // Fallback to DGuiApplicationHelper for theme color when wayland wallpaper color is not available. // TODO: remove this when initWallpaperColorManager is re-enabled QObject::connect(Dtk::Gui::DGuiApplicationHelper::instance(), &Dtk::Gui::DGuiApplicationHelper::themeTypeChanged, @@ -495,10 +497,10 @@ void DockPanel::setIsResizing(bool resizing) emit isResizingChanged(m_isResizing); } -bool DockPanel::moveXEmbedWindow(uint32_t wid, double dx, double dy) +bool DockPanel::moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow) { if (m_helper) { - return m_helper->moveXEmbedWindow(wid, dx, dy); + return m_helper->moveXEmbedWindow(wid, dx, dy, anchorWindow); } return false; } diff --git a/panels/dock/dockpanel.h b/panels/dock/dockpanel.h index 6101fd2a5..8fcc14fd6 100644 --- a/panels/dock/dockpanel.h +++ b/panels/dock/dockpanel.h @@ -84,8 +84,9 @@ class DockPanel : public DS_NAMESPACE::DPanel, public QDBusContext Q_INVOKABLE void notifyDockPositionChanged(int offsetX, int offsetY); - // Move XEmbed window relative to dock surface (Wayland only) - Q_INVOKABLE bool moveXEmbedWindow(uint32_t wid, double dx, double dy); + // Move XEmbed window relative to anchor window's surface (Wayland only) + // anchorWindow: the window containing the plugin item (dock panel or popup window) + Q_INVOKABLE bool moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow = nullptr); bool showInPrimary() const; void setShowInPrimary(bool newShowInPrimary); @@ -138,6 +139,9 @@ private Q_SLOTS: void contextDraggingChanged(); void isResizingChanged(bool isResizing); + // Emitted when async XEmbed window move completes + void xembedWindowMoveResult(uint32_t wid, bool success); + private: ColorTheme m_theme; HideState m_hideState; diff --git a/panels/dock/package/main.qml b/panels/dock/package/main.qml index bde239b72..b7d7e87b1 100644 --- a/panels/dock/package/main.qml +++ b/panels/dock/package/main.qml @@ -855,10 +855,16 @@ Window { return Panel.devicePixelRatio }) - DockCompositor.moveXEmbedWindowHandler = function(wid, dx, dy) { - return Panel.moveXEmbedWindow(wid, dx, dy) + DockCompositor.moveXEmbedWindowHandler = function(wid, dx, dy, anchorWindow) { + return Panel.moveXEmbedWindow(wid, dx, dy, anchorWindow) + // If true: result arrives asynchronously via Panel.xembedWindowMoveResult signal + // If false: pre-check failed, caller should notify failure immediately } + Panel.xembedWindowMoveResult.connect(function(wid, success) { + DockCompositor.notifyXEmbedWindowMoveResult(wid, success) + }) + dock.itemIconSizeBase = dock.dockItemMaxSize dock.visible = Panel.hideState !== Dock.Hide changeDragAreaAnchor() diff --git a/panels/dock/pluginmanagerextension.cpp b/panels/dock/pluginmanagerextension.cpp index 9fb999e1d..c6ff5e517 100644 --- a/panels/dock/pluginmanagerextension.cpp +++ b/panels/dock/pluginmanagerextension.cpp @@ -264,6 +264,16 @@ QPoint PluginSurface::itemPosition() const return m_itemPosition; } +void PluginSurface::setAnchorWindow(QQuickWindow *window) +{ + m_anchorWindow = window; +} + +QQuickWindow *PluginSurface::anchorWindow() const +{ + return m_anchorWindow; +} + void PluginSurface::plugin_mouse_event(QtWaylandServer::plugin::Resource *resource, int32_t type) { Q_UNUSED(resource) @@ -706,8 +716,8 @@ void PluginManager::plugin_manager_v1_move_xembed_window(Resource *resource, uin // Store pending callback for later response (supports concurrent requests) m_pendingXEmbedCallbacks[xembed_winid] = {callback, resource->handle}; - // Emit signal with position info for QML to handle - Q_EMIT moveXEmbedWindowRequested(xembed_winid, plugin_id, item_key, dx, dy); + // Emit signal with position info and anchor window for QML to handle + Q_EMIT moveXEmbedWindowRequested(xembed_winid, plugin_id, item_key, dx, dy, pluginSurface->anchorWindow()); } void PluginManager::notifyXEmbedWindowMoveResult(uint32_t wid, bool success) diff --git a/panels/dock/pluginmanagerextension_p.h b/panels/dock/pluginmanagerextension_p.h index 1b7b7ea6e..45e94b101 100644 --- a/panels/dock/pluginmanagerextension_p.h +++ b/panels/dock/pluginmanagerextension_p.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -99,8 +100,8 @@ class PluginManager : public QWaylandCompositorExtensionTemplate, void dockSizeChanged(); void requestShutdown(const QString &type); // Signal emitted when XEmbed window move is requested - // Parameters: wid (window ID), pluginId, itemKey, dx (relative x offset), dy (relative y offset) - void moveXEmbedWindowRequested(uint32_t wid, const QString &pluginId, const QString &itemKey, double dx, double dy); + // Parameters: wid (window ID), pluginId, itemKey, dx, dy, anchorWindow (the window containing the plugin item) + void moveXEmbedWindowRequested(uint32_t wid, const QString &pluginId, const QString &itemKey, double dx, double dy, QQuickWindow *anchorWindow); private Q_SLOTS: void onFontChanged(); @@ -192,10 +193,13 @@ class PluginSurface : public QWaylandShellSurfaceTemplate, public Q_INVOKABLE void updatePluginGeometry(const QRect &geometry); Q_INVOKABLE void setGlobalPos(const QPoint &pos); + Q_INVOKABLE void setAnchorWindow(QQuickWindow *window); - // Position relative to the dock window, set from QML via updatePluginGeometry + // Position relative to the containing window, set from QML via updatePluginGeometry QPoint itemPosition() const; + QQuickWindow *anchorWindow() const; + int margins() const; void setMargins(int newMargins); @@ -234,7 +238,8 @@ class PluginSurface : public QWaylandShellSurfaceTemplate, public int m_margins = 0; int m_height; int m_width; - QPoint m_itemPosition; // Position relative to dock window, for XEmbed window positioning + QPoint m_itemPosition; + QPointer m_anchorWindow; }; class PluginPopup : public QWaylandShellSurfaceTemplate, public QtWaylandServer::plugin_popup diff --git a/panels/dock/tray/package/ActionLegacyTrayPluginDelegate.qml b/panels/dock/tray/package/ActionLegacyTrayPluginDelegate.qml index 2a7fe3919..b67f190db 100644 --- a/panels/dock/tray/package/ActionLegacyTrayPluginDelegate.qml +++ b/panels/dock/tray/package/ActionLegacyTrayPluginDelegate.qml @@ -92,6 +92,7 @@ AppletItemButton { if (!pluginItem.plugin || !itemVisible) return updatePluginMargins() + pluginItem.plugin.setAnchorWindow(pluginItem.Window.window) pluginItem.plugin.updatePluginGeometry(Qt.rect(pluginItem.itemGlobalPoint.x, pluginItem.itemGlobalPoint.y, 0, 0)) pluginItem.plugin.setGlobalPos(pluginItem.itemGlobalPos) } @@ -105,6 +106,7 @@ AppletItemButton { if (!pluginItem.plugin || !itemVisible) return updatePluginMargins() + pluginItem.plugin.setAnchorWindow(pluginItem.Window.window) if (pluginItem.itemGlobalPoint.x >= 0 && pluginItem.itemGlobalPoint.y >= 0) { pluginItem.plugin.updatePluginGeometry(Qt.rect(pluginItem.itemGlobalPoint.x, pluginItem.itemGlobalPoint.y, 0, 0)) } diff --git a/panels/dock/waylanddockhelper.cpp b/panels/dock/waylanddockhelper.cpp index 00eeb1ff2..5b23bcf08 100644 --- a/panels/dock/waylanddockhelper.cpp +++ b/panels/dock/waylanddockhelper.cpp @@ -158,7 +158,7 @@ void WaylandDockHelper::setDockColorTheme(const ColorTheme &theme) m_panel->setColorTheme(theme); } -bool WaylandDockHelper::moveXEmbedWindow(uint32_t wid, double dx, double dy) +bool WaylandDockHelper::moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow) { // Update dock wl_surface if needed if (!m_dockWlSurface && m_panel->window()) { @@ -168,14 +168,43 @@ bool WaylandDockHelper::moveXEmbedWindow(uint32_t wid, double dx, double dy) } } - if (!m_ddeShellManager || !m_ddeShellManager->isActive() || !m_dockWlSurface) { + struct ::wl_surface *anchorSurface = m_dockWlSurface; + + if (anchorWindow) { + auto waylandWindow = dynamic_cast(anchorWindow->handle()); + if (waylandWindow && waylandWindow->waylandSurface()) { + anchorSurface = waylandWindow->waylandSurface()->object(); + } + } + + if (!m_ddeShellManager || !m_ddeShellManager->isActive() || !anchorSurface) { qWarning() << "WaylandDockHelper::moveXEmbedWindow: not ready, manager active:" << (m_ddeShellManager && m_ddeShellManager->isActive()) - << "surface:" << (m_dockWlSurface != nullptr); + << "surface:" << (anchorSurface != nullptr); return false; } - m_ddeShellManager->setXWindowPositionRelative(wid, m_dockWlSurface, dx, dy); + struct wl_callback *cb = m_ddeShellManager->setXWindowPositionRelative(wid, anchorSurface, dx, dy); + + // Register wl_callback listener — result arrives asynchronously + if (!cb) { + qWarning() << "WaylandDockHelper::moveXEmbedWindow: setXWindowPositionRelative returned null callback"; + Q_EMIT xembedWindowMoveResult(wid, false); + return true; + } + + struct CallbackData { uint32_t wid; WaylandDockHelper *helper; }; + static struct wl_callback_listener s_callbackListener = { + .done = [](void *data, wl_callback *callback, uint32_t callback_data) { + auto *d = static_cast(data); + bool success = (callback_data == 0); + Q_EMIT d->helper->xembedWindowMoveResult(d->wid, success); + wl_callback_destroy(callback); + delete d; + } + }; + wl_callback_add_listener(cb, &s_callbackListener, new CallbackData{wid, this}); + return true; } diff --git a/panels/dock/waylanddockhelper.h b/panels/dock/waylanddockhelper.h index 65d747f3a..ff053173b 100644 --- a/panels/dock/waylanddockhelper.h +++ b/panels/dock/waylanddockhelper.h @@ -31,10 +31,14 @@ class WaylandDockHelper : public DockHelper void setDockColorTheme(const ColorTheme &theme); QString dockScreenName(); - // Move XEmbed window relative to dock surface - // dx, dy: offset relative to dock surface top-left - // Returns true if the request was sent successfully - bool moveXEmbedWindow(uint32_t wid, double dx, double dy) override; + // Move XEmbed window relative to anchor window's surface + // anchorWindow: the window containing the plugin item; falls back to dock panel if null + // dx, dy: offset relative to anchor window top-left + // Returns true if the request was sent (does NOT guarantee completion) + bool moveXEmbedWindow(uint32_t wid, double dx, double dy, QQuickWindow *anchorWindow = nullptr) override; + +Q_SIGNALS: + void xembedWindowMoveResult(uint32_t wid, bool success); protected: bool currentActiveWindowFullscreened() override;