Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ add_subdirectory(test_color_control)
add_subdirectory(test_shortcut_manager)
add_subdirectory(test_keystate)
add_subdirectory(test_set_wallpaper)
add_subdirectory(test_set_xwindow_position)
22 changes: 22 additions & 0 deletions examples/test_set_xwindow_position/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
find_package(Qt6 REQUIRED COMPONENTS WaylandClient Widgets)
find_package(TreelandProtocols REQUIRED)

qt_add_executable(test-set-xwindow-position
main.cpp
)

qt_generate_wayland_protocol_client_sources(test-set-xwindow-position
FILES
${TREELAND_PROTOCOLS_DATA_DIR}/treeland-dde-shell-v1.xml
NO_INCLUDE_CORE_ONLY
)

target_link_libraries(test-set-xwindow-position
PRIVATE
Qt6::Gui
Qt6::GuiPrivate
Qt6::Widgets
Qt6::WaylandClient
)

install(TARGETS test-set-xwindow-position RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
98 changes: 98 additions & 0 deletions examples/test_set_xwindow_position/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (C) 2025 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include <QDebug>

Check warning on line 4 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QFile>

Check warning on line 5 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QFile> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QGuiApplication>

Check warning on line 6 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QGuiApplication> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QtWidgets/QtWidgets>

Check warning on line 7 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QtWidgets/QtWidgets> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QtWaylandClient/QWaylandClientExtension>

Check warning on line 8 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QtWaylandClient/QWaylandClientExtension> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTimer>

Check warning on line 9 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTimer> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <qpa/qplatformnativeinterface.h>

Check warning on line 11 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <qpa/qplatformnativeinterface.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <qwayland-treeland-dde-shell-v1.h>

Check warning on line 13 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <qwayland-treeland-dde-shell-v1.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <unistd.h>

Check warning on line 15 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <unistd.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

class DDEShellManagerV1
: public QWaylandClientExtensionTemplate<DDEShellManagerV1>
, public QtWayland::treeland_dde_shell_manager_v1
{
Q_OBJECT
public:
DDEShellManagerV1()
: QWaylandClientExtensionTemplate<DDEShellManagerV1>(
treeland_dde_shell_manager_v1_interface.version)
{
}
};

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

qWarning()
<< "In this example, an xeyes will be launched. Its window will be moved to (0, 0) "
"first using X API. Then, A test window of size 640x480 will be created. The xeyes "
"window will be moved to the top-right of the test window once per second using "
"set_xwindow_position_relative().\n\n"
"xeyes should be installed to run this example (sudo apt install x11-apps).";

system("bash -c \"pkill xeyes; xeyes & disown\"");
sleep(1);
system("xdotool search --name \"xeyes\" windowmove 0 0");
sleep(1);
system("xdotool search --name \"xeyes\" > /tmp/xeyes_wid.txt");

QWidget *window = new QWidget();
window->resize(640, 480);
window->setWindowTitle("Test set xwindow position");
window->show();

uint32_t wid = 0;
QFile file("/tmp/xeyes_wid.txt");
if (file.open(QIODevice::ReadWrite)) {
wid = file.readLine().trimmed().toUInt();
file.close();
file.remove();
} else {
qCritical() << "Failed to open /tmp/xeyes_wid.txt";
return 1;
}

DDEShellManagerV1 manager;

struct wl_callback_listener callback_listener = { .done = []([[maybe_unused]] void *data,
wl_callback *callback,
uint32_t ok) {
wl_callback_destroy(callback);
if (ok != 0)
qCritical() << "Failed to set xwindow position relative!";
else
qWarning() << "Successfully set xwindow position relative. Check screen for result.";
} };

QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&] {
if (!manager.isActive()) {
qCritical() << "DDEShellManagerV1 is not active!";
return;
}
struct wl_surface *surface = static_cast<wl_surface *>(
QGuiApplication::platformNativeInterface()->nativeResourceForWindow(
QStringLiteral("surface").toLocal8Bit(),
window->windowHandle()));

wl_fixed_t dx = wl_fixed_from_int(640);
wl_fixed_t dy = wl_fixed_from_int(0);

wl_callback *callback = manager.set_xwindow_position_relative(wid, surface, dx, dy);
wl_callback_add_listener(callback, &callback_listener, nullptr);
qWarning() << "Setting xwindow position relative, wait for result...";
});
timer.start(1000);

return app.exec();
}

#include "main.moc"

Check warning on line 98 in examples/test_set_xwindow_position/main.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "main.moc" not found.
24 changes: 24 additions & 0 deletions src/modules/dde-shell/ddeshellmanagerinterfacev1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@

