diff --git a/xembed-sni-proxy/CMakeLists.txt b/xembed-sni-proxy/CMakeLists.txt --- a/xembed-sni-proxy/CMakeLists.txt +++ b/xembed-sni-proxy/CMakeLists.txt @@ -26,14 +26,13 @@ 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 @@ Qt5::DBus KF5::WindowSystem ${XCB_LIBS} + Xtst ) install(TARGETS xembedsniproxy ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/xembed-sni-proxy/sniproxy.h b/xembed-sni-proxy/sniproxy.h --- a/xembed-sni-proxy/sniproxy.h +++ b/xembed-sni-proxy/sniproxy.h @@ -140,6 +140,11 @@ 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 @@ xcb_window_t m_containerWid; static int s_serviceCount; QPixmap m_pixmap; + + InjectMode m_injectMode; }; #endif // SNIPROXY_H diff --git a/xembed-sni-proxy/sniproxy.cpp b/xembed-sni-proxy/sniproxy.cpp --- a/xembed-sni-proxy/sniproxy.cpp +++ b/xembed-sni-proxy/sniproxy.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #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 @@ //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 @@ 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 @@ 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 @@ 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 @@ 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/xtestsender.h b/xembed-sni-proxy/xtestsender.h new file mode 100644 --- /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 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 diff --git a/xembed-sni-proxy/xtestsender.cpp b/xembed-sni-proxy/xtestsender.cpp new file mode 100644 --- /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 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 +#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); +}