/* $Id: display.cpp 41207 2012-05-08 13:53:03Z vboxsync $ */ /** @file * X11 guest client - display management. */ /* * Copyright (C) 2006-2007 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. */ /** @todo this should probably be replaced by something IPRT */ /* For system() and WEXITSTATUS() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "VBoxClient.h" static int initDisplay(Display *pDisplay) { int rc = VINF_SUCCESS; uint32_t fMouseFeatures = 0; LogRelFlowFunc(("testing dynamic resizing\n")); int iDummy; if (!XRRQueryExtension(pDisplay, &iDummy, &iDummy)) rc = VERR_NOT_SUPPORTED; if (RT_SUCCESS(rc)) rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0); else VbglR3CtlFilterMask(0, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST); /* Log and ignore the return value, as there is not much we can do with * it. */ LogRelFlowFunc(("dynamic resizing: result %Rrc\n", rc)); /* Enable support for switching between hardware and software cursors */ LogRelFlowFunc(("enabling relative mouse re-capturing support\n")); rc = VbglR3GetMouseStatus(&fMouseFeatures, NULL, NULL); if (RT_SUCCESS(rc)) { rc = VbglR3CtlFilterMask(VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED, 0); if (RT_SUCCESS(rc)) rc = VbglR3SetMouseStatus ( fMouseFeatures & ~VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR); } if (RT_FAILURE(rc)) { VbglR3CtlFilterMask(0, VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED); VbglR3SetMouseStatus( fMouseFeatures | VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR); } LogRelFlowFunc(("mouse re-capturing support: result %Rrc\n", rc)); return VINF_SUCCESS; } void cleanupDisplay(void) { uint32_t fMouseFeatures = 0; LogRelFlowFunc(("\n")); VbglR3CtlFilterMask(0, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED); int rc = VbglR3GetMouseStatus(&fMouseFeatures, NULL, NULL); if (RT_SUCCESS(rc)) VbglR3SetMouseStatus( fMouseFeatures | VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR); LogRelFlowFunc(("returning\n")); } /** This thread just runs a dummy X11 event loop to be sure that we get * terminated should the X server exit. */ static int x11ConnectionMonitor(RTTHREAD, void *) { XEvent ev; Display *pDisplay = XOpenDisplay(NULL); while (true) XNextEvent(pDisplay, &ev); return 0; } /** * This method first resets the current resolution using RandR to wake up * the graphics driver, then sets the resolution requested if it is among * those offered by the driver. */ static void setSize(Display *pDisplay, uint32_t cx, uint32_t cy) { XRRScreenConfiguration *pConfig; XRRScreenSize *pSizes; int cSizes; pConfig = XRRGetScreenInfo(pDisplay, DefaultRootWindow(pDisplay)); /* Reset the current mode */ LogRelFlowFunc(("Setting size %ux%u\n", cx, cy)); if (pConfig) { pSizes = XRRConfigSizes(pConfig, &cSizes); unsigned uDist = UINT32_MAX; int iMode = -1; for (int i = 0; i < cSizes; ++i) { #define VBCL_SQUARE(x) (x) * (x) unsigned uThisDist = VBCL_SQUARE(pSizes[i].width - cx) + VBCL_SQUARE(pSizes[i].height - cy); LogRelFlowFunc(("Found size %dx%d, distance %u\n", pSizes[i].width, pSizes[i].height, uThisDist)); #undef VBCL_SQUARE if (uThisDist < uDist) { uDist = uThisDist; iMode = i; } } if (iMode >= 0) { Time config_timestamp = 0; XRRConfigTimes(pConfig, &config_timestamp); LogRelFlowFunc(("Setting new size %d\n", iMode)); XRRSetScreenConfig(pDisplay, pConfig, DefaultRootWindow(pDisplay), iMode, RR_Rotate_0, config_timestamp); } XRRFreeScreenConfigInfo(pConfig); } } /** * Display change request monitor thread function. * Before entering the loop, we re-read the last request * received, and if the first one received inside the * loop is identical we ignore it, because it is probably * stale. */ static int runDisplay(Display *pDisplay) { LogRelFlowFunc(("\n")); Cursor hClockCursor = XCreateFontCursor(pDisplay, XC_watch); Cursor hArrowCursor = XCreateFontCursor(pDisplay, XC_left_ptr); int RRMaj, RRMin; if (!XRRQueryVersion(pDisplay, &RRMaj, &RRMin)) RRMin = 0; const char *pcszXrandr = "xrandr"; if (RTFileExists("/usr/X11/bin/xrandr")) pcszXrandr = "/usr/X11/bin/xrandr"; int rc = RTThreadCreate(NULL, x11ConnectionMonitor, NULL, 0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "X11 monitor"); if (RT_FAILURE(rc)) return rc; while (true) { uint32_t fEvents = 0, cx = 0, cy = 0, cBits = 0, iDisplay = 0; rc = VbglR3WaitEvent( VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED, RT_INDEFINITE_WAIT, &fEvents); if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED) /* VERR_NO_MEMORY? */ return rc; /* Jiggle the mouse pointer to wake up the driver. */ XGrabPointer(pDisplay, DefaultRootWindow(pDisplay), true, 0, GrabModeAsync, GrabModeAsync, None, hClockCursor, CurrentTime); XFlush(pDisplay); XGrabPointer(pDisplay, DefaultRootWindow(pDisplay), true, 0, GrabModeAsync, GrabModeAsync, None, hArrowCursor, CurrentTime); XFlush(pDisplay); XUngrabPointer(pDisplay, CurrentTime); XFlush(pDisplay); /* And if it is a size hint, set the new size now that the video * driver has had a chance to update its list. */ if (RT_SUCCESS(rc) && (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)) { int rc2 = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBits, &iDisplay, true); /* If we are not stopping, sleep for a bit to avoid using up too much CPU while retrying. */ if (RT_FAILURE(rc2)) RTThreadYield(); else if (RRMin < 2) setSize(pDisplay, cx, cy); else { char szCommand[256]; RTStrPrintf(szCommand, sizeof(szCommand), "%s --output VBOX%u --set VBOX_MODE %dx%d", pcszXrandr, iDisplay, cx, cy); system(szCommand); RTStrPrintf(szCommand, sizeof(szCommand), "%s --output VBOX%u --preferred", pcszXrandr, iDisplay); system(szCommand); } } } LogRelFlowFunc(("returning VINF_SUCCESS\n")); return VINF_SUCCESS; } class DisplayService : public VBoxClient::Service { public: virtual const char *getPidFilePath() { return ".vboxclient-display.pid"; } virtual int run(bool fDaemonised /* = false */) { Display *pDisplay = XOpenDisplay(NULL); if (!pDisplay) return VERR_NOT_FOUND; int rc = initDisplay(pDisplay); if (RT_SUCCESS(rc)) rc = runDisplay(pDisplay); XCloseDisplay(pDisplay); return rc; } virtual void cleanup() { cleanupDisplay(); } }; VBoxClient::Service *VBoxClient::GetDisplayService() { return new DisplayService; }