VirtualBox

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

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

Guest Additions/VBoxClient: Moved the X11-specific code for seamless mode into an own submodule and using the actual service as a (runtime) wrapper (for also the Wayland-specific code later) [build fix]. bugref:10427

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

© 2023 Oracle
ContactPrivacy policyTerms of Use