VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/display.cpp@ 60378

Last change on this file since 60378 was 60083, checked in by vboxsync, 9 years ago

bugref:8288: Additions/x11: rework VBoxClient video mode hint handling: reworked the display parts of VBoxClient. Now they only use standard X11 mechanisms for any recent server version, which means that the same code works with the kernel and the user-space driver, and also wait for about two seconds before changing the mode, cancelling if some one else is faster, to avoid interfering with mechanisms provided by the desktop environment. With old servers we get mode hints from a property updated by the driver. The user space driver code has been updated to match, and has also dropped pre-HGSMI mechanisms for getting mode hints.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.0 KB
Line 
1/* $Id: display.cpp 60083 2016-03-17 18:40:28Z vboxsync $ */
2/** @file
3 * X11 guest client - display management.
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "VBoxClient.h"
19
20#include <iprt/err.h>
21#include <iprt/file.h>
22#include <iprt/mem.h>
23#include <iprt/string.h>
24
25#include <X11/Xlib.h>
26#include <X11/Xatom.h>
27#include <X11/extensions/Xrandr.h>
28
29/** @todo this should probably be replaced by something IPRT */
30/* For system() and WEXITSTATUS() */
31#include <stdlib.h>
32#include <sys/types.h>
33#include <sys/wait.h>
34#include <errno.h>
35#include <limits.h>
36#include <poll.h>
37#include <time.h>
38
39/* TESTING: Dynamic resizing and mouse integration toggling should work
40 * correctly with a range of X servers (pre-1.3, 1.3 and later under Linux, 1.3
41 * and later under Solaris) with Guest Additions installed. Switching to a
42 * virtual terminal while a user session is in place should disable dynamic
43 * resizing and cursor integration, switching back should re-enable them. */
44
45/** Display magic number, start of a UUID. */
46#define DISPLAYSTATE_MAGIC UINT32_C(0xf0029993)
47
48/** State information needed for the service. The main VBoxClient code provides
49 * the daemon logic needed by all services. */
50struct DISPLAYSTATE
51{
52 /** The service interface. */
53 struct VBCLSERVICE *pInterface;
54 /** Magic number for sanity checks. */
55 uint32_t magic;
56 /** Are we initialised yet? */
57 bool mfInit;
58 /** The connection to the server. */
59 Display *pDisplay;
60 /** The RandR extension base event number. */
61 int cRREventBase;
62 /** Can we use version 1.2 or later of the RandR protocol here? */
63 bool fHaveRandR12;
64 /** The command argument to use for the xrandr binary. Currently only
65 * used to support the non-standard location on some Solaris systems -
66 * would it make sense to use absolute paths on all systems? */
67 const char *pcszXrandr;
68 /** Was there a recent mode hint with no following root window resize, and
69 * if so, have we waited for a reasonable time? */
70 time_t timeLastModeHint;
71};
72
73static unsigned char *getRootProperty(struct DISPLAYSTATE *pState, const char *pszName,
74 long cItems, Atom type)
75{
76 Atom actualType = None;
77 int iFormat = 0;
78 ulong cReturned = 0;
79 ulong cAfter = 0;
80 unsigned char *pData = 0;
81
82 if (XGetWindowProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),
83 XInternAtom(pState->pDisplay, pszName, 0), 0, cItems,
84 False /* delete */, type, &actualType, &iFormat,
85 &cReturned, &cAfter, &pData))
86 return NULL;
87 return pData;
88}
89
90static void doResize(struct DISPLAYSTATE *pState)
91{
92 /** @note The xrandr command can fail if something else accesses RandR at
93 * the same time. We just ignore failure for now as we do not know what
94 * someone else is doing. */
95 if (!pState->fHaveRandR12)
96 {
97 char szCommand[256];
98 unsigned char *pData;
99
100 pData = getRootProperty(pState, "VBOXVIDEO_PREFERRED_MODE", 1, XA_INTEGER);
101 if (pData != NULL)
102 {
103 RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u",
104 pState->pcszXrandr, ((unsigned long *)pData)[0] >> 16, ((unsigned long *)pData)[0] & 0xFFFF);
105 system(szCommand);
106 XFree(pData);
107 }
108 }
109 else
110 {
111 const char szCommandBase[] =
112 "%s --output VGA-0 --auto --output VGA-1 --auto --right-of VGA-0 "
113 "--output VGA-2 --auto --right-of VGA-1 --output VGA-3 --auto --right-of VGA-2 "
114 "--output VGA-4 --auto --right-of VGA-3 --output VGA-5 --auto --right-of VGA-4 "
115 "--output VGA-6 --auto --right-of VGA-5 --output VGA-7 --auto --right-of VGA-6 "
116 "--output VGA-8 --auto --right-of VGA-7 --output VGA-9 --auto --right-of VGA-8 "
117 "--output VGA-10 --auto --right-of VGA-9 --output VGA-11 --auto --right-of VGA-10 "
118 "--output VGA-12 --auto --right-of VGA-11 --output VGA-13 --auto --right-of VGA-12 "
119 "--output VGA-14 --auto --right-of VGA-13 --output VGA-15 --auto --right-of VGA-14 "
120 "--output VGA-16 --auto --right-of VGA-15 --output VGA-17 --auto --right-of VGA-16 "
121 "--output VGA-18 --auto --right-of VGA-17 --output VGA-19 --auto --right-of VGA-18 "
122 "--output VGA-20 --auto --right-of VGA-19 --output VGA-21 --auto --right-of VGA-20 "
123 "--output VGA-22 --auto --right-of VGA-21 --output VGA-23 --auto --right-of VGA-22 "
124 "--output VGA-24 --auto --right-of VGA-23 --output VGA-25 --auto --right-of VGA-24 "
125 "--output VGA-26 --auto --right-of VGA-25 --output VGA-27 --auto --right-of VGA-26 "
126 "--output VGA-28 --auto --right-of VGA-27 --output VGA-29 --auto --right-of VGA-28 "
127 "--output VGA-30 --auto --right-of VGA-29 --output VGA-31 --auto --right-of VGA-30";
128 char szCommand[sizeof(szCommandBase) + 256];
129 RTStrPrintf(szCommand, sizeof(szCommand), szCommandBase, pState->pcszXrandr);
130 system(szCommand);
131 }
132}
133
134/** Main loop: handle display hot-plug events, property updates (which can
135 * signal VT switches hot-plug in old X servers). */
136static void runDisplay(struct DISPLAYSTATE *pState)
137{
138 Display *pDisplay = pState->pDisplay;
139 long cValue = 1;
140
141 /* One way or another we want the preferred mode at server start-up. */
142 doResize(pState);
143 XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask | StructureNotifyMask);
144 if (pState->fHaveRandR12)
145 XRRSelectInput(pDisplay, DefaultRootWindow(pDisplay), RRScreenChangeNotifyMask);
146 /* Semantics: when VBOXCLIENT_STARTED is set, pre-1.3 X.Org Server driver
147 * assumes that a client capable of handling mode hints will be present for the
148 * rest of the X session. If we crash things will not work as they should.
149 * I thought that preferable to implementing complex crash-handling logic.
150 */
151 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOXCLIENT_STARTED", 0),
152 XA_INTEGER, 32, PropModeReplace, (unsigned char *)&cValue, 1);
153 /* Interrupting this cleanly will be more work than making it robust
154 * against spontaneous termination, especially as it will never get
155 * properly tested, so I will go for the second. */
156 while (true)
157 {
158 XEvent event;
159 struct pollfd PollFd;
160 int pollTimeOut = -1;
161 int cFds;
162
163 /* Do not handle overflow. */
164 if (pState->timeLastModeHint > 0 && pState->timeLastModeHint < INT_MAX - 2)
165 pollTimeOut = 2 - (time(0) - pState->timeLastModeHint);
166 PollFd.fd = ConnectionNumber(pDisplay);
167 PollFd.events = POLLIN; /* Hang-up is always reported. */
168 XFlush(pDisplay);
169 cFds = poll(&PollFd, 1, pollTimeOut >= 0 ? pollTimeOut * 1000 : -1);
170 while (XPending(pDisplay))
171 {
172 XNextEvent(pDisplay, &event);
173 /* This property is deleted when the server regains the virtual
174 * terminal. Force the main thread to call xrandr again, as old X
175 * servers could not handle it while switched out. */
176 if ( !pState->fHaveRandR12
177 && event.type == PropertyNotify
178 && event.xproperty.state == PropertyDelete
179 && event.xproperty.window == DefaultRootWindow(pDisplay)
180 && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_NO_VT", False))
181 doResize(pState);
182 if ( !pState->fHaveRandR12
183 && event.type == PropertyNotify
184 && event.xproperty.state == PropertyNewValue
185 && event.xproperty.window == DefaultRootWindow(pDisplay)
186 && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_PREFERRED_MODE", False))
187 doResize(pState);
188 if ( pState->fHaveRandR12
189 && event.type == pState->cRREventBase + RRScreenChangeNotify)
190 pState->timeLastModeHint = time(0);
191 if ( event.type == ConfigureNotify
192 && event.xproperty.window == DefaultRootWindow(pDisplay))
193 pState->timeLastModeHint = 0;
194 }
195 if (cFds == 0 && pState->timeLastModeHint > 0)
196 doResize(pState);
197 }
198}
199
200static int initDisplay(struct DISPLAYSTATE *pState)
201{
202 char szCommand[256];
203 int status;
204
205 pState->pDisplay = XOpenDisplay(NULL);
206 if (!pState->pDisplay)
207 return VERR_NOT_FOUND;
208 if (!XRRQueryExtension(pState->pDisplay, &pState->cRREventBase, &status))
209 return VERR_NOT_FOUND;
210 pState->fHaveRandR12 = false;
211 pState->pcszXrandr = "xrandr";
212 if (RTFileExists("/usr/X11/bin/xrandr"))
213 pState->pcszXrandr = "/usr/X11/bin/xrandr";
214 status = system(pState->pcszXrandr);
215 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
216 VBClFatalError(("Failed to execute the xrandr utility.\n"));
217 RTStrPrintf(szCommand, sizeof(szCommand), "%s --q12", pState->pcszXrandr);
218 status = system(szCommand);
219 if (WEXITSTATUS(status) == 0)
220 pState->fHaveRandR12 = true;
221 return VINF_SUCCESS;
222}
223
224static const char *getPidFilePath()
225{
226 return ".vboxclient-display.pid";
227}
228
229static struct DISPLAYSTATE *getStateFromInterface(struct VBCLSERVICE **ppInterface)
230{
231 struct DISPLAYSTATE *pSelf = (struct DISPLAYSTATE *)ppInterface;
232 if (pSelf->magic != DISPLAYSTATE_MAGIC)
233 VBClFatalError(("Bad display service object!\n"));
234 return pSelf;
235}
236
237static int init(struct VBCLSERVICE **ppInterface)
238{
239 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
240 int rc;
241
242 if (pSelf->mfInit)
243 return VERR_WRONG_ORDER;
244 rc = initDisplay(pSelf);
245 if (RT_FAILURE(rc))
246 return rc;
247 if (RT_SUCCESS(rc))
248 pSelf->mfInit = true;
249 return rc;
250}
251
252static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
253{
254 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
255 int rc;
256
257 if (!pSelf->mfInit)
258 return VERR_WRONG_ORDER;
259 runDisplay(pSelf);
260 return VERR_INTERNAL_ERROR; /* "Should never reach here." */
261}
262
263struct VBCLSERVICE vbclDisplayInterface =
264{
265 getPidFilePath,
266 init,
267 run,
268 VBClServiceDefaultCleanup
269};
270
271struct VBCLSERVICE **VBClGetDisplayService()
272{
273 struct DISPLAYSTATE *pService = (struct DISPLAYSTATE *)RTMemAlloc(sizeof(*pService));
274
275 if (!pService)
276 VBClFatalError(("Out of memory\n"));
277 pService->pInterface = &vbclDisplayInterface;
278 pService->magic = DISPLAYSTATE_MAGIC;
279 pService->mfInit = false;
280 return &pService->pInterface;
281}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette