Skip to content

Commit 9df815e

Browse files
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
1 parent 3dc5886 commit 9df815e

File tree

5 files changed

+96
-5
lines changed

5 files changed

+96
-5
lines changed

Diff for: xembed-sni-proxy/CMakeLists.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,13 @@ set(XCB_LIBS
2626
XCB::IMAGE
2727
)
2828

29-
30-
3129
set(XEMBED_SNI_PROXY_SOURCES
3230
main.cpp
3331
fdoselectionmanager.cpp
3432
snidbus.cpp
3533
sniproxy.cpp
36-
)
34+
xtestsender.cpp
35+
)
3736

3837
qt5_add_dbus_adaptor(XEMBED_SNI_PROXY_SOURCES org.kde.StatusNotifierItem.xml
3938
sniproxy.h SNIProxy)
@@ -59,6 +58,7 @@ target_link_libraries(xembedsniproxy
5958
Qt5::DBus
6059
KF5::WindowSystem
6160
${XCB_LIBS}
61+
Xtst
6262
)
6363

6464
install(TARGETS xembedsniproxy ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

Diff for: xembed-sni-proxy/sniproxy.cpp

+26-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <xcb/xcb_atom.h>
2525
#include <xcb/xcb_event.h>
2626
#include <xcb/xcb_image.h>
27+
#include <xcb/xinput.h>
2728

2829
#include "xcbutils.h"
2930
#include "debug.h"
@@ -41,6 +42,10 @@
4142
#include "statusnotifieritemadaptor.h"
4243
#include "statusnotifierwatcher_interface.h"
4344

45+
#include "xtestsender.h"
46+
47+
//#define VISUAL_DEBUG
48+
4449
#define SNI_WATCHER_SERVICE_NAME "org.kde.StatusNotifierWatcher"
4550
#define SNI_WATCHER_PATH "/StatusNotifierWatcher"
4651

@@ -72,7 +77,8 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent):
7277
//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
7378
//instead lets use one DBus connection per SNI
7479
m_dbus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, QStringLiteral("XembedSniProxy%1").arg(s_serviceCount++))),
75-
m_windowId(wid)
80+
m_windowId(wid),
81+
m_injectMode(Direct)
7682
{
7783
//create new SNI
7884
new StatusNotifierItemAdaptor(this);
@@ -195,6 +201,19 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent):
195201

196202
xcb_flush(c);
197203

204+
//guess which input injection method to use
205+
//we can either send an X event to the client or XTest
206+
//some don't support direct X events (GTK3/4), and some don't support XTest because reasons
207+
//note also some clients might not have the XTest extension. We may as well assume it does and just fail to send later.
208+
209+
//we query if the client selected button presses in the event mask
210+
//if the client does supports that we send directly, otherwise we'll use xtest
211+
auto waCookie = xcb_get_window_attributes(c, wid);
212+
auto windowAttributes = xcb_get_window_attributes_reply(c, waCookie, nullptr);
213+
if (! windowAttributes->all_event_masks & XCB_EVENT_MASK_BUTTON_PRESS) {
214+
m_injectMode = XTest;
215+
}
216+
198217
//there's no damage event for the first paint, and sometimes it's not drawn immediately
199218
//not ideal, but it works better than nothing
200219
//test with xchat before changing
@@ -470,7 +489,7 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y)
470489
xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackAboveData);
471490

472491
//mouse down
473-
{
492+
if (m_injectMode == Direct) {
474493
xcb_button_press_event_t* event = new xcb_button_press_event_t;
475494
memset(event, 0x00, sizeof(xcb_button_press_event_t));
476495
event->response_type = XCB_BUTTON_PRESS;
@@ -488,9 +507,12 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y)
488507

489508
xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_PRESS, (char *) event);
490509
delete event;
510+
} else {
511+
sendXTestPressed(QX11Info::display(), mouseButton);
491512
}
492513

493514
//mouse up
515+
if (m_injectMode == Direct)
494516
{
495517
xcb_button_release_event_t* event = new xcb_button_release_event_t;
496518
memset(event, 0x00, sizeof(xcb_button_release_event_t));
@@ -509,6 +531,8 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y)
509531

510532
xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_RELEASE, (char *) event);
511533
delete event;
534+
} else {
535+
sendXTestReleased(QX11Info::display(), mouseButton);
512536
}
513537

514538
#ifndef VISUAL_DEBUG

Diff for: xembed-sni-proxy/sniproxy.h

+7
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ public Q_SLOTS:
140140
void NewStatus(const QString &status);
141141

142142
private:
143+
enum InjectMode {
144+
Direct,
145+
XTest
146+
};
147+
143148
void sendClick(uint8_t mouseButton, int x, int y);
144149
QImage getImageNonComposite() const;
145150
bool isTransparentImage(const QImage &image) const;
@@ -150,6 +155,8 @@ public Q_SLOTS:
150155
xcb_window_t m_containerWid;
151156
static int s_serviceCount;
152157
QPixmap m_pixmap;
158+
159+
InjectMode m_injectMode;
153160
};
154161

155162
#endif // SNIPROXY_H

Diff for: xembed-sni-proxy/xtestsender.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* Wrap XLIB code in a new file as it defines keywords that conflict with Qt
2+
*
3+
* Copyright (C) 2017 <[email protected]> David Edmundson
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 2.1 of the License, or (at your option) any later version.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*
19+
*/
20+
21+
#include <X11/extensions/XTest.h>
22+
#include "xtestsender.h"
23+
24+
void sendXTestPressed(Display *display, int button)
25+
{
26+
XTestFakeButtonEvent(display, button, true, 0);
27+
}
28+
29+
void sendXTestReleased(Display *display, int button)
30+
{
31+
XTestFakeButtonEvent(display, button, false, 0);
32+
}

Diff for: xembed-sni-proxy/xtestsender.h

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* Wrap XLIB code in a new file as it defines keywords that conflict with Qt
2+
*
3+
* Copyright (C) 2017 <[email protected]> David Edmundson
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 2.1 of the License, or (at your option) any later version.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*
19+
*/
20+
#ifndef XTEST_SENDER_H
21+
#define XTEST_SENDER_H
22+
23+
typedef _XDisplay Display;
24+
25+
void sendXTestPressed(Display *display, int button);
26+
void sendXTestReleased(Display *display, int button);
27+
28+
#endif

0 commit comments

Comments
 (0)