From c4d3d42ec4a9ec6fe08f2077072056dfb6b9994c Mon Sep 17 00:00:00 2001 From: Eugene Paskevich Date: Wed, 4 Nov 2015 14:20:09 +0000 Subject: [PATCH 05/20] Merge xembed SNI proxy updates Hush the NetWinInfo deprecation warning Hush the int narrowing warning Create the container window on-screen Switch to 32x32 pixel icons and avoid scaling Made sure container window is under mouse upon click Reviewed-by: David Edmundson --- xembed-sni-proxy/sniproxy.cpp | 100 +++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/xembed-sni-proxy/sniproxy.cpp b/xembed-sni-proxy/sniproxy.cpp index b5cf3c3..ca2667f 100644 --- a/xembed-sni-proxy/sniproxy.cpp +++ b/xembed-sni-proxy/sniproxy.cpp @@ -44,7 +44,7 @@ #define SNI_WATCHER_SERVICE_NAME "org.kde.StatusNotifierWatcher" #define SNI_WATCHER_PATH "/StatusNotifierWatcher" -static uint16_t s_embedSize = 48; //max size of window to embed. We no longer resize the embedded window as Chromium acts stupidly. +static uint16_t s_embedSize = 32; //max size of window to embed. We no longer resize the embedded window as Chromium acts stupidly. int SNIProxy::s_serviceCount = 0; @@ -93,7 +93,7 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent): //create a container window auto screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data; m_containerWid = xcb_generate_id(c); - uint32_t values[2]; + 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 embeded icon doesn't get garbage in it values[1] = true; //bypass wM @@ -101,9 +101,9 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent): XCB_COPY_FROM_PARENT, /* depth */ m_containerWid, /* window Id */ screen->root, /* parent window */ - -500, 0, /* x, y */ + 0, 0, /* x, y */ s_embedSize, s_embedSize, /* width, height */ - 0, /* border_width */ + 0, /* border_width */ XCB_WINDOW_CLASS_INPUT_OUTPUT,/* class */ screen->root_visual, /* visual */ mask, values); /* masks */ @@ -123,7 +123,7 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent): const uint32_t stackBelowData[] = {XCB_STACK_MODE_BELOW}; xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackBelowData); - NETWinInfo wm(c, m_containerWid, screen->root, 0); + NETWinInfo wm(c, m_containerWid, screen->root, 0, 0); wm.setOpacity(0); #endif @@ -161,13 +161,13 @@ SNIProxy::SNIProxy(xcb_window_t wid, QObject* parent): //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 (clientGeom->width < 12 || clientGeom->width > s_embedSize || - clientGeom->height < 12 || clientGeom->height > s_embedSize) + if (clientGeom->width > s_embedSize || clientGeom->height > s_embedSize ) { const uint32_t windowMoveConfigVals[2] = { s_embedSize, s_embedSize }; xcb_configure_window(c, wid, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, windowMoveConfigVals); + qCDebug(SNIPROXY) << "Resizing window" << wid << Title() << "from w*h" << clientGeom->width << clientGeom->height; } //show the embedded window otherwise nothing happens @@ -192,18 +192,22 @@ void SNIProxy::update() { const QImage image = getImageNonComposite(); - bool isTransparentImage = true; - - int sum = 0; - for (int x = 0; x < image.width(); ++x) { - for (int y = 0; y < image.height(); ++y) { - sum += qAlpha(image.pixel(x, y)); - if (sum >= 255) { - // There is enough amount of opaque pixels. - isTransparentImage = false; - break; - } - } + int w = image.width(); + int h = image.height(); + + // check for the center and sub-center pixels first and avoid full image scan + bool isTransparentImage = qAlpha(image.pixel(w >> 1, h >> 1)) + qAlpha(image.pixel(w >> 2, h >> 2)) == 0; + + // skip scan altogether if sub-center pixel found to be opaque + // and break out from the outer loop too on full scan + for (int x = 0; x < w && isTransparentImage; ++x) { + for (int y = 0; y < h; ++y) { + if (qAlpha(image.pixel(x, y))) { + // Found an opaque pixel. + isTransparentImage = false; + break; + } + } } // Update icon only if it is at least partially opaque. @@ -212,10 +216,15 @@ void SNIProxy::update() // with WINE applications. if (!isTransparentImage) { m_pixmap = QPixmap::fromImage(image); + if (w != s_embedSize || h != s_embedSize) { + qCDebug(SNIPROXY) << "Scaling pixmap of window" << m_windowId << Title() << "from w*h" << w << h; + m_pixmap = m_pixmap.scaled(s_embedSize, s_embedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } emit NewIcon(); + emit NewToolTip(); } else { - qCDebug(SNIPROXY) << "Skip transparent xembed icon"; + qCDebug(SNIPROXY) << "Skip transparent xembed icon for" << m_windowId << Title(); } } @@ -282,14 +291,14 @@ void SNIProxy::Activate(int x, int y) sendClick(XCB_BUTTON_INDEX_1, x, y); } -void SNIProxy::ContextMenu(int x, int y) +void SNIProxy::SecondaryActivate(int x, int y) { - sendClick(XCB_BUTTON_INDEX_3, x, y); + sendClick(XCB_BUTTON_INDEX_2, x, y); } -void SNIProxy::SecondaryActivate(int x, int y) +void SNIProxy::ContextMenu(int x, int y) { - sendClick(XCB_BUTTON_INDEX_2, x, y); + sendClick(XCB_BUTTON_INDEX_3, x, y); } void SNIProxy::Scroll(int delta, const QString& orientation) @@ -311,18 +320,41 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y) //note x,y are not actually where the mouse is, but the plasmoid //ideally we should make this match the plasmoid hit area - qCDebug(SNIPROXY) << "Sending click " << mouseButton << "to" << x << y; + qCDebug(SNIPROXY) << "Received click" << mouseButton << "with passed x*y" << x << y; auto c = QX11Info::connection(); - //set our window so the middle is where the mouse is + auto cookieSize = xcb_get_geometry(c, m_windowId); + QScopedPointer clientGeom(xcb_get_geometry_reply(c, cookieSize, Q_NULLPTR)); + + auto cookie = xcb_query_pointer(c, m_windowId); + QScopedPointer pointer(xcb_query_pointer_reply(c, cookie, Q_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}; + if (mouseButton >= XCB_BUTTON_INDEX_4) { + //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); + if (pointer->root_y > y + clientGeom->height) + configVals[1] = pointer->root_y - clientGeom->height + 1; + else + configVals[1] = static_cast(y); + } + xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, configVals); + + //pull window up const uint32_t stackAboveData[] = {XCB_STACK_MODE_ABOVE}; xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackAboveData); - const uint32_t config_vals[4] = {x, y, s_embedSize, s_embedSize }; - xcb_configure_window(c, m_containerWid, - XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, - config_vals); //mouse down { xcb_button_press_event_t* event = new xcb_button_press_event_t; @@ -364,13 +396,9 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y) xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_BUTTON_RELEASE, (char *) event); free(event); } + #ifndef VISUAL_DEBUG const uint32_t stackBelowData[] = {XCB_STACK_MODE_BELOW}; xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackBelowData); #endif - } - - - - -// +} -- 2.5.0