Compare commits

...

11 Commits

18 changed files with 617 additions and 410 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
build/ build/
.cache/ .cache/
.vscode/

View File

@@ -8,59 +8,28 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) # ensure adapter class can include related hea
find_package(Qt6 6.8 CONFIG REQUIRED COMPONENTS DBus) find_package(Qt6 6.8 CONFIG REQUIRED COMPONENTS DBus)
find_package(ECM REQUIRED NO_MODULE) find_package(ECM REQUIRED NO_MODULE)
find_package(PkgConfig REQUIRED)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(ECMQtDeclareLoggingCategory)
include(KDEInstallDirs) include(KDEInstallDirs)
include(ECMConfiguredInstall)
find_package(KF6 6.6 REQUIRED COMPONENTS find_package(KF6WindowSystem 6.6 REQUIRED)
WindowSystem)
find_package(XCB pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11 xcb xcb-image xcb-damage xcb-composite xcb-xfixes xcb-util xcb-shape xtst xcb-xtest xcb-res xcb-ewmh)
REQUIRED COMPONENTS
XCB
XFIXES
DAMAGE
COMPOSITE
RANDR
SHM
UTIL
IMAGE
)
set(XCB_LIBS
XCB::XCB
XCB::XFIXES
XCB::DAMAGE
XCB::COMPOSITE
XCB::RANDR
XCB::SHM
XCB::UTIL
XCB::IMAGE
)
set(XEMBED_SNI_PROXY_SOURCES set(XEMBED_SNI_PROXY_SOURCES
main.cpp main.cpp
fdoselectionmanager.cpp fdoselectionmanager.h fdoselectionmanager.cpp fdoselectionmanager.h
traymanager1.cpp traymanager1.h traymanager1.cpp traymanager1.h
xtestsender.cpp xtestsender.h util.cpp util.h
) )
set_source_files_properties( set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/org.deepin.dde.TrayManager1.xml ${CMAKE_CURRENT_SOURCE_DIR}/api/dbus/org.deepin.dde.TrayManager1.xml
PROPERTIES INCLUDE traylist.h PROPERTIES INCLUDE api/types/traylist.h
CLASSNAME TrayManager CLASSNAME TrayManager
) )
qt_add_dbus_adaptor(XEMBED_SNI_PROXY_SOURCES org.deepin.dde.TrayManager1.xml traymanager1.h TrayManager1) qt_add_dbus_adaptor(XEMBED_SNI_PROXY_SOURCES api/dbus/org.deepin.dde.TrayManager1.xml traymanager1.h TrayManager1)
ecm_qt_declare_logging_category(XEMBED_SNI_PROXY_SOURCES HEADER debug.h
IDENTIFIER SNIPROXY
CATEGORY_NAME dde.xembedsniproxy
DEFAULT_SEVERITY Info
DESCRIPTION "xembed sni proxy"
EXPORT PLASMAWORKSPACE
)
add_executable(xembed-traymanager-proxy ${XEMBED_SNI_PROXY_SOURCES}) add_executable(xembed-traymanager-proxy ${XEMBED_SNI_PROXY_SOURCES})
set_property(TARGET xembed-traymanager-proxy PROPERTY AUTOMOC ON) set_property(TARGET xembed-traymanager-proxy PROPERTY AUTOMOC ON)
@@ -73,11 +42,8 @@ target_link_libraries(xembed-traymanager-proxy
Qt::Core Qt::Core
Qt::DBus Qt::DBus
KF6::WindowSystem KF6::WindowSystem
${XCB_LIBS} PkgConfig::X11
X11::Xtst
) )
install(TARGETS xembed-traymanager-proxy ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS xembed-traymanager-proxy ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES xembedsniproxy.desktop DESTINATION ${KDE_INSTALL_AUTOSTARTDIR}) install(FILES xembedsniproxy.desktop DESTINATION ${KDE_INSTALL_AUTOSTARTDIR})
ecm_install_configured_files(INPUT plasma-xembedsniproxy.service.in @ONLY DESTINATION ${KDE_INSTALL_SYSTEMDUSERUNITDIR})

View File

@@ -1,34 +1,24 @@
## Note ## XEmbed TrayManager1 Proxy
This project is modified from KDE's xembed-sni-proxy project, extracted from plasma-workspace. The goal of this project is to make xembed system trays available in DDE Treeland (wayland) session.
##XEmbed SNI Proxy This is to allow legacy apps (xchat, pidgin, tuxguitar) etc. system trays[1] available in DDE Treeland which only supports StatusNotifierItem [2].
The goal of this project is to make xembed system trays available in Plasma. > [!NOTE]
> The initial commit was a minimal KF6 dependency version of KDE's `xembed-sni-proxy` project, extracted from `plasma-workspace`.
> Currently this project is a standalone project that provides Xembed tray information on TrayManager1 D-Bus, so `dde-tray-loader` could consume it and provide Xembed tray icons.
> This project will be integrated into `dde-tray-loader` project.
This is to allow legacy apps (xchat, pidgin, tuxguitar) etc. system trays[1] available in Plasma which only supports StatusNotifierItem [2]. This tool can be used on a Wayland session with `wayland` QPA, `xcb` QPA can also be used but it's not mandatory, but either way it's required to have a X connection (via X11 or Xwayland).
Ideally we also want this to work in an xwayland session, making X system tray icons available even when plasmashell only has a wayland connection. ## Build instructions
This project should be portable onto all other DEs that speak SNI. ```shell
$ cmake -Bbuild .
$ cmake --build build
```
##How it works (in theory) After building, run `xembed-traymanager-proxy`.
* We register a window as a system tray container
* We render embedded windows composited offscreen
* We render contents into an image and send this over DBus via the SNI protocol
* XDamage events trigger a repaint
* Activate and context menu events are replyed via X send event into the embedded container as left and right clicks
There are a few extra hacks in the real code to deal with some toolkits being awkward.
##Build instructions
cmake .
make
sudo make install
After building, run `xembedsniproxy`.
[1] http://standards.freedesktop.org/systemtray-spec/systemtray-spec-latest.html [1] http://standards.freedesktop.org/systemtray-spec/systemtray-spec-latest.html
[2] http://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/ [2] http://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/

