seamless.cpp revision 5b5c073298425e5aa1744546e2ca8590cacbcb5b
ece9652d971886b99a269656ea4782319637e75avboxsync/** @file
ece9652d971886b99a269656ea4782319637e75avboxsync * X11 Guest client - seamless mode: main logic, communication with the host and
ece9652d971886b99a269656ea4782319637e75avboxsync * wrapper interface for the main code of the VBoxClient deamon. The
ece9652d971886b99a269656ea4782319637e75avboxsync * X11-specific parts are split out into their own file for ease of testing.
ece9652d971886b99a269656ea4782319637e75avboxsync */
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsync/*
ece9652d971886b99a269656ea4782319637e75avboxsync * Copyright (C) 2006-2014 Oracle Corporation
ece9652d971886b99a269656ea4782319637e75avboxsync *
ece9652d971886b99a269656ea4782319637e75avboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
ece9652d971886b99a269656ea4782319637e75avboxsync * available from http://www.virtualbox.org. This file is free software;
ece9652d971886b99a269656ea4782319637e75avboxsync * you can redistribute it and/or modify it under the terms of the GNU
ece9652d971886b99a269656ea4782319637e75avboxsync * General Public License (GPL) as published by the Free Software
ece9652d971886b99a269656ea4782319637e75avboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
ece9652d971886b99a269656ea4782319637e75avboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
ece9652d971886b99a269656ea4782319637e75avboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
ece9652d971886b99a269656ea4782319637e75avboxsync */
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsync/*****************************************************************************
ece9652d971886b99a269656ea4782319637e75avboxsync* Header files *
ece9652d971886b99a269656ea4782319637e75avboxsync*****************************************************************************/
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsync#include <X11/Xlib.h>
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsync#include <VBox/log.h>
ece9652d971886b99a269656ea4782319637e75avboxsync#include <VBox/VMMDev.h>
e7f5b62e52275099a4d14501306063e23876b771vboxsync#include <VBox/VBoxGuestLib.h>
e7f5b62e52275099a4d14501306063e23876b771vboxsync#include <iprt/err.h>
e7f5b62e52275099a4d14501306063e23876b771vboxsync#include <iprt/mem.h>
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsync#include "VBoxClient.h"
ece9652d971886b99a269656ea4782319637e75avboxsync#include "seamless.h"
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsync#include <new>
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsyncSeamlessMain::SeamlessMain(void)
ece9652d971886b99a269656ea4782319637e75avboxsync{
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync mX11MonitorThread = NIL_RTTHREAD;
e7f5b62e52275099a4d14501306063e23876b771vboxsync mX11MonitorThreadStopping = false;
ece9652d971886b99a269656ea4782319637e75avboxsync mMode = VMMDev_Seamless_Disabled;
ece9652d971886b99a269656ea4782319637e75avboxsync mfPaused = false;
e7f5b62e52275099a4d14501306063e23876b771vboxsync}
e7f5b62e52275099a4d14501306063e23876b771vboxsync
e7f5b62e52275099a4d14501306063e23876b771vboxsyncSeamlessMain::~SeamlessMain()
e7f5b62e52275099a4d14501306063e23876b771vboxsync{
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("\n"));
e7f5b62e52275099a4d14501306063e23876b771vboxsync stop();
ece9652d971886b99a269656ea4782319637e75avboxsync}
ece9652d971886b99a269656ea4782319637e75avboxsync
e7f5b62e52275099a4d14501306063e23876b771vboxsync/**
e7f5b62e52275099a4d14501306063e23876b771vboxsync * Update the set of visible rectangles in the host.
e7f5b62e52275099a4d14501306063e23876b771vboxsync */
e7f5b62e52275099a4d14501306063e23876b771vboxsyncstatic void sendRegionUpdate(RTRECT *pRects, size_t cRects)
e7f5b62e52275099a4d14501306063e23876b771vboxsync{
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync if (cRects && !pRects) /* Assertion */
ece9652d971886b99a269656ea4782319637e75avboxsync {
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFunc(("ERROR: called with null pointer!\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync return;
ece9652d971886b99a269656ea4782319637e75avboxsync }
ece9652d971886b99a269656ea4782319637e75avboxsync VbglR3SeamlessSendRects(cRects, pRects);
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("returning\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync}
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsync/**
e7f5b62e52275099a4d14501306063e23876b771vboxsync * initialise the service.
e7f5b62e52275099a4d14501306063e23876b771vboxsync */
ece9652d971886b99a269656ea4782319637e75avboxsyncint SeamlessMain::init(void)
ece9652d971886b99a269656ea4782319637e75avboxsync{
ece9652d971886b99a269656ea4782319637e75avboxsync int rc;
ece9652d971886b99a269656ea4782319637e75avboxsync const char *pcszStage;
ece9652d971886b99a269656ea4782319637e75avboxsync
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync do {
ece9652d971886b99a269656ea4782319637e75avboxsync pcszStage = "Connecting to the X server";
ece9652d971886b99a269656ea4782319637e75avboxsync rc = mX11Monitor.init(sendRegionUpdate);
1cc6d0ca9b70d90116a4fb8f7e60869cc98ad57cvboxsync if (RT_FAILURE(rc))
e7f5b62e52275099a4d14501306063e23876b771vboxsync break;
e7f5b62e52275099a4d14501306063e23876b771vboxsync pcszStage = "Setting guest IRQ filter mask";
e7f5b62e52275099a4d14501306063e23876b771vboxsync rc = VbglR3CtlFilterMask(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 0);
e7f5b62e52275099a4d14501306063e23876b771vboxsync if (RT_FAILURE(rc))
e7f5b62e52275099a4d14501306063e23876b771vboxsync break;
e7f5b62e52275099a4d14501306063e23876b771vboxsync pcszStage = "Reporting support for seamless capability";
e7f5b62e52275099a4d14501306063e23876b771vboxsync rc = VbglR3SeamlessSetCap(true);
e7f5b62e52275099a4d14501306063e23876b771vboxsync if (RT_FAILURE(rc))
e7f5b62e52275099a4d14501306063e23876b771vboxsync break;
e7f5b62e52275099a4d14501306063e23876b771vboxsync } while(0);
e7f5b62e52275099a4d14501306063e23876b771vboxsync if (RT_FAILURE(rc))
e7f5b62e52275099a4d14501306063e23876b771vboxsync LogRel(("VBoxClient (seamless): failed to start. Stage: \"%s\" Error: %Rrc\n",
e7f5b62e52275099a4d14501306063e23876b771vboxsync pcszStage, rc));
e7f5b62e52275099a4d14501306063e23876b771vboxsync return rc;
e7f5b62e52275099a4d14501306063e23876b771vboxsync}
e7f5b62e52275099a4d14501306063e23876b771vboxsync
e7f5b62e52275099a4d14501306063e23876b771vboxsync/**
e7f5b62e52275099a4d14501306063e23876b771vboxsync * Run the main service thread which listens for host state change
e7f5b62e52275099a4d14501306063e23876b771vboxsync * notifications.
e7f5b62e52275099a4d14501306063e23876b771vboxsync * @returns iprt status value. Service will be set to the stopped state on
e7f5b62e52275099a4d14501306063e23876b771vboxsync * failure.
e7f5b62e52275099a4d14501306063e23876b771vboxsync */
e7f5b62e52275099a4d14501306063e23876b771vboxsyncint SeamlessMain::run(void)
e7f5b62e52275099a4d14501306063e23876b771vboxsync{
e7f5b62e52275099a4d14501306063e23876b771vboxsync int rc = VINF_SUCCESS;
e7f5b62e52275099a4d14501306063e23876b771vboxsync
e7f5b62e52275099a4d14501306063e23876b771vboxsync LogRelFlowFunc(("\n"));
e7f5b62e52275099a4d14501306063e23876b771vboxsync /* This will only exit if something goes wrong. */
e7f5b62e52275099a4d14501306063e23876b771vboxsync while (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED)
e7f5b62e52275099a4d14501306063e23876b771vboxsync {
e7f5b62e52275099a4d14501306063e23876b771vboxsync if (RT_FAILURE(rc))
e7f5b62e52275099a4d14501306063e23876b771vboxsync /* If we are not stopping, sleep for a bit to avoid using up too
e7f5b62e52275099a4d14501306063e23876b771vboxsync much CPU while retrying. */
e7f5b62e52275099a4d14501306063e23876b771vboxsync RTThreadYield();
e7f5b62e52275099a4d14501306063e23876b771vboxsync rc = nextStateChangeEvent();
e7f5b62e52275099a4d14501306063e23876b771vboxsync }
e7f5b62e52275099a4d14501306063e23876b771vboxsync if (RT_FAILURE(rc))
e7f5b62e52275099a4d14501306063e23876b771vboxsync {
ece9652d971886b99a269656ea4782319637e75avboxsync LogRel(("VBoxClient (seamless): event loop failed with error: %Rrc\n",
ece9652d971886b99a269656ea4782319637e75avboxsync rc));
ece9652d971886b99a269656ea4782319637e75avboxsync stop();
1cc6d0ca9b70d90116a4fb8f7e60869cc98ad57cvboxsync }
1cc6d0ca9b70d90116a4fb8f7e60869cc98ad57cvboxsync return rc;
1cc6d0ca9b70d90116a4fb8f7e60869cc98ad57cvboxsync}
1cc6d0ca9b70d90116a4fb8f7e60869cc98ad57cvboxsync
e7f5b62e52275099a4d14501306063e23876b771vboxsync/** Stops the service. */
ece9652d971886b99a269656ea4782319637e75avboxsyncvoid SeamlessMain::stop()
ece9652d971886b99a269656ea4782319637e75avboxsync{
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync VbglR3SeamlessSetCap(false);
ece9652d971886b99a269656ea4782319637e75avboxsync VbglR3CtlFilterMask(0, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST);
ece9652d971886b99a269656ea4782319637e75avboxsync stopX11MonitorThread();
ece9652d971886b99a269656ea4782319637e75avboxsync mX11Monitor.uninit();
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("returning\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync}
ece9652d971886b99a269656ea4782319637e75avboxsync
e7f5b62e52275099a4d14501306063e23876b771vboxsync/**
ece9652d971886b99a269656ea4782319637e75avboxsync * Waits for a seamless state change events from the host and dispatch it.
e7f5b62e52275099a4d14501306063e23876b771vboxsync *
e7f5b62e52275099a4d14501306063e23876b771vboxsync * @returns IRPT return code.
e7f5b62e52275099a4d14501306063e23876b771vboxsync */
e7f5b62e52275099a4d14501306063e23876b771vboxsyncint SeamlessMain::nextStateChangeEvent(void)
e7f5b62e52275099a4d14501306063e23876b771vboxsync{
e7f5b62e52275099a4d14501306063e23876b771vboxsync VMMDevSeamlessMode newMode = VMMDev_Seamless_Disabled;
e7f5b62e52275099a4d14501306063e23876b771vboxsync
e7f5b62e52275099a4d14501306063e23876b771vboxsync LogRelFlowFunc(("\n"));
e7f5b62e52275099a4d14501306063e23876b771vboxsync int rc = VbglR3SeamlessWaitEvent(&newMode);
e7f5b62e52275099a4d14501306063e23876b771vboxsync if (RT_SUCCESS(rc))
e7f5b62e52275099a4d14501306063e23876b771vboxsync {
e7f5b62e52275099a4d14501306063e23876b771vboxsync mMode = newMode;
e7f5b62e52275099a4d14501306063e23876b771vboxsync switch (newMode)
e7f5b62e52275099a4d14501306063e23876b771vboxsync {
ece9652d971886b99a269656ea4782319637e75avboxsync case VMMDev_Seamless_Visible_Region:
ece9652d971886b99a269656ea4782319637e75avboxsync /* A simplified seamless mode, obtained by making the host VM window
ece9652d971886b99a269656ea4782319637e75avboxsync * borderless and making the guest desktop transparent. */
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("\"Visible region\" mode requested (VBoxClient).\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync break;
ece9652d971886b99a269656ea4782319637e75avboxsync case VMMDev_Seamless_Disabled:
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFlowFunc(("\"Disabled\" mode requested (VBoxClient).\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync break;
ece9652d971886b99a269656ea4782319637e75avboxsync case VMMDev_Seamless_Host_Window:
ece9652d971886b99a269656ea4782319637e75avboxsync /* One host window represents one guest window. Not yet implemented. */
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFunc(("Unsupported \"host window\" mode requested (VBoxClient).\n"));
ece9652d971886b99a269656ea4782319637e75avboxsync return VERR_NOT_SUPPORTED;
ece9652d971886b99a269656ea4782319637e75avboxsync default:
ece9652d971886b99a269656ea4782319637e75avboxsync LogRelFunc(("Unsupported mode %d requested (VBoxClient).\n",
ece9652d971886b99a269656ea4782319637e75avboxsync newMode));
ece9652d971886b99a269656ea4782319637e75avboxsync return VERR_NOT_SUPPORTED;
ece9652d971886b99a269656ea4782319637e75avboxsync }
ece9652d971886b99a269656ea4782319637e75avboxsync }
e7f5b62e52275099a4d14501306063e23876b771vboxsync if (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN)
e7f5b62e52275099a4d14501306063e23876b771vboxsync {
e7f5b62e52275099a4d14501306063e23876b771vboxsync if (mMode == VMMDev_Seamless_Visible_Region && !mfPaused)
e7f5b62e52275099a4d14501306063e23876b771vboxsync /* This does it's own logging on failure. */
e7f5b62e52275099a4d14501306063e23876b771vboxsync rc = startX11MonitorThread();
e7f5b62e52275099a4d14501306063e23876b771vboxsync else
e7f5b62e52275099a4d14501306063e23876b771vboxsync /* This does it's own logging on failure. */
e7f5b62e52275099a4d14501306063e23876b771vboxsync rc = stopX11MonitorThread();
e7f5b62e52275099a4d14501306063e23876b771vboxsync }
e7f5b62e52275099a4d14501306063e23876b771vboxsync else
e7f5b62e52275099a4d14501306063e23876b771vboxsync {
e7f5b62e52275099a4d14501306063e23876b771vboxsync LogRelFunc(("VbglR3SeamlessWaitEvent returned %Rrc (VBoxClient)\n", rc));
e7f5b62e52275099a4d14501306063e23876b771vboxsync }
e7f5b62e52275099a4d14501306063e23876b771vboxsync LogRelFlowFunc(("returning %Rrc\n", rc));
e7f5b62e52275099a4d14501306063e23876b771vboxsync return rc;
e7f5b62e52275099a4d14501306063e23876b771vboxsync}
e7f5b62e52275099a4d14501306063e23876b771vboxsync
ece9652d971886b99a269656ea4782319637e75avboxsyncint SeamlessMain::cancelEvent(void)
{
return VbglR3InterruptEventWaits();
}
/**
* The actual X11 window configuration change monitor thread function.
*/
int SeamlessMain::x11MonitorThread(RTTHREAD self, void *pvUser)
{
SeamlessMain *pHost = (SeamlessMain *)pvUser;
int rc = VINF_SUCCESS;
LogRelFlowFunc(("\n"));
rc = pHost->mX11Monitor.start();
if (RT_SUCCESS(rc))
{
while (!pHost->mX11MonitorThreadStopping)
pHost->mX11Monitor.nextConfigurationEvent();
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;
}
/** Pause the service loop. */
int SeamlessMain::pause()
{
int rc;
const char *pcszStage;
LogRelFlowFunc(("\n"));
mfPaused = true;
do {
pcszStage = "Reporting end of support for seamless capability";
rc = VbglR3SeamlessSetCap(false);
if (RT_FAILURE(rc))
break;
pcszStage = "Interrupting the event loop";
rc = cancelEvent();
if (RT_FAILURE(rc))
break;
} while (0);
if (RT_FAILURE(rc))
LogRelFunc(("Failure. Stage: \"%s\" Error: %Rrc (VBoxClient)\n",
pcszStage, rc));
return rc;
}
/** Resume after pausing. */
int SeamlessMain::resume()
{
int rc;
const char *pcszStage;
LogRelFlowFunc(("\n"));
mfPaused = false;
do {
pcszStage = "Reporting support for seamless capability";
rc = VbglR3SeamlessSetCap(true);
if (RT_FAILURE(rc))
break;
pcszStage = "Interrupting the event loop";
rc = cancelEvent();
if (RT_FAILURE(rc))
break;
} while (0);
if (RT_FAILURE(rc))
LogRelFunc(("Failure. Stage: \"%s\" Error: %Rrc (VBoxClient)\n",
pcszStage, rc));
return rc;
}
/** @todo Expand this? */
int SeamlessMain::selfTest()
{
int rc = VERR_INTERNAL_ERROR;
const char *pcszStage;
LogRelFlowFunc(("\n"));
do {
pcszStage = "Testing event loop cancellation";
VbglR3InterruptEventWaits();
if (RT_FAILURE(VbglR3WaitEvent(VMMDEV_EVENT_VALID_EVENT_MASK, 0, NULL)))
break;
if ( VbglR3WaitEvent(VMMDEV_EVENT_VALID_EVENT_MASK, 0, NULL)
!= VERR_TIMEOUT)
break;
rc = VINF_SUCCESS;
} while(0);
if (RT_FAILURE(rc))
LogRel(("VBoxClient (seamless): self test failed. Stage: \"%s\"\n",
pcszStage));
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;
rc = pSelf->mSeamless.init();
if (RT_FAILURE(rc))
return rc;
rc = pSelf->mSeamless.selfTest();
if (RT_FAILURE(rc))
{
pSelf->mSeamless.stop();
return rc;
}
pSelf->mIsInitialised = true;
return VINF_SUCCESS;
}
static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
{
struct SEAMLESSSERVICE *pSelf = getClassFromInterface(ppInterface);
int rc;
if (!pSelf->mIsInitialised)
return VERR_INTERNAL_ERROR;
rc = VBClStartVTMonitor();
if (RT_FAILURE(rc))
VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
/* This only exits on error. */
rc = pSelf->mSeamless.run();
pSelf->mIsInitialised = false;
return rc;
}
static int pause(struct VBCLSERVICE **ppInterface)
{
struct SEAMLESSSERVICE *pSelf = getClassFromInterface(ppInterface);
return pSelf->mSeamless.pause();
}
static int resume(struct VBCLSERVICE **ppInterface)
{
struct SEAMLESSSERVICE *pSelf = getClassFromInterface(ppInterface);
return pSelf->mSeamless.resume();
}
static void cleanup(struct VBCLSERVICE **ppInterface)
{
NOREF(ppInterface);
VbglR3SeamlessSetCap(false);
}
struct VBCLSERVICE vbclSeamlessInterface =
{
getPidFilePath,
init,
run,
pause,
resume,
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;
}