summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/x11/VBoxClient/seamless.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Additions/x11/VBoxClient/seamless.cpp
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Additions/x11/VBoxClient/seamless.cpp')
-rw-r--r--src/VBox/Additions/x11/VBoxClient/seamless.cpp344
1 files changed, 344 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..62822bf7
--- /dev/null
+++ b/src/VBox/Additions/x11/VBoxClient/seamless.cpp
@@ -0,0 +1,344 @@
+/* $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-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header files *
+*********************************************************************************************************************************/
+#include <X11/Xlib.h>
+
+#include <VBox/log.h>
+#include <VBox/VBoxGuestLib.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+
+#include "VBoxClient.h"
+#include "seamless.h"
+
+#include <new>
+
+SeamlessMain::SeamlessMain(void)
+{
+ LogRelFlowFunc(("\n"));
+ mX11MonitorThread = NIL_RTTHREAD;
+ mX11MonitorThreadStopping = false;
+ mMode = VMMDev_Seamless_Disabled;
+ mfPaused = true;
+}
+
+SeamlessMain::~SeamlessMain()
+{
+ LogRelFlowFunc(("\n"));
+ stop();
+}
+
+/**
+ * Update the set of visible rectangles in the host.
+ */
+static void sendRegionUpdate(RTRECT *pRects, size_t cRects)
+{
+ LogRelFlowFunc(("\n"));
+ if (cRects && !pRects) /* Assertion */
+ {
+ LogRelFunc(("ERROR: called with null pointer!\n"));
+ return;
+ }
+ VbglR3SeamlessSendRects(cRects, pRects);
+ LogRelFlowFunc(("returning\n"));
+}
+
+/**
+ * initialise the service.
+ */
+int SeamlessMain::init(void)
+{
+ int rc;
+ const char *pcszStage;
+
+ LogRelFlowFunc(("\n"));
+ 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))
+ VBClFatalError(("VBoxClient (seamless): failed to start. Stage: \"%s\" Error: %Rrc\n",
+ pcszStage, rc));
+ return rc;
+}
+
+/**
+ * Run the main service thread which listens for host state change
+ * notifications.
+ * @returns iprt status value. Service will be set to the stopped state on
+ * failure.
+ */
+int SeamlessMain::run(void)
+{
+ int rc = VINF_SUCCESS;
+
+ LogRelFlowFunc(("\n"));
+ /* This will only exit if something goes wrong. */
+ while (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED)
+ {
+ if (RT_FAILURE(rc))
+ /* If we are not stopping, sleep for a bit to avoid using up too
+ much CPU while retrying. */
+ RTThreadYield();
+ rc = nextStateChangeEvent();
+ }
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VBoxClient (seamless): event loop failed with error: %Rrc\n",
+ rc));
+ stop();
+ }
+ return rc;
+}
+
+/** Stops the service. */
+void SeamlessMain::stop()
+{
+ LogRelFlowFunc(("\n"));
+ VbglR3SeamlessSetCap(false);
+ VbglR3CtlFilterMask(0, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST);
+ stopX11MonitorThread();
+ mX11Monitor.uninit();
+ LogRelFlowFunc(("returning\n"));
+}
+
+/**
+ * Waits for a seamless state change events from the host and dispatch it.
+ *
+ * @returns IRPT return code.
+ */
+int SeamlessMain::nextStateChangeEvent(void)
+{
+ VMMDevSeamlessMode newMode = VMMDev_Seamless_Disabled;
+
+ LogRelFlowFunc(("\n"));
+ 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. */
+ LogRelFlowFunc(("\"Visible region\" mode requested (VBoxClient).\n"));
+ break;
+ case VMMDev_Seamless_Disabled:
+ LogRelFlowFunc(("\"Disabled\" mode requested (VBoxClient).\n"));
+ break;
+ case VMMDev_Seamless_Host_Window:
+ /* One host window represents one guest window. Not yet implemented. */
+ LogRelFunc(("Unsupported \"host window\" mode requested (VBoxClient).\n"));
+ return VERR_NOT_SUPPORTED;
+ default:
+ LogRelFunc(("Unsupported mode %d requested (VBoxClient).\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
+ {
+ LogRelFunc(("VbglR3SeamlessWaitEvent returned %Rrc (VBoxClient)\n", rc));
+ }
+ LogRelFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * The actual X11 window configuration change monitor thread function.
+ */
+int SeamlessMain::x11MonitorThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF1(hThreadSelf);
+ SeamlessMain *pHost = (SeamlessMain *)pvUser;
+ int rc = VINF_SUCCESS;
+
+ LogRelFlowFunc(("\n"));
+ while (!pHost->mX11MonitorThreadStopping)
+ {
+ if (!pHost->mfPaused)
+ {
+ rc = pHost->mX11Monitor.start();
+ if (RT_FAILURE(rc))
+ VBClFatalError(("Failed to change the X11 seamless service state, mfPaused=%RTbool, rc=%Rrc\n",
+ pHost->mfPaused, rc));
+ }
+ pHost->mX11Monitor.nextConfigurationEvent();
+ if (pHost->mfPaused || pHost->mX11MonitorThreadStopping)
+ pHost->mX11Monitor.stop();
+ }
+ LogRelFlowFunc(("returning %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Start the X11 window configuration change monitor thread.
+ */
+int SeamlessMain::startX11MonitorThread(void)
+{
+ int rc;
+
+ mX11MonitorThreadStopping = false;
+ if (isX11MonitorThreadRunning())
+ return VINF_SUCCESS;
+ rc = RTThreadCreate(&mX11MonitorThread, x11MonitorThread, this, 0,
+ RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
+ "X11 events");
+ if (RT_FAILURE(rc))
+ LogRelFunc(("Warning: failed to start X11 monitor thread (VBoxClient).\n"));
+ return rc;
+}
+
+/**
+ * Send a signal to the thread function that it should exit
+ */
+int SeamlessMain::stopX11MonitorThread(void)
+{
+ int rc;
+
+ mX11MonitorThreadStopping = true;
+ if (!isX11MonitorThreadRunning())
+ return VINF_SUCCESS;
+ mX11Monitor.interruptEventWait();
+ rc = RTThreadWait(mX11MonitorThread, RT_INDEFINITE_WAIT, NULL);
+ if (RT_SUCCESS(rc))
+ mX11MonitorThread = NIL_RTTHREAD;
+ else
+ LogRelThisFunc(("Failed to stop X11 monitor thread, rc=%Rrc!\n",
+ rc));
+ return rc;
+}
+
+/** Service magic number, start of a UUID. */
+#define SEAMLESSSERVICE_MAGIC 0xd28ba727
+
+/** VBoxClient service class wrapping the logic for the seamless service while
+ * the main VBoxClient code provides the daemon logic needed by all services.
+ */
+struct SEAMLESSSERVICE
+{
+ /** The service interface. */
+ struct VBCLSERVICE *pInterface;
+ /** Magic number for sanity checks. */
+ uint32_t magic;
+ /** Seamless service object. */
+ SeamlessMain mSeamless;
+ /** Are we initialised yet? */
+ bool mIsInitialised;
+};
+
+static const char *getPidFilePath(void)
+{
+ return ".vboxclient-seamless.pid";
+}
+
+static struct SEAMLESSSERVICE *getClassFromInterface(struct VBCLSERVICE **
+ ppInterface)
+{
+ struct SEAMLESSSERVICE *pSelf = (struct SEAMLESSSERVICE *)ppInterface;
+ if (pSelf->magic != SEAMLESSSERVICE_MAGIC)
+ VBClFatalError(("Bad seamless service object!\n"));
+ return pSelf;
+}
+
+static int init(struct VBCLSERVICE **ppInterface)
+{
+ struct SEAMLESSSERVICE *pSelf = getClassFromInterface(ppInterface);
+ int rc;
+
+ if (pSelf->mIsInitialised)
+ return VERR_INTERNAL_ERROR;
+ /* Initialise the guest library. */
+ rc = VbglR3InitUser();
+ if (RT_FAILURE(rc))
+ VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
+ rc = pSelf->mSeamless.init();
+ if (RT_FAILURE(rc))
+ return rc;
+ pSelf->mIsInitialised = true;
+ return VINF_SUCCESS;
+}
+
+static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
+{
+ RT_NOREF1(fDaemonised);
+ struct SEAMLESSSERVICE *pSelf = getClassFromInterface(ppInterface);
+ int rc;
+
+ if (!pSelf->mIsInitialised)
+ return VERR_INTERNAL_ERROR;
+ /* This only exits on error. */
+ rc = pSelf->mSeamless.run();
+ pSelf->mIsInitialised = false;
+ return rc;
+}
+
+static void cleanup(struct VBCLSERVICE **ppInterface)
+{
+ NOREF(ppInterface);
+ VbglR3SeamlessSetCap(false);
+ VbglR3Term();
+}
+
+struct VBCLSERVICE vbclSeamlessInterface =
+{
+ getPidFilePath,
+ init,
+ run,
+ cleanup
+};
+
+struct VBCLSERVICE **VBClGetSeamlessService()
+{
+ struct SEAMLESSSERVICE *pService =
+ (struct SEAMLESSSERVICE *)RTMemAlloc(sizeof(*pService));
+
+ if (!pService)
+ VBClFatalError(("Out of memory\n"));
+ pService->pInterface = &vbclSeamlessInterface;
+ pService->magic = SEAMLESSSERVICE_MAGIC;
+ new(&pService->mSeamless) SeamlessMain();
+ pService->mIsInitialised = false;
+ return &pService->pInterface;
+}