View File

@@ -2,37 +2,40 @@
Registers as a embed container Registers as a embed container
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org> SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2019 Konrad Materka <materka@gmail.com> SPDX-FileCopyrightText: 2019 Konrad Materka <materka@gmail.com>
SPDX-FileCopyrightText: 2025 Wang Zichong <wangzichong@deepin.org>
SPDX-License-Identifier: LGPL-2.1-or-later SPDX-License-Identifier: LGPL-2.1-or-later
*/ */
#include "fdoselectionmanager.h" #include "fdoselectionmanager.h"
#include "debug.h"
#include <QTimer> #include <QTimer>
#include <QDBusConnection> #include <QDBusConnection>
#include <QLoggingCategory>
#include <KSelectionOwner> #include <KSelectionOwner>
#include <qassert.h>
#include <xcb/composite.h> #include <xcb/composite.h>
#include <xcb/damage.h> #include <xcb/damage.h>
#include <xcb/xcb_atom.h> #include <xcb/xcb_atom.h>
#include <xcb/xcb_event.h> #include <xcb/xcb_event.h>
#include "traymanager1.h" #include "traymanager1.h"
#include "xcbutils.h" #include "c_ptr.h"
#include "util.h"
using Util = tray::Util;
#define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
FdoSelectionManager::FdoSelectionManager() Q_LOGGING_CATEGORY(SELECTIONMGR, "org.deepin.dde.trayloader.selectionmgr")
: QObject()
, m_x11Interface(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()) FdoSelectionManager::FdoSelectionManager(QObject *parent)
, m_selectionOwner(new KSelectionOwner(Xcb::atoms->selectionAtom, -1, this)) : QObject(parent)
, m_selectionOwner(new KSelectionOwner(UTIL->getAtomFromDisplay("_NET_SYSTEM_TRAY"), UTIL->getX11Connection(), UTIL->getRootWindow(), this))
{ {
qDebug(SNIPROXY) << "starting"; qCDebug(SELECTIONMGR) << "starting";
// we may end up calling QCoreApplication::quit() in this method, at which point we need the event loop running // we may end up calling QCoreApplication::quit() in this method, at which point we need the event loop running
QTimer::singleShot(0, this, &FdoSelectionManager::init); QTimer::singleShot(0, this, &FdoSelectionManager::init);
@@ -40,14 +43,14 @@ FdoSelectionManager::FdoSelectionManager()
FdoSelectionManager::~FdoSelectionManager() FdoSelectionManager::~FdoSelectionManager()
{ {
qCDebug(SNIPROXY) << "closing"; qCDebug(SELECTIONMGR) << "closing";
m_selectionOwner->release(); m_selectionOwner->release();
} }
void FdoSelectionManager::init() void FdoSelectionManager::init()
{ {
// load damage extension // load damage extension
xcb_connection_t *c = m_x11Interface->connection(); xcb_connection_t *c = Util::instance()->getX11Connection();
xcb_prefetch_extension_data(c, &xcb_damage_id); xcb_prefetch_extension_data(c, &xcb_damage_id);
const auto *reply = xcb_get_extension_data(c, &xcb_damage_id); const auto *reply = xcb_get_extension_data(c, &xcb_damage_id);
if (reply && reply->present) { if (reply && reply->present) {
@@ -55,7 +58,7 @@ void FdoSelectionManager::init()
xcb_damage_query_version_unchecked(c, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION); xcb_damage_query_version_unchecked(c, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION);
} else { } else {
// no XDamage means // no XDamage means
qCCritical(SNIPROXY) << "could not load damage extension. Quitting"; qCCritical(SELECTIONMGR) << "could not load damage extension. Quitting";
qApp->exit(-1); qApp->exit(-1);
} }
@@ -64,14 +67,18 @@ void FdoSelectionManager::init()
connect(m_selectionOwner, &KSelectionOwner::claimedOwnership, this, &FdoSelectionManager::onClaimedOwnership); connect(m_selectionOwner, &KSelectionOwner::claimedOwnership, this, &FdoSelectionManager::onClaimedOwnership);
connect(m_selectionOwner, &KSelectionOwner::failedToClaimOwnership, this, &FdoSelectionManager::onFailedToClaimOwnership); connect(m_selectionOwner, &KSelectionOwner::failedToClaimOwnership, this, &FdoSelectionManager::onFailedToClaimOwnership);
connect(m_selectionOwner, &KSelectionOwner::lostOwnership, this, &FdoSelectionManager::onLostOwnership); connect(m_selectionOwner, &KSelectionOwner::lostOwnership, this, &FdoSelectionManager::onLostOwnership);
m_selectionOwner->claim(false); m_selectionOwner->claim(true);
connect(m_trayManager, &TrayManager1::reclainRequested, this, [this](){
m_selectionOwner->claim(true);
});
} }
bool FdoSelectionManager::addDamageWatch(xcb_window_t client) bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
{ {
qCDebug(SNIPROXY) << "adding damage watch for " << client; qCDebug(SELECTIONMGR) << "adding damage watch for " << client;
xcb_connection_t *c = m_x11Interface->connection(); xcb_connection_t *c = Util::instance()->getX11Connection();
const auto attribsCookie = xcb_get_window_attributes_unchecked(c, client); const auto attribsCookie = xcb_get_window_attributes_unchecked(c, client);
const auto damageId = xcb_generate_id(c); const auto damageId = xcb_generate_id(c);
@@ -114,7 +121,7 @@ bool FdoSelectionManager::nativeEventFilter(const QByteArray &eventType, void *m
const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev); const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev);
if (responseType == XCB_CLIENT_MESSAGE) { if (responseType == XCB_CLIENT_MESSAGE) {
const auto ce = reinterpret_cast<xcb_client_message_event_t *>(ev); const auto ce = reinterpret_cast<xcb_client_message_event_t *>(ev);
if (ce->type == Xcb::atoms->opcodeAtom) { if (ce->type == UTIL->getAtomByName("_NET_SYSTEM_TRAY_OPCODE")) {
switch (ce->data.data32[1]) { switch (ce->data.data32[1]) {
case SYSTEM_TRAY_REQUEST_DOCK: case SYSTEM_TRAY_REQUEST_DOCK:
dock(ce->data.data32[2]); dock(ce->data.data32[2]);
@@ -132,12 +139,8 @@ bool FdoSelectionManager::nativeEventFilter(const QByteArray &eventType, void *m
undock(destroyedWId); undock(destroyedWId);
} }
} else if (responseType == m_damageEventBase + XCB_DAMAGE_NOTIFY) { } else if (responseType == m_damageEventBase + XCB_DAMAGE_NOTIFY) {
// const auto damagedWId = reinterpret_cast<xcb_damage_notify_event_t *>(ev)->drawable; const auto damagedWId = reinterpret_cast<xcb_damage_notify_event_t *>(ev)->drawable;
// const auto tmProxy = m_proxies.value(damagedWId); m_trayManager->notifyIconChanged(damagedWId);
// if (tmProxy) {
// tmProxy->update();
// xcb_damage_subtract(m_x11Interface->connection(), m_damageWatches[damagedWId], XCB_NONE, XCB_NONE);
// }
} else if (responseType == XCB_CONFIGURE_REQUEST) { } else if (responseType == XCB_CONFIGURE_REQUEST) {
// const auto event = reinterpret_cast<xcb_configure_request_event_t *>(ev); // const auto event = reinterpret_cast<xcb_configure_request_event_t *>(ev);
// const auto tmProxy = m_proxies.value(event->window); // const auto tmProxy = m_proxies.value(event->window);
@@ -155,16 +158,13 @@ bool FdoSelectionManager::nativeEventFilter(const QByteArray &eventType, void *m
void FdoSelectionManager::dock(xcb_window_t winId) void FdoSelectionManager::dock(xcb_window_t winId)
{ {
Q_CHECK_PTR(m_trayManager); Q_CHECK_PTR(m_trayManager);
qCDebug(SNIPROXY) << "trying to dock window " << winId; qCDebug(SELECTIONMGR) << "trying to dock window " << winId;
if (m_trayManager->haveIcon(winId)) { if (m_trayManager->haveIcon(winId)) {
return; return;
} }
if (addDamageWatch(winId)) { if (addDamageWatch(winId)) {
// auto proxy = new TrayManagerProxy(winId, this);
// m_proxies[winId] = proxy;
// Register with TrayManager1 if available // Register with TrayManager1 if available
m_trayManager->registerIcon(winId); m_trayManager->registerIcon(winId);
} }
@@ -173,12 +173,13 @@ void FdoSelectionManager::dock(xcb_window_t winId)
void FdoSelectionManager::undock(xcb_window_t winId) void FdoSelectionManager::undock(xcb_window_t winId)
{ {
Q_CHECK_PTR(m_trayManager); Q_CHECK_PTR(m_trayManager);
qCDebug(SNIPROXY) << "trying to undock window " << winId; qCDebug(SELECTIONMGR) << "trying to undock window " << winId;
if (m_trayManager->haveIcon(winId)) { if (!m_trayManager->haveIcon(winId)) {
qCDebug(SELECTIONMGR) << "failed to find winId to undock:" << winId;
return; return;
} }
// Unregister from TrayManager1 if available // Unregister from TrayManager1 if available
m_trayManager->unregisterIcon(winId); m_trayManager->unregisterIcon(winId);
@@ -188,7 +189,7 @@ void FdoSelectionManager::undock(xcb_window_t winId)
void FdoSelectionManager::onClaimedOwnership() void FdoSelectionManager::onClaimedOwnership()
{ {
qCDebug(SNIPROXY) << "Manager selection claimed"; qCDebug(SELECTIONMGR) << "Manager selection claimed";
initTrayManager(); initTrayManager();
setSystemTrayVisual(); setSystemTrayVisual();
@@ -196,19 +197,17 @@ void FdoSelectionManager::onClaimedOwnership()
void FdoSelectionManager::onFailedToClaimOwnership() void FdoSelectionManager::onFailedToClaimOwnership()
{ {
qCWarning(SNIPROXY) << "failed to claim ownership of Systray Manager"; qCWarning(SELECTIONMGR) << "failed to claim ownership of Systray Manager";
qApp->exit(-1);
} }
void FdoSelectionManager::onLostOwnership() void FdoSelectionManager::onLostOwnership()
{ {
qCWarning(SNIPROXY) << "lost ownership of Systray Manager"; qCWarning(SELECTIONMGR) << "lost ownership of Systray Manager";
qApp->exit(-1);
} }
void FdoSelectionManager::setSystemTrayVisual() void FdoSelectionManager::setSystemTrayVisual()
{ {
xcb_connection_t *c = m_x11Interface->connection(); xcb_connection_t *c = Util::instance()->getX11Connection();
auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data; auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
auto trayVisual = screen->root_visual; auto trayVisual = screen->root_visual;
xcb_depth_iterator_t depth_iterator = xcb_screen_allowed_depths_iterator(screen); xcb_depth_iterator_t depth_iterator = xcb_screen_allowed_depths_iterator(screen);
@@ -234,7 +233,7 @@ void FdoSelectionManager::setSystemTrayVisual()
} }
} }
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), Xcb::atoms->visualAtom, XCB_ATOM_VISUALID, 32, 1, &trayVisual); xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("_NET_SYSTEM_TRAY_VISUAL"), XCB_ATOM_VISUALID, 32, 1, &trayVisual);
} }
void FdoSelectionManager::initTrayManager() void FdoSelectionManager::initTrayManager()
@@ -255,7 +254,7 @@ void FdoSelectionManager::initTrayManager()
QStringLiteral("org.deepin.dde.TrayManager1") QStringLiteral("org.deepin.dde.TrayManager1")
); );
qCDebug(SNIPROXY) << "TrayManager1 DBus interface registered"; qCDebug(SELECTIONMGR) << "TrayManager1 DBus interface registered";
} }
} }

View File

@@ -23,7 +23,7 @@ class FdoSelectionManager : public QObject, public QAbstractNativeEventFilter
Q_OBJECT Q_OBJECT
public: public:
FdoSelectionManager(); FdoSelectionManager(QObject *parent = nullptr);
~FdoSelectionManager() override; ~FdoSelectionManager() override;
protected: protected:
@@ -42,7 +42,6 @@ private:
void setSystemTrayVisual(); void setSystemTrayVisual();
void initTrayManager(); void initTrayManager();
QNativeInterface::QX11Application *m_x11Interface = nullptr;
TrayManager1 *m_trayManager = nullptr; TrayManager1 *m_trayManager = nullptr;
uint8_t m_damageEventBase; uint8_t m_damageEventBase;

View File

@@ -9,8 +9,7 @@
#include "fdoselectionmanager.h" #include "fdoselectionmanager.h"
#include "debug.h" #include "util.h"
#include "xcbutils.h"
#ifdef None #ifdef None
#ifndef FIXX11H_None #ifndef FIXX11H_None
@@ -26,34 +25,33 @@ inline constexpr XID None = XNone;
#include <KWindowSystem> #include <KWindowSystem>
namespace Xcb using Util = tray::Util;
{
Xcb::Atoms *atoms;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
// the whole point of this is to interact with X, if we are in any other session, force trying to connect to X // We will use the X connection managed by UTIL, we don't really care if the tool itself is
// if the QPA can't load xcb, this app is useless anyway. // running with xcb or wayland QPA.
qputenv("QT_QPA_PLATFORM", "xcb"); // We'll use wayland QPA for testing on DDE, the following code uses dde-tray-loader's wayland
// display, thus we can ensure our X events are not from QPA.
qputenv("QT_QPA_PLATFORM", "wayland");
qputenv("WAYLAND_DISPLAY", "dockplugin");
qputenv("QT_WAYLAND_SHELL_INTEGRATION", "plugin-shell");
// qputenv("QT_QPA_PLATFORM", "xcb");
QGuiApplication::setDesktopSettingsAware(false); QGuiApplication::setDesktopSettingsAware(false);
QCoreApplication::setAttribute(Qt::AA_DisableSessionManager); QCoreApplication::setAttribute(Qt::AA_DisableSessionManager);
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
if (!KWindowSystem::isPlatformX11()) { if (!UTIL->isXAvaliable()) {
qFatal("xembed-traymanager-proxy requires X11. Aborting"); qFatal("xembed-traymanager-proxy requires X11. Aborting");
} }
app.setQuitOnLastWindowClosed(false); app.setQuitOnLastWindowClosed(false);
Xcb::atoms = new Xcb::Atoms();
FdoSelectionManager manager; FdoSelectionManager manager;
auto rc = app.exec(); auto rc = app.exec();
delete Xcb::atoms;
return rc; return rc;
} }

View File

@@ -1,63 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<!-- This is a minimally cut down version of the interface only implementing the
methods and properties used by xembedsniproxy -->
<interface name="org.kde.StatusNotifierItem">
<property name="Category" type="s" access="read"/>
<property name="Id" type="s" access="read"/>
<property name="Title" type="s" access="read"/>
<property name="Status" type="s" access="read"/>
<property name="WindowId" type="i" access="read"/>
<property name="ItemIsMenu" type="b" access="read"/>
<property name="IconPixmap" type="(iiay)" access="read">
<annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusImageVector"/>
</property>
<!-- interaction: the systemtray wants the application to do something -->
<method name="ContextMenu">
<!-- we're passing the coordinates of the icon, so the app knows where to put the popup window -->
<arg name="x" type="i" direction="in"/>
<arg name="y" type="i" direction="in"/>
</method>
<method name="Activate">
<arg name="x" type="i" direction="in"/>
<arg name="y" type="i" direction="in"/>
</method>
<method name="SecondaryActivate">
<arg name="x" type="i" direction="in"/>
<arg name="y" type="i" direction="in"/>
</method>
<method name="Scroll">
<arg name="delta" type="i" direction="in"/>
<arg name="orientation" type="s" direction="in"/>
</method>
<!-- Signals: the client wants to change something in the status-->
<signal name="NewTitle">
</signal>
<signal name="NewIcon">
</signal>
<signal name="NewAttentionIcon">
</signal>
<signal name="NewOverlayIcon">
</signal>
<signal name="NewToolTip">
</signal>
<signal name="NewStatus">
<arg name="status" type="s"/>
</signal>
</interface>
</node>

View File

@@ -1,42 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.StatusNotifierWatcher">
<!-- methods -->
<method name="RegisterStatusNotifierItem">
<arg name="service" type="s" direction="in"/>
</method>
<method name="RegisterStatusNotifierHost">
<arg name="service" type="s" direction="in"/>
</method>
<!-- properties -->
<property name="RegisteredStatusNotifierItems" type="as" access="read">
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QStringList"/>
</property>
<property name="IsStatusNotifierHostRegistered" type="b" access="read"/>
<property name="ProtocolVersion" type="i" access="read"/>
<!-- signals -->
<signal name="StatusNotifierItemRegistered">
<arg type="s"/>
</signal>
<signal name="StatusNotifierItemUnregistered">
<arg type="s"/>
</signal>
<signal name="StatusNotifierHostRegistered">
</signal>
<signal name="StatusNotifierHostUnregistered">
</signal>
</interface>
</node>

View File

@@ -1,11 +0,0 @@
[Unit]
Description=Handle legacy xembed system tray icons
PartOf=graphical-session.target
After=plasma-core.target
[Service]
ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/xembedsniproxy
Restart=on-failure
Type=simple
Slice=background.slice
TimeoutSec=5sec

View File

@@ -1,35 +1,41 @@
/* // Deepin DDE TrayManager1 implementation
Deepin DDE TrayManager1 implementation //
SPDX-License-Identifier: LGPL-2.1-or-later // SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
*/ //
// SPDX-License-Identifier: GPL-3.0-or-later
#include "traymanager1.h" #include "traymanager1.h"
#include "debug.h"
#include "traymanager1adaptor.h" #include "traymanager1adaptor.h"
#include "util.h"
#include <KWindowInfo> #include <KWindowInfo>
#include <QGuiApplication> #include <QGuiApplication>
#include <QLoggingCategory>
Q_LOGGING_CATEGORY(TRAYMGR, "org.deepin.dde.trayloader.traymgr")
TrayManager1::TrayManager1(QObject *parent) TrayManager1::TrayManager1(QObject *parent)
: QObject(parent) : QObject(parent)
, m_adaptor(new TrayManager1Adaptor(this)) , m_adaptor(new TrayManager1Adaptor(this))
{ {
qCDebug(SNIPROXY) << "TrayManager1 created"; qCDebug(TRAYMGR) << "TrayManager1 created";
} }
TrayManager1::~TrayManager1() TrayManager1::~TrayManager1()
{ {
qCDebug(SNIPROXY) << "TrayManager1 destroyed"; qCDebug(TRAYMGR) << "TrayManager1 destroyed";
} }
void TrayManager1::registerIcon(xcb_window_t win) void TrayManager1::registerIcon(xcb_window_t win)
{ {
if (m_icons.contains(win)) { if (m_icons.contains(win)) {
qCWarning(SNIPROXY) << "Icon already registered:" << win; qCWarning(TRAYMGR) << "Icon already registered:" << win;
return; return;
} }
m_icons[win] = true; m_icons[win] = true;
qCDebug(SNIPROXY) << "Icon registered:" << win ;//<< "name:" << proxy->name(); qCDebug(TRAYMGR) << "Icon registered:" << win ;//<< "name:" << proxy->name();
Q_EMIT Added(static_cast<uint32_t>(win)); Q_EMIT Added(static_cast<uint32_t>(win));
} }
@@ -37,12 +43,12 @@ void TrayManager1::registerIcon(xcb_window_t win)
void TrayManager1::unregisterIcon(xcb_window_t win) void TrayManager1::unregisterIcon(xcb_window_t win)
{ {
if (!m_icons.contains(win)) { if (!m_icons.contains(win)) {
qCWarning(SNIPROXY) << "Icon not found for removal:" << win; qCWarning(TRAYMGR) << "Icon not found for removal:" << win;
return; return;
} }
m_icons.remove(win); m_icons.remove(win);
qCDebug(SNIPROXY) << "Icon unregistered:" << win; qCDebug(TRAYMGR) << "Icon unregistered:" << win;
Q_EMIT Removed(static_cast<uint32_t>(win)); Q_EMIT Removed(static_cast<uint32_t>(win));
} }
@@ -53,7 +59,12 @@ void TrayManager1::notifyIconChanged(xcb_window_t win)
return; return;
} }
qCDebug(SNIPROXY) << "Icon changed:" << win; if (!m_icons[win]) {
qCDebug(TRAYMGR) << "EnableNotification is false, not sending changed signal for:" << win;
return;
}
qCDebug(TRAYMGR) << "Icon changed:" << win;
Q_EMIT Changed(static_cast<uint32_t>(win)); Q_EMIT Changed(static_cast<uint32_t>(win));
} }
@@ -75,19 +86,24 @@ bool TrayManager1::haveIcon(xcb_window_t win) const
// DBus method implementations // DBus method implementations
bool TrayManager1::Manage() bool TrayManager1::Manage()
{ {
qCDebug(SNIPROXY) << "Manage() called via DBus"; qCDebug(TRAYMGR) << "Manage() called via DBus";
emit reclainRequested();
return true; return true;
} }
QString TrayManager1::GetName(uint32_t win) QString TrayManager1::GetName(uint32_t win)
{ {
auto connection = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(); using Util = tray::Util;
KWindowInfo info(win, NET::WMName | NET::WMIconName); return UTIL->getX11WindowName(win);
return info.name();
} }
void TrayManager1::EnableNotification(uint32_t win, bool enabled) void TrayManager1::EnableNotification(uint32_t win, bool enabled)
{ {
// TODO: Implement if (!m_icons.contains(win)) {
qCDebug(SNIPROXY) << "TODO: EnableNotification for" << win << "=" << enabled; return;
}
m_icons[win] = enabled;
qCDebug(TRAYMGR) << "EnableNotification for" << win << "=" << enabled;
} }

