Changeset View
Standalone View
xembed-sni-proxy/sniproxy.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Holds one embedded window, registers as DBus entry | 2 | * Holds one embedded window, registers as DBus entry | ||
3 | * Copyright (C) 2015 <davidedmundson@kde.org> David Edmundson | 3 | * Copyright (C) 2015 <davidedmundson@kde.org> David Edmundson | ||
4 | * | 4 | * | ||
davidedmundson: make sure you update this sometime | |||||
I have another fix ready :) In future I will want to cleanup the code a bit (formatting, maybe some refactoring). I don't know if this welcomed because this will break git history. kmaterka: I have another fix ready :)
In future I will want to cleanup the code a bit (formatting, maybe… | |||||
5 | * This library is free software; you can redistribute it and/or | 5 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | 6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; either | 7 | * License as published by the Free Software Foundation; either | ||
8 | * version 2.1 of the License, or (at your option) any later version. | 8 | * version 2.1 of the License, or (at your option) any later version. | ||
9 | * | 9 | * | ||
10 | * This library is distributed in the hope that it will be useful, | 10 | * This library is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
Show All 31 Lines | |||||
44 | #include "xtestsender.h" | 44 | #include "xtestsender.h" | ||
45 | 45 | | |||
46 | //#define VISUAL_DEBUG | 46 | //#define VISUAL_DEBUG | ||
47 | 47 | | |||
48 | #define SNI_WATCHER_SERVICE_NAME "org.kde.StatusNotifierWatcher" | 48 | #define SNI_WATCHER_SERVICE_NAME "org.kde.StatusNotifierWatcher" | ||
49 | #define SNI_WATCHER_PATH "/StatusNotifierWatcher" | 49 | #define SNI_WATCHER_PATH "/StatusNotifierWatcher" | ||
50 | 50 | | |||
51 | static uint16_t s_embedSize = 32; //max size of window to embed. We no longer resize the embedded window as Chromium acts stupidly. | 51 | 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 unsigned int XEMBED_VERSION = 0; | ||||
52 | 53 | | |||
53 | int SNIProxy::s_serviceCount = 0; | 54 | int SNIProxy::s_serviceCount = 0; | ||
54 | 55 | | |||
55 | void | 56 | void | ||
56 | xembed_message_send(xcb_window_t towin, | 57 | xembed_message_send(xcb_window_t towin, | ||
57 | long message, long d1, long d2, long d3) | 58 | long message, long d1, long d2, long d3) | ||
58 | { | 59 | { | ||
59 | xcb_client_message_event_t ev; | 60 | xcb_client_message_event_t ev; | ||
Show All 30 Lines | 90 | if (reply.isError()) { | |||
90 | qCWarning(SNIPROXY) << "could not register SNI:" << reply.error().message(); | 91 | qCWarning(SNIPROXY) << "could not register SNI:" << reply.error().message(); | ||
91 | } | 92 | } | ||
92 | 93 | | |||
93 | auto c = QX11Info::connection(); | 94 | auto c = QX11Info::connection(); | ||
94 | 95 | | |||
95 | //create a container window | 96 | //create a container window | ||
96 | auto screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data; | 97 | auto screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data; | ||
97 | m_containerWid = xcb_generate_id(c); | 98 | m_containerWid = xcb_generate_id(c); | ||
98 | uint32_t values[2]; | 99 | uint32_t values[3]; | ||
99 | auto mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT; | 100 | uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; | ||
100 | values[0] = screen->black_pixel; //draw a solid background so the embedded icon doesn't get garbage in it | 101 | values[0] = screen->black_pixel; //draw a solid background so the embedded icon doesn't get garbage in it | ||
101 | values[1] = true; //bypass wM | 102 | values[1] = true; //bypass wM | ||
103 | // Redirect and handle structure (size, position) requests on the embedded window. | ||||
104 | values[2] = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; | ||||
102 | xcb_create_window (c, /* connection */ | 105 | xcb_create_window (c, /* connection */ | ||
103 | XCB_COPY_FROM_PARENT, /* depth */ | 106 | XCB_COPY_FROM_PARENT, /* depth */ | ||
104 | m_containerWid, /* window Id */ | 107 | m_containerWid, /* window Id */ | ||
105 | screen->root, /* parent window */ | 108 | screen->root, /* parent window */ | ||
106 | 0, 0, /* x, y */ | 109 | 0, 0, /* x, y */ | ||
107 | s_embedSize, s_embedSize, /* width, height */ | 110 | s_embedSize, s_embedSize, /* width, height */ | ||
108 | 0, /* border_width */ | 111 | 0, /* border_width */ | ||
109 | XCB_WINDOW_CLASS_INPUT_OUTPUT,/* class */ | 112 | XCB_WINDOW_CLASS_INPUT_OUTPUT,/* class */ | ||
Show All 33 Lines | 132 | #endif | |||
143 | 146 | | |||
144 | 147 | | |||
145 | /* we grab the window, but also make sure it's automatically reparented back | 148 | /* we grab the window, but also make sure it's automatically reparented back | ||
146 | * to the root window if we should die. | 149 | * to the root window if we should die. | ||
147 | */ | 150 | */ | ||
148 | xcb_change_save_set(c, XCB_SET_MODE_INSERT, wid); | 151 | xcb_change_save_set(c, XCB_SET_MODE_INSERT, wid); | ||
149 | 152 | | |||
150 | //tell client we're embedding it | 153 | //tell client we're embedding it | ||
151 | xembed_message_send(wid, XEMBED_EMBEDDED_NOTIFY, m_containerWid, 0, 0); | 154 | xembed_message_send(wid, XEMBED_EMBEDDED_NOTIFY, 0, m_containerWid, XEMBED_VERSION); | ||
davidedmundson: can you explain the swapping 0 and m_containerWid here? | |||||
I knew someone will ask, I wanted to add comment here but you were too quick :) I was not following the standard - worked because most clients (including Qt) ignores parent window. Wine is using d[3] field to read parent window. I checked other implementations on github, most (but "i3") uses data[3] to set parent window. From specification:
...
So data1 = l[3] = ev.data.data32[3] kmaterka: I knew someone will ask, I wanted to add comment here but you were too quick :)
I was not… | |||||
This change is not mandatory, but should go in this change. It is related to Wine bug, when this bug is fixed it will be required to correctly set parent (if I understand Wine code correctly... :) ) kmaterka: This change is not mandatory, but should go in this change. It is related to Wine bug, when… | |||||
152 | 155 | | |||
153 | //move window we're embedding | 156 | //move window we're embedding | ||
154 | const uint32_t windowMoveConfigVals[2] = { 0, 0 }; | 157 | const uint32_t windowMoveConfigVals[2] = { 0, 0 }; | ||
155 | 158 | | |||
156 | xcb_configure_window(c, wid, | 159 | xcb_configure_window(c, wid, | ||
157 | XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, | 160 | XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, | ||
158 | windowMoveConfigVals); | 161 | windowMoveConfigVals); | ||
159 | 162 | | |||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Line(s) | |||||
215 | 218 | | |||
216 | void SNIProxy::stackContainerWindow(const uint32_t stackMode) const | 219 | void SNIProxy::stackContainerWindow(const uint32_t stackMode) const | ||
217 | { | 220 | { | ||
218 | auto c = QX11Info::connection(); | 221 | auto c = QX11Info::connection(); | ||
219 | const uint32_t stackData[] = {stackMode}; | 222 | const uint32_t stackData[] = {stackMode}; | ||
220 | xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackData); | 223 | xcb_configure_window(c, m_containerWid, XCB_CONFIG_WINDOW_STACK_MODE, stackData); | ||
221 | } | 224 | } | ||
222 | 225 | | |||
226 | void SNIProxy::sendConfigureNotification() const | ||||
227 | { | ||||
228 | xcb_configure_notify_event_t event; | ||||
229 | memset(&event, 0x00, sizeof(xcb_configure_notify_event_t)); | ||||
230 | event.response_type = XCB_CONFIGURE_NOTIFY; | ||||
231 | event.event = m_windowId; | ||||
232 | event.window = m_windowId; | ||||
233 | event.x = 0; | ||||
234 | event.y = 0; | ||||
235 | event.width = s_embedSize; | ||||
236 | event.height = s_embedSize; | ||||
237 | | ||||
238 | auto connection = QX11Info::connection(); | ||||
239 | xcb_send_event(connection, false, m_windowId, XCB_EVENT_MASK_STRUCTURE_NOTIFY, reinterpret_cast<char *>(&event)); | ||||
240 | } | ||||
241 | | ||||
223 | QSize SNIProxy::calculateClientWindowSize() const | 242 | QSize SNIProxy::calculateClientWindowSize() const | ||
224 | { | 243 | { | ||
225 | auto c = QX11Info::connection(); | 244 | auto c = QX11Info::connection(); | ||
226 | 245 | | |||
227 | auto cookie = xcb_get_geometry(c, m_windowId); | 246 | auto cookie = xcb_get_geometry(c, m_windowId); | ||
228 | QScopedPointer<xcb_get_geometry_reply_t, QScopedPointerPodDeleter> | 247 | QScopedPointer<xcb_get_geometry_reply_t, QScopedPointerPodDeleter> | ||
229 | clientGeom(xcb_get_geometry_reply(c, cookie, nullptr)); | 248 | clientGeom(xcb_get_geometry_reply(c, cookie, nullptr)); | ||
230 | 249 | | |||
231 | QSize clientWindowSize; | 250 | QSize clientWindowSize; | ||
232 | if (clientGeom) { | 251 | if (clientGeom) { | ||
233 | clientWindowSize = QSize(clientGeom->width, clientGeom->height); | 252 | clientWindowSize = QSize(clientGeom->width, clientGeom->height); | ||
234 | } | 253 | } | ||
235 | //if the window is a clearly stupid size resize to be something sensible | 254 | //if the window is a clearly stupid size resize to be something sensible | ||
236 | //this is needed as chromium and such when resized just fill the icon with transparent space and only draw in the middle | 255 | //this is needed as chromium and such when resized just fill the icon with transparent space and only draw in the middle | ||
237 | //however KeePass2 does need this as by default the window size is 273px wide and is not transparent | 256 | //however KeePass2 does need this as by default the window size is 273px wide and is not transparent | ||
238 | //use an artbitrary heuristic to make sure icons are always sensible | 257 | //use an artbitrary heuristic to make sure icons are always sensible | ||
239 | if (clientWindowSize.isEmpty() || clientWindowSize.width() > s_embedSize || clientWindowSize.height() > s_embedSize) { | 258 | if (clientWindowSize.isEmpty() || clientWindowSize.width() > s_embedSize || clientWindowSize.height() > s_embedSize) { | ||
240 | qCDebug(SNIPROXY) << "Resizing window" << m_windowId << Title() << "from w*h" << clientWindowSize; | 259 | qCDebug(SNIPROXY) << "Resizing window" << m_windowId << Title() << "from w*h" << clientWindowSize; | ||
241 | 260 | | |||
242 | xcb_configure_notify_event_t event; | 261 | sendConfigureNotification(); | ||
243 | memset(&event, 0x00, sizeof(xcb_configure_notify_event_t)); | | |||
244 | event.response_type = XCB_CONFIGURE_NOTIFY; | | |||
245 | event.event = m_windowId; | | |||
246 | event.window = m_windowId; | | |||
247 | event.width = s_embedSize; | | |||
248 | event.height = s_embedSize; | | |||
249 | xcb_send_event(c, false, m_windowId, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *) &event); | | |||
250 | 262 | | |||
251 | const uint32_t windowMoveConfigVals[2] = { s_embedSize, s_embedSize }; | 263 | const uint32_t windowSizeConfigVals[2] = { s_embedSize, s_embedSize }; | ||
252 | xcb_configure_window(c, m_windowId, | 264 | xcb_configure_window(c, m_windowId, | ||
253 | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, | 265 | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, | ||
254 | windowMoveConfigVals); | 266 | windowSizeConfigVals); | ||
255 | 267 | | |||
256 | clientWindowSize = QSize(s_embedSize, s_embedSize); | 268 | clientWindowSize = QSize(s_embedSize, s_embedSize); | ||
257 | } | 269 | } | ||
258 | 270 | | |||
259 | return clientWindowSize; | 271 | return clientWindowSize; | ||
260 | } | 272 | } | ||
261 | 273 | | |||
262 | void sni_cleanup_xcb_image(void *data) { | 274 | void sni_cleanup_xcb_image(void *data) { | ||
▲ Show 20 Lines • Show All 337 Lines • Show Last 20 Lines |
make sure you update this sometime