File xembedsniproxy-inject-mouse-clicks-with-XTest.patch of Package plasma5-workspace
From 9df815e843a4385465fff0cb9a76ddc94ed35b38 Mon Sep 17 00:00:00 2001
From: David Edmundson <kde@davidedmundson.co.uk>
Date: Thu, 23 Mar 2017 18:08:45 +0000
Subject: Inject mouse clicks from SNI to xembedded icons with XTest
Summary:
A certain toolkit doesn't register for mouse press release events
because it now uses XI2 only.
Injecting those directly to the window is too difficult, fortunately in
the GTK3 case we can use XTest to send an event. Something I had
previously chosen against using because it didn't work with something
else (can't remember what). I now have a bit of code choosing which
method to use, which will hopefully cover all cases.
Code is a bit convuluted because the xcb version of xtest doesn't have
the high-level method I want to use in it's API, so I just used Xlib
version.
CCBUG: 375017
CCBUG: 362941
Test Plan:
Ran my usual bunch of test apps:
- xchat
- a GTK3 systray demo I made
- skype (A Qt4 app without SNI patches)
All worked as before
Reviewers: #plasma
Subscribers: plasma-devel
Tags: #plasma
Differential Revision: https://phabricator.kde.org/D5156
---
xembed-sni-proxy/CMakeLists.txt | 6 +++---
xembed-sni-proxy/sniproxy.cpp | 28 ++++++++++++++++++++++++++--
xembed-sni-proxy/sniproxy.h | 7 +++++++
xembed-sni-proxy/xtestsender.cpp | 32 ++++++++++++++++++++++++++++++++
xembed-sni-proxy/xtestsender.h | 28 ++++++++++++++++++++++++++++
5 files changed, 96 insertions(+), 5 deletions(-)
create mode 100644 xembed-sni-proxy/xtestsender.cpp
create mode 100644 xembed-sni-proxy/xtestsender.h
diff --git a/xembed-sni-proxy/CMakeLists.txt b/xembed-sni-proxy/CMakeLists.txt
index 2a96cd0..1a5322a 100644
--- a/xembed-sni-proxy/CMakeLists.txt
+++ b/xembed-sni-proxy/CMakeLists.txt
@@ -26,14 +26,13 @@ set(XCB_LIBS
XCB::IMAGE
)
-
-
set(XEMBED_SNI_PROXY_SOURCES
main.cpp
fdoselectionmanager.cpp
snidbus.cpp
sniproxy.cpp
-)
+ xtestsender.cpp
+ )
qt5_add_dbus_adaptor(XEMBED_SNI_PROXY_SOURCES org.kde.StatusNotifierItem.xml
sniproxy.h SNIProxy)
@@ -59,6 +58,7 @@ target_link_libraries(xembedsniproxy
Qt5::DBus
KF5::WindowSystem
${XCB_LIBS}
+ Xtst
)
install(TARGETS xembedsniproxy ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/xembed-sni-proxy/sniproxy.cpp b/xembed-sni-proxy/sniproxy.cpp
index c935c9e..47e98bb 100644
--- a/xembed-sni-proxy/sniproxy.cpp
+++ b/xembed-sni-proxy/sniproxy.cpp
@@ -24,6 +24,7 @@
#include <xcb/xcb_atom.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_image.h>
+#include <xcb/xinput.h>
#include "xcbutils.h"
#include "debug.h"
@@ -41,6 +42,10 @@
#include "statusnotifieritemadaptor.h"
#include "statusnotifierwatcher_interface.h"
+#include "xtestsender.h"
+
+//#define VISUAL_DEBUG
+
#define SNI_WATCHER_SERVICE_NAME "org.kde.StatusNotifierWatcher"
#define SNI_WATCHER_PATH "/StatusNotifierWatcher"
@@ -72,7 +77,8 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent):
//there is an undocumented feature that you can register an SNI by path, however it doesn't detect an object on a service being removed, only the entire service closing
//instead lets use one DBus connection per SNI
m_dbus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, QStringLiteral("XembedSniProxy%1").arg(s_serviceCount++))),
- m_windowId(wid)
+ m_windowId(wid),
+ m_injectMode(Direct)
{
//create new SNI
new StatusNotifierItemAdaptor(this);
@@ -195,6 +201,19 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent):
xcb_flush(c);
+ //guess which input injection method to use
+ //we can either send an X event to the client or XTest
+ //some don't support direct X events (GTK3/4), and some don't support XTest because reasons
+ //note also some clients might not have the XTest extension. We may as well assume it does and just fail to send later.
+
+ //we query if the client selected button presses in the event mask
+ //if the client does supports that we send directly, otherwise we'll use xtest
+ auto waCookie = xcb_get_window_attributes(c, wid);
+ auto windowAttributes = xcb_get_window_attributes_reply(c, waCookie, nullptr);
+ if (! windowAttributes->all_event_masks & XCB_EVENT_MASK_BUTTON_PRESS) {
+ m_injectMode = XTest;
+ }
+
//there's no damage event for the first paint, and sometimes it's not drawn immediately
//not ideal, but it works better than nothing
//test with xchat before changing
@@ -470,7 +489,7 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y)
xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackAboveData);
//mouse down
- {
+ if (m_injectMode == Direct) {
xcb_button_press_event_t* event = new xcb_button_press_event_t;
memset(event, 0x00, sizeof(xcb_button_press_event_t));
event->response_type = XCB_BUTTON_PRESS;
@@ -488,9 +507,12 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y)
xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_PRESS, (char *) event);
delete event;
+ } else {
+ sendXTestPressed(QX11Info::display(), mouseButton);
}
//mouse up
+ if (m_injectMode == Direct)
{
xcb_button_release_event_t* event = new xcb_button_release_event_t;
memset(event, 0x00, sizeof(xcb_button_release_event_t));
@@ -509,6 +531,8 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y)
xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_RELEASE, (char *) event);
delete event;
+ } else {
+ sendXTestReleased(QX11Info::display(), mouseButton);
}
#ifndef VISUAL_DEBUG
diff --git a/xembed-sni-proxy/sniproxy.h b/xembed-sni-proxy/sniproxy.h
index 7366c67..e897a92 100644
--- a/xembed-sni-proxy/sniproxy.h
+++ b/xembed-sni-proxy/sniproxy.h
@@ -140,6 +140,11 @@ Q_SIGNALS:
void NewStatus(const QString &status);
private:
+ enum InjectMode {
+ Direct,
+ XTest
+ };
+
void sendClick(uint8_t mouseButton, int x, int y);
QImage getImageNonComposite() const;
bool isTransparentImage(const QImage &image) const;
@@ -150,6 +155,8 @@ private:
xcb_window_t m_containerWid;
static int s_serviceCount;
QPixmap m_pixmap;
+
+ InjectMode m_injectMode;
};
#endif // SNIPROXY_H
diff --git a/xembed-sni-proxy/xtestsender.cpp b/xembed-sni-proxy/xtestsender.cpp
new file mode 100644
index 0000000..112f0aa
--- /dev/null
+++ b/xembed-sni-proxy/xtestsender.cpp
@@ -0,0 +1,32 @@
+/* Wrap XLIB code in a new file as it defines keywords that conflict with Qt
+ *
+ * Copyright (C) 2017 <davidedmundson@kde.org> David Edmundson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <X11/extensions/XTest.h>
+#include "xtestsender.h"
+
+void sendXTestPressed(Display *display, int button)
+{
+ XTestFakeButtonEvent(display, button, true, 0);
+}
+
+void sendXTestReleased(Display *display, int button)
+{
+ XTestFakeButtonEvent(display, button, false, 0);
+}
diff --git a/xembed-sni-proxy/xtestsender.h b/xembed-sni-proxy/xtestsender.h
new file mode 100644
index 0000000..d058d6b
--- /dev/null
+++ b/xembed-sni-proxy/xtestsender.h
@@ -0,0 +1,28 @@
+/* Wrap XLIB code in a new file as it defines keywords that conflict with Qt
+ *
+ * Copyright (C) 2017 <davidedmundson@kde.org> David Edmundson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef XTEST_SENDER_H
+#define XTEST_SENDER_H
+
+typedef _XDisplay Display;
+
+void sendXTestPressed(Display *display, int button);
+void sendXTestReleased(Display *display, int button);
+
+#endif
--
cgit v0.11.2