diff --git a/xembed-sni-proxy/main.cpp b/xembed-sni-proxy/main.cpp --- a/xembed-sni-proxy/main.cpp +++ b/xembed-sni-proxy/main.cpp @@ -35,7 +35,7 @@ Xcb::Atoms* atoms; } -int main(int argc, char ** argv) +int main(int argc, char **argv) { //the whole point of this is to interact with X, if we are in any other session, force trying to connect to X //if the QPA can't load xcb, this app is useless anyway. 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 @@ -146,6 +146,7 @@ XTest }; + QSize getClientWindowSize() const; void sendClick(uint8_t mouseButton, int x, int y); QImage getImageNonComposite() const; bool isTransparentImage(const QImage &image) const; 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 @@ -70,7 +70,7 @@ xcb_send_event(QX11Info::connection(), false, towin, XCB_EVENT_MASK_NO_EVENT, (char *) &ev); } -SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent): +SNIProxy::SNIProxy(xcb_window_t wid, QObject *parent): QObject(parent), //Work round a bug in our SNIWatcher with multiple SNIs per connection. //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 @@ -92,27 +92,23 @@ auto c = QX11Info::connection(); - auto cookie = xcb_get_geometry(c, m_windowId); - QScopedPointer - clientGeom(xcb_get_geometry_reply(c, cookie, nullptr)); - //create a container window - auto screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data; + auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data; m_containerWid = xcb_generate_id(c); uint32_t values[2]; auto mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT; values[0] = screen->black_pixel; //draw a solid background so the embedded icon doesn't get garbage in it values[1] = true; //bypass wM - xcb_create_window (c, /* connection */ - XCB_COPY_FROM_PARENT, /* depth */ - m_containerWid, /* window Id */ - screen->root, /* parent window */ - 0, 0, /* x, y */ - s_embedSize, s_embedSize, /* width, height */ - 0, /* border_width */ - XCB_WINDOW_CLASS_INPUT_OUTPUT,/* class */ - screen->root_visual, /* visual */ - mask, values); /* masks */ + xcb_create_window(c, /* connection */ + XCB_COPY_FROM_PARENT, /* depth */ + m_containerWid, /* window Id */ + screen->root, /* parent window */ + 0, 0, /* x, y */ + s_embedSize, s_embedSize, /* width, height */ + 0, /* border_width */ + XCB_WINDOW_CLASS_INPUT_OUTPUT,/* class */ + screen->root_visual, /* visual */ + mask, values); /* masks */ /* We need the window to exist and be mapped otherwise the child won't render it's contents @@ -146,7 +142,6 @@ */ xcb_composite_redirect_window(c, wid, XCB_COMPOSITE_REDIRECT_MANUAL); - /* we grab the window, but also make sure it's automatically reparented back * to the root window if we should die. */ @@ -159,39 +154,10 @@ const uint32_t windowMoveConfigVals[2] = { 0, 0 }; xcb_configure_window(c, wid, - XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, - windowMoveConfigVals); - - - QSize clientWindowSize; + XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, + windowMoveConfigVals); - if (clientGeom) { - clientWindowSize = QSize(clientGeom->width, clientGeom->height); - } - //if the window is a clearly stupid size resize to be something sensible - //this is needed as chormium and such when resized just fill the icon with transparent space and only draw in the middle - //however spotify does need this as by default the window size is 900px wide. - //use an artbitrary heuristic to make sure icons are always sensible - if (clientWindowSize.isEmpty() || clientWindowSize.width() > s_embedSize || clientWindowSize.height() > s_embedSize ) - { - qCDebug(SNIPROXY) << "Resizing window" << wid << Title() << "from w*h" << clientWindowSize; - - xcb_configure_notify_event_t event; - memset(&event, 0x00, sizeof(xcb_configure_notify_event_t)); - event.response_type = XCB_CONFIGURE_NOTIFY; - event.event = wid; - event.window = wid; - event.width = s_embedSize; - event.height = s_embedSize; - xcb_send_event(c, false, wid, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *) &event); - - const uint32_t windowMoveConfigVals[2] = { s_embedSize, s_embedSize }; - xcb_configure_window(c, wid, - XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, - windowMoveConfigVals); - - clientWindowSize = QSize(s_embedSize, s_embedSize); - } + QSize clientWindowSize = getClientWindowSize(); //show the embedded window otherwise nothing happens xcb_map_window(c, wid); @@ -209,7 +175,7 @@ //if the client does supports that we send directly, otherwise we'll use xtest auto waCookie = xcb_get_window_attributes(c, wid); QScopedPointer windowAttributes(xcb_get_window_attributes_reply(c, waCookie, nullptr)); - if (windowAttributes && ! (windowAttributes->all_event_masks & XCB_EVENT_MASK_BUTTON_PRESS)) { + if (windowAttributes && !(windowAttributes->all_event_masks & XCB_EVENT_MASK_BUTTON_PRESS)) { m_injectMode = XTest; } @@ -247,18 +213,59 @@ emit NewToolTip(); } -void sni_cleanup_xcb_image(void *data) { - xcb_image_destroy(static_cast(data)); +QSize SNIProxy::getClientWindowSize() const +{ + auto c = QX11Info::connection(); + + auto cookie = xcb_get_geometry(c, m_windowId); + QScopedPointer + clientGeom(xcb_get_geometry_reply(c, cookie, nullptr)); + + QSize clientWindowSize; + if (clientGeom) { + clientWindowSize = QSize(clientGeom->width, clientGeom->height); + } + //if the window is a clearly stupid size resize to be something sensible + //this is needed as chromium and such when resized just fill the icon with transparent space and only draw in the middle + //however KeePass2 does need this as by default the window size is 273px wide and is not transparent + //use an artbitrary heuristic to make sure icons are always sensible + if (clientWindowSize.isEmpty() || clientWindowSize.width() > s_embedSize || clientWindowSize.height() > s_embedSize) { + qCDebug(SNIPROXY) << "Resizing window" << m_windowId << Title() << "from w*h" << clientWindowSize; + + xcb_configure_notify_event_t event; + memset(&event, 0x00, sizeof(xcb_configure_notify_event_t)); + event.response_type = XCB_CONFIGURE_NOTIFY; + event.event = m_windowId; + event.window = m_windowId; + event.width = s_embedSize; + event.height = s_embedSize; + xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *) &event); + + const uint32_t windowMoveConfigVals[2] = { s_embedSize, s_embedSize }; + xcb_configure_window(c, m_windowId, + XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, + windowMoveConfigVals); + + clientWindowSize = QSize(s_embedSize, s_embedSize); + } + + return clientWindowSize; +} + +void sni_cleanup_xcb_image(void *data) +{ + xcb_image_destroy(static_cast(data)); } -bool SNIProxy::isTransparentImage(const QImage& image) const +bool SNIProxy::isTransparentImage(const QImage &image) const { int w = image.width(); int h = image.height(); // check for the center and sub-center pixels first and avoid full image scan - if (! (qAlpha(image.pixel(w >> 1, h >> 1)) + qAlpha(image.pixel(w >> 2, h >> 2)) == 0)) + if (!(qAlpha(image.pixel(w >> 1, h >> 1)) + qAlpha(image.pixel(w >> 2, h >> 2)) == 0)) { return false; + } // skip scan altogether if sub-center pixel found to be opaque // and break out from the outer loop too on full scan @@ -277,15 +284,10 @@ QImage SNIProxy::getImageNonComposite() const { auto c = QX11Info::connection(); - auto cookie = xcb_get_geometry(c, m_windowId); - QScopedPointer - geom(xcb_get_geometry_reply(c, cookie, nullptr)); - if (!geom) { - return QImage(); - } + QSize clientWindowSize = getClientWindowSize(); - xcb_image_t *image = xcb_image_get(c, m_windowId, 0, 0, geom->width, geom->height, 0xFFFFFFFF, XCB_IMAGE_FORMAT_Z_PIXMAP); + xcb_image_t *image = xcb_image_get(c, m_windowId, 0, 0, clientWindowSize.width(), clientWindowSize.height(), 0xFFFFFFFF, XCB_IMAGE_FORMAT_Z_PIXMAP); // Don't hook up cleanup yet, we may use a different QImage after all QImage naiveConversion; @@ -306,8 +308,9 @@ if (isTransparentImage(elaborateConversion)) { qCDebug(SNIPROXY) << "Skip transparent xembed icon for" << m_windowId << Title(); return QImage(); - } else + } else { return elaborateConversion; + } } else { // Now we are sure we can eventually delete the xcb_image_t with this version return QImage(image->data, image->width, image->height, image->stride, QImage::Format_ARGB32, sni_cleanup_xcb_image, image); @@ -354,8 +357,7 @@ return QImage(); } - if (format == QImage::Format_RGB32 && xcbImage->bpp == 32) - { + if (format == QImage::Format_RGB32 && xcbImage->bpp == 32) { QImage m = image.createHeuristicMask(); QBitmap mask(QPixmap::fromImage(m)); QPixmap p = QPixmap::fromImage(image); @@ -453,7 +455,7 @@ QString SNIProxy::Title() const { - KWindowInfo window (m_windowId, NET::WMName); + KWindowInfo window(m_windowId, NET::WMName); return window.name(); } @@ -479,12 +481,12 @@ sendClick(XCB_BUTTON_INDEX_3, x, y); } -void SNIProxy::Scroll(int delta, const QString& orientation) +void SNIProxy::Scroll(int delta, const QString &orientation) { if (orientation == QLatin1String("vertical")) { - sendClick(delta > 0 ? XCB_BUTTON_INDEX_4: XCB_BUTTON_INDEX_5, 0, 0); + sendClick(delta > 0 ? XCB_BUTTON_INDEX_4 : XCB_BUTTON_INDEX_5, 0, 0); } else { - sendClick(delta > 0 ? 6: 7, 0, 0); + sendClick(delta > 0 ? 6 : 7, 0, 0); } } @@ -504,7 +506,7 @@ auto cookieSize = xcb_get_geometry(c, m_windowId); QScopedPointer - clientGeom(xcb_get_geometry_reply(c, cookieSize, nullptr)); + clientGeom(xcb_get_geometry_reply(c, cookieSize, nullptr)); if (!clientGeom) { return; @@ -513,26 +515,25 @@ auto cookie = xcb_query_pointer(c, m_windowId); QScopedPointer pointer(xcb_query_pointer_reply(c, cookie, nullptr)); - /*qCDebug(SNIPROXY) << "samescreen" << pointer->same_screen << endl - << "root x*y" << pointer->root_x << pointer->root_y << endl - << "win x*y" << pointer->win_x << pointer->win_y;*/ //move our window so the mouse is within its geometry uint32_t configVals[2] = {0, 0}; const QPoint clickPoint = calculateClickPoint(); if (mouseButton >= XCB_BUTTON_INDEX_4) { - //scroll event, take pointer position - configVals[0] = pointer->root_x; - configVals[1] = pointer->root_y; + //scroll event, take pointer position + configVals[0] = pointer->root_x; + configVals[1] = pointer->root_y; } else { - if (pointer->root_x > x + clientGeom->width) - configVals[0] = pointer->root_x - clientGeom->width + 1; - else - configVals[0] = static_cast(x - clickPoint.x()); - if (pointer->root_y > y + clientGeom->height) - configVals[1] = pointer->root_y - clientGeom->height + 1; - else - configVals[1] = static_cast(y - clickPoint.y()); + if (pointer->root_x > x + clientGeom->width) { + configVals[0] = pointer->root_x - clientGeom->width + 1; + } else { + configVals[0] = static_cast(x - clickPoint.x()); + } + if (pointer->root_y > y + clientGeom->height) { + configVals[1] = pointer->root_y - clientGeom->height + 1; + } else { + configVals[1] = static_cast(y - clickPoint.y()); + } } xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, configVals); @@ -542,7 +543,7 @@ //mouse down if (m_injectMode == Direct) { - xcb_button_press_event_t* event = new xcb_button_press_event_t; + 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; event->event = m_windowId; @@ -564,9 +565,8 @@ } //mouse up - if (m_injectMode == Direct) - { - xcb_button_release_event_t* event = new xcb_button_release_event_t; + 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)); event->response_type = XCB_BUTTON_RELEASE; event->event = m_windowId;