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
wilder-5.14
David Edmundson 9 years ago
parent 3dc5886e62
commit 9df815e843
  1. 6
      xembed-sni-proxy/CMakeLists.txt
  2. 28
      xembed-sni-proxy/sniproxy.cpp
  3. 7
      xembed-sni-proxy/sniproxy.h
  4. 32
      xembed-sni-proxy/xtestsender.cpp
  5. 28
      xembed-sni-proxy/xtestsender.h

@ -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})

@ -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

@ -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

@ -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);
}

@ -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
Loading…
Cancel
Save