View File

@@ -1,7 +1,8 @@
/* // Deepin DDE TrayManager1 implementation
Deepin DDE TrayManager1 implementation //
SPDX-License-Identifier: LGPL-2.1-or-later // SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
*/ //
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once #pragma once
@@ -74,7 +75,9 @@ Q_SIGNALS:
void Changed(uint32_t id); void Changed(uint32_t id);
void Inited(); void Inited();
void reclainRequested();
private: private:
TrayManager1Adaptor * m_adaptor; TrayManager1Adaptor * m_adaptor;
QHash<xcb_window_t, bool> m_icons; QHash<xcb_window_t, bool> m_icons; // <winid, enableNotify>
}; };

421
util.cpp Normal file
View File

@@ -0,0 +1,421 @@
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QLoggingCategory>
#include "util.h"
#include <QSize>
#include <QPixmap>
#include <QBitmap>
#include <QFileInfo>
#include <QtGlobal>
#include <QSocketNotifier>
#include <QCoreApplication>
#include <QAbstractEventDispatcher>
#include <X11/Xlib.h>
#include <mutex>
#include <xcb/res.h>
#include <xcb/xcb.h>
#include <xcb/xcb_atom.h>
#include <xcb/xtest.h>
#include <xcb/xproto.h>
#include <xcb/composite.h>
Q_LOGGING_CATEGORY(TRAYUTIL, "org.deepin.dde.trayloader.util")
namespace tray {
void clean_xcb_image(void *data)
{
xcb_image_destroy(static_cast<xcb_image_t *>(data));
}
Util* Util::instance()
{
static Util* _instance = nullptr;
if (_instance == nullptr)
_instance = new Util();
return _instance;
}
void Util::dispatchEvents(DispatchEventsMode mode)
{
xcb_connection_t *connection = m_x11connection;
if (!connection) {
qCWarning(TRAYUTIL, "Attempting to dispatch X11 events with no connection");
return;
}
auto pollEventFunc = mode == DispatchEventsMode::Poll ? xcb_poll_for_event : xcb_poll_for_queued_event;
while (xcb_generic_event_t *event = pollEventFunc(connection)) {
qintptr result = 0;
QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
dispatcher->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result);
free(event);
}
xcb_flush(connection);
}
Util::Util()
: QObject()
{
m_x11connection = xcb_connect(nullptr, nullptr);
m_display = XOpenDisplay("");
if (!m_x11connection || !isXAvaliable()) {
return;
}
const xcb_setup_t *setup = xcb_get_setup(m_x11connection);
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
xcb_screen_t* screen = iter.data;
m_rootWindow = screen->root;
xcb_ewmh_init_atoms_replies(&m_ewmh, xcb_ewmh_init_atoms(m_x11connection, &m_ewmh), nullptr);
const int fd = xcb_get_file_descriptor(m_x11connection);
QSocketNotifier * qfd = new QSocketNotifier(fd, QSocketNotifier::Read, this);
connect(qfd, &QSocketNotifier::activated, this, [this](){
dispatchEvents(DispatchEventsMode::Poll);
});
QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this, [this]() {
dispatchEvents(DispatchEventsMode::EventQueue);
});
connect(dispatcher, &QAbstractEventDispatcher::awake, this, [this]() {
dispatchEvents(DispatchEventsMode::EventQueue);
});
}
Util::~Util()
{
}
bool Util::isXAvaliable()
{
static std::once_flag flag;
static bool avaliable = false;
std::call_once(flag, [this](){
if (!(m_x11connection && m_display)) return;
// xtest support
const xcb_query_extension_reply_t *xtest_ext_reply;
xtest_ext_reply = xcb_get_extension_data(m_x11connection, &xcb_test_id);
// xshape support
const xcb_query_extension_reply_t *xshape_ext_reply;
xshape_ext_reply = xcb_get_extension_data(m_x11connection, &xcb_shape_id);
// xewmh support
xcb_ewmh_connection_t ewmh;
xcb_intern_atom_cookie_t *ewmh_cookie = xcb_ewmh_init_atoms(m_x11connection, &ewmh);
if (!ewmh_cookie) return;
xcb_ewmh_init_atoms_replies(&ewmh, ewmh_cookie, NULL);
avaliable = m_x11connection && m_display &&
(xtest_ext_reply && xtest_ext_reply->present) &&
(xshape_ext_reply && xshape_ext_reply->present) &&
(ewmh._NET_WM_STATE && ewmh._NET_WM_WINDOW_TYPE);
});
return avaliable;
}
xcb_connection_t* Util::getX11Connection()
{
return m_x11connection;
}
xcb_window_t Util::getRootWindow()
{
return m_rootWindow;
}
_XDisplay* Util::getDisplay()
{
return m_display;
}
xcb_atom_t Util::getAtomByName(const QString &name)
{
xcb_atom_t ret = m_atoms.value(name, 0);
if (!ret) {
xcb_intern_atom_cookie_t cookie = xcb_intern_atom(m_x11connection, false, name.size(), name.toStdString().c_str());
QSharedPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_x11connection, cookie, nullptr), [=](xcb_intern_atom_reply_t* reply){
free(reply);}
);
if (reply) {
m_atoms.insert(name, xcb_atom_t(reply->atom));
ret = reply->atom;
}
}
return ret;
}
QString Util::getNameByAtom(const xcb_atom_t& atom)
{
auto name = m_atoms.key(atom);
if (name.isEmpty()) {
xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(m_x11connection, atom);
QSharedPointer<xcb_get_atom_name_reply_t> reply(
xcb_get_atom_name_reply(m_x11connection, cookie, nullptr),
[=](xcb_get_atom_name_reply_t* reply) {free(reply);});
if (!reply) {
return name;
}
std::string tmp;
tmp.assign(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get()));
name = tmp.c_str();
if (!name.isEmpty()) {
m_atoms.insert(name, atom);
}
}
return name;
}
xcb_atom_t Util::getAtomFromDisplay(const char * name)
{
return getAtomByName(xcb_atom_name_by_screen(name, DefaultScreen(getDisplay())));
}
void Util::moveX11Window(const xcb_window_t& window, const uint32_t& x, const uint32_t& y)
{
const uint32_t windowMoveConfigVals[2] = {x, y};
xcb_configure_window(m_x11connection, window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, windowMoveConfigVals);
xcb_flush(m_x11connection);
}
void Util::setX11WindowSize(const xcb_window_t& window, const QSize& size)
{
const int windowSizeConfigVals[2] = {size.width(), size.height()};
xcb_configure_window(m_x11connection, window, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, windowSizeConfigVals);
xcb_flush(m_x11connection);
}
QSize Util::getX11WindowSize(const xcb_window_t& window)
{
auto cookie = xcb_get_geometry(m_x11connection, window);
QSharedPointer<xcb_get_geometry_reply_t> clientGeom(xcb_get_geometry_reply(m_x11connection, cookie, nullptr));
return clientGeom ? QSize(clientGeom->width, clientGeom->height) : QSize(0, 0);
}
QString Util::getX11WindowName(const xcb_window_t& window)
{
std::string ret;
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name(&m_ewmh, window);
xcb_ewmh_get_utf8_strings_reply_t reply;
if (xcb_ewmh_get_wm_name_reply(&m_ewmh, cookie, &reply, nullptr)) {
ret.assign(reply.strings, reply.strings_len);
xcb_ewmh_get_utf8_strings_reply_wipe(&reply);
}
return ret.c_str();
}
void Util::setX11WindowInputShape(const xcb_window_t& window, const QSize& size)
{
xcb_rectangle_t rectangle;
rectangle.x = 0;
rectangle.y = 0;
rectangle.width = size.width();
rectangle.height = size.height();
xcb_shape_rectangles(m_x11connection, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED, window, 0, 0, 1, &rectangle);
xcb_shape_mask(m_x11connection, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, window, 0, 0, XCB_PIXMAP_NONE);
const uint32_t stackData[] = {size.width() > 0 && size.height() > 0 ? XCB_STACK_MODE_ABOVE : XCB_STACK_MODE_BELOW};
xcb_configure_window(m_x11connection, window, XCB_CONFIG_WINDOW_STACK_MODE, stackData);
xcb_flush(m_x11connection);
}
QImage Util::getX11WidnowImageNonComposite(const xcb_window_t& window)
{
QSize size = getX11WindowSize(window);
xcb_image_t *image = xcb_image_get(m_x11connection, window, 0, 0, size.width(), size.height(), 0xFFFFFFFF, XCB_IMAGE_FORMAT_Z_PIXMAP);
QImage naiveConversion;
if (image) {
naiveConversion = QImage(image->data, image->width, image->height, QImage::Format_ARGB32);
} else {
return QImage();
}
if (isTransparentImage(naiveConversion)) {
QImage elaborateConversion = QImage(convertFromNative(image));
if (isTransparentImage(elaborateConversion)) {
return QImage();
} else
return elaborateConversion;
} else {
return QImage(image->data, image->width, image->height, image->stride, QImage::Format_ARGB32, clean_xcb_image, image);
}
}
void Util::setX11WindowOpacity(const xcb_window_t& window, const double& opacity)
{
xcb_atom_t opacityAtom = getAtomByName("_NET_WM_WINDOW_OPACITY");
quint32 value = qRound64(qBound(qreal(0), opacity, qreal(1)) * 0xffffffff);
xcb_change_property(m_x11connection,
XCB_PROP_MODE_REPLACE,
window,
opacityAtom,
XCB_ATOM_CARDINAL,
32,
1,
(uchar *)&value);
xcb_flush(m_x11connection);
}
pid_t Util::getWindowPid(const xcb_window_t& window)
{
xcb_res_client_id_spec_t spec = { window, XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID };
xcb_res_query_client_ids_cookie_t cookie = xcb_res_query_client_ids_unchecked(m_x11connection, 1, &spec);
QSharedPointer<xcb_res_query_client_ids_reply_t> reply(xcb_res_query_client_ids_reply(m_x11connection, cookie, NULL),[](xcb_res_query_client_ids_reply_t* reply){
free(reply);
});
if (reply) {
xcb_res_client_id_value_iterator_t iter = xcb_res_query_client_ids_ids_iterator(reply.get());
for (; iter.rem; xcb_res_client_id_value_next(&iter)) {
if (iter.data->spec.mask == XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID && xcb_res_client_id_value_value_length(iter.data) == 1) {
return xcb_res_client_id_value_value(iter.data)[0];
}
}
}
// qWarning() << "failed to get pid for window: " << window;
return 0;
}
QString Util::getProcExe(const pid_t& pid)
{
return QFileInfo(QString("/proc/").append(QString::number(pid).append("/exe"))).canonicalFilePath().split("/").last();
}
void Util::sendXembedMessage(const xcb_window_t& window, const long& message, const long& data1, const long& data2, const long& data3)
{
xcb_client_message_event_t ev;
ev.response_type = XCB_CLIENT_MESSAGE;
ev.window = window;
ev.format = 32;
ev.data.data32[0] = XCB_CURRENT_TIME;
ev.data.data32[1] = message;
ev.data.data32[2] = data1;
ev.data.data32[3] = data2;
ev.data.data32[4] = data3;
ev.type = getAtomByName(QStringLiteral("_XEMBED"));
xcb_send_event(m_x11connection, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)&ev);
}
QString Util::generateUniqueId(const QString &id)
{
for (int i = 0; i < 100; i++) {
QString newId = id + "-" + QString::number(i);
if (!m_currentIds.contains(newId)) {
m_currentIds.insert(newId);
return newId;
}
}
qWarning() << "failed to generate unique id:" << id;
return id;
}
void Util::removeUniqueId(const QString &id) {
m_currentIds.remove(id);
}
bool Util::isTransparentImage(const QImage &image)
{
int w = image.width();
int h = image.height();
if (!(qAlpha(image.pixel(w >> 1, h >> 1)) + qAlpha(image.pixel(w >> 2, h >> 2)) == 0))
return false;
for (int x = 0; x < w; ++x) {
for (int y = 0; y < h; ++y) {
if (qAlpha(image.pixel(x, y))) {
return false;
}
}
}
return true;
}
QImage Util::convertFromNative(xcb_image_t *xcbImage)
{
QImage::Format format = QImage::Format_Invalid;
switch (xcbImage->depth) {
case 1:
format = QImage::Format_MonoLSB;
break;
case 16:
format = QImage::Format_RGB16;
break;
case 24:
format = QImage::Format_RGB32;
break;
case 30: {
quint32 *pixels = reinterpret_cast<quint32 *>(xcbImage->data);
for (uint i = 0; i < (xcbImage->size / 4); i++) {
int r = (pixels[i] >> 22) & 0xff;
int g = (pixels[i] >> 12) & 0xff;
int b = (pixels[i] >> 2) & 0xff;
pixels[i] = qRgba(r, g, b, 0xff);
}
Q_FALLTHROUGH();
}
case 32:
format = QImage::Format_ARGB32_Premultiplied;
break;
default:
return QImage();
}
QImage image(xcbImage->data, xcbImage->width, xcbImage->height, xcbImage->stride, format, clean_xcb_image, xcbImage);
if (image.isNull()) {
return QImage();
}
if (format == QImage::Format_RGB32 && xcbImage->bpp == 32) {
QImage m = image.createHeuristicMask();
QPixmap p = QPixmap::fromImage(std::move(image));
p.setMask(QBitmap::fromImage(std::move(m)));
image = p.toImage();
}
if (image.format() == QImage::Format_MonoLSB) {
image.setColorCount(2);
image.setColor(0, QColor(Qt::white).rgb());
image.setColor(1, QColor(Qt::black).rgb());
}
return image;
}
QPoint Util::getMousePos() const
{
QPoint pos;
xcb_query_pointer_cookie_t cookie = xcb_query_pointer(m_x11connection, m_rootWindow);
QScopedPointer<xcb_query_pointer_reply_t> reply(xcb_query_pointer_reply(m_x11connection, cookie, NULL));
if (reply) {
pos = QPoint(reply->root_x, reply->root_y);
}
return pos;
}
}

83
util.h Normal file
View File

@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QHash>
#include <QImage>
#include <QSharedPointer>
#include <QSet>
#include <QObject>
#include <cstdint>
#include <sys/types.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include <xcb/xcb_ewmh.h>
#include <xcb/xcb_image.h>
struct _XDisplay;
namespace tray {
#define UTIL Util::instance()
class Util : public QObject
{
public:
static Util* instance();
bool isXAvaliable();
xcb_connection_t* getX11Connection();
xcb_window_t getRootWindow();
_XDisplay* getDisplay();
xcb_atom_t getAtomByName(const QString& name);
QString getNameByAtom(const xcb_atom_t& atom);
xcb_atom_t getAtomFromDisplay(const char * name);
void moveX11Window(const xcb_window_t& window, const uint32_t& x, const uint32_t& y);
void setX11WindowSize(const xcb_window_t& window, const QSize& size);
QSize getX11WindowSize(const xcb_window_t& window);
QString getX11WindowName(const xcb_window_t& window);
void setX11WindowInputShape(const xcb_window_t& widnow, const QSize& size);
QImage getX11WidnowImageNonComposite(const xcb_window_t& window);
void setX11WindowOpacity(const xcb_window_t& window, const double& opacity);
pid_t getWindowPid(const xcb_window_t& window);
QString getProcExe(const pid_t& pid);
void sendXembedMessage(const xcb_window_t& window, const long& message, const long& data1, const long& data2, const long& data3);
QString generateUniqueId(const QString &id);
void removeUniqueId(const QString &id);
QPoint getMousePos() const;
private:
Util();
~Util();
Util(const Util&) = delete;
Util& operator=(const Util&) = delete;
enum class DispatchEventsMode {
Poll,
EventQueue
};
void dispatchEvents(DispatchEventsMode mode);
bool isTransparentImage(const QImage &image);
QImage convertFromNative(xcb_image_t* image);
private:
xcb_ewmh_connection_t m_ewmh;
QHash<QString, xcb_atom_t> m_atoms;
xcb_connection_t* m_x11connection;
xcb_window_t m_rootWindow;
_XDisplay *m_display;
QSet<QString> m_currentIds;
};
}

