VirtualBox

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

Last change on this file since 60352 was 57489, checked in by vboxsync, 9 years ago

Additions/X11/seamless: Try fix broken seamless handling: additional comment.

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