VirtualBox

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

Last change on this file since 85922 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 15.3 KB
Line 
1/* $Id: seamless-x11.cpp 85121 2020-07-08 19:33:26Z 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 * 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 LogRelFlowFuncEnter();
114 if (mEnabled)
115 return VINF_SUCCESS;
116 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
117 mEnabled = true;
118 monitorClientList();
119 rebuildWindowTree();
120 LogRelFlowFuncLeaveRC(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 LogRelFlowFuncEnter();
129 if (!mEnabled)
130 return;
131 mEnabled = false;
132 unmonitorClientList();
133 freeWindowTree();
134 LogRelFlowFuncLeave();
135}
136
137void SeamlessX11::monitorClientList(void)
138{
139 LogRelFlowFuncEnter();
140 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask | SubstructureNotifyMask);
141}
142
143void SeamlessX11::unmonitorClientList(void)
144{
145 LogRelFlowFuncEnter();
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 LogRelFlowFuncEnter();
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 LogRelFlowFuncEnter();
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 LogRelFlowFuncLeave();
187}
188
189
190void SeamlessX11::addClientWindow(const Window hWin)
191{
192 LogRelFlowFuncEnter();
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 VBClLogError("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", 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 LogRelFlowFuncLeave();
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 LogRelFlowFuncEnter();
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 LogRelFlowFuncEnter();
292 mGuestWindows.detachAll(VBoxGuestWinFree, mDisplay);
293 LogRelFlowFuncLeave();
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 LogRelFlowFuncEnter();
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 hWin the window to be examined
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 hWin the window to be examined
390 */
391void SeamlessX11::doShapeEvent(Window hWin)
392{
393 LogRelFlowFuncEnter();
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 LogRelFlowFuncLeave();
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
432static DECLCALLBACK(int) getRectsCallback(VBoxGuestWinInfo *pInfo, struct RectList *pRects)
433{
434 if (pInfo->mhasShape)
435 {
436 for (int i = 0; i < pInfo->mcRects; ++i)
437 {
438 RTRECT *pRect;
439
440 pRect = RectListPushBack(pRects);
441 if (!pRect)
442 return VERR_NO_MEMORY;
443 pRect->xLeft = pInfo->mX
444 + pInfo->mpRects[i].x;
445 pRect->yBottom = pInfo->mY
446 + pInfo->mpRects[i].y
447 + pInfo->mpRects[i].height;
448 pRect->xRight = pInfo->mX
449 + pInfo->mpRects[i].x
450 + pInfo->mpRects[i].width;
451 pRect->yTop = pInfo->mY
452 + pInfo->mpRects[i].y;
453 }
454 }
455 else
456 {
457 RTRECT *pRect;
458
459 pRect = RectListPushBack(pRects);
460 if (!pRect)
461 return VERR_NO_MEMORY;
462 pRect->xLeft = pInfo->mX;
463 pRect->yBottom = pInfo->mY
464 + pInfo->mHeight;
465 pRect->xRight = pInfo->mX
466 + pInfo->mWidth;
467 pRect->yTop = pInfo->mY;
468 }
469 return VINF_SUCCESS;
470}
471
472/**
473 * Updates the list of seamless rectangles
474 */
475int SeamlessX11::updateRects(void)
476{
477 LogRelFlowFuncEnter();
478 struct RectList rects = RTVEC_INITIALIZER;
479
480 if (mcRects != 0)
481 {
482 int rc = RectListReserve(&rects, mcRects * 2);
483 if (RT_FAILURE(rc))
484 return rc;
485 }
486 mGuestWindows.doWithAll((PFNVBOXGUESTWINCALLBACK)getRectsCallback, &rects);
487 if (mpRects)
488 RTMemFree(mpRects);
489 mcRects = RectListSize(&rects);
490 mpRects = RectListDetach(&rects);
491 LogRelFlowFuncLeave();
492 return VINF_SUCCESS;
493}
494
495/**
496 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
497 *
498 * @note This function should only be called from the host event thread.
499 */
500bool SeamlessX11::interruptEventWait(void)
501{
502 bool rc = false;
503 Display *pDisplay = XOpenDisplay(NULL);
504
505 LogRelFlowFuncEnter();
506 if (pDisplay == NULL)
507 VBClLogFatalError("Failed to open X11 display\n");
508 /* Message contents set to zero. */
509 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
510
511 if (XSendEvent(pDisplay, DefaultRootWindow(mDisplay), false,
512 PropertyChangeMask, (XEvent *)&clientMessage))
513 rc = true;
514 XCloseDisplay(pDisplay);
515 LogRelFlowFunc(("returning %RTbool\n", rc));
516 return rc;
517}
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