VirtualBox

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

Last change on this file since 77819 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • 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 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * X11 Seamless mode.
4 */
5
6/*
7 * Copyright (C) 2008-2019 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 LogRelFlowFunc(("\n"));
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 LogRelFlowFunc(("returning\n"));
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 LogRelFlowFunc(("\n"));
82 if (mHostCallback != NULL) /* Assertion */
83 {
84 LogRel(("VBoxClient: ERROR: attempt to initialise seamless guest object twice!\n"));
85 return VERR_INTERNAL_ERROR;
86 }
87 if (!(mDisplay = XOpenDisplay(NULL)))
88 {
89 LogRel(("VBoxClient: 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 LogRelFlowFunc(("returning %Rrc\n", rc));
96 return rc;
97}
98
99/**
100 * Read information about currently visible windows in the guest and subscribe to X11
101 * events about changes to this information.
102 *
103 * @note This class does not contain its own event thread, so an external thread must
104 * call nextConfigurationEvent() for as long as events are wished.
105 * @todo This function should switch the guest to fullscreen mode.
106 */
107int SeamlessX11::start(void)
108{
109 int rc = VINF_SUCCESS;
110 /** Dummy values for XShapeQueryExtension */
111 int error, event;
112
113 LogRelFlowFunc(("\n"));
114 if (mEnabled)
115 return VINF_SUCCESS;
116 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
117 mEnabled = true;
118 monitorClientList();
119 rebuildWindowTree();
120 LogRelFlowFunc(("returning %Rrc\n", rc));
121 return rc;
122}
123
124/** Stop reporting seamless events to the host. Free information about guest windows
125 and stop requesting updates. */
126void SeamlessX11::stop(void)
127{
128 LogRelFlowFunc(("\n"));
129 if (!mEnabled)
130 return;
131 mEnabled = false;
132 unmonitorClientList();
133 freeWindowTree();
134 LogRelFlowFunc(("returning\n"));
135}
136
137void SeamlessX11::monitorClientList(void)
138{
139 LogRelFlowFunc(("called\n"));
140 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask | SubstructureNotifyMask);
141}
142
143void SeamlessX11::unmonitorClientList(void)
144{
145 LogRelFlowFunc(("called\n"));
146 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask);
147}
148
149/**
150 * Recreate the table of toplevel windows of clients on the default root window of the
151 * X server.
152 */
153void SeamlessX11::rebuildWindowTree(void)
154{
155 LogRelFlowFunc(("called\n"));
156 freeWindowTree();
157 addClients(DefaultRootWindow(mDisplay));
158 mChanged = true;
159}
160
161
162/**
163 * Look at the list of children of a virtual root window and add them to the list of clients
164 * if they belong to a client which is not a virtual root.
165 *
166 * @param hRoot the virtual root window to be examined
167 */
168void SeamlessX11::addClients(const Window hRoot)
169{
170 /** Unused out parameters of XQueryTree */
171 Window hRealRoot, hParent;
172 /** The list of children of the root supplied, raw pointer */
173 Window *phChildrenRaw = NULL;
174 /** The list of children of the root supplied, auto-pointer */
175 Window *phChildren;
176 /** The number of children of the root supplied */
177 unsigned cChildren;
178
179 LogRelFlowFunc(("\n"));
180 if (!XQueryTree(mDisplay, hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
181 return;
182 phChildren = phChildrenRaw;
183 for (unsigned i = 0; i < cChildren; ++i)
184 addClientWindow(phChildren[i]);
185 XFree(phChildrenRaw);
186 LogRelFlowFunc(("returning\n"));
187}
188
189
190void SeamlessX11::addClientWindow(const Window hWin)
191{
192 LogRelFlowFunc(("\n"));
193 XWindowAttributes winAttrib;
194 bool fAddWin = true;
195 Window hClient = XmuClientWindow(mDisplay, hWin);
196
197 if (isVirtualRoot(hClient))
198 fAddWin = false;
199 if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
200 {
201 LogRelFunc(("VBoxClient: Failed to get the window attributes for window %d\n", hWin));
202 fAddWin = false;
203 }
204 if (fAddWin && (winAttrib.map_state == IsUnmapped))
205 fAddWin = false;
206 XSizeHints dummyHints;
207 long dummyLong;
208 /* Apparently (?) some old kwin versions had unwanted client windows
209 * without normal hints. */
210 if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints,
211 &dummyLong)))
212 {
213 LogRelFlowFunc(("window %lu, client window %lu has no size hints\n",
214 hWin, hClient));
215 fAddWin = false;
216 }
217 if (fAddWin)
218 {
219 XRectangle *pRects = NULL;
220 int cRects = 0, iOrdering;
221 bool hasShape = false;
222
223 LogRelFlowFunc(("adding window %lu, client window %lu\n", hWin,
224 hClient));
225 if (mSupportsShape)
226 {
227 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
228 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
229 if (!pRects)
230 cRects = 0;
231 else
232 {
233 if ( (cRects > 1)
234 || (pRects[0].x != 0)
235 || (pRects[0].y != 0)
236 || (pRects[0].width != winAttrib.width)
237 || (pRects[0].height != winAttrib.height)
238 )
239 hasShape = true;
240 }
241 }
242 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
243 winAttrib.width, winAttrib.height, cRects,
244 pRects);
245 }
246 LogRelFlowFunc(("returning\n"));
247}
248
249
250/**
251 * Checks whether a window is a virtual root.
252 * @returns true if it is, false otherwise
253 * @param hWin the window to be examined
254 */
255bool SeamlessX11::isVirtualRoot(Window hWin)
256{
257 unsigned char *windowTypeRaw = NULL;
258 Atom *windowType;
259 unsigned long ulCount;
260 bool rc = false;
261
262 LogRelFlowFunc(("\n"));
263 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
264 if (windowTypeRaw != NULL)
265 {
266 windowType = (Atom *)(windowTypeRaw);
267 if ( (ulCount != 0)
268 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
269 rc = true;
270 }
271 if (windowTypeRaw)
272 XFree(windowTypeRaw);
273 LogRelFlowFunc(("returning %RTbool\n", rc));
274 return rc;
275}
276
277DECLCALLBACK(int) VBoxGuestWinFree(VBoxGuestWinInfo *pInfo, void *pvParam)
278{
279 Display *pDisplay = (Display *)pvParam;
280
281 XShapeSelectInput(pDisplay, pInfo->Core.Key, 0);
282 delete pInfo;
283 return VINF_SUCCESS;
284}
285
286/**
287 * Free all information in the tree of visible windows
288 */
289void SeamlessX11::freeWindowTree(void)
290{
291 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
292 LogRelFlowFunc(("\n"));
293 mGuestWindows.detachAll(VBoxGuestWinFree, mDisplay);
294 LogRelFlowFunc(("returning\n"));
295}
296
297
298/**
299 * Waits for a position or shape-related event from guest windows
300 *
301 * @note Called from the guest event thread.
302 */
303void SeamlessX11::nextConfigurationEvent(void)
304{
305 XEvent event;
306
307 LogRelFlowFunc(("\n"));
308 /* Start by sending information about the current window setup to the host. We do this
309 here because we want to send all such information from a single thread. */
310 if (mChanged && mEnabled)
311 {
312 updateRects();
313 mHostCallback(mpRects, mcRects);
314 }
315 mChanged = false;
316 /* We execute this even when seamless is disabled, as it also waits for
317 * enable and disable notification. */
318 XNextEvent(mDisplay, &event);
319 if (!mEnabled)
320 return;
321 switch (event.type)
322 {
323 case ConfigureNotify:
324 {
325 XConfigureEvent *pConf = &event.xconfigure;
326 LogRelFlowFunc(("configure event, window=%lu, x=%i, y=%i, w=%i, h=%i, send_event=%RTbool\n",
327 (unsigned long) pConf->window, (int) pConf->x,
328 (int) pConf->y, (int) pConf->width,
329 (int) pConf->height, pConf->send_event));
330 }
331 doConfigureEvent(event.xconfigure.window);
332 break;
333 case MapNotify:
334 LogRelFlowFunc(("map event, window=%lu, send_event=%RTbool\n",
335 (unsigned long) event.xmap.window,
336 event.xmap.send_event));
337 rebuildWindowTree();
338 break;
339 case PropertyNotify:
340 if ( event.xproperty.atom != XInternAtom(mDisplay, "_NET_CLIENT_LIST", True /* only_if_exists */)
341 || event.xproperty.window != DefaultRootWindow(mDisplay))
342 break;
343 LogRelFlowFunc(("_NET_CLIENT_LIST property event on root window.\n"));
344 rebuildWindowTree();
345 break;
346 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
347 LogRelFlowFunc(("shape event, window=%lu, send_event=%RTbool\n",
348 (unsigned long) event.xany.window,
349 event.xany.send_event));
350 /* the window member in xany is in the same place as in the shape event */
351 doShapeEvent(event.xany.window);
352 break;
353 case UnmapNotify:
354 LogRelFlowFunc(("unmap event, window=%lu, send_event=%RTbool\n",
355 (unsigned long) event.xunmap.window,
356 event.xunmap.send_event));
357 rebuildWindowTree();
358 break;
359 default:
360 break;
361 }
362 LogRelFlowFunc(("processed event\n"));
363}
364
365/**
366 * Handle a configuration event in the seamless event thread by setting the new position.
367 *
368 * @param hWin the window to be examined
369 */
370void SeamlessX11::doConfigureEvent(Window hWin)
371{
372 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
373 if (pInfo)
374 {
375 XWindowAttributes winAttrib;
376
377 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
378 return;
379 pInfo->mX = winAttrib.x;
380 pInfo->mY = winAttrib.y;
381 pInfo->mWidth = winAttrib.width;
382 pInfo->mHeight = winAttrib.height;
383 mChanged = true;
384 }
385}
386
387/**
388 * Handle a window shape change event in the seamless event thread.
389 *
390 * @param hWin the window to be examined
391 */
392void SeamlessX11::doShapeEvent(Window hWin)
393{
394 LogRelFlowFunc(("\n"));
395 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
396 if (pInfo)
397 {
398 XRectangle *pRects;
399 int cRects = 0, iOrdering;
400
401 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
402 &iOrdering);
403 if (!pRects)
404 cRects = 0;
405 pInfo->mhasShape = true;
406 if (pInfo->mpRects)
407 XFree(pInfo->mpRects);
408 pInfo->mcRects = cRects;
409 pInfo->mpRects = pRects;
410 mChanged = true;
411 }
412 LogRelFlowFunc(("returning\n"));
413}
414
415/**
416 * Gets the list of visible rectangles
417 */
418RTRECT *SeamlessX11::getRects(void)
419{
420 return mpRects;
421}
422
423/**
424 * Gets the number of rectangles in the visible rectangle list
425 */
426size_t SeamlessX11::getRectCount(void)
427{
428 return mcRects;
429}
430
431RTVEC_DECL(RectList, RTRECT)
432
433DECLCALLBACK(int) getRectsCallback(VBoxGuestWinInfo *pInfo,
434 struct RectList *pRects)
435{
436 if (pInfo->mhasShape)
437 {
438 for (int i = 0; i < pInfo->mcRects; ++i)
439 {
440 RTRECT *pRect;
441
442 pRect = RectListPushBack(pRects);
443 if (!pRect)
444 return VERR_NO_MEMORY;
445 pRect->xLeft = pInfo->mX
446 + pInfo->mpRects[i].x;
447 pRect->yBottom = pInfo->mY
448 + pInfo->mpRects[i].y
449 + pInfo->mpRects[i].height;
450 pRect->xRight = pInfo->mX
451 + pInfo->mpRects[i].x
452 + pInfo->mpRects[i].width;
453 pRect->yTop = pInfo->mY
454 + pInfo->mpRects[i].y;
455 }
456 }
457 else
458 {
459 RTRECT *pRect;
460
461 pRect = RectListPushBack(pRects);
462 if (!pRect)
463 return VERR_NO_MEMORY;
464 pRect->xLeft = pInfo->mX;
465 pRect->yBottom = pInfo->mY
466 + pInfo->mHeight;
467 pRect->xRight = pInfo->mX
468 + pInfo->mWidth;
469 pRect->yTop = pInfo->mY;
470 }
471 return VINF_SUCCESS;
472}
473
474/**
475 * Updates the list of seamless rectangles
476 */
477int SeamlessX11::updateRects(void)
478{
479 LogRelFlowFunc(("\n"));
480 struct RectList rects = RTVEC_INITIALIZER;
481
482 if (mcRects != 0)
483 {
484 int rc = RectListReserve(&rects, mcRects * 2);
485 if (RT_FAILURE(rc))
486 return rc;
487 }
488 mGuestWindows.doWithAll((PVBOXGUESTWINCALLBACK)getRectsCallback,
489 &rects);
490 if (mpRects)
491 RTMemFree(mpRects);
492 mcRects = RectListSize(&rects);
493 mpRects = RectListDetach(&rects);
494 LogRelFlowFunc(("returning\n"));
495 return VINF_SUCCESS;
496}
497
498/**
499 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
500 *
501 * @note This function should only be called from the host event thread.
502 */
503bool SeamlessX11::interruptEventWait(void)
504{
505 bool rc = false;
506 Display *pDisplay = XOpenDisplay(NULL);
507
508 LogRelFlowFunc(("\n"));
509 if (pDisplay == NULL)
510 VBClFatalError(("Failed to open X11 display.\n"));
511 /* Message contents set to zero. */
512 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
513
514 if (XSendEvent(pDisplay, DefaultRootWindow(mDisplay), false,
515 PropertyChangeMask, (XEvent *)&clientMessage))
516 rc = true;
517 XCloseDisplay(pDisplay);
518 LogRelFlowFunc(("returning %RTbool\n", rc));
519 return rc;
520}
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