View File

@@ -1,122 +0,0 @@
/*
SPDX-FileCopyrightText: 2012, 2013 Martin Graesslin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2015 David Edmudson <davidedmundson@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <xcb/composite.h>
#include <xcb/damage.h>
#include <xcb/randr.h>
#include <xcb/shm.h>
#include <xcb/xcb.h>
#include <xcb/xcb_atom.h>
#include <xcb/xcb_event.h>
#include <QGuiApplication>
#include <QList>
#include "c_ptr.h"
#include <X11/Xlib.h>
/** XEMBED messages */
#define XEMBED_EMBEDDED_NOTIFY 0
#define XEMBED_WINDOW_ACTIVATE 1
#define XEMBED_WINDOW_DEACTIVATE 2
#define XEMBED_REQUEST_FOCUS 3
#define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5
#define XEMBED_FOCUS_NEXT 6
#define XEMBED_FOCUS_PREV 7
namespace Xcb
{
typedef xcb_window_t WindowId;
class Atom
{
public:
explicit Atom(const QByteArray &name,
bool onlyIfExists = false,
xcb_connection_t *c = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection())
: m_connection(c)
, m_retrieved(false)
, m_cookie(xcb_intern_atom_unchecked(m_connection, onlyIfExists, name.length(), name.constData()))
, m_atom(XCB_ATOM_NONE)
, m_name(name)
{
}
Atom() = delete;
Atom(const Atom &) = delete;
~Atom()
{
if (!m_retrieved && m_cookie.sequence) {
xcb_discard_reply(m_connection, m_cookie.sequence);
}
}
operator xcb_atom_t() const
{
(const_cast<Atom *>(this))->getReply();
return m_atom;
}
bool isValid()
{
getReply();
return m_atom != XCB_ATOM_NONE;
}
bool isValid() const
{
(const_cast<Atom *>(this))->getReply();
return m_atom != XCB_ATOM_NONE;
}
inline const QByteArray &name() const
{
return m_name;
}
private:
void getReply()
{
if (m_retrieved || !m_cookie.sequence) {
return;
}
UniqueCPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_connection, m_cookie, nullptr));
if (reply) {
m_atom = reply->atom;
}
m_retrieved = true;
}
xcb_connection_t *m_connection;
bool m_retrieved;
xcb_intern_atom_cookie_t m_cookie;
xcb_atom_t m_atom;
QByteArray m_name;
};
class Atoms
{
public:
Atoms()
: xembedAtom("_XEMBED")
, selectionAtom(xcb_atom_name_by_screen("_NET_SYSTEM_TRAY", DefaultScreen(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display())))
, opcodeAtom("_NET_SYSTEM_TRAY_OPCODE")
, messageData("_NET_SYSTEM_TRAY_MESSAGE_DATA")
, visualAtom("_NET_SYSTEM_TRAY_VISUAL")
{
}
Atom xembedAtom;
Atom selectionAtom;
Atom opcodeAtom;
Atom messageData;
Atom visualAtom;
};
extern Atoms *atoms;
} // namespace Xcb

View File

@@ -1,19 +0,0 @@
/* Wrap XLIB code in a new file as it defines keywords that conflict with Qt
SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "xtestsender.h"
#include <X11/extensions/XTest.h>
void sendXTestPressed(Display *display, int button)
{
XTestFakeButtonEvent(display, button, true, 0);
}
void sendXTestReleased(Display *display, int button)
{
XTestFakeButtonEvent(display, button, false, 0);
}

View File

@@ -1,12 +0,0 @@
/* Wrap XLIB code in a new file as it defines keywords that conflict with Qt
SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
typedef struct _XDisplay Display;
void sendXTestPressed(Display *display, int button);
void sendXTestReleased(Display *display, int button);