Changeset View
Changeset View
Standalone View
Standalone View
xembed-sni-proxy/sniproxy.cpp
Show All 18 Lines | |||||
19 | */ | 19 | */ | ||
20 | 20 | | |||
21 | #include "sniproxy.h" | 21 | #include "sniproxy.h" | ||
22 | 22 | | |||
23 | #include <xcb/xcb.h> | 23 | #include <xcb/xcb.h> | ||
24 | #include <xcb/xcb_atom.h> | 24 | #include <xcb/xcb_atom.h> | ||
25 | #include <xcb/xcb_event.h> | 25 | #include <xcb/xcb_event.h> | ||
26 | #include <xcb/xcb_image.h> | 26 | #include <xcb/xcb_image.h> | ||
27 | #include <xcb/xinput.h> | ||||
27 | 28 | | |||
28 | #include "xcbutils.h" | 29 | #include "xcbutils.h" | ||
29 | #include "debug.h" | 30 | #include "debug.h" | ||
30 | 31 | | |||
31 | #include <QX11Info> | 32 | #include <QX11Info> | ||
32 | #include <QScreen> | 33 | #include <QScreen> | ||
33 | #include <QGuiApplication> | 34 | #include <QGuiApplication> | ||
34 | #include <QTimer> | 35 | #include <QTimer> | ||
35 | 36 | | |||
36 | #include <QBitmap> | 37 | #include <QBitmap> | ||
37 | 38 | | |||
38 | #include <KWindowSystem> | 39 | #include <KWindowSystem> | ||
39 | #include <netwm.h> | 40 | #include <netwm.h> | ||
40 | 41 | | |||
41 | #include "statusnotifieritemadaptor.h" | 42 | #include "statusnotifieritemadaptor.h" | ||
42 | #include "statusnotifierwatcher_interface.h" | 43 | #include "statusnotifierwatcher_interface.h" | ||
43 | 44 | | |||
45 | #include "xtestsender.h" | ||||
46 | | ||||
47 | //#define VISUAL_DEBUG | ||||
48 | | ||||
44 | #define SNI_WATCHER_SERVICE_NAME "org.kde.StatusNotifierWatcher" | 49 | #define SNI_WATCHER_SERVICE_NAME "org.kde.StatusNotifierWatcher" | ||
45 | #define SNI_WATCHER_PATH "/StatusNotifierWatcher" | 50 | #define SNI_WATCHER_PATH "/StatusNotifierWatcher" | ||
46 | 51 | | |||
47 | static uint16_t s_embedSize = 32; //max size of window to embed. We no longer resize the embedded window as Chromium acts stupidly. | 52 | static uint16_t s_embedSize = 32; //max size of window to embed. We no longer resize the embedded window as Chromium acts stupidly. | ||
48 | 53 | | |||
49 | int SNIProxy::s_serviceCount = 0; | 54 | int SNIProxy::s_serviceCount = 0; | ||
50 | 55 | | |||
51 | void | 56 | void | ||
Show All 15 Lines | |||||
67 | } | 72 | } | ||
68 | 73 | | |||
69 | SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent): | 74 | SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent): | ||
70 | QObject(parent), | 75 | QObject(parent), | ||
71 | //Work round a bug in our SNIWatcher with multiple SNIs per connection. | 76 | //Work round a bug in our SNIWatcher with multiple SNIs per connection. | ||
72 | //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 | 77 | //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 | ||
73 | //instead lets use one DBus connection per SNI | 78 | //instead lets use one DBus connection per SNI | ||
74 | m_dbus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, QStringLiteral("XembedSniProxy%1").arg(s_serviceCount++))), | 79 | m_dbus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, QStringLiteral("XembedSniProxy%1").arg(s_serviceCount++))), | ||
75 | m_windowId(wid) | 80 | m_windowId(wid), | ||
81 | m_injectMode(Direct) | ||||
anthonyfieroni: You mean Direct here, no? | |||||
davidedmundson: I do, good catch. | |||||
76 | { | 82 | { | ||
77 | //create new SNI | 83 | //create new SNI | ||
78 | new StatusNotifierItemAdaptor(this); | 84 | new StatusNotifierItemAdaptor(this); | ||
79 | m_dbus.registerObject(QStringLiteral("/StatusNotifierItem"), this); | 85 | m_dbus.registerObject(QStringLiteral("/StatusNotifierItem"), this); | ||
80 | 86 | | |||
81 | auto statusNotifierWatcher = new org::kde::StatusNotifierWatcher(QStringLiteral(SNI_WATCHER_SERVICE_NAME), QStringLiteral(SNI_WATCHER_PATH), QDBusConnection::sessionBus(), this); | 87 | auto statusNotifierWatcher = new org::kde::StatusNotifierWatcher(QStringLiteral(SNI_WATCHER_SERVICE_NAME), QStringLiteral(SNI_WATCHER_PATH), QDBusConnection::sessionBus(), this); | ||
82 | auto reply = statusNotifierWatcher->RegisterStatusNotifierItem(m_dbus.baseService()); | 88 | auto reply = statusNotifierWatcher->RegisterStatusNotifierItem(m_dbus.baseService()); | ||
83 | reply.waitForFinished(); | 89 | reply.waitForFinished(); | ||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Line(s) | 135 | #endif | |||
190 | 196 | | |||
191 | //show the embedded window otherwise nothing happens | 197 | //show the embedded window otherwise nothing happens | ||
192 | xcb_map_window(c, wid); | 198 | xcb_map_window(c, wid); | ||
193 | 199 | | |||
194 | xcb_clear_area(c, 0, wid, 0, 0, clientWindowSize.width(), clientWindowSize.height()); | 200 | xcb_clear_area(c, 0, wid, 0, 0, clientWindowSize.width(), clientWindowSize.height()); | ||
195 | 201 | | |||
196 | xcb_flush(c); | 202 | xcb_flush(c); | ||
197 | 203 | | |||
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 | | ||||
198 | //there's no damage event for the first paint, and sometimes it's not drawn immediately | 217 | //there's no damage event for the first paint, and sometimes it's not drawn immediately | ||
199 | //not ideal, but it works better than nothing | 218 | //not ideal, but it works better than nothing | ||
200 | //test with xchat before changing | 219 | //test with xchat before changing | ||
201 | QTimer::singleShot(500, this, &SNIProxy::update); | 220 | QTimer::singleShot(500, this, &SNIProxy::update); | ||
202 | } | 221 | } | ||
203 | 222 | | |||
204 | SNIProxy::~SNIProxy() | 223 | SNIProxy::~SNIProxy() | ||
205 | { | 224 | { | ||
▲ Show 20 Lines • Show All 259 Lines • ▼ Show 20 Line(s) | 482 | else | |||
465 | } | 484 | } | ||
466 | xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, configVals); | 485 | xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, configVals); | ||
467 | 486 | | |||
468 | //pull window up | 487 | //pull window up | ||
469 | const uint32_t stackAboveData[] = {XCB_STACK_MODE_ABOVE}; | 488 | const uint32_t stackAboveData[] = {XCB_STACK_MODE_ABOVE}; | ||
470 | xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackAboveData); | 489 | xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackAboveData); | ||
471 | 490 | | |||
472 | //mouse down | 491 | //mouse down | ||
473 | { | 492 | if (m_injectMode == Direct) { | ||
474 | xcb_button_press_event_t* event = new xcb_button_press_event_t; | 493 | xcb_button_press_event_t* event = new xcb_button_press_event_t; | ||
475 | memset(event, 0x00, sizeof(xcb_button_press_event_t)); | 494 | memset(event, 0x00, sizeof(xcb_button_press_event_t)); | ||
476 | event->response_type = XCB_BUTTON_PRESS; | 495 | event->response_type = XCB_BUTTON_PRESS; | ||
477 | event->event = m_windowId; | 496 | event->event = m_windowId; | ||
478 | event->time = QX11Info::getTimestamp(); | 497 | event->time = QX11Info::getTimestamp(); | ||
479 | event->same_screen = 1; | 498 | event->same_screen = 1; | ||
480 | event->root = QX11Info::appRootWindow(); | 499 | event->root = QX11Info::appRootWindow(); | ||
481 | event->root_x = x; | 500 | event->root_x = x; | ||
482 | event->root_y = y; | 501 | event->root_y = y; | ||
483 | event->event_x = 0; | 502 | event->event_x = 0; | ||
484 | event->event_y = 0; | 503 | event->event_y = 0; | ||
485 | event->child = 0; | 504 | event->child = 0; | ||
486 | event->state = 0; | 505 | event->state = 0; | ||
487 | event->detail = mouseButton; | 506 | event->detail = mouseButton; | ||
488 | 507 | | |||
489 | xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_PRESS, (char *) event); | 508 | xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_PRESS, (char *) event); | ||
490 | delete event; | 509 | delete event; | ||
510 | } else { | ||||
511 | sendXTestPressed(QX11Info::display(), mouseButton); | ||||
491 | } | 512 | } | ||
492 | 513 | | |||
493 | //mouse up | 514 | //mouse up | ||
515 | if (m_injectMode == Direct) | ||||
494 | { | 516 | { | ||
495 | xcb_button_release_event_t* event = new xcb_button_release_event_t; | 517 | xcb_button_release_event_t* event = new xcb_button_release_event_t; | ||
496 | memset(event, 0x00, sizeof(xcb_button_release_event_t)); | 518 | memset(event, 0x00, sizeof(xcb_button_release_event_t)); | ||
497 | event->response_type = XCB_BUTTON_RELEASE; | 519 | event->response_type = XCB_BUTTON_RELEASE; | ||
498 | event->event = m_windowId; | 520 | event->event = m_windowId; | ||
499 | event->time = QX11Info::getTimestamp(); | 521 | event->time = QX11Info::getTimestamp(); | ||
500 | event->same_screen = 1; | 522 | event->same_screen = 1; | ||
501 | event->root = QX11Info::appRootWindow(); | 523 | event->root = QX11Info::appRootWindow(); | ||
502 | event->root_x = x; | 524 | event->root_x = x; | ||
503 | event->root_y = y; | 525 | event->root_y = y; | ||
504 | event->event_x = 0; | 526 | event->event_x = 0; | ||
505 | event->event_y = 0; | 527 | event->event_y = 0; | ||
506 | event->child = 0; | 528 | event->child = 0; | ||
507 | event->state = 0; | 529 | event->state = 0; | ||
508 | event->detail = mouseButton; | 530 | event->detail = mouseButton; | ||
509 | 531 | | |||
510 | xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_RELEASE, (char *) event); | 532 | xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_RELEASE, (char *) event); | ||
511 | delete event; | 533 | delete event; | ||
534 | } else { | ||||
535 | sendXTestReleased(QX11Info::display(), mouseButton); | ||||
512 | } | 536 | } | ||
513 | 537 | | |||
514 | #ifndef VISUAL_DEBUG | 538 | #ifndef VISUAL_DEBUG | ||
515 | const uint32_t stackBelowData[] = {XCB_STACK_MODE_BELOW}; | 539 | const uint32_t stackBelowData[] = {XCB_STACK_MODE_BELOW}; | ||
516 | xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackBelowData); | 540 | xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackBelowData); | ||
517 | #endif | 541 | #endif | ||
518 | } | 542 | } |
You mean Direct here, no?