Skip to content

Commit 46c66aa

Browse files
BLumiajustforlxz
authored andcommitted
feat: support move xembed window on treeland
通过 treeland 提供的 API 移动 xembed 窗口 Log:
1 parent 263f2ec commit 46c66aa

10 files changed

Lines changed: 178 additions & 3 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ panels/dock/tray/plugins/power/dbusinterface/generation_dbus_interface/
1616
obj-x86_64-linux-gnu/
1717
*.autosave
1818
*/utils/dbus/xml2cpp/*_interface.*
19+
.qmlls.ini

panels/dock/DockCompositor.qml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -18,6 +18,7 @@ Item {
1818
property alias dockPosition: pluginManager.dockPosition
1919
property alias dockColorTheme: pluginManager.dockColorTheme
2020
property alias dockSize: pluginManager.dockSize
21+
property var moveXEmbedWindowHandler: null
2122

2223
property ListModel trayPluginSurfaces: ListModel {}
2324
property ListModel quickPluginSurfaces: ListModel {}
@@ -113,6 +114,17 @@ Item {
113114
console.log("quick panel closed")
114115
dockCompositor.popupClosed()
115116
}
117+
118+
onMoveXEmbedWindowRequested: (wid, pluginId, itemKey, dx, dy) => {
119+
console.log("move xembed window requested:", wid, pluginId, itemKey, dx, dy)
120+
if (typeof dockCompositor.moveXEmbedWindowHandler === 'function') {
121+
var success = dockCompositor.moveXEmbedWindowHandler(wid, dx, dy)
122+
pluginManager.notifyXEmbedWindowMoveResult(wid, success)
123+
} else {
124+
console.warn("moveXEmbedWindowHandler not available")
125+
pluginManager.notifyXEmbedWindowMoveResult(wid, false)
126+
}
127+
}
116128
}
117129

118130
PluginScaleManager{

panels/dock/dockhelper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class DockHelper : public QObject
2121

2222
void enterScreen(QScreen *screen);
2323
void leaveScreen();
24+
25+
// Move XEmbed window relative to dock surface (no-op on X11)
26+
virtual bool moveXEmbedWindow(uint32_t wid, double dx, double dy) { return false; }
2427

2528
Q_SIGNALS:
2629
void isWindowOverlapChanged(bool overlap);

panels/dock/dockpanel.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,14 @@ void DockPanel::setIsResizing(bool resizing)
489489
m_isResizing = resizing;
490490
emit isResizingChanged(m_isResizing);
491491
}
492+
493+
bool DockPanel::moveXEmbedWindow(uint32_t wid, double dx, double dy)
494+
{
495+
if (m_helper) {
496+
return m_helper->moveXEmbedWindow(wid, dx, dy);
497+
}
498+
return false;
499+
}
492500
}
493501

494502
#include "dockpanel.moc"

panels/dock/dockpanel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ class DockPanel : public DS_NAMESPACE::DPanel, public QDBusContext
8383
Q_INVOKABLE void openDockSettings() const;
8484

8585
Q_INVOKABLE void notifyDockPositionChanged(int offsetX, int offsetY);
86+
87+
// Move XEmbed window relative to dock surface (Wayland only)
88+
Q_INVOKABLE bool moveXEmbedWindow(uint32_t wid, double dx, double dy);
8689

8790
bool showInPrimary() const;
8891
void setShowInPrimary(bool newShowInPrimary);

panels/dock/package/main.qml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,10 @@ Window {
855855
return Panel.devicePixelRatio
856856
})
857857

858+
DockCompositor.moveXEmbedWindowHandler = function(wid, dx, dy) {
859+
return Panel.moveXEmbedWindow(wid, dx, dy)
860+
}
861+
858862
dock.itemIconSizeBase = dock.dockItemMaxSize
859863
dock.visible = Panel.hideState !== Dock.Hide
860864
changeDragAreaAnchor()

panels/dock/pluginmanagerextension.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <DPlatformTheme>
1111

1212
#include <cstdint>
13+
#include <wayland-server-core.h>
1314

1415
#include <QtWaylandCompositor/QWaylandSurface>
1516
#include <QtWaylandCompositor/QWaylandResource>
@@ -254,9 +255,15 @@ bool PluginSurface::isItemActive() const
254255

255256
void PluginSurface::updatePluginGeometry(const QRect &geometry)
256257
{
258+
m_itemPosition = geometry.topLeft();
257259
send_geometry(geometry.x(), geometry.y(), geometry.width(), geometry.height());
258260
}
259261

262+
QPoint PluginSurface::itemPosition() const
263+
{
264+
return m_itemPosition;
265+
}
266+
260267
void PluginSurface::plugin_mouse_event(QtWaylandServer::plugin::Resource *resource, int32_t type)
261268
{
262269
Q_UNUSED(resource)
@@ -673,6 +680,62 @@ void PluginManager::plugin_manager_v1_create_popup_at(Resource *resource, const
673680
Q_EMIT pluginPopupCreated(plugin);
674681
}
675682

683+
void PluginManager::plugin_manager_v1_move_xembed_window(Resource *resource, uint32_t xembed_winid, const QString &plugin_id, const QString &item_key, uint32_t callback)
684+
{
685+
qInfo() << "server pluginManager receive move_xembed_window:" << xembed_winid << plugin_id << item_key;
686+
687+
// Look up the plugin surface to get its position relative to the dock window
688+
PluginSurface *pluginSurface = findPluginSurface(plugin_id, item_key);
689+
if (!pluginSurface) {
690+
qWarning() << "move_xembed_window: plugin surface not found for" << plugin_id << item_key;
691+
// Send error response immediately if surface not found
692+
wl_resource *callbackResource = wl_resource_create(resource->client(), &wl_callback_interface,
693+
1, callback);
694+
if (callbackResource) {
695+
wl_callback_send_done(callbackResource, 1); // 1 = error
696+
wl_resource_destroy(callbackResource);
697+
} else {
698+
qWarning() << "move_xembed_window: failed to create callback resource";
699+
}
700+
return;
701+
}
702+
703+
double dx = pluginSurface->itemPosition().x();
704+
double dy = pluginSurface->itemPosition().y();
705+
706+
// Store pending callback for later response (supports concurrent requests)
707+
m_pendingXEmbedCallbacks[xembed_winid] = {callback, resource->handle};
708+
709+
// Emit signal with position info for QML to handle
710+
Q_EMIT moveXEmbedWindowRequested(xembed_winid, plugin_id, item_key, dx, dy);
711+
}
712+
713+
void PluginManager::notifyXEmbedWindowMoveResult(uint32_t wid, bool success)
714+
{
715+
if (!m_pendingXEmbedCallbacks.contains(wid)) {
716+
qWarning() << "notifyXEmbedWindowMoveResult: no pending callback for wid" << wid;
717+
return;
718+
}
719+
720+
PendingXEmbedCallback pending = m_pendingXEmbedCallbacks.take(wid);
721+
722+
// Get client from stored resource (safe during request handling)
723+
struct ::wl_client *client = wl_resource_get_client(pending.resource);
724+
if (!client) {
725+
qWarning() << "notifyXEmbedWindowMoveResult: client no longer exists for wid" << wid;
726+
return;
727+
}
728+
729+
wl_resource *callbackResource = wl_resource_create(client, &wl_callback_interface,
730+
1, pending.callback);
731+
if (callbackResource) {
732+
wl_callback_send_done(callbackResource, success ? 0 : 1);
733+
wl_resource_destroy(callbackResource);
734+
} else {
735+
qWarning() << "notifyXEmbedWindowMoveResult: failed to create callback resource for wid" << wid;
736+
}
737+
}
738+
676739
QJsonObject PluginManager::getRootObj(const QString &jsonStr) {
677740
QJsonParseError jsonParseError;
678741
const QJsonDocument &resultDoc = QJsonDocument::fromJson(jsonStr.toLocal8Bit(), &jsonParseError);
@@ -757,6 +820,16 @@ void PluginManager::onActiveColorChanged()
757820
});
758821
}
759822

823+
PluginSurface* PluginManager::findPluginSurface(const QString &pluginId, const QString &itemKey) const
824+
{
825+
for (PluginSurface *plugin : m_pluginSurfaces) {
826+
if (plugin->pluginId() == pluginId && plugin->itemKey() == itemKey) {
827+
return plugin;
828+
}
829+
}
830+
return nullptr;
831+
}
832+
760833
void PluginManager::onThemeChanged()
761834
{
762835
foreachPluginSurface([this](Resource *source) {

panels/dock/pluginmanagerextension_p.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include <QMap>
78
#include <QPointer>
89
#include <QtWaylandCompositor/QWaylandCompositor>
910
#include <QtWaylandCompositor/QWaylandQuickExtension>
@@ -65,6 +66,11 @@ class PluginManager : public QWaylandCompositorExtensionTemplate<PluginManager>,
6566

6667
Q_INVOKABLE void updateDockOverflowState(int state);
6768
Q_INVOKABLE void setPopupMinHeight(int height);
69+
70+
// Called from QML when moveXEmbedWindowRequested is handled
71+
// wid: the XEmbed window ID from the original request
72+
// result: true = success, false = error
73+
Q_INVOKABLE void notifyXEmbedWindowMoveResult(uint32_t wid, bool result);
6874

6975
uint32_t dockPosition() const;
7076
void setDockPosition(uint32_t dockPosition);
@@ -92,6 +98,9 @@ class PluginManager : public QWaylandCompositorExtensionTemplate<PluginManager>,
9298
void messageRequest(PluginSurface *, const QString &msg);
9399
void dockSizeChanged();
94100
void requestShutdown(const QString &type);
101+
// Signal emitted when XEmbed window move is requested
102+
// Parameters: wid (window ID), pluginId, itemKey, dx (relative x offset), dy (relative y offset)
103+
void moveXEmbedWindowRequested(uint32_t wid, const QString &pluginId, const QString &itemKey, double dx, double dy);
95104

96105
private Q_SLOTS:
97106
void onFontChanged();
@@ -103,6 +112,7 @@ private Q_SLOTS:
103112
virtual void plugin_manager_v1_request_message(Resource *resource, const QString &plugin_id, const QString &item_key, const QString &msg) override;
104113
virtual void plugin_manager_v1_create_popup_at(Resource *resource, const QString &plugin_id, const QString &item_key, int32_t type, int32_t x, int32_t y, struct ::wl_resource *surface, uint32_t id) override;
105114
virtual void plugin_manager_v1_create_plugin(Resource *resource, const QString &plugin_id, const QString &item_key, const QString &display_name, int32_t plugin_flags, int32_t type, int32_t size_policy, struct ::wl_resource *surface, uint32_t id) override;
115+
virtual void plugin_manager_v1_move_xembed_window(Resource *resource, uint32_t xembed_winid, const QString &plugin_id, const QString &item_key, uint32_t callback) override;
106116

107117
private:
108118
static QJsonObject getRootObj(const QString &jsonStr);
@@ -113,14 +123,24 @@ private Q_SLOTS:
113123
QString popupMinHeightMsg() const;
114124
using PluginSurfaceCallback = std::function<void(Resource *)>;
115125
void foreachPluginSurface(PluginSurfaceCallback callback);
126+
PluginSurface* findPluginSurface(const QString &pluginId, const QString &itemKey) const;
116127

117128
private:
129+
struct PendingXEmbedCallback {
130+
uint32_t callback;
131+
struct ::wl_resource *resource; // Store resource instead of client for lifecycle safety
132+
};
133+
118134
QList<PluginSurface*> m_pluginSurfaces;
119135

120136
uint32_t m_dockPosition = 0;
121137
uint32_t m_dockColorTheme = 0;
122138
QSize m_dockSize;
123139
int m_popupMinHeight = 0;
140+
141+
// Map of pending XEmbed callbacks: wid -> callback info
142+
// Supports multiple concurrent requests from different clients
143+
QMap<uint32_t, PendingXEmbedCallback> m_pendingXEmbedCallbacks;
124144
};
125145

126146
class PluginSurface : public QWaylandShellSurfaceTemplate<PluginSurface>, public QtWaylandServer::plugin
@@ -173,6 +193,9 @@ class PluginSurface : public QWaylandShellSurfaceTemplate<PluginSurface>, public
173193
Q_INVOKABLE void updatePluginGeometry(const QRect &geometry);
174194
Q_INVOKABLE void setGlobalPos(const QPoint &pos);
175195

196+
// Position relative to the dock window, set from QML via updatePluginGeometry
197+
QPoint itemPosition() const;
198+
176199
int margins() const;
177200
void setMargins(int newMargins);
178201

@@ -211,6 +234,7 @@ class PluginSurface : public QWaylandShellSurfaceTemplate<PluginSurface>, public
211234
int m_margins = 0;
212235
int m_height;
213236
int m_width;
237+
QPoint m_itemPosition; // Position relative to dock window, for XEmbed window positioning
214238
};
215239

216240
class PluginPopup : public QWaylandShellSurfaceTemplate<PluginPopup>, public QtWaylandServer::plugin_popup

panels/dock/waylanddockhelper.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -28,6 +28,14 @@ WaylandDockHelper::WaylandDockHelper(DockPanel *panel)
2828
if (auto applet = bridge.applet()) {
2929
connect(applet, SIGNAL(windowFullscreenChanged(bool)), this, SLOT(setCurrentActiveWindowFullscreened(bool)));
3030
}
31+
32+
// Store dock window's wl_surface for XEmbed window positioning
33+
if (m_panel->window()) {
34+
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow*>(m_panel->window()->handle());
35+
if (waylandWindow) {
36+
m_dockWlSurface = waylandWindow->waylandSurface()->object();
37+
}
38+
}
3139

3240
connect(m_panel, &DockPanel::rootObjectChanged, this, [this]() {
3341
m_wallpaperColorManager->watchScreen(dockScreenName());
@@ -144,6 +152,27 @@ void WaylandDockHelper::setDockColorTheme(const ColorTheme &theme)
144152
m_panel->setColorTheme(theme);
145153
}
146154

155+
bool WaylandDockHelper::moveXEmbedWindow(uint32_t wid, double dx, double dy)
156+
{
157+
// Update dock wl_surface if needed
158+
if (!m_dockWlSurface && m_panel->window()) {
159+
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow*>(m_panel->window()->handle());
160+
if (waylandWindow && waylandWindow->waylandSurface()) {
161+
m_dockWlSurface = waylandWindow->waylandSurface()->object();
162+
}
163+
}
164+
165+
if (!m_ddeShellManager || !m_ddeShellManager->isActive() || !m_dockWlSurface) {
166+
qWarning() << "WaylandDockHelper::moveXEmbedWindow: not ready, manager active:"
167+
<< (m_ddeShellManager && m_ddeShellManager->isActive())
168+
<< "surface:" << (m_dockWlSurface != nullptr);
169+
return false;
170+
}
171+
172+
m_ddeShellManager->setXWindowPositionRelative(wid, m_dockWlSurface, dx, dy);
173+
return true;
174+
}
175+
147176
WallpaperColorManager::WallpaperColorManager(WaylandDockHelper *helper)
148177
: QWaylandClientExtensionTemplate<WallpaperColorManager>(treeland_wallpaper_color_manager_v1_interface.version)
149178
, m_helper(helper)
@@ -169,6 +198,14 @@ TreeLandDDEShellManager::TreeLandDDEShellManager()
169198
{
170199
}
171200

201+
struct ::wl_callback *TreeLandDDEShellManager::setXWindowPositionRelative(uint32_t wid, struct ::wl_surface *anchor, double dx, double dy)
202+
{
203+
if (!isActive()) {
204+
return nullptr;
205+
}
206+
return QtWayland::treeland_dde_shell_manager_v1::set_xwindow_position_relative(wid, anchor, wl_fixed_from_double(dx), wl_fixed_from_double(dy));
207+
}
208+
172209
TreeLandWindowOverlapChecker::TreeLandWindowOverlapChecker(WaylandDockHelper *helper, struct ::treeland_window_overlap_checker *checker)
173210
: QWaylandClientExtensionTemplate<TreeLandWindowOverlapChecker>(treeland_dde_shell_manager_v1_interface.version)
174211
, m_helper(helper)

panels/dock/waylanddockhelper.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44
#pragma once
@@ -30,6 +30,11 @@ class WaylandDockHelper : public DockHelper
3030

3131
void setDockColorTheme(const ColorTheme &theme);
3232
QString dockScreenName();
33+
34+
// Move XEmbed window relative to dock surface
35+
// dx, dy: offset relative to dock surface top-left
36+
// Returns true if the request was sent successfully
37+
bool moveXEmbedWindow(uint32_t wid, double dx, double dy) override;
3338

3439
protected:
3540
bool currentActiveWindowFullscreened() override;
@@ -51,6 +56,7 @@ protected Q_SLOTS:
5156
QScopedPointer<WallpaperColorManager> m_wallpaperColorManager;
5257
QScopedPointer<TreeLandWindowOverlapChecker> m_overlapChecker;
5358
QScopedPointer<TreeLandDDEShellManager> m_ddeShellManager;
59+
struct ::wl_surface *m_dockWlSurface = nullptr; // Dock's wl_surface for XEmbed positioning
5460
};
5561

5662
class WallpaperColorManager : public QWaylandClientExtensionTemplate<WallpaperColorManager>, public QtWayland::treeland_wallpaper_color_manager_v1
@@ -75,6 +81,10 @@ class TreeLandDDEShellManager : public QWaylandClientExtensionTemplate<TreeLandD
7581

7682
public:
7783
explicit TreeLandDDEShellManager();
84+
85+
// Move XEmbed window to position relative to anchor surface
86+
// Returns wl_callback* or nullptr if not active
87+
struct ::wl_callback *setXWindowPositionRelative(uint32_t wid, struct ::wl_surface *anchor, double dx, double dy);
7888
};
7989

8090
class TreeLandWindowOverlapChecker : public QWaylandClientExtensionTemplate<TreeLandWindowOverlapChecker>, public QtWayland::treeland_window_overlap_checker

0 commit comments

Comments
 (0)