#include "qwayland-server-treeland-dde-shell-v1.h"

#include "helper.h"

#include <woutput.h>

#include <qwcompositor.h>
#include <qwdisplay.h>
#include <qwoutput.h>
#include <qwseat.h>

#include <wayland-server.h>

#define TREELAND_DDE_SHELL_MANAGER_V1_VERSION 1

static QList<DDEShellSurfaceInterface *> s_shellSurfaces;
Expand Down Expand Up @@ -45,6 +49,12 @@ class DDEShellManagerInterfaceV1Private : public QtWaylandServer::treeland_dde_s
uint32_t id) override;
void treeland_dde_shell_manager_v1_get_treeland_lockscreen(Resource *resource,
uint32_t id) override;
void treeland_dde_shell_manager_v1_set_xwindow_position_relative(Resource *resource,
uint32_t callback,
uint32_t wid,
struct ::wl_resource *anchor,
wl_fixed_t dx,
wl_fixed_t dy) override;
};

void DDEShellManagerInterfaceV1Private::treeland_dde_shell_manager_v1_get_treeland_lockscreen(
Expand All @@ -69,6 +79,20 @@ void DDEShellManagerInterfaceV1Private::treeland_dde_shell_manager_v1_get_treela
Q_EMIT q->lockScreenCreated(lockScreen);
}

void DDEShellManagerInterfaceV1Private::treeland_dde_shell_manager_v1_set_xwindow_position_relative(Resource *resource,
uint32_t callback,
uint32_t wid,
struct ::wl_resource *anchor,
wl_fixed_t dx,
wl_fixed_t dy)
{
WSurface *wsurface = WSurface::fromHandle(qw_surface::from_resource(anchor));
uint32_t ok = (wsurface && Helper::instance()->setXWindowPositionRelative(wid, wsurface, dx, dy)) ? 0 : 1;
wl_resource *cb = wl_resource_create(resource->client(), &wl_callback_interface, 1, callback);
wl_callback_send_done(cb, ok);
wl_resource_destroy(cb);
}

DDEShellManagerInterfaceV1Private::DDEShellManagerInterfaceV1Private(DDEShellManagerInterfaceV1 *_q)
: q(_q)
{
Expand Down
38 changes: 38 additions & 0 deletions src/seat/helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2924,3 +2924,41 @@ void Helper::configureNumlock() {
}
}

/**
* Move a XWayland window's surface corresponding to wid, to a
* position relative to a WSurface. Top-left point is always used.
*
* @param wid X Window ID for the XWayland surface
* @param anchor The anchor WSurface to be relative to
* @param dx Horizontal distance between the top-left point of anchor and the destination
* @param dy Vertical distance between the top-left point of anchor and the destination
*/
bool Helper::setXWindowPositionRelative(uint wid, WSurface *anchor, wl_fixed_t dx, wl_fixed_t dy) const
{
SurfaceWrapper *ach = m_rootSurfaceContainer->getSurface(anchor);
if (!ach) {
qCWarning(treelandCore) << "setXWindowPositionRelative: Failed to get SurfaceWrapper from WSurface";
return false;
}

SurfaceWrapper *target = nullptr;
for (SurfaceWrapper *wrapper : std::as_const(rootSurfaceContainer()->surfaces())) {
if (wrapper->type() == SurfaceWrapper::Type::XWayland) {
wlr_xwayland_surface *surface =
wlr_xwayland_surface_try_from_wlr_surface(wrapper->surface()->handle()->handle());
if (surface && surface->window_id == static_cast<xcb_window_t>(wid)) {
target = wrapper;
break;
}
}
}
if (!target) {
qCWarning(treelandCore) << "setXWindowPositionRelative: XWayland surface corresponding to WID" << wid << "not found!";
return false;
}

QRectF rect(ach->position(), target->size());
rect.translate(wl_fixed_to_double(dx), wl_fixed_to_double(dy));
target->setPosition(rect.topLeft());
return true;
}
2 changes: 2 additions & 0 deletions src/seat/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ class Helper : public WSeatEventFilter

void updateIdleInhibitor();

bool setXWindowPositionRelative(uint wid, WSurface *anchor, wl_fixed_t dx, wl_fixed_t dy) const;

public Q_SLOTS:
void activateSurface(SurfaceWrapper *wrapper, Qt::FocusReason reason = Qt::OtherFocusReason);
void forceActivateSurface(SurfaceWrapper *wrapper,
Expand Down