Skip to content

Commit d0bfc61

Browse files
committed
feat: support move xembed window on treeland
通过 treeland 提供的 API 移动 xembed 窗口 Log:
1 parent 2ccf53c commit d0bfc61

8 files changed

Lines changed: 144 additions & 0 deletions

panels/dock/DockCompositor.qml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,18 @@ Item {
113113
console.log("quick panel closed")
114114
dockCompositor.popupClosed()
115115
}
116+
117+
onMoveXEmbedWindowRequested: (wid, pluginId, itemKey, dx, dy) => {
118+
console.log("move xembed window requested:", wid, pluginId, itemKey, dx, dy)
119+
// Delegate to Panel which has access to WaylandDockHelper
120+
if (Panel && typeof Panel.moveXEmbedWindow === 'function') {
121+
var success = Panel.moveXEmbedWindow(wid, dx, dy)
122+
pluginManager.notifyXEmbedWindowMoveResult(success)
123+
} else {
124+
console.warn("Panel.moveXEmbedWindow not available")
125+
pluginManager.notifyXEmbedWindowMoveResult(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
@@ -481,6 +481,14 @@ void DockPanel::setIsResizing(bool resizing)
481481
m_isResizing = resizing;
482482
emit isResizingChanged(m_isResizing);
483483
}
484+
485+
bool DockPanel::moveXEmbedWindow(uint32_t wid, double dx, double dy)
486+
{
487+
if (m_helper) {
488+
return m_helper->moveXEmbedWindow(wid, dx, dy);
489+
}
490+
return false;
491+
}
484492
}
485493

486494
#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/pluginmanagerextension.cpp

Lines changed: 54 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,43 @@ 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+
Q_UNUSED(resource)
686+
qInfo() << "server pluginManager receive move_xembed_window:" << xembed_winid << plugin_id << item_key;
687+
688+
// Look up the plugin surface to get its position relative to the dock window
689+
double dx = 0.0, dy = 0.0;
690+
PluginSurface *pluginSurface = findPluginSurface(plugin_id, item_key);
691+
if (pluginSurface) {
692+
dx = pluginSurface->itemPosition().x();
693+
dy = pluginSurface->itemPosition().y();
694+
} else {
695+
qWarning() << "move_xembed_window: plugin surface not found for" << plugin_id << item_key;
696+
}
697+
698+
// Store pending callback for later response
699+
m_pendingXEmbedCallback = callback;
700+
m_pendingXEmbedClient = resource->client();
701+
702+
// Emit signal with position info for QML to handle
703+
Q_EMIT moveXEmbedWindowRequested(xembed_winid, plugin_id, item_key, dx, dy);
704+
}
705+
706+
void PluginManager::notifyXEmbedWindowMoveResult(bool success)
707+
{
708+
if (m_pendingXEmbedCallback > 0 && m_pendingXEmbedClient) {
709+
wl_resource *callbackResource = wl_resource_create(m_pendingXEmbedClient, &wl_callback_interface,
710+
1, m_pendingXEmbedCallback);
711+
if (callbackResource) {
712+
wl_callback_send_done(callbackResource, success ? 0 : 1);
713+
wl_resource_destroy(callbackResource);
714+
}
715+
}
716+
m_pendingXEmbedCallback = 0;
717+
m_pendingXEmbedClient = nullptr;
718+
}
719+
676720
QJsonObject PluginManager::getRootObj(const QString &jsonStr) {
677721
QJsonParseError jsonParseError;
678722
const QJsonDocument &resultDoc = QJsonDocument::fromJson(jsonStr.toLocal8Bit(), &jsonParseError);
@@ -757,6 +801,16 @@ void PluginManager::onActiveColorChanged()
757801
});
758802
}
759803

804+
PluginSurface* PluginManager::findPluginSurface(const QString &pluginId, const QString &itemKey) const
805+
{
806+
for (PluginSurface *plugin : m_pluginSurfaces) {
807+
if (plugin->pluginId() == pluginId && plugin->itemKey() == itemKey) {
808+
return plugin;
809+
}
810+
}
811+
return nullptr;
812+
}
813+
760814
void PluginManager::onThemeChanged()
761815
{
762816
foreachPluginSurface([this](Resource *source) {

panels/dock/pluginmanagerextension_p.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ class PluginManager : public QWaylandCompositorExtensionTemplate<PluginManager>,
6565

6666
Q_INVOKABLE void updateDockOverflowState(int state);
6767
Q_INVOKABLE void setPopupMinHeight(int height);
68+
69+
// Called from QML when moveXEmbedWindowRequested is handled
70+
// result: true = success, false = error
71+
Q_INVOKABLE void notifyXEmbedWindowMoveResult(bool result);
6872

6973
uint32_t dockPosition() const;
7074
void setDockPosition(uint32_t dockPosition);
@@ -92,6 +96,9 @@ class PluginManager : public QWaylandCompositorExtensionTemplate<PluginManager>,
9296
void messageRequest(PluginSurface *, const QString &msg);
9397
void dockSizeChanged();
9498
void requestShutdown(const QString &type);
99+
// Signal emitted when XEmbed window move is requested
100+
// Parameters: wid (window ID), pluginId, itemKey, dx (relative x offset), dy (relative y offset)
101+
void moveXEmbedWindowRequested(uint32_t wid, const QString &pluginId, const QString &itemKey, double dx, double dy);
95102

96103
private Q_SLOTS:
97104
void onFontChanged();
@@ -103,6 +110,7 @@ private Q_SLOTS:
103110
virtual void plugin_manager_v1_request_message(Resource *resource, const QString &plugin_id, const QString &item_key, const QString &msg) override;
104111
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;
105112
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;
113+
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;
106114

107115
private:
108116
static QJsonObject getRootObj(const QString &jsonStr);
@@ -113,6 +121,7 @@ private Q_SLOTS:
113121
QString popupMinHeightMsg() const;
114122
using PluginSurfaceCallback = std::function<void(Resource *)>;
115123
void foreachPluginSurface(PluginSurfaceCallback callback);
124+
PluginSurface* findPluginSurface(const QString &pluginId, const QString &itemKey) const;
116125

117126
private:
118127
QList<PluginSurface*> m_pluginSurfaces;
@@ -121,6 +130,10 @@ private Q_SLOTS:
121130
uint32_t m_dockColorTheme = 0;
122131
QSize m_dockSize;
123132
int m_popupMinHeight = 0;
133+
134+
// For pending XEmbed callback response
135+
uint32_t m_pendingXEmbedCallback = 0;
136+
struct ::wl_client *m_pendingXEmbedClient = nullptr;
124137
};
125138

126139
class PluginSurface : public QWaylandShellSurfaceTemplate<PluginSurface>, public QtWaylandServer::plugin
@@ -173,6 +186,9 @@ class PluginSurface : public QWaylandShellSurfaceTemplate<PluginSurface>, public
173186
Q_INVOKABLE void updatePluginGeometry(const QRect &geometry);
174187
Q_INVOKABLE void setGlobalPos(const QPoint &pos);
175188

189+
// Position relative to the dock window, set from QML via updatePluginGeometry
190+
QPoint itemPosition() const;
191+
176192
int margins() const;
177193
void setMargins(int newMargins);
178194

@@ -211,6 +227,7 @@ class PluginSurface : public QWaylandShellSurfaceTemplate<PluginSurface>, public
211227
int m_margins = 0;
212228
int m_height;
213229
int m_width;
230+
QPoint m_itemPosition; // Position relative to dock window, for XEmbed window positioning
214231
};
215232

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

panels/dock/waylanddockhelper.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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)