Skip to content

Commit 8678773

Browse files
committed
add linux support for ShowSystemMenu
1 parent 4f59fc9 commit 8678773

10 files changed

+379
-3
lines changed

src/core/CMakeLists.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,17 @@ else()
6666
contexts/qtwindowcontext_p.h
6767
contexts/qtwindowcontext.cpp
6868
)
69+
70+
if(LINUX)
71+
list(APPEND _src
72+
contexts/linuxdesktopenvapi.h
73+
contexts/linuxdesktopenvapi.cpp
74+
contexts/linuxx11context_p.h
75+
contexts/linuxx11context.cpp
76+
contexts/linuxwaylandcontext_p.h
77+
contexts/linuxwaylandcontext.cpp
78+
)
79+
endif()
6980
endif()
7081
endif()
7182

@@ -101,4 +112,4 @@ qwk_add_library(${PROJECT_NAME} AUTOGEN
101112
)
102113

103114
set(QWINDOWKIT_ENABLED_TARGETS ${QWINDOWKIT_ENABLED_TARGETS} ${PROJECT_NAME} PARENT_SCOPE)
104-
set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} core PARENT_SCOPE)
115+
set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} core PARENT_SCOPE)

src/core/contexts/abstractwindowcontext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ namespace QWK {
126126
return false;
127127
}
128128

129-
for (auto item : m_hitTestVisibleItems) {
129+
for (const auto &item : m_hitTestVisibleItems) {
130130
if (item && m_delegate->isVisible(item) &&
131131
m_delegate->mapGeometryToScene(item).contains(pos)) {
132132
return false;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
2+
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "linuxdesktopenvapi.h"
6+
7+
#include <QString>
8+
#include <QGuiApplication>
9+
#include <QLibrary>
10+
11+
namespace QWK {
12+
13+
bool LinuxDesktopEnvAPI::isWaylandPlatform() {
14+
static const bool isWayland = QGuiApplication::platformName().startsWith(
15+
QStringLiteral("wayland"), Qt::CaseInsensitive);
16+
return isWayland;
17+
}
18+
19+
bool LinuxDesktopEnvAPI::isX11Platform() {
20+
static const bool isX11 =
21+
QGuiApplication::platformName().startsWith(QStringLiteral("xcb"), Qt::CaseInsensitive);
22+
return isX11;
23+
}
24+
25+
LinuxX11API &QWK::LinuxDesktopEnvAPI::x11API() {
26+
static LinuxX11API api;
27+
if (!api.isValid() && isX11Platform()) {
28+
QString libName = QStringLiteral(
29+
#if defined(__CYGWIN__)
30+
"libX11-6.so"
31+
#elif defined(__OpenBSD__) || defined(__NetBSD__)
32+
"libX11.so"
33+
#else
34+
"libX11.so.6"
35+
#endif
36+
);
37+
QLibrary x11lib(libName);
38+
if (x11lib.load()) {
39+
api.XInternAtom =
40+
reinterpret_cast<LinuxX11API::XInternAtomFn>(x11lib.resolve("XInternAtom"));
41+
api.XSendEvent =
42+
reinterpret_cast<LinuxX11API::XSendEventFn>(x11lib.resolve("XSendEvent"));
43+
api.XFlush = reinterpret_cast<LinuxX11API::XFlushFn>(x11lib.resolve("XFlush"));
44+
}
45+
}
46+
return api;
47+
}
48+
49+
LinuxWaylandAPI &LinuxDesktopEnvAPI::waylandAPI() {
50+
static LinuxWaylandAPI api;
51+
if (!api.isValid() && isWaylandPlatform()) {
52+
QLibrary waylib(QStringLiteral("libwayland-client.so"));
53+
bool loaded = false;
54+
if (waylib.load()) {
55+
loaded = true;
56+
} else {
57+
waylib.setFileName(QStringLiteral("libwayland-client.so.0"));
58+
if (waylib.load()) {
59+
loaded = true;
60+
}
61+
}
62+
63+
if (loaded) {
64+
api.wl_display_flush = reinterpret_cast<LinuxWaylandAPI::wl_display_flush_fn>(
65+
waylib.resolve("wl_display_flush"));
66+
api.wl_proxy_marshal_flags =
67+
reinterpret_cast<LinuxWaylandAPI::wl_proxy_marshal_flags_fn>(
68+
waylib.resolve("wl_proxy_marshal_flags"));
69+
api.wl_proxy_get_version =
70+
reinterpret_cast<LinuxWaylandAPI::wl_proxy_get_version_fn>(
71+
waylib.resolve("wl_proxy_get_version"));
72+
}
73+
}
74+
return api;
75+
}
76+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
2+
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#ifndef LINUXDESKTOPENVAPI_H
6+
#define LINUXDESKTOPENVAPI_H
7+
8+
//
9+
// W A R N I N G !!!
10+
// -----------------
11+
//
12+
// This file is not part of the QWindowKit API. It is used purely as an
13+
// implementation detail. This header file may change from version to
14+
// version without notice, or may even be removed.
15+
//
16+
17+
#include "qguiapplication_platform.h"
18+
19+
// some declarations about x11
20+
using Atom = unsigned long;
21+
using Bool = int;
22+
using XID = unsigned long;
23+
using Window = XID;
24+
25+
union _XEvent;
26+
using XEvent = union _XEvent;
27+
28+
namespace QWK {
29+
struct LinuxX11API {
30+
using XInternAtomFn = Atom (*)(Display *, const char *, Bool);
31+
using XSendEventFn = int (*)(Display *, Window, Bool, long, XEvent *);
32+
using XFlushFn = int (*)(Display *);
33+
34+
XInternAtomFn XInternAtom = nullptr;
35+
XSendEventFn XSendEvent = nullptr;
36+
XFlushFn XFlush = nullptr;
37+
38+
inline bool isValid() const {
39+
return XInternAtom && XSendEvent && XFlush;
40+
}
41+
};
42+
43+
struct LinuxWaylandAPI {
44+
using wl_display_flush_fn = int (*)(struct wl_display *);
45+
using wl_proxy_marshal_flags_fn = void (*)(struct wl_proxy *, uint32_t,
46+
const struct wl_interface *, uint32_t, uint32_t,
47+
...);
48+
using wl_proxy_get_version_fn = int (*)(struct wl_proxy *);
49+
50+
wl_display_flush_fn wl_display_flush = nullptr;
51+
wl_proxy_marshal_flags_fn wl_proxy_marshal_flags = nullptr;
52+
wl_proxy_get_version_fn wl_proxy_get_version = nullptr;
53+
54+
inline bool isValid() const {
55+
return wl_display_flush && wl_proxy_marshal_flags && wl_proxy_get_version;
56+
}
57+
};
58+
59+
60+
class LinuxDesktopEnvAPI {
61+
public:
62+
static bool isWaylandPlatform();
63+
64+
static bool isX11Platform();
65+
66+
public:
67+
static LinuxX11API &x11API();
68+
static LinuxWaylandAPI &waylandAPI();
69+
};
70+
}
71+
72+
#endif // LINUXDESKTOPENVAPI_H
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
2+
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "linuxwaylandcontext_p.h"
6+
7+
#include "linuxdesktopenvapi.h"
8+
9+
#include <QtGui/qpa/qplatformnativeinterface.h>
10+
11+
namespace QWK {
12+
static inline void xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel,
13+
struct wl_seat *seat, uint32_t serial,
14+
int32_t x, int32_t y) {
15+
constexpr auto XDG_TOPLEVEL_SHOW_WINDOW_MENU = 4;
16+
auto &api = LinuxDesktopEnvAPI::waylandAPI();
17+
Q_ASSERT(api.isValid());
18+
api.wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, XDG_TOPLEVEL_SHOW_WINDOW_MENU,
19+
NULL, api.wl_proxy_get_version((struct wl_proxy *) xdg_toplevel),
20+
0, seat, serial, x, y);
21+
}
22+
23+
LinuxWaylandContext::LinuxWaylandContext() : QtWindowContext() {
24+
}
25+
26+
LinuxWaylandContext::~LinuxWaylandContext() = default;
27+
28+
QString LinuxWaylandContext::key() const {
29+
return QStringLiteral("wayland");
30+
}
31+
32+
void LinuxWaylandContext::virtual_hook(int id, void *data) {
33+
if (id == ShowSystemMenuHook) {
34+
auto *native = QGuiApplication::platformNativeInterface();
35+
auto *waylandApp = qApp->nativeInterface<QNativeInterface::QWaylandApplication>();
36+
if (!waylandApp) {
37+
return;
38+
}
39+
uint serial = waylandApp->lastInputSerial();
40+
wl_seat *seat = waylandApp->lastInputSeat();
41+
if (serial == 0 || !seat) {
42+
return;
43+
}
44+
void *rawToplevel = native->nativeResourceForWindow("xdg_toplevel", m_windowHandle);
45+
if (!rawToplevel) {
46+
return;
47+
}
48+
xdg_toplevel *toplevel = static_cast<xdg_toplevel *>(rawToplevel);
49+
auto pos = static_cast<QPoint *>(data);
50+
int lx = pos->x();
51+
int ly = pos->y();
52+
xdg_toplevel_show_window_menu(toplevel, seat, serial, lx, ly);
53+
54+
wl_display *d = waylandApp->display();
55+
if (d) {
56+
auto &api = LinuxDesktopEnvAPI::waylandAPI();
57+
Q_ASSERT(api.isValid());
58+
api.wl_display_flush(d);
59+
}
60+
} else {
61+
QtWindowContext::virtual_hook(id, data);
62+
}
63+
}
64+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
2+
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
6+
#ifndef LINUXWAYLANDCONTEXT_P_H
7+
#define LINUXWAYLANDCONTEXT_P_H
8+
9+
//
10+
// W A R N I N G !!!
11+
// -----------------
12+
//
13+
// This file is not part of the QWindowKit API. It is used purely as an
14+
// implementation detail. This header file may change from version to
15+
// version without notice, or may even be removed.
16+
//
17+
18+
19+
#include "qtwindowcontext_p.h"
20+
21+
namespace QWK {
22+
23+
class LinuxWaylandContext : public QtWindowContext {
24+
Q_OBJECT
25+
public:
26+
LinuxWaylandContext();
27+
~LinuxWaylandContext() override;
28+
29+
QString key() const override;
30+
void virtual_hook(int id, void *data) override;
31+
};
32+
33+
}
34+
35+
#endif // LINUXWAYLANDCONTEXT_P_H
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
2+
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
6+
#include "linuxx11context_p.h"
7+
8+
#include "linuxdesktopenvapi.h"
9+
10+
#include <X11/Xlib.h>
11+
#include <X11/Xatom.h>
12+
13+
namespace QWK {
14+
15+
LinuxX11Context::LinuxX11Context() : QtWindowContext() {
16+
}
17+
18+
LinuxX11Context::~LinuxX11Context() = default;
19+
20+
QString LinuxX11Context::key() const {
21+
return QStringLiteral("xcb");
22+
}
23+
24+
void LinuxX11Context::virtual_hook(int id, void *data) {
25+
if (id == ShowSystemMenuHook) {
26+
auto *x11app = qApp->nativeInterface<QNativeInterface::QX11Application>();
27+
if (!x11app) {
28+
return;
29+
}
30+
31+
auto display = x11app->display();
32+
if (!display) {
33+
return;
34+
}
35+
36+
auto api = LinuxDesktopEnvAPI::x11API();
37+
Q_ASSERT(api.isValid());
38+
39+
// use window id (XID)
40+
Window xwin = static_cast<Window>(m_windowId);
41+
Atom atom = api.XInternAtom(display, "_GTK_SHOW_WINDOW_MENU", False);
42+
if (atom == None)
43+
return; // WM might not support this atom
44+
auto pos = static_cast<QPoint *>(data);
45+
XEvent ev;
46+
memset(&ev, 0, sizeof(ev));
47+
ev.xclient.type = ClientMessage;
48+
ev.xclient.window = xwin;
49+
ev.xclient.message_type = atom;
50+
ev.xclient.format = 32;
51+
52+
QScreen *screen = QGuiApplication::screenAt(*pos);
53+
qreal dpr = screen ? screen->devicePixelRatio() : m_windowHandle->devicePixelRatio();
54+
int root_x = qRound(pos->x() * dpr);
55+
int root_y = qRound(pos->y() * dpr);
56+
57+
ev.xclient.data.l[0] = 3;
58+
ev.xclient.data.l[1] = root_x;
59+
ev.xclient.data.l[2] = root_y;
60+
ev.xclient.data.l[3] = 0;
61+
62+
Window root = DefaultRootWindow(display);
63+
api.XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask,
64+
&ev);
65+
api.XFlush(display);
66+
} else {
67+
QtWindowContext::virtual_hook(id, data);
68+
}
69+
}
70+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
2+
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#ifndef LINUXX11CONTEXT_P_H
6+
#define LINUXX11CONTEXT_P_H
7+
8+
//
9+
// W A R N I N G !!!
10+
// -----------------
11+
//
12+
// This file is not part of the QWindowKit API. It is used purely as an
13+
// implementation detail. This header file may change from version to
14+
// version without notice, or may even be removed.
15+
//
16+
17+
#include "qtwindowcontext_p.h"
18+
19+
namespace QWK {
20+
21+
class LinuxX11Context : public QtWindowContext {
22+
Q_OBJECT
23+
public:
24+
LinuxX11Context();
25+
~LinuxX11Context() override;
26+
27+
QString key() const override;
28+
void virtual_hook(int id, void *data) override;
29+
};
30+
31+
}
32+
33+
#endif // LINUXX11CONTEXT_P_H

0 commit comments

Comments
 (0)