VirtualBox

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

最後變更 在這個檔案從83045是 83045,由 vboxsync 提交於 5 年 前

bugref:9637. Some build fix to make our GA build box happy

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

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