VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/seamless-x11.cpp@ 86607

Last change on this file since 86607 was 86394, checked in by vboxsync, 4 years ago

VBoxClient/seamless: Free mpRects in SeamlessX11::uninit. bugref:9841

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 15.6 KB
Line 
1/* $Id: seamless-x11.cpp 86394 2020-10-01 17:06:30Z vboxsync $ */
2/** @file
3 * X11 Seamless mode.
4 */
5
6/*
7 * Copyright (C) 2008-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header files *
21*********************************************************************************************************************************/
22
23#include <iprt/errcore.h>
24#include <iprt/assert.h>
25#include <iprt/vector.h>
26#include <VBox/log.h>
27
28#include "seamless-x11.h"
29#include "VBoxClient.h"
30
31#include <X11/Xatom.h>
32#include <X11/Xmu/WinUtil.h>
33
34#include <limits.h>
35
36#ifdef TESTCASE
37#undef DefaultRootWindow
38#define DefaultRootWindow XDefaultRootWindow
39#endif
40
41/*****************************************************************************
42* Static functions *
43*****************************************************************************/
44
45static unsigned char *XXGetProperty (Display *aDpy, Window aWnd, Atom aPropType,
46 const char *aPropName, unsigned long *nItems)
47{
48 LogRelFlowFuncEnter();
49 Atom propNameAtom = XInternAtom (aDpy, aPropName,
50 True /* only_if_exists */);
51 if (propNameAtom == None)
52 {
53 return NULL;
54 }
55
56 Atom actTypeAtom = None;
57 int actFmt = 0;
58 unsigned long nBytesAfter = 0;
59 unsigned char *propVal = 0;
60 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
61 0, LONG_MAX, False /* delete */,
62 aPropType, &actTypeAtom, &actFmt,
63 nItems, &nBytesAfter, &propVal);
64 if (rc != Success)
65 return NULL;
66
67 LogRelFlowFuncLeave();
68 return propVal;
69}
70
71/**
72 * Initialise the guest and ensure that it is capable of handling seamless mode
73 *
74 * @param pHostCallback host callback.
75 * @returns true if it can handle seamless, false otherwise
76 */
77int SeamlessX11::init(PFNSENDREGIONUPDATE pHostCallback)
78{
79 int rc = VINF_SUCCESS;
80
81 LogRelFlowFuncEnter();
82 if (mHostCallback != NULL) /* Assertion */
83 {
84 VBClLogError("Attempting to initialise seamless guest object twice!\n");
85 return VERR_INTERNAL_ERROR;
86 }
87 if (!(mDisplay = XOpenDisplay(NULL)))
88 {
89 VBClLogError("Seamless guest object failed to acquire a connection to the display\n");
90 return VERR_ACCESS_DENIED;
91 }
92 mHostCallback = pHostCallback;
93 mEnabled = false;
94 unmonitorClientList();
95 LogRelFlowFuncLeaveRC(rc);
96 return rc;
97}
98
99/**
100 * Shutdown seamless event monitoring.
101 */
102void SeamlessX11::uninit(void)
103{
104 if (mHostCallback)
105 stop();
106 mHostCallback = NULL;
107 if (mDisplay)
108 XCloseDisplay(mDisplay);
109 mDisplay = NULL;
110 if (mpRects)
111 {
112 RTMemFree(mpRects);
113 mpRects = NULL;
114 }
115}
116
117/**
118 * Read information about currently visible windows in the guest and subscribe to X11
119 * events about changes to this information.
120 *
121 * @note This class does not contain its own event thread, so an external thread must
122 * call nextConfigurationEvent() for as long as events are wished.
123 * @todo This function should switch the guest to fullscreen mode.
124 */
125int SeamlessX11::start(void)
126{
127 int rc = VINF_SUCCESS;
128 /** Dummy values for XShapeQueryExtension */
129 int error, event;
130
131 LogRelFlowFuncEnter();
132 if (mEnabled)
133 return VINF_SUCCESS;
134 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
135 mEnabled = true;
136 monitorClientList();
137 rebuildWindowTree();
138 LogRelFlowFuncLeaveRC(rc);
139 return rc;
140}
141
142/** Stop reporting seamless events to the host. Free information about guest windows
143 and stop requesting updates. */
144void SeamlessX11::stop(void)
145{
146 LogRelFlowFuncEnter();
147 if (!mEnabled)
148 return;
149 mEnabled = false;
150 unmonitorClientList();
151 freeWindowTree();
152 LogRelFlowFuncLeave();
153}
154
155void SeamlessX11::monitorClientList(void)
156{
157 LogRelFlowFuncEnter();
158 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask | SubstructureNotifyMask);
159}
160
161void SeamlessX11::unmonitorClientList(void)
162{
163 LogRelFlowFuncEnter();
164 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask);
165}
166
167/**
168 * Recreate the table of toplevel windows of clients on the default root window of the
169 * X server.
170 */
171void SeamlessX11::rebuildWindowTree(void)
172{
173 LogRelFlowFuncEnter();
174 freeWindowTree();
175 addClients(DefaultRootWindow(mDisplay));
176 mChanged = true;
177}
178
179
180/**
181 * Look at the list of children of a virtual root window and add them to the list of clients
182 * if they belong to a client which is not a virtual root.
183 *
184 * @param hRoot the virtual root window to be examined
185 */
186void SeamlessX11::addClients(const Window hRoot)
187{
188 /** Unused out parameters of XQueryTree */
189 Window hRealRoot, hParent;
190 /** The list of children of the root supplied, raw pointer */
191 Window *phChildrenRaw = NULL;
192 /** The list of children of the root supplied, auto-pointer */
193 Window *phChildren;
194 /** The number of children of the root supplied */
195 unsigned cChildren;
196
197 LogRelFlowFuncEnter();
198 if (!XQueryTree(mDisplay, hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
199 return;
200 phChildren = phChildrenRaw;
201 for (unsigned i = 0; i < cChildren; ++i)
202 addClientWindow(phChildren[i]);
203 XFree(phChildrenRaw);
204 LogRelFlowFuncLeave();
205}
206
207
208void SeamlessX11::addClientWindow(const Window hWin)
209{
210 LogRelFlowFuncEnter();
211 XWindowAttributes winAttrib;
212 bool fAddWin = true;
213 Window hClient = XmuClientWindow(mDisplay, hWin);
214
215 if (isVirtualRoot(hClient))
216 fAddWin = false;
217 if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
218 {
219 VBClLogError("Failed to get the window attributes for window %d\n", hWin);
220 fAddWin = false;
221 }
222 if (fAddWin && (winAttrib.map_state == IsUnmapped))
223 fAddWin = false;
224 XSizeHints dummyHints;
225 long dummyLong;
226 /* Apparently (?) some old kwin versions had unwanted client windows
227 * without normal hints. */
228 if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints,
229 &dummyLong)))
230 {
231 LogRelFlowFunc(("window %lu, client window %lu has no size hints\n", hWin, hClient));
232 fAddWin = false;
233 }
234 if (fAddWin)
235 {
236 XRectangle *pRects = NULL;
237 int cRects = 0, iOrdering;
238 bool hasShape = false;
239
240 LogRelFlowFunc(("adding window %lu, client window %lu\n", hWin,
241 hClient));
242 if (mSupportsShape)
243 {
244 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
245 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
246 if (!pRects)
247 cRects = 0;
248 else
249 {
250 if ( (cRects > 1)
251 || (pRects[0].x != 0)
252 || (pRects[0].y != 0)
253 || (pRects[0].width != winAttrib.width)
254 || (pRects[0].height != winAttrib.height)
255 )
256 hasShape = true;
257 }
258 }
259 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
260 winAttrib.width, winAttrib.height, cRects,
261 pRects);
262 }
263 LogRelFlowFuncLeave();
264}
265
266
267/**
268 * Checks whether a window is a virtual root.
269 * @returns true if it is, false otherwise
270 * @param hWin the window to be examined
271 */
272bool SeamlessX11::isVirtualRoot(Window hWin)
273{
274 unsigned char *windowTypeRaw = NULL;
275 Atom *windowType;
276 unsigned long ulCount;
277 bool rc = false;
278
279 LogRelFlowFuncEnter();
280 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
281 if (windowTypeRaw != NULL)
282 {
283 windowType = (Atom *)(windowTypeRaw);
284 if ( (ulCount != 0)
285 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
286 rc = true;
287 }
288 if (windowTypeRaw)
289 XFree(windowTypeRaw);
290 LogRelFlowFunc(("returning %RTbool\n", rc));
291 return rc;
292}
293
294DECLCALLBACK(int) VBoxGuestWinFree(VBoxGuestWinInfo *pInfo, void *pvParam)
295{
296 Display *pDisplay = (Display *)pvParam;
297
298 XShapeSelectInput(pDisplay, pInfo->Core.Key, 0);
299 delete pInfo;
300 return VINF_SUCCESS;
301}
302
303/**
304 * Free all information in the tree of visible windows
305 */
306void SeamlessX11::freeWindowTree(void)
307{
308 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
309 LogRelFlowFuncEnter();
310 mGuestWindows.detachAll(VBoxGuestWinFree, mDisplay);
311 LogRelFlowFuncLeave();
312}
313
314
315/**
316 * Waits for a position or shape-related event from guest windows
317 *
318 * @note Called from the guest event thread.
319 */
320void SeamlessX11::nextConfigurationEvent(void)
321{
322 XEvent event;
323
324 LogRelFlowFuncEnter();
325 /* Start by sending information about the current window setup to the host. We do this
326 here because we want to send all such information from a single thread. */
327 if (mChanged && mEnabled)
328 {
329 updateRects();
330 mHostCallback(mpRects, mcRects);
331 }
332 mChanged = false;
333 /* We execute this even when seamless is disabled, as it also waits for
334 * enable and disable notification. */
335 XNextEvent(mDisplay, &event);
336 if (!mEnabled)
337 return;
338 switch (event.type)
339 {
340 case ConfigureNotify:
341 {
342 XConfigureEvent *pConf = &event.xconfigure;
343 LogRelFlowFunc(("configure event, window=%lu, x=%i, y=%i, w=%i, h=%i, send_event=%RTbool\n",
344 (unsigned long) pConf->window, (int) pConf->x,
345 (int) pConf->y, (int) pConf->width,
346 (int) pConf->height, pConf->send_event));
347 }
348 doConfigureEvent(event.xconfigure.window);
349 break;
350 case MapNotify:
351 LogRelFlowFunc(("map event, window=%lu, send_event=%RTbool\n",
352 (unsigned long) event.xmap.window,
353 event.xmap.send_event));
354 rebuildWindowTree();
355 break;
356 case PropertyNotify:
357 if ( event.xproperty.atom != XInternAtom(mDisplay, "_NET_CLIENT_LIST", True /* only_if_exists */)
358 || event.xproperty.window != DefaultRootWindow(mDisplay))
359 break;
360 LogRelFlowFunc(("_NET_CLIENT_LIST property event on root window\n"));
361 rebuildWindowTree();
362 break;
363 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
364 LogRelFlowFunc(("shape event, window=%lu, send_event=%RTbool\n",
365 (unsigned long) event.xany.window,
366 event.xany.send_event));
367 /* the window member in xany is in the same place as in the shape event */
368 doShapeEvent(event.xany.window);
369 break;
370 case UnmapNotify:
371 LogRelFlowFunc(("unmap event, window=%lu, send_event=%RTbool\n",
372 (unsigned long) event.xunmap.window,
373 event.xunmap.send_event));
374 rebuildWindowTree();
375 break;
376 default:
377 break;
378 }
379 LogRelFlowFunc(("processed event\n"));
380}
381
382/**
383 * Handle a configuration event in the seamless event thread by setting the new position.
384 *
385 * @param hWin the window to be examined
386 */
387void SeamlessX11::doConfigureEvent(Window hWin)
388{
389 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
390 if (pInfo)
391 {
392 XWindowAttributes winAttrib;
393
394 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
395 return;
396 pInfo->mX = winAttrib.x;
397 pInfo->mY = winAttrib.y;
398 pInfo->mWidth = winAttrib.width;
399 pInfo->mHeight = winAttrib.height;
400 mChanged = true;
401 }
402}
403
404/**
405 * Handle a window shape change event in the seamless event thread.
406 *
407 * @param hWin the window to be examined
408 */
409void SeamlessX11::doShapeEvent(Window hWin)
410{
411 LogRelFlowFuncEnter();
412 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
413 if (pInfo)
414 {
415 XRectangle *pRects;
416 int cRects = 0, iOrdering;
417
418 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
419 &iOrdering);
420 if (!pRects)
421 cRects = 0;
422 pInfo->mhasShape = true;
423 if (pInfo->mpRects)
424 XFree(pInfo->mpRects);
425 pInfo->mcRects = cRects;
426 pInfo->mpRects = pRects;
427 mChanged = true;
428 }
429 LogRelFlowFuncLeave();
430}
431
432/**
433 * Gets the list of visible rectangles
434 */
435RTRECT *SeamlessX11::getRects(void)
436{
437 return mpRects;
438}
439
440/**
441 * Gets the number of rectangles in the visible rectangle list
442 */
443size_t SeamlessX11::getRectCount(void)
444{
445 return mcRects;
446}
447
448RTVEC_DECL(RectList, RTRECT)
449
450static DECLCALLBACK(int) getRectsCallback(VBoxGuestWinInfo *pInfo, struct RectList *pRects)
451{
452 if (pInfo->mhasShape)
453 {
454 for (int i = 0; i < pInfo->mcRects; ++i)
455 {
456 RTRECT *pRect;
457
458 pRect = RectListPushBack(pRects);
459 if (!pRect)
460 return VERR_NO_MEMORY;
461 pRect->xLeft = pInfo->mX
462 + pInfo->mpRects[i].x;
463 pRect->yBottom = pInfo->mY
464 + pInfo->mpRects[i].y
465 + pInfo->mpRects[i].height;
466 pRect->xRight = pInfo->mX
467 + pInfo->mpRects[i].x
468 + pInfo->mpRects[i].width;
469 pRect->yTop = pInfo->mY
470 + pInfo->mpRects[i].y;
471 }
472 }
473 else
474 {
475 RTRECT *pRect;
476
477 pRect = RectListPushBack(pRects);
478 if (!pRect)
479 return VERR_NO_MEMORY;
480 pRect->xLeft = pInfo->mX;
481 pRect->yBottom = pInfo->mY
482 + pInfo->mHeight;
483 pRect->xRight = pInfo->mX
484 + pInfo->mWidth;
485 pRect->yTop = pInfo->mY;
486 }
487 return VINF_SUCCESS;
488}
489
490/**
491 * Updates the list of seamless rectangles
492 */
493int SeamlessX11::updateRects(void)
494{
495 LogRelFlowFuncEnter();
496 struct RectList rects = RTVEC_INITIALIZER;
497
498 if (mcRects != 0)
499 {
500 int rc = RectListReserve(&rects, mcRects * 2);
501 if (RT_FAILURE(rc))
502 return rc;
503 }
504 mGuestWindows.doWithAll((PFNVBOXGUESTWINCALLBACK)getRectsCallback, &rects);
505 if (mpRects)
506 RTMemFree(mpRects);
507 mcRects = RectListSize(&rects);
508 mpRects = RectListDetach(&rects);
509 LogRelFlowFuncLeave();
510 return VINF_SUCCESS;
511}
512
513/**
514 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
515 *
516 * @note This function should only be called from the host event thread.
517 */
518bool SeamlessX11::interruptEventWait(void)
519{
520 bool rc = false;
521 Display *pDisplay = XOpenDisplay(NULL);
522
523 LogRelFlowFuncEnter();
524 if (pDisplay == NULL)
525 VBClLogFatalError("Failed to open X11 display\n");
526 /* Message contents set to zero. */
527 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
528
529 if (XSendEvent(pDisplay, DefaultRootWindow(mDisplay), false,
530 PropertyChangeMask, (XEvent *)&clientMessage))
531 rc = true;
532 XCloseDisplay(pDisplay);
533 LogRelFlowFunc(("returning %RTbool\n", rc));
534 return rc;
535}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette