summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/x11/VBoxClient/seamless.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/x11/VBoxClient/seamless.cpp')
-rw-r--r--src/VBox/Additions/x11/VBoxClient/seamless.cpp360
1 files changed, 360 insertions, 0 deletions
diff --git a/src/VBox/Additions/x11/VBoxClient/seamless.cpp b/src/VBox/Additions/x11/VBoxClient/seamless.cpp
new file mode 100644
index 00000000..365b31f6
--- /dev/null
+++ b/src/VBox/Additions/x11/VBoxClient/seamless.cpp
@@ -0,0 +1,360 @@
+/* $Id: seamless.cpp $ */
+/** @file
+ * X11 Guest client - seamless mode: main logic, communication with the host and
+ * wrapper interface for the main code of the VBoxClient deamon. The
+ * X11-specific parts are split out into their own file for ease of testing.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header files *
+*********************************************************************************************************************************/
+#include <new>
+
+#include <X11/Xlib.h>
+
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+
+#include <VBox/log.h>
+#include <VBox/VBoxGuestLib.h>
+
+#include "VBoxClient.h"
+#include "seamless.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/**
+ * Struct for keeping a service instance.
+ */
+struct SEAMLESSSERVICE
+{
+ /** Seamless service object. */
+ SeamlessMain mSeamless;
+};
+
+/** Service instance data. */
+static SEAMLESSSERVICE g_Svc;
+
+
+SeamlessMain::SeamlessMain(void)
+{
+ mX11MonitorThread = NIL_RTTHREAD;
+ mX11MonitorThreadStopping = false;
+
+ mMode = VMMDev_Seamless_Disabled;
+ mfPaused = true;
+}
+
+SeamlessMain::~SeamlessMain()
+{
+ /* Stopping will be done via main.cpp. */
+}
+
+/**
+ * Update the set of visible rectangles in the host.
+ */
+static void sendRegionUpdate(RTRECT *pRects, size_t cRects)
+{
+ if ( cRects
+ && !pRects) /* Assertion */
+ {
+ VBClLogError(("Region update called with NULL pointer\n"));
+ return;
+ }
+ VbglR3SeamlessSendRects(cRects, pRects);
+}
+
+/** @copydoc VBCLSERVICE::pfnInit */
+int SeamlessMain::init(void)
+{
+ int rc;
+ const char *pcszStage;
+
+ do
+ {
+ pcszStage = "Connecting to the X server";
+ rc = mX11Monitor.init(sendRegionUpdate);
+ if (RT_FAILURE(rc))
+ break;
+ pcszStage = "Setting guest IRQ filter mask";
+ rc = VbglR3CtlFilterMask(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 0);
+ if (RT_FAILURE(rc))
+ break;
+ pcszStage = "Reporting support for seamless capability";
+ rc = VbglR3SeamlessSetCap(true);
+ if (RT_FAILURE(rc))
+ break;
+ rc = startX11MonitorThread();
+ if (RT_FAILURE(rc))
+ break;
+
+ } while(0);
+
+ if (RT_FAILURE(rc))
+ VBClLogError("Failed to start in stage '%s' -- error %Rrc\n", pcszStage, rc);
+
+ return rc;
+}
+
+/** @copydoc VBCLSERVICE::pfnWorker */
+int SeamlessMain::worker(bool volatile *pfShutdown)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Let the main thread know that it can continue spawning services. */
+ RTThreadUserSignal(RTThreadSelf());
+
+ /* This will only exit if something goes wrong. */
+ for (;;)
+ {
+ if (ASMAtomicReadBool(pfShutdown))
+ break;
+
+ rc = nextStateChangeEvent();
+
+ if (rc == VERR_TRY_AGAIN)
+ rc = VINF_SUCCESS;
+
+ if (RT_FAILURE(rc))
+ break;
+
+ if (ASMAtomicReadBool(pfShutdown))
+ break;
+
+ /* If we are not stopping, sleep for a bit to avoid using up too
+ much CPU while retrying. */
+ RTThreadYield();
+ }
+
+ return rc;
+}
+
+/** @copydoc VBCLSERVICE::pfnStop */
+void SeamlessMain::stop(void)
+{
+ VbglR3SeamlessSetCap(false);
+ VbglR3CtlFilterMask(0, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST);
+ stopX11MonitorThread();
+}
+
+/** @copydoc VBCLSERVICE::pfnTerm */
+int SeamlessMain::term(void)
+{
+ mX11Monitor.uninit();
+ return VINF_SUCCESS;
+}
+
+/**
+ * Waits for a seamless state change events from the host and dispatch it.
+ *
+ * @returns VBox return code, or
+ * VERR_TRY_AGAIN if no new status is available and we have to try it again
+ * at some later point in time.
+ */
+int SeamlessMain::nextStateChangeEvent(void)
+{
+ VMMDevSeamlessMode newMode = VMMDev_Seamless_Disabled;
+
+ int rc = VbglR3SeamlessWaitEvent(&newMode);
+ if (RT_SUCCESS(rc))
+ {
+ mMode = newMode;
+ switch (newMode)
+ {
+ case VMMDev_Seamless_Visible_Region:
+ /* A simplified seamless mode, obtained by making the host VM window
+ * borderless and making the guest desktop transparent. */
+ VBClLogVerbose(2, "\"Visible region\" mode requested\n");
+ break;
+ case VMMDev_Seamless_Disabled:
+ VBClLogVerbose(2, "\"Disabled\" mode requested\n");
+ break;
+ case VMMDev_Seamless_Host_Window:
+ /* One host window represents one guest window. Not yet implemented. */
+ VBClLogVerbose(2, "Unsupported \"host window\" mode requested\n");
+ return VERR_NOT_SUPPORTED;
+ default:
+ VBClLogError("Unsupported mode %d requested\n", newMode);
+ return VERR_NOT_SUPPORTED;
+ }
+ }
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_TRY_AGAIN)
+ {
+ if (mMode == VMMDev_Seamless_Visible_Region)
+ mfPaused = false;
+ else
+ mfPaused = true;
+ mX11Monitor.interruptEventWait();
+ }
+ else
+ VBClLogError("VbglR3SeamlessWaitEvent returned %Rrc\n", rc);
+
+ return rc;
+}
+
+/**
+ * The actual X11 window configuration change monitor thread function.
+ */
+int SeamlessMain::x11MonitorThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf);
+
+ SeamlessMain *pThis = (SeamlessMain *)pvUser;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+
+ RTThreadUserSignal(hThreadSelf);
+
+ VBClLogVerbose(2, "X11 monitor thread started\n");
+
+ while (!pThis->mX11MonitorThreadStopping)
+ {
+ if (!pThis->mfPaused)
+ {
+ rc = pThis->mX11Monitor.start();
+ if (RT_FAILURE(rc))
+ VBClLogFatalError("Failed to change the X11 seamless service state, mfPaused=%RTbool, rc=%Rrc\n",
+ pThis->mfPaused, rc);
+ }
+
+ pThis->mX11Monitor.nextConfigurationEvent();
+
+ if ( pThis->mfPaused
+ || pThis->mX11MonitorThreadStopping)
+ {
+ pThis->mX11Monitor.stop();
+ }
+ }
+
+ VBClLogVerbose(2, "X11 monitor thread ended\n");
+
+ return rc;
+}
+
+/**
+ * Start the X11 window configuration change monitor thread.
+ */
+int SeamlessMain::startX11MonitorThread(void)
+{
+ mX11MonitorThreadStopping = false;
+
+ if (isX11MonitorThreadRunning())
+ return VINF_SUCCESS;
+
+ int rc = RTThreadCreate(&mX11MonitorThread, x11MonitorThread, this, 0,
+ RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
+ "seamless x11");
+ if (RT_SUCCESS(rc))
+ rc = RTThreadUserWait(mX11MonitorThread, RT_MS_30SEC);
+
+ if (RT_FAILURE(rc))
+ VBClLogError("Failed to start X11 monitor thread, rc=%Rrc\n", rc);
+
+ return rc;
+}
+
+/**
+ * Stops the monitor thread.
+ */
+int SeamlessMain::stopX11MonitorThread(void)
+{
+ if (!isX11MonitorThreadRunning())
+ return VINF_SUCCESS;
+
+ mX11MonitorThreadStopping = true;
+ if (!mX11Monitor.interruptEventWait())
+ {
+ VBClLogError("Unable to notify X11 monitor thread\n");
+ return VERR_INVALID_STATE;
+ }
+
+ int rcThread;
+ int rc = RTThreadWait(mX11MonitorThread, RT_MS_30SEC, &rcThread);
+ if (RT_SUCCESS(rc))
+ rc = rcThread;
+
+ if (RT_SUCCESS(rc))
+ {
+ mX11MonitorThread = NIL_RTTHREAD;
+ }
+ else
+ VBClLogError("Waiting for X11 monitor thread to stop failed, rc=%Rrc\n", rc);
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{VBCLSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vbclSeamlessInit(void)
+{
+ return g_Svc.mSeamless.init();
+}
+
+/**
+ * @interface_method_impl{VBCLSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vbclSeamlessWorker(bool volatile *pfShutdown)
+{
+ return g_Svc.mSeamless.worker(pfShutdown);
+}
+
+/**
+ * @interface_method_impl{VBCLSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vbclSeamlessStop(void)
+{
+ return g_Svc.mSeamless.stop();
+}
+
+/**
+ * @interface_method_impl{VBCLSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(int) vbclSeamlessTerm(void)
+{
+ return g_Svc.mSeamless.term();
+}
+
+VBCLSERVICE g_SvcSeamless =
+{
+ "seamless", /* szName */
+ "Seamless Mode Support", /* pszDescription */
+ ".vboxclient-seamless", /* pszPidFilePathTemplate */
+ NULL, /* pszUsage */
+ NULL, /* pszOptions */
+ NULL, /* pfnOption */
+ vbclSeamlessInit, /* pfnInit */
+ vbclSeamlessWorker, /* pfnWorker */
+ vbclSeamlessStop, /* pfnStop*/
+ vbclSeamlessTerm /* pfnTerm */
+};
+