VirtualBox

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

Last change on this file was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 23.9 KB
RevLine 
[55401]1/* $Id: seamless-x11.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
[6202]2/** @file
[21227]3 * X11 Seamless mode.
[6202]4 */
5
6/*
[98103]7 * Copyright (C) 2008-2023 Oracle and/or its affiliates.
[6202]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[6202]26 */
27
28
[57358]29/*********************************************************************************************************************************
30* Header files *
31*********************************************************************************************************************************/
[76474]32#include <iprt/errcore.h>
[6202]33#include <iprt/assert.h>
[37721]34#include <iprt/vector.h>
[90057]35#include <iprt/thread.h>
[7449]36#include <VBox/log.h>
[6202]37
[99600]38#include "seamless.h"
[83135]39#include "seamless-x11.h"
40#include "VBoxClient.h"
41
[83045]42#include <X11/Xatom.h>
43#include <X11/Xmu/WinUtil.h>
44
[7595]45#include <limits.h>
46
[98535]47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
[21207]51#ifdef TESTCASE
[98535]52# undef DefaultRootWindow
53# define DefaultRootWindow XDefaultRootWindow
[21207]54#endif
[7106]55
[6202]56
[98535]57/*********************************************************************************************************************************
58* Internal Functions *
59*********************************************************************************************************************************/
60
[99739]61static unsigned char *XXGetProperty(Display *aDpy, Window aWnd, Atom aPropType,
[7048]62 const char *aPropName, unsigned long *nItems)
[6202]63{
[81040]64 LogRelFlowFuncEnter();
[6202]65 Atom propNameAtom = XInternAtom (aDpy, aPropName,
66 True /* only_if_exists */);
67 if (propNameAtom == None)
68 {
[7048]69 return NULL;
[6202]70 }
71
72 Atom actTypeAtom = None;
73 int actFmt = 0;
74 unsigned long nBytesAfter = 0;
75 unsigned char *propVal = 0;
76 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
77 0, LONG_MAX, False /* delete */,
78 aPropType, &actTypeAtom, &actFmt,
79 nItems, &nBytesAfter, &propVal);
80 if (rc != Success)
[7048]81 return NULL;
[6202]82
[81040]83 LogRelFlowFuncLeave();
[7048]84 return propVal;
[6202]85}
86
[99600]87int VBClX11SeamlessMonitor::init(PFNSENDREGIONUPDATE pHostCallback)
[6202]88{
89 int rc = VINF_SUCCESS;
90
[81040]91 LogRelFlowFuncEnter();
[52564]92 if (mHostCallback != NULL) /* Assertion */
[6202]93 {
[81040]94 VBClLogError("Attempting to initialise seamless guest object twice!\n");
[6202]95 return VERR_INTERNAL_ERROR;
96 }
[36662]97 if (!(mDisplay = XOpenDisplay(NULL)))
[6202]98 {
[81040]99 VBClLogError("Seamless guest object failed to acquire a connection to the display\n");
[6202]100 return VERR_ACCESS_DENIED;
101 }
[52564]102 mHostCallback = pHostCallback;
[57486]103 mEnabled = false;
[57344]104 unmonitorClientList();
[81040]105 LogRelFlowFuncLeaveRC(rc);
[6202]106 return rc;
107}
108
109/**
[86394]110 * Shutdown seamless event monitoring.
111 */
[99600]112void VBClX11SeamlessMonitor::uninit(void)
[86394]113{
114 if (mHostCallback)
115 stop();
116 mHostCallback = NULL;
[90057]117
118 /* Before closing a Display, make sure X11 is still running. The indicator
119 * that is when XOpenDisplay() returns non NULL. If it is not a
120 * case, XCloseDisplay() will hang on internal X11 mutex forever. */
121 Display *pDisplay = XOpenDisplay(NULL);
122 if (pDisplay)
123 {
124 XCloseDisplay(pDisplay);
125 if (mDisplay)
126 {
127 XCloseDisplay(mDisplay);
128 mDisplay = NULL;
129 }
130 }
131
[86394]132 if (mpRects)
133 {
134 RTMemFree(mpRects);
135 mpRects = NULL;
136 }
137}
138
139/**
[6202]140 * Read information about currently visible windows in the guest and subscribe to X11
141 * events about changes to this information.
142 *
143 * @note This class does not contain its own event thread, so an external thread must
[50346]144 * call nextConfigurationEvent() for as long as events are wished.
[6202]145 * @todo This function should switch the guest to fullscreen mode.
146 */
[99600]147int VBClX11SeamlessMonitor::start(void)
[6202]148{
149 int rc = VINF_SUCCESS;
[6290]150 /** Dummy values for XShapeQueryExtension */
151 int error, event;
[6202]152
[81040]153 LogRelFlowFuncEnter();
[57344]154 if (mEnabled)
155 return VINF_SUCCESS;
[6290]156 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
157 mEnabled = true;
158 monitorClientList();
159 rebuildWindowTree();
[81040]160 LogRelFlowFuncLeaveRC(rc);
[6202]161 return rc;
162}
163
164/** Stop reporting seamless events to the host. Free information about guest windows
165 and stop requesting updates. */
[99600]166void VBClX11SeamlessMonitor::stop(void)
[6202]167{
[81040]168 LogRelFlowFuncEnter();
[57344]169 if (!mEnabled)
170 return;
[6290]171 mEnabled = false;
172 unmonitorClientList();
[6202]173 freeWindowTree();
[81040]174 LogRelFlowFuncLeave();
[6202]175}
176
[99600]177void VBClX11SeamlessMonitor::monitorClientList(void)
[6202]178{
[81040]179 LogRelFlowFuncEnter();
[57265]180 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask | SubstructureNotifyMask);
[6202]181}
182
[99600]183void VBClX11SeamlessMonitor::unmonitorClientList(void)
[6290]184{
[81040]185 LogRelFlowFuncEnter();
[57344]186 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask);
[6290]187}
188
[7048]189/**
190 * Recreate the table of toplevel windows of clients on the default root window of the
191 * X server.
192 */
[99600]193void VBClX11SeamlessMonitor::rebuildWindowTree(void)
[6202]194{
[81040]195 LogRelFlowFuncEnter();
[7048]196 freeWindowTree();
[36662]197 addClients(DefaultRootWindow(mDisplay));
[21441]198 mChanged = true;
[7048]199}
[6202]200
[7048]201
202/**
203 * Look at the list of children of a virtual root window and add them to the list of clients
204 * if they belong to a client which is not a virtual root.
205 *
206 * @param hRoot the virtual root window to be examined
207 */
[99600]208void VBClX11SeamlessMonitor::addClients(const Window hRoot)
[7048]209{
210 /** Unused out parameters of XQueryTree */
211 Window hRealRoot, hParent;
212 /** The list of children of the root supplied, raw pointer */
[36662]213 Window *phChildrenRaw = NULL;
[7048]214 /** The list of children of the root supplied, auto-pointer */
[36662]215 Window *phChildren;
[7048]216 /** The number of children of the root supplied */
217 unsigned cChildren;
218
[81040]219 LogRelFlowFuncEnter();
[36662]220 if (!XQueryTree(mDisplay, hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
[7048]221 return;
222 phChildren = phChildrenRaw;
223 for (unsigned i = 0; i < cChildren; ++i)
[57264]224 addClientWindow(phChildren[i]);
[36662]225 XFree(phChildrenRaw);
[81040]226 LogRelFlowFuncLeave();
[7106]227}
228
229
[99600]230void VBClX11SeamlessMonitor::addClientWindow(const Window hWin)
[7106]231{
[81040]232 LogRelFlowFuncEnter();
[7106]233 XWindowAttributes winAttrib;
234 bool fAddWin = true;
[57264]235 Window hClient = XmuClientWindow(mDisplay, hWin);
[7106]236
[57264]237 if (isVirtualRoot(hClient))
238 fAddWin = false;
[7106]239 if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
[6202]240 {
[81040]241 VBClLogError("Failed to get the window attributes for window %d\n", hWin);
[7106]242 fAddWin = false;
[6202]243 }
[7106]244 if (fAddWin && (winAttrib.map_state == IsUnmapped))
245 fAddWin = false;
[21638]246 XSizeHints dummyHints;
247 long dummyLong;
[57265]248 /* Apparently (?) some old kwin versions had unwanted client windows
249 * without normal hints. */
[21638]250 if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints,
251 &dummyLong)))
252 {
[81040]253 LogRelFlowFunc(("window %lu, client window %lu has no size hints\n", hWin, hClient));
[21638]254 fAddWin = false;
255 }
[7106]256 if (fAddWin)
257 {
[36672]258 XRectangle *pRects = NULL;
[7106]259 int cRects = 0, iOrdering;
260 bool hasShape = false;
261
[27709]262 LogRelFlowFunc(("adding window %lu, client window %lu\n", hWin,
[21638]263 hClient));
[7106]264 if (mSupportsShape)
265 {
266 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
[36662]267 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
268 if (!pRects)
[7106]269 cRects = 0;
270 else
271 {
272 if ( (cRects > 1)
[36662]273 || (pRects[0].x != 0)
274 || (pRects[0].y != 0)
275 || (pRects[0].width != winAttrib.width)
276 || (pRects[0].height != winAttrib.height)
[7106]277 )
278 hasShape = true;
279 }
280 }
281 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
[36662]282 winAttrib.width, winAttrib.height, cRects,
283 pRects);
[7106]284 }
[81040]285 LogRelFlowFuncLeave();
[6202]286}
287
[7048]288
289/**
290 * Checks whether a window is a virtual root.
291 * @returns true if it is, false otherwise
292 * @param hWin the window to be examined
293 */
[99600]294bool VBClX11SeamlessMonitor::isVirtualRoot(Window hWin)
[7048]295{
[36662]296 unsigned char *windowTypeRaw = NULL;
297 Atom *windowType;
[7048]298 unsigned long ulCount;
299 bool rc = false;
300
[81040]301 LogRelFlowFuncEnter();
[7048]302 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
[7446]303 if (windowTypeRaw != NULL)
304 {
[36662]305 windowType = (Atom *)(windowTypeRaw);
[7446]306 if ( (ulCount != 0)
307 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
308 rc = true;
309 }
[36662]310 if (windowTypeRaw)
311 XFree(windowTypeRaw);
[37719]312 LogRelFlowFunc(("returning %RTbool\n", rc));
[7048]313 return rc;
314}
315
[36685]316DECLCALLBACK(int) VBoxGuestWinFree(VBoxGuestWinInfo *pInfo, void *pvParam)
317{
318 Display *pDisplay = (Display *)pvParam;
[7048]319
[57344]320 XShapeSelectInput(pDisplay, pInfo->Core.Key, 0);
[36685]321 delete pInfo;
322 return VINF_SUCCESS;
323}
324
[6202]325/**
326 * Free all information in the tree of visible windows
327 */
[99600]328void VBClX11SeamlessMonitor::freeWindowTree(void)
[6202]329{
330 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
[81040]331 LogRelFlowFuncEnter();
[36685]332 mGuestWindows.detachAll(VBoxGuestWinFree, mDisplay);
[81040]333 LogRelFlowFuncLeave();
[6202]334}
335
[7106]336
[6202]337/**
[21227]338 * Waits for a position or shape-related event from guest windows
[6202]339 *
340 * @note Called from the guest event thread.
341 */
[99600]342void VBClX11SeamlessMonitor::nextConfigurationEvent(void)
[6202]343{
344 XEvent event;
345
[81040]346 LogRelFlowFuncEnter();
[6202]347 /* Start by sending information about the current window setup to the host. We do this
348 here because we want to send all such information from a single thread. */
[57486]349 if (mChanged && mEnabled)
[36806]350 {
351 updateRects();
[52564]352 mHostCallback(mpRects, mcRects);
[36806]353 }
[21441]354 mChanged = false;
[90057]355
356 if (XPending(mDisplay) > 0)
357 {
358 /* We execute this even when seamless is disabled, as it also waits for
359 * enable and disable notification. */
360 XNextEvent(mDisplay, &event);
361 } else
362 {
363 /* This function is called in a loop by upper layer. In order to
364 * prevent CPU spinning, sleep a bit before returning. */
365 RTThreadSleep(300 /* ms */);
366 return;
367 }
368
[57486]369 if (!mEnabled)
370 return;
[6202]371 switch (event.type)
372 {
373 case ConfigureNotify:
[37719]374 {
375 XConfigureEvent *pConf = &event.xconfigure;
376 LogRelFlowFunc(("configure event, window=%lu, x=%i, y=%i, w=%i, h=%i, send_event=%RTbool\n",
377 (unsigned long) pConf->window, (int) pConf->x,
378 (int) pConf->y, (int) pConf->width,
379 (int) pConf->height, pConf->send_event));
380 }
[21216]381 doConfigureEvent(event.xconfigure.window);
[6202]382 break;
383 case MapNotify:
[37719]384 LogRelFlowFunc(("map event, window=%lu, send_event=%RTbool\n",
385 (unsigned long) event.xmap.window,
386 event.xmap.send_event));
[57265]387 rebuildWindowTree();
[6202]388 break;
[57265]389 case PropertyNotify:
390 if ( event.xproperty.atom != XInternAtom(mDisplay, "_NET_CLIENT_LIST", True /* only_if_exists */)
391 || event.xproperty.window != DefaultRootWindow(mDisplay))
392 break;
[81040]393 LogRelFlowFunc(("_NET_CLIENT_LIST property event on root window\n"));
[57265]394 rebuildWindowTree();
395 break;
[7106]396 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
[37719]397 LogRelFlowFunc(("shape event, window=%lu, send_event=%RTbool\n",
398 (unsigned long) event.xany.window,
399 event.xany.send_event));
[21216]400 /* the window member in xany is in the same place as in the shape event */
401 doShapeEvent(event.xany.window);
[6202]402 break;
403 case UnmapNotify:
[37719]404 LogRelFlowFunc(("unmap event, window=%lu, send_event=%RTbool\n",
405 (unsigned long) event.xunmap.window,
406 event.xunmap.send_event));
[57265]407 rebuildWindowTree();
[6202]408 break;
409 default:
410 break;
411 }
[57486]412 LogRelFlowFunc(("processed event\n"));
[6202]413}
414
415/**
[6290]416 * Handle a configuration event in the seamless event thread by setting the new position.
[6202]417 *
[65099]418 * @param hWin the window to be examined
[6202]419 */
[99600]420void VBClX11SeamlessMonitor::doConfigureEvent(Window hWin)
[6202]421{
[36685]422 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
423 if (pInfo)
[6202]424 {
[7106]425 XWindowAttributes winAttrib;
426
[21940]427 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
428 return;
[36685]429 pInfo->mX = winAttrib.x;
430 pInfo->mY = winAttrib.y;
431 pInfo->mWidth = winAttrib.width;
432 pInfo->mHeight = winAttrib.height;
[21441]433 mChanged = true;
[6202]434 }
435}
436
437/**
[6290]438 * Handle a window shape change event in the seamless event thread.
[6202]439 *
[65099]440 * @param hWin the window to be examined
[6202]441 */
[99600]442void VBClX11SeamlessMonitor::doShapeEvent(Window hWin)
[6202]443{
[81040]444 LogRelFlowFuncEnter();
[36685]445 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
446 if (pInfo)
[6202]447 {
[36662]448 XRectangle *pRects;
[7106]449 int cRects = 0, iOrdering;
450
[36662]451 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
452 &iOrdering);
453 if (!pRects)
[21940]454 cRects = 0;
[36685]455 pInfo->mhasShape = true;
456 if (pInfo->mpRects)
457 XFree(pInfo->mpRects);
458 pInfo->mcRects = cRects;
459 pInfo->mpRects = pRects;
[21441]460 mChanged = true;
[6202]461 }
[81040]462 LogRelFlowFuncLeave();
[6202]463}
464
465/**
[36806]466 * Gets the list of visible rectangles
467 */
[99600]468RTRECT *VBClX11SeamlessMonitor::getRects(void)
[36806]469{
470 return mpRects;
471}
472
473/**
474 * Gets the number of rectangles in the visible rectangle list
475 */
[99600]476size_t VBClX11SeamlessMonitor::getRectCount(void)
[36806]477{
478 return mcRects;
479}
480
481RTVEC_DECL(RectList, RTRECT)
482
[85121]483static DECLCALLBACK(int) getRectsCallback(VBoxGuestWinInfo *pInfo, struct RectList *pRects)
[36685]484{
485 if (pInfo->mhasShape)
[6202]486 {
[36685]487 for (int i = 0; i < pInfo->mcRects; ++i)
488 {
[36806]489 RTRECT *pRect;
[37423]490
[36806]491 pRect = RectListPushBack(pRects);
492 if (!pRect)
493 return VERR_NO_MEMORY;
494 pRect->xLeft = pInfo->mX
495 + pInfo->mpRects[i].x;
496 pRect->yBottom = pInfo->mY
497 + pInfo->mpRects[i].y
498 + pInfo->mpRects[i].height;
499 pRect->xRight = pInfo->mX
500 + pInfo->mpRects[i].x
501 + pInfo->mpRects[i].width;
502 pRect->yTop = pInfo->mY
503 + pInfo->mpRects[i].y;
[36685]504 }
[7079]505 }
[36685]506 else
507 {
[36806]508 RTRECT *pRect;
509
510 pRect = RectListPushBack(pRects);
511 if (!pRect)
512 return VERR_NO_MEMORY;
513 pRect->xLeft = pInfo->mX;
514 pRect->yBottom = pInfo->mY
515 + pInfo->mHeight;
516 pRect->xRight = pInfo->mX
517 + pInfo->mWidth;
518 pRect->yTop = pInfo->mY;
[36685]519 }
520 return VINF_SUCCESS;
[6202]521}
522
523/**
[36806]524 * Updates the list of seamless rectangles
[6202]525 */
[99600]526int VBClX11SeamlessMonitor::updateRects(void)
[6202]527{
[81040]528 LogRelFlowFuncEnter();
[36806]529 struct RectList rects = RTVEC_INITIALIZER;
[6202]530
[62883]531 if (mcRects != 0)
[6202]532 {
[36806]533 int rc = RectListReserve(&rects, mcRects * 2);
534 if (RT_FAILURE(rc))
535 return rc;
[6202]536 }
[85121]537 mGuestWindows.doWithAll((PFNVBOXGUESTWINCALLBACK)getRectsCallback, &rects);
[36806]538 if (mpRects)
539 RTMemFree(mpRects);
540 mcRects = RectListSize(&rects);
541 mpRects = RectListDetach(&rects);
[81040]542 LogRelFlowFuncLeave();
[36806]543 return VINF_SUCCESS;
[6202]544}
545
546/**
547 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
548 *
549 * @note This function should only be called from the host event thread.
550 */
[99600]551bool VBClX11SeamlessMonitor::interruptEventWait(void)
[6202]552{
[98534]553 LogRelFlowFuncEnter();
554
[57344]555 Display *pDisplay = XOpenDisplay(NULL);
556 if (pDisplay == NULL)
[89766]557 {
[90057]558 VBClLogError("Failed to open X11 display\n");
[89766]559 return false;
560 }
561
[6290]562 /* Message contents set to zero. */
[97170]563 XClientMessageEvent clientMessage =
[98534]564 {
565 /* .type = */ ClientMessage,
566 /* .serial = */ 0,
567 /* .send_event = */ 0,
568 /* .display = */ 0,
569 /* .window = */ 0,
570 /* .message_type = */ XInternAtom(pDisplay, "VBOX_CLIENT_SEAMLESS_HEARTBEAT", false),
571 /* .format = */ 8,
572 /* .data ... */
573 };
[6290]574
[98534]575 bool rc = false;
[57344]576 if (XSendEvent(pDisplay, DefaultRootWindow(mDisplay), false,
577 PropertyChangeMask, (XEvent *)&clientMessage))
[7449]578 rc = true;
[98534]579
[57344]580 XCloseDisplay(pDisplay);
[37719]581 LogRelFlowFunc(("returning %RTbool\n", rc));
[7449]582 return rc;
[6202]583}
[99600]584
585
586/*********************************************************************************************************************************
587 * VBClX11SeamlessSvc implementation *
588 ********************************************************************************************************************************/
589
590VBClX11SeamlessSvc::VBClX11SeamlessSvc(void)
591{
592 mX11MonitorThread = NIL_RTTHREAD;
593 mX11MonitorThreadStopping = false;
594
595 mMode = VMMDev_Seamless_Disabled;
596 mfPaused = true;
597}
598
599VBClX11SeamlessSvc::~VBClX11SeamlessSvc()
600{
601 /* Stopping will be done via main.cpp. */
602}
603
604/** @copydoc VBCLSERVICE::pfnInit */
605int VBClX11SeamlessSvc::init(void)
606{
607 int rc;
608 const char *pcszStage;
609
610 do
611 {
612 pcszStage = "Connecting to the X server";
[99601]613 rc = mX11Monitor.init(VBClSeamlessSendRegionUpdate);
[99600]614 if (RT_FAILURE(rc))
615 break;
616 pcszStage = "Setting guest IRQ filter mask";
617 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 0);
618 if (RT_FAILURE(rc))
619 break;
620 pcszStage = "Reporting support for seamless capability";
621 rc = VbglR3SeamlessSetCap(true);
622 if (RT_FAILURE(rc))
623 break;
624 rc = startX11MonitorThread();
625 if (RT_FAILURE(rc))
626 break;
627
628 } while(0);
629
630 if (RT_FAILURE(rc))
631 VBClLogError("Failed to start in stage '%s' -- error %Rrc\n", pcszStage, rc);
632
633 return rc;
634}
635
636/** @copydoc VBCLSERVICE::pfnWorker */
637int VBClX11SeamlessSvc::worker(bool volatile *pfShutdown)
638{
639 int rc = VINF_SUCCESS;
640
641 /* Let the main thread know that it can continue spawning services. */
642 RTThreadUserSignal(RTThreadSelf());
643
644 /* This will only exit if something goes wrong. */
645 for (;;)
646 {
647 if (ASMAtomicReadBool(pfShutdown))
648 break;
649
650 rc = nextStateChangeEvent();
651
652 if (rc == VERR_TRY_AGAIN)
653 rc = VINF_SUCCESS;
654
655 if (RT_FAILURE(rc))
656 break;
657
658 if (ASMAtomicReadBool(pfShutdown))
659 break;
660
661 /* If we are not stopping, sleep for a bit to avoid using up too
662 much CPU while retrying. */
663 RTThreadYield();
664 }
665
666 return rc;
667}
668
669/** @copydoc VBCLSERVICE::pfnStop */
670void VBClX11SeamlessSvc::stop(void)
671{
672 VbglR3SeamlessSetCap(false);
673 VbglR3CtlFilterMask(0, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST);
674 stopX11MonitorThread();
675}
676
677/** @copydoc VBCLSERVICE::pfnTerm */
678int VBClX11SeamlessSvc::term(void)
679{
680 mX11Monitor.uninit();
681 return VINF_SUCCESS;
682}
683
684/**
685 * Waits for a seamless state change events from the host and dispatch it.
686 *
687 * @returns VBox return code, or
688 * VERR_TRY_AGAIN if no new status is available and we have to try it again
689 * at some later point in time.
690 */
691int VBClX11SeamlessSvc::nextStateChangeEvent(void)
692{
693 VMMDevSeamlessMode newMode = VMMDev_Seamless_Disabled;
694
695 int rc = VbglR3SeamlessWaitEvent(&newMode);
696 if (RT_SUCCESS(rc))
697 {
698 mMode = newMode;
699 switch (newMode)
700 {
701 case VMMDev_Seamless_Visible_Region:
702 /* A simplified seamless mode, obtained by making the host VM window
703 * borderless and making the guest desktop transparent. */
704 VBClLogVerbose(2, "\"Visible region\" mode requested\n");
705 break;
706 case VMMDev_Seamless_Disabled:
707 VBClLogVerbose(2, "\"Disabled\" mode requested\n");
708 break;
709 case VMMDev_Seamless_Host_Window:
710 /* One host window represents one guest window. Not yet implemented. */
711 VBClLogVerbose(2, "Unsupported \"host window\" mode requested\n");
712 return VERR_NOT_SUPPORTED;
713 default:
714 VBClLogError("Unsupported mode %d requested\n", newMode);
715 return VERR_NOT_SUPPORTED;
716 }
717 }
718 if ( RT_SUCCESS(rc)
719 || rc == VERR_TRY_AGAIN)
720 {
721 if (mMode == VMMDev_Seamless_Visible_Region)
722 mfPaused = false;
723 else
724 mfPaused = true;
725 mX11Monitor.interruptEventWait();
726 }
727 else
728 VBClLogError("VbglR3SeamlessWaitEvent returned %Rrc\n", rc);
729
730 return rc;
731}
732
733/**
734 * The actual X11 window configuration change monitor thread function.
735 */
736int VBClX11SeamlessSvc::x11MonitorThread(RTTHREAD hThreadSelf, void *pvUser)
737{
738 RT_NOREF(hThreadSelf);
739
740 VBClX11SeamlessSvc *pThis = (VBClX11SeamlessSvc *)pvUser;
741 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
742
743 int rc = VINF_SUCCESS;
744
745 RTThreadUserSignal(hThreadSelf);
746
747 VBClLogVerbose(2, "X11 monitor thread started\n");
748
749 while (!pThis->mX11MonitorThreadStopping)
750 {
751 if (!pThis->mfPaused)
752 {
753 rc = pThis->mX11Monitor.start();
754 if (RT_FAILURE(rc))
755 VBClLogFatalError("Failed to change the X11 seamless service state, mfPaused=%RTbool, rc=%Rrc\n",
756 pThis->mfPaused, rc);
757 }
758
759 pThis->mX11Monitor.nextConfigurationEvent();
760
761 if ( pThis->mfPaused
762 || pThis->mX11MonitorThreadStopping)
763 {
764 pThis->mX11Monitor.stop();
765 }
766 }
767
768 VBClLogVerbose(2, "X11 monitor thread ended\n");
769
770 return rc;
771}
772
773/**
774 * Start the X11 window configuration change monitor thread.
775 */
776int VBClX11SeamlessSvc::startX11MonitorThread(void)
777{
778 mX11MonitorThreadStopping = false;
779
780 if (isX11MonitorThreadRunning())
781 return VINF_SUCCESS;
782
783 int rc = RTThreadCreate(&mX11MonitorThread, x11MonitorThread, this, 0,
784 RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
785 "seamless x11");
786 if (RT_SUCCESS(rc))
787 rc = RTThreadUserWait(mX11MonitorThread, RT_MS_30SEC);
788
789 if (RT_FAILURE(rc))
790 VBClLogError("Failed to start X11 monitor thread, rc=%Rrc\n", rc);
791
792 return rc;
793}
794
795/**
796 * Stops the monitor thread.
797 */
798int VBClX11SeamlessSvc::stopX11MonitorThread(void)
799{
800 if (!isX11MonitorThreadRunning())
801 return VINF_SUCCESS;
802
803 mX11MonitorThreadStopping = true;
804 if (!mX11Monitor.interruptEventWait())
805 {
806 VBClLogError("Unable to notify X11 monitor thread\n");
807 return VERR_INVALID_STATE;
808 }
809
810 int rcThread;
811 int rc = RTThreadWait(mX11MonitorThread, RT_MS_30SEC, &rcThread);
812 if (RT_SUCCESS(rc))
813 rc = rcThread;
814
815 if (RT_SUCCESS(rc))
816 {
817 mX11MonitorThread = NIL_RTTHREAD;
818 }
819 else
820 VBClLogError("Waiting for X11 monitor thread to stop failed, rc=%Rrc\n", rc);
821
822 return rc;
823}
824
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use