VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 23.9 KB
Line 
1/* $Id: seamless-x11.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * X11 Seamless mode.
4 */
5
6/*
7 * Copyright (C) 2008-2024 Oracle and/or its affiliates.
8 *
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
26 */
27
28
29/*********************************************************************************************************************************
30* Header files *
31*********************************************************************************************************************************/
32#include <iprt/errcore.h>
33#include <iprt/assert.h>
34#include <iprt/vector.h>
35#include <iprt/thread.h>
36#include <VBox/log.h>
37
38#include "seamless.h"
39#include "seamless-x11.h"
40#include "VBoxClient.h"
41
42#include <X11/Xatom.h>
43#include <X11/Xmu/WinUtil.h>
44
45#include <limits.h>
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51#ifdef TESTCASE
52# undef DefaultRootWindow
53# define DefaultRootWindow XDefaultRootWindow
54#endif
55
56
57/*********************************************************************************************************************************
58* Internal Functions *
59*********************************************************************************************************************************/
60
61static unsigned char *XXGetProperty(Display *aDpy, Window aWnd, Atom aPropType,
62 const char *aPropName, unsigned long *nItems)
63{
64 LogRelFlowFuncEnter();
65 Atom propNameAtom = XInternAtom (aDpy, aPropName,
66 True /* only_if_exists */);
67 if (propNameAtom == None)
68 {
69 return NULL;
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)
81 return NULL;
82
83 LogRelFlowFuncLeave();
84 return propVal;
85}
86
87int VBClX11SeamlessMonitor::init(PFNSENDREGIONUPDATE pHostCallback)
88{
89 int rc = VINF_SUCCESS;
90
91 LogRelFlowFuncEnter();
92 if (mHostCallback != NULL) /* Assertion */
93 {
94 VBClLogError("Attempting to initialise seamless guest object twice!\n");
95 return VERR_INTERNAL_ERROR;
96 }
97 if (!(mDisplay = XOpenDisplay(NULL)))
98 {
99 VBClLogError("Seamless guest object failed to acquire a connection to the display\n");
100 return VERR_ACCESS_DENIED;
101 }
102 mHostCallback = pHostCallback;
103 mEnabled = false;
104 unmonitorClientList();
105 LogRelFlowFuncLeaveRC(rc);
106 return rc;
107}
108
109/**
110 * Shutdown seamless event monitoring.
111 */
112void VBClX11SeamlessMonitor::uninit(void)
113{
114 if (mHostCallback)
115 stop();
116 mHostCallback = NULL;
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
132 if (mpRects)
133 {
134 RTMemFree(mpRects);
135 mpRects = NULL;
136 }
137}
138
139/**
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
144 * call nextConfigurationEvent() for as long as events are wished.
145 * @todo This function should switch the guest to fullscreen mode.
146 */
147int VBClX11SeamlessMonitor::start(void)
148{
149 int rc = VINF_SUCCESS;
150 /** Dummy values for XShapeQueryExtension */
151 int error, event;
152
153 LogRelFlowFuncEnter();
154 if (mEnabled)
155 return VINF_SUCCESS;
156 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
157 mEnabled = true;
158 monitorClientList();
159 rebuildWindowTree();
160 LogRelFlowFuncLeaveRC(rc);
161 return rc;
162}
163
164/** Stop reporting seamless events to the host. Free information about guest windows
165 and stop requesting updates. */
166void VBClX11SeamlessMonitor::stop(void)
167{
168 LogRelFlowFuncEnter();
169 if (!mEnabled)
170 return;
171 mEnabled = false;
172 unmonitorClientList();
173 freeWindowTree();
174 LogRelFlowFuncLeave();
175}
176
177void VBClX11SeamlessMonitor::monitorClientList(void)
178{
179 LogRelFlowFuncEnter();
180 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask | SubstructureNotifyMask);
181}
182
183void VBClX11SeamlessMonitor::unmonitorClientList(void)
184{
185 LogRelFlowFuncEnter();
186 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask);
187}
188
189/**
190 * Recreate the table of toplevel windows of clients on the default root window of the
191 * X server.
192 */
193void VBClX11SeamlessMonitor::rebuildWindowTree(void)
194{
195 LogRelFlowFuncEnter();
196 freeWindowTree();
197 addClients(DefaultRootWindow(mDisplay));
198 mChanged = true;
199}
200
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 */
208void VBClX11SeamlessMonitor::addClients(const Window hRoot)
209{
210 /** Unused out parameters of XQueryTree */
211 Window hRealRoot, hParent;
212 /** The list of children of the root supplied, raw pointer */
213 Window *phChildrenRaw = NULL;
214 /** The list of children of the root supplied, auto-pointer */
215 Window *phChildren;
216 /** The number of children of the root supplied */
217 unsigned cChildren;
218
219 LogRelFlowFuncEnter();
220 if (!XQueryTree(mDisplay, hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
221 return;
222 phChildren = phChildrenRaw;
223 for (unsigned i = 0; i < cChildren; ++i)
224 addClientWindow(phChildren[i]);
225 XFree(phChildrenRaw);
226 LogRelFlowFuncLeave();
227}
228
229
230void VBClX11SeamlessMonitor::addClientWindow(const Window hWin)
231{
232 LogRelFlowFuncEnter();
233 XWindowAttributes winAttrib;
234 bool fAddWin = true;
235 Window hClient = XmuClientWindow(mDisplay, hWin);
236
237 if (isVirtualRoot(hClient))
238 fAddWin = false;
239 if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
240 {
241 VBClLogError("Failed to get the window attributes for window %d\n", hWin);
242 fAddWin = false;
243 }
244 if (fAddWin && (winAttrib.map_state == IsUnmapped))
245 fAddWin = false;
246 XSizeHints dummyHints;
247 long dummyLong;
248 /* Apparently (?) some old kwin versions had unwanted client windows
249 * without normal hints. */
250 if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints,
251 &dummyLong)))
252 {
253 LogRelFlowFunc(("window %lu, client window %lu has no size hints\n", hWin, hClient));
254 fAddWin = false;
255 }
256 if (fAddWin)
257 {
258 XRectangle *pRects = NULL;
259 int cRects = 0, iOrdering;
260 bool hasShape = false;
261
262 LogRelFlowFunc(("adding window %lu, client window %lu\n", hWin,
263 hClient));
264 if (mSupportsShape)
265 {
266 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
267 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
268 if (!pRects)
269 cRects = 0;
270 else
271 {
272 if ( (cRects > 1)
273 || (pRects[0].x != 0)
274 || (pRects[0].y != 0)
275 || (pRects[0].width != winAttrib.width)
276 || (pRects[0].height != winAttrib.height)
277 )
278 hasShape = true;
279 }
280 }
281 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
282 winAttrib.width, winAttrib.height, cRects,
283 pRects);
284 }
285 LogRelFlowFuncLeave();
286}
287
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 */
294bool VBClX11SeamlessMonitor::isVirtualRoot(Window hWin)
295{
296 unsigned char *windowTypeRaw = NULL;
297 Atom *windowType;
298 unsigned long ulCount;
299 bool rc = false;
300
301 LogRelFlowFuncEnter();
302 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
303 if (windowTypeRaw != NULL)
304 {
305 windowType = (Atom *)(windowTypeRaw);
306 if ( (ulCount != 0)
307 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
308 rc = true;
309 }
310 if (windowTypeRaw)
311 XFree(windowTypeRaw);
312 LogRelFlowFunc(("returning %RTbool\n", rc));
313 return rc;
314}
315
316DECLCALLBACK(int) VBoxGuestWinFree(VBoxGuestWinInfo *pInfo, void *pvParam)
317{
318 Display *pDisplay = (Display *)pvParam;
319
320 XShapeSelectInput(pDisplay, pInfo->Core.Key, 0);
321 delete pInfo;
322 return VINF_SUCCESS;
323}
324
325/**
326 * Free all information in the tree of visible windows
327 */
328void VBClX11SeamlessMonitor::freeWindowTree(void)
329{
330 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
331 LogRelFlowFuncEnter();
332 mGuestWindows.detachAll(VBoxGuestWinFree, mDisplay);
333 LogRelFlowFuncLeave();
334}
335
336
337/**
338 * Waits for a position or shape-related event from guest windows
339 *
340 * @note Called from the guest event thread.
341 */
342void VBClX11SeamlessMonitor::nextConfigurationEvent(void)
343{
344 XEvent event;
345
346 LogRelFlowFuncEnter();
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. */
349 if (mChanged && mEnabled)
350 {
351 updateRects();
352 mHostCallback(mpRects, mcRects);
353 }
354 mChanged = false;
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
369 if (!mEnabled)
370 return;
371 switch (event.type)
372 {
373 case ConfigureNotify:
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 }
381 doConfigureEvent(event.xconfigure.window);
382 break;
383 case MapNotify:
384 LogRelFlowFunc(("map event, window=%lu, send_event=%RTbool\n",
385 (unsigned long) event.xmap.window,
386 event.xmap.send_event));
387 rebuildWindowTree();
388 break;
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;
393 LogRelFlowFunc(("_NET_CLIENT_LIST property event on root window\n"));
394 rebuildWindowTree();
395 break;
396 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
397 LogRelFlowFunc(("shape event, window=%lu, send_event=%RTbool\n",
398 (unsigned long) event.xany.window,
399 event.xany.send_event));
400 /* the window member in xany is in the same place as in the shape event */
401 doShapeEvent(event.xany.window);
402 break;
403 case UnmapNotify:
404 LogRelFlowFunc(("unmap event, window=%lu, send_event=%RTbool\n",
405 (unsigned long) event.xunmap.window,
406 event.xunmap.send_event));
407 rebuildWindowTree();
408 break;
409 default:
410 break;
411 }
412 LogRelFlowFunc(("processed event\n"));
413}
414
415/**
416 * Handle a configuration event in the seamless event thread by setting the new position.
417 *
418 * @param hWin the window to be examined
419 */
420void VBClX11SeamlessMonitor::doConfigureEvent(Window hWin)
421{
422 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
423 if (pInfo)
424 {
425 XWindowAttributes winAttrib;
426
427 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
428 return;
429 pInfo->mX = winAttrib.x;
430 pInfo->mY = winAttrib.y;
431 pInfo->mWidth = winAttrib.width;
432 pInfo->mHeight = winAttrib.height;
433 mChanged = true;
434 }
435}
436
437/**
438 * Handle a window shape change event in the seamless event thread.
439 *
440 * @param hWin the window to be examined
441 */
442void VBClX11SeamlessMonitor::doShapeEvent(Window hWin)
443{
444 LogRelFlowFuncEnter();
445 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
446 if (pInfo)
447 {
448 XRectangle *pRects;
449 int cRects = 0, iOrdering;
450
451 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
452 &iOrdering);
453 if (!pRects)
454 cRects = 0;
455 pInfo->mhasShape = true;
456 if (pInfo->mpRects)
457 XFree(pInfo->mpRects);
458 pInfo->mcRects = cRects;
459 pInfo->mpRects = pRects;
460 mChanged = true;
461 }
462 LogRelFlowFuncLeave();
463}
464
465/**
466 * Gets the list of visible rectangles
467 */
468RTRECT *VBClX11SeamlessMonitor::getRects(void)
469{
470 return mpRects;
471}
472
473/**
474 * Gets the number of rectangles in the visible rectangle list
475 */
476size_t VBClX11SeamlessMonitor::getRectCount(void)
477{
478 return mcRects;
479}
480
481RTVEC_DECL(RectList, RTRECT)
482
483static DECLCALLBACK(int) getRectsCallback(VBoxGuestWinInfo *pInfo, struct RectList *pRects)
484{
485 if (pInfo->mhasShape)
486 {
487 for (int i = 0; i < pInfo->mcRects; ++i)
488 {
489 RTRECT *pRect;
490
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;
504 }
505 }
506 else
507 {
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;
519 }
520 return VINF_SUCCESS;
521}
522
523/**
524 * Updates the list of seamless rectangles
525 */
526int VBClX11SeamlessMonitor::updateRects(void)
527{
528 LogRelFlowFuncEnter();
529 struct RectList rects = RTVEC_INITIALIZER;
530
531 if (mcRects != 0)
532 {
533 int rc = RectListReserve(&rects, mcRects * 2);
534 if (RT_FAILURE(rc))
535 return rc;
536 }
537 mGuestWindows.doWithAll((PFNVBOXGUESTWINCALLBACK)getRectsCallback, &rects);
538 if (mpRects)
539 RTMemFree(mpRects);
540 mcRects = RectListSize(&rects);
541 mpRects = RectListDetach(&rects);
542 LogRelFlowFuncLeave();
543 return VINF_SUCCESS;
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 */
551bool VBClX11SeamlessMonitor::interruptEventWait(void)
552{
553 LogRelFlowFuncEnter();
554
555 Display *pDisplay = XOpenDisplay(NULL);
556 if (pDisplay == NULL)
557 {
558 VBClLogError("Failed to open X11 display\n");
559 return false;
560 }
561
562 /* Message contents set to zero. */
563 XClientMessageEvent clientMessage =
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 };
574
575 bool rc = false;
576 if (XSendEvent(pDisplay, DefaultRootWindow(mDisplay), false,
577 PropertyChangeMask, (XEvent *)&clientMessage))
578 rc = true;
579
580 XCloseDisplay(pDisplay);
581 LogRelFlowFunc(("returning %RTbool\n", rc));
582 return rc;
583}
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";
613 rc = mX11Monitor.init(VBClSeamlessSendRegionUpdate);
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.

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