display.cpp revision a5f07c4bc554dddeed320b485b68ccd6b3448be9
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/* $Id$ */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/** @file
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * X11 guest client - display management.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/*
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * Copyright (C) 2006-2012 Oracle Corporation
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync *
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * available from http://www.virtualbox.org. This file is free software;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * you can redistribute it and/or modify it under the terms of the GNU
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * General Public License (GPL) as published by the Free Software
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/** @todo this should probably be replaced by something IPRT */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/* For system() and WEXITSTATUS() */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#include <stdlib.h>
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#include <sys/types.h>
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#include <sys/wait.h>
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync#include <errno.h>
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync#include <X11/Xlib.h>
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync#include <X11/Xatom.h>
b0db50948c349fa76655abf252f7946b515e8204vboxsync
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync#include <iprt/assert.h>
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#include <iprt/err.h>
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync#include <iprt/file.h>
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#include <iprt/mem.h>
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#include <iprt/string.h>
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync#include <iprt/thread.h>
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync#include <VBox/log.h>
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#include <VBox/VMMDev.h>
f5e53763b0a581b0299e98028c6c52192eb06785vboxsync#include <VBox/VBoxGuestLib.h>
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync#include "VBoxClient.h"
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync/* TESTING: Dynamic resizing and mouse integration toggling should work
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * correctly with a range of X servers (pre-1.3, 1.3 and later under Linux, 1.3
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * and later under Solaris) with Guest Additions installed. Switching to a
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * virtual terminal while a user session is in place should disable dynamic
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * resizing and cursor integration, switching back should re-enable them. */
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync/** Most recent information received for a particular screen. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstruct screenInformation
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync{
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync unsigned cx;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync unsigned cy;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync unsigned cBPP;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync unsigned x;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync unsigned y;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync bool fEnabled;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync bool fUpdateSize;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync bool fUpdatePosition;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync};
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/** Display magic number, start of a UUID. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#define DISPLAYSTATE_MAGIC UINT32_C(0xf0029993)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/** State information needed for the service. The main VBoxClient code provides
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * the daemon logic needed by all services. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstruct DISPLAYSTATE
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync{
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** The service interface. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync struct VBCLSERVICE *pInterface;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** Magic number for sanity checks. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync uint32_t magic;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** Are we initialised yet? */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync bool mfInit;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** The connection to the server. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync Display *pDisplay;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** Can we use version 1.2 or later of the RandR protocol here? */
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync bool fHaveRandR12;
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync /** The command argument to use for the xrandr binary. Currently only
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * used to support the non-standard location on some Solaris systems -
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * would it make sense to use absolute paths on all systems? */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync const char *pcszXrandr;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** The number of screens we are currently aware of. */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync unsigned cScreensTracked;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync /** Array of information about different screens. */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync struct screenInformation *paScreenInformation;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync};
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync/** Tell the VBoxGuest driver we no longer want any events and tell the host
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * we no longer support any capabilities. */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic int disableEventsAndCaps()
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync{
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync int rc = VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS);
36411046d85fccaa66061120a064225fd1b5ae01vboxsync if (RT_FAILURE(rc))
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsync VBClFatalError(("Failed to unset graphics capability, rc=%Rrc.\n", rc));
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsync rc = VbglR3SetMouseStatus(VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
0c4004948fca34f2db87e7b38013137e9472c306vboxsync if (RT_FAILURE(rc))
0c4004948fca34f2db87e7b38013137e9472c306vboxsync VBClFatalError(("Failed to unset mouse status, rc=%Rrc.\n", rc));
0c4004948fca34f2db87e7b38013137e9472c306vboxsync rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync | VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync if (RT_FAILURE(rc))
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync VBClFatalError(("Failed to unset filter mask, rc=%Rrc.\n", rc));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync return VINF_SUCCESS;
44a2ecaf2d0fc196ab76cab13b3f909299e386d1vboxsync}
44a2ecaf2d0fc196ab76cab13b3f909299e386d1vboxsync
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync/** Tell the VBoxGuest driver which events we want and tell the host which
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync * capabilities we support. */
65697a26b524640b83828b715160c798c43a0424vboxsyncstatic int enableEventsAndCaps()
65697a26b524640b83828b715160c798c43a0424vboxsync{
6f76f373e9274aa29ec1485be3bfebafb670af0evboxsync int rc = VbglR3CtlFilterMask( VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED
6f76f373e9274aa29ec1485be3bfebafb670af0evboxsync | VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync if (RT_FAILURE(rc))
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync VBClFatalError(("Failed to set filter mask, rc=%Rrc.\n", rc));
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync rc = VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync if (RT_FAILURE(rc))
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync VBClFatalError(("Failed to set graphics capability, rc=%Rrc.\n", rc));
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync rc = VbglR3SetMouseStatus(0);
0a3cfc177eb2cca0805ca23cf5f98b0080b2ec21vboxsync if (RT_FAILURE(rc))
0a3cfc177eb2cca0805ca23cf5f98b0080b2ec21vboxsync VBClFatalError(("Failed to set mouse status, rc=%Rrc.\n", rc));
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync return VINF_SUCCESS;
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync}
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
0381007ae929f1a0885e69644b7d586a1dbb3a2avboxsyncstatic int initDisplay(struct DISPLAYSTATE *pState)
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync{
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync char szCommand[256];
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync int status;
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync /* Initialise the guest library. */
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync int rc = VbglR3InitUser();
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync if (RT_FAILURE(rc))
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync pState->pDisplay = XOpenDisplay(NULL);
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync if (!pState->pDisplay)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync return VERR_NOT_FOUND;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync pState->fHaveRandR12 = false;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->pcszXrandr = "xrandr";
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (RTFileExists("/usr/X11/bin/xrandr"))
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync pState->pcszXrandr = "/usr/X11/bin/xrandr";
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync status = system(pState->pcszXrandr);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync VBClFatalError(("Failed to execute the xrandr utility.\n"));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync RTStrPrintf(szCommand, sizeof(szCommand), "%s --q12", pState->pcszXrandr);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync status = system(szCommand);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (WEXITSTATUS(status) == 0)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->fHaveRandR12 = true;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync pState->cScreensTracked = 0;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync pState->paScreenInformation = NULL;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync return VINF_SUCCESS;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync}
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsyncstatic void updateScreenInformation(struct DISPLAYSTATE *pState, unsigned cx, unsigned cy, unsigned cBPP, unsigned iDisplay,
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync unsigned x, unsigned y, bool fEnabled, bool fUpdatePosition)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync{
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync uint32_t i;
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync if (iDisplay >= pState->cScreensTracked)
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync {
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync pState->paScreenInformation =
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync (struct screenInformation *)RTMemRealloc(pState->paScreenInformation,
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync (iDisplay + 1) * sizeof(*pState->paScreenInformation));
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (!pState->paScreenInformation)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync VBClFatalError(("Failed to re-allocate screen information.\n"));
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync for (i = pState->cScreensTracked; i < iDisplay + 1; ++i)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync RT_ZERO(pState->paScreenInformation[i]);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->cScreensTracked = iDisplay + 1;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync }
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->paScreenInformation[iDisplay].cx = cx;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync pState->paScreenInformation[iDisplay].cy = cy;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->paScreenInformation[iDisplay].cBPP = cBPP;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync pState->paScreenInformation[iDisplay].x = x;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->paScreenInformation[iDisplay].y = y;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->paScreenInformation[iDisplay].fEnabled = fEnabled;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->paScreenInformation[iDisplay].fUpdateSize = true;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->paScreenInformation[iDisplay].fUpdatePosition = fUpdatePosition;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync}
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsyncstatic void updateSizeHintsProperty(struct DISPLAYSTATE *pState)
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync{
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync long *paSizeHints = (long *)RTMemTmpAllocZ(pState->cScreensTracked * sizeof(long));
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync unsigned i;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (paSizeHints == NULL)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync VBClFatalError(("Failed to allocate size hint property memory.\n"));
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync for (i = 0; i < pState->cScreensTracked; ++i)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync {
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if ( pState->paScreenInformation[i].fEnabled
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync && pState->paScreenInformation[i].cx != 0 && pState->paScreenInformation[i].cy != 0)
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync paSizeHints[i] = (pState->paScreenInformation[i].cx & 0x8fff) << 16 | (pState->paScreenInformation[i].cy & 0x8fff);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync else if (pState->paScreenInformation[i].cx != 0 && pState->paScreenInformation[i].cy != 0)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync paSizeHints[i] = -1;
93f91841f87620d1cb6d0238b3d0d5e52cd3b9a4vboxsync }
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOX_SIZE_HINTS", 0),
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync XA_INTEGER, 32, PropModeReplace, (unsigned char *)paSizeHints, pState->cScreensTracked);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync XFlush(pState->pDisplay);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync RTMemTmpFree(paSizeHints);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync}
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic void notifyXServer(struct DISPLAYSTATE *pState)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync{
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync char szCommand[256];
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync unsigned i;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync bool fUpdateInformation = false;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync /** @note The xrandr command can fail if something else accesses RandR at
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync * the same time. We just ignore failure for now and let the user try
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync * again as we do not know what someone else is doing. */
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync for (i = 0; i < pState->cScreensTracked; ++i)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync if (pState->paScreenInformation[i].fUpdateSize)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync fUpdateInformation = true;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if ( !pState->fHaveRandR12 && pState->paScreenInformation[0].fUpdateSize
65697a26b524640b83828b715160c798c43a0424vboxsync && pState->paScreenInformation[0].cx > 0 && pState->paScreenInformation[0].cy > 0)
65697a26b524640b83828b715160c798c43a0424vboxsync {
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u",
65697a26b524640b83828b715160c798c43a0424vboxsync pState->pcszXrandr, pState->paScreenInformation[0].cx, pState->paScreenInformation[0].cy);
65697a26b524640b83828b715160c798c43a0424vboxsync system(szCommand);
65697a26b524640b83828b715160c798c43a0424vboxsync pState->paScreenInformation[0].fUpdateSize = false;
65697a26b524640b83828b715160c798c43a0424vboxsync }
65697a26b524640b83828b715160c798c43a0424vboxsync else if (pState->fHaveRandR12 && fUpdateInformation)
65697a26b524640b83828b715160c798c43a0424vboxsync for (i = 0; i < pState->cScreensTracked; ++i)
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync {
65697a26b524640b83828b715160c798c43a0424vboxsync if (pState->paScreenInformation[i].fUpdateSize)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync {
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync RTStrPrintf(szCommand, sizeof(szCommand), "%s --output VGA-%u --preferred", pState->pcszXrandr, i);
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync system(szCommand);
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync }
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync if (pState->paScreenInformation[i].fUpdatePosition)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync {
65697a26b524640b83828b715160c798c43a0424vboxsync RTStrPrintf(szCommand, sizeof(szCommand), "%s --output VGA-%u --auto --pos %ux%u",
65697a26b524640b83828b715160c798c43a0424vboxsync pState->pcszXrandr, i, pState->paScreenInformation[i].x, pState->paScreenInformation[i].y);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync system(szCommand);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync }
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->paScreenInformation[i].fUpdateSize = pState->paScreenInformation[i].fUpdatePosition = false;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync }
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync else
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync {
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync RTStrPrintf(szCommand, sizeof(szCommand), "%s", pState->pcszXrandr);
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync system(szCommand);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync }
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync}
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic void updateMouseCapabilities(struct DISPLAYSTATE *pState)
65697a26b524640b83828b715160c798c43a0424vboxsync{
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync uint32_t fFeatures = 0;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync int rc;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync unsigned i;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync rc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync if (rc != VINF_SUCCESS)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync VBClFatalError(("Failed to get mouse status, rc=%Rrc\n", rc));
f687f34bd232be13744edbc0cc5155fa5d4540edvboxsync XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync XInternAtom(pState->pDisplay, "VBOX_MOUSE_CAPABILITIES", 0), XA_INTEGER, 32, PropModeReplace,
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync (unsigned char *)&fFeatures, 1);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync XFlush(pState->pDisplay);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (pState->fHaveRandR12)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync for (i = 0; i < pState->cScreensTracked; ++i)
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync pState->paScreenInformation[i].fUpdateSize = true;
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync else
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pState->paScreenInformation[0].fUpdateSize = true;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync}
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync/**
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync * Display change request monitor thread function.
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync */
ead016c68c61b5f2e1fe4d237054eebea9327d4bvboxsyncstatic void runDisplay(struct DISPLAYSTATE *pState)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync{
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync int status, rc;
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync unsigned i, cScreensTracked;
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync char szCommand[256];
7ccfefe49db4cd93c3701d7b60873ebf404b5b87vboxsync
7ccfefe49db4cd93c3701d7b60873ebf404b5b87vboxsync LogRelFlowFunc(("\n"));
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync rc = VbglR3VideoModeGetHighestSavedScreen(&cScreensTracked);
64f58e4154eaa20c47782b429eeaff09070369bfvboxsync if (rc != VINF_SUCCESS && rc != VERR_NOT_SUPPORTED)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync VBClFatalError(("Failed to get the number of saved screen modes, rc=%Rrc\n", rc));
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync /* Make sure that we have an entry for screen 1 at least. */
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync updateScreenInformation(pState, 1024, 768, 0, 1, 0, 0, true, false);
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync if (rc == VINF_SUCCESS)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync {
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync /* The "8" is for the sanity test below. */
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync for (i = 0; i < RT_MAX(cScreensTracked + 1, 8); ++i)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync {
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync unsigned cx = 0, cy = 0, cBPP = 0, x = 0, y = 0;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync bool fEnabled = true;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync rc = VbglR3RetrieveVideoMode(i, &cx, &cy, &cBPP, &x, &y,
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync &fEnabled);
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync /* Sanity test for VbglR3VideoModeGetHighestSavedScreen(). */
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync if (i > cScreensTracked && rc != VERR_NOT_FOUND)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync VBClFatalError(("Internal error retrieving the number of saved screen modes.\n"));
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync if (rc == VINF_SUCCESS)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync updateScreenInformation(pState, cx, cy, cBPP, i, x, y, fEnabled, true);
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync }
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync }
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync while (true)
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync {
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync uint32_t fEvents;
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync updateMouseCapabilities(pState);
eb8371e8297aff06a1982468835ff62e1ab77b30vboxsync updateSizeHintsProperty(pState);
64f58e4154eaa20c47782b429eeaff09070369bfvboxsync notifyXServer(pState);
7ccfefe49db4cd93c3701d7b60873ebf404b5b87vboxsync do
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync rc = VbglR3WaitEvent( VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
7ccfefe49db4cd93c3701d7b60873ebf404b5b87vboxsync | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED,
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync RT_INDEFINITE_WAIT, &fEvents);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync while(rc == VERR_INTERRUPTED);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync if (RT_FAILURE(rc)) /* VERR_NO_MEMORY? */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync VBClFatalError(("event wait failed, rc=%Rrc\n", rc));
65697a26b524640b83828b715160c798c43a0424vboxsync /* If it is a size hint, set the new size. */
65697a26b524640b83828b715160c798c43a0424vboxsync if (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
65697a26b524640b83828b715160c798c43a0424vboxsync {
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync uint32_t cx = 0, cy = 0, cBPP = 0, iDisplay = 0, x = 0, y = 0;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync bool fEnabled = true, fUpdatePosition = true;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync VMMDevSeamlessMode Mode;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBPP, &iDisplay,
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync &x, &y, &fEnabled,
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync &fUpdatePosition, true);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (rc != VINF_SUCCESS)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync VBClFatalError(("Failed to get display change request, rc=%Rrc\n",
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync rc));
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync else
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync LogRelFlowFunc(("Got size hint from host cx=%d, cy=%d, bpp=%d, iDisplay=%d, x=%d, y=%d fEnabled=%d\n",
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync cx, cy, cBPP, iDisplay, x, y, fEnabled));
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (iDisplay > INT32_MAX)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync VBClFatalError(("Received a size hint for too high display number %u\n",
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync (unsigned) iDisplay));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync updateScreenInformation(pState, cx, cy, cBPP, iDisplay, x, y, fEnabled, fUpdatePosition);
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync rc = VbglR3SeamlessGetLastEvent(&Mode);
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync if (RT_FAILURE(rc))
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync VBClFatalError(("Failed to check seamless mode, rc=%Rrc\n", rc));
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync if (Mode == VMMDev_Seamless_Disabled)
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync {
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync rc = VbglR3SaveVideoMode(iDisplay, cx, cy, cBPP, x, y,
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync fEnabled);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED)
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync VBClFatalError(("Failed to save size hint, rc=%Rrc\n", rc));
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync }
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync }
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync }
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync}
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsyncstatic const char *getPidFilePath()
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync{
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync return ".vboxclient-display.pid";
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync}
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsyncstatic struct DISPLAYSTATE *getStateFromInterface(struct VBCLSERVICE **ppInterface)
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync{
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync struct DISPLAYSTATE *pSelf = (struct DISPLAYSTATE *)ppInterface;
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync if (pSelf->magic != DISPLAYSTATE_MAGIC)
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync VBClFatalError(("Bad display service object!\n"));
f4aad55f8addd816ef005845842a2418bbdc3ea2vboxsync return pSelf;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync}
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic int init(struct VBCLSERVICE **ppInterface)
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync{
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync int rc;
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync if (pSelf->mfInit)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync return VERR_WRONG_ORDER;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync rc = initDisplay(pSelf);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (RT_FAILURE(rc))
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync return rc;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync rc = enableEventsAndCaps();
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (RT_SUCCESS(rc))
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync pSelf->mfInit = true;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync return rc;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync}
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsyncstatic int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync{
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync int rc;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (!pSelf->mfInit)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync return VERR_WRONG_ORDER;
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync rc = VBClStartVTMonitor();
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync if (RT_FAILURE(rc))
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync runDisplay(pSelf);
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync return VERR_INTERNAL_ERROR; /* "Should never reach here." */
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync}
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsyncstatic int pause(struct VBCLSERVICE **ppInterface)
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync{
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync if (!pSelf->mfInit)
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync return VERR_WRONG_ORDER;
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync return disableEventsAndCaps();
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync}
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic int resume(struct VBCLSERVICE **ppInterface)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync{
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync if (!pSelf->mfInit)
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync return VERR_WRONG_ORDER;
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync return enableEventsAndCaps();
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync}
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsyncstatic void cleanup(struct VBCLSERVICE **ppInterface)
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync{
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync NOREF(ppInterface);
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync disableEventsAndCaps();
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync VbglR3Term();
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync}
c2f2661efd8da5281e2a3af6ddd10e737d333909vboxsync
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsyncstruct VBCLSERVICE vbclDisplayInterface =
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync{
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync getPidFilePath,
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync init,
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync run,
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync pause,
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync resume,
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync cleanup
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync};
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsyncstruct VBCLSERVICE **VBClGetDisplayService()
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync{
96a7e06717e2d7398642eadb5ebab1bf13fbe2dbvboxsync struct DISPLAYSTATE *pService = (struct DISPLAYSTATE *)RTMemAlloc(sizeof(*pService));
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync if (!pService)
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync VBClFatalError(("Out of memory\n"));
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync pService->pInterface = &vbclDisplayInterface;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync pService->magic = DISPLAYSTATE_MAGIC;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync pService->mfInit = false;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync return &pService->pInterface;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync}
36411046d85fccaa66061120a064225fd1b5ae01vboxsync