VirtualBox

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

Last change on this file since 53552 was 53552, checked in by vboxsync, 10 years ago

Additions/x11/VBoxClient: do not bail out if xrandr fails, as this is usually due to an inherent race in the protocol.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.9 KB
Line 
1/* $Id: display.cpp 53552 2014-12-16 20:45:06Z 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.215389.xyz. 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/** @todo this should probably be replaced by something IPRT */
19/* For system() and WEXITSTATUS() */
20#include <stdlib.h>
21#include <sys/types.h>
22#include <sys/wait.h>
23#include <errno.h>
24
25#include <X11/Xlib.h>
26#include <X11/Xatom.h>
27#include <X11/cursorfont.h>
28
29#include <iprt/assert.h>
30#include <iprt/err.h>
31#include <iprt/file.h>
32#include <iprt/mem.h>
33#include <iprt/string.h>
34#include <iprt/thread.h>
35#include <VBox/log.h>
36#include <VBox/VMMDev.h>
37#include <VBox/VBoxGuestLib.h>
38
39#include "VBoxClient.h"
40
41/** State information we need for interacting with the X server. */
42struct x11State
43{
44 /** The connection to the server. */
45 Display *pDisplay;
46 /** Can we use version 1.2 or later of the RandR protocol here? */
47 bool fHaveRandR12;
48 /** The command argument to use for the xrandr binary. Currently only
49 * used to support the non-standard location on some Solaris systems -
50 * would it make sense to use absolute paths on all systems? */
51 const char *pcszXrandr;
52 /** The size of our array of size hints. */
53 unsigned cSizeHints;
54 /** Array of size hints. Large enough to hold the highest display
55 * number we have had a hint for so far, reallocated when a higher one
56 * comes. Zero means no hint for that display. */
57 long *paSizeHints;
58};
59
60/** Tell the VBoxGuest driver we no longer want any events and tell the host
61 * we no longer support any capabilities. */
62static int disableEventsAndCaps()
63{
64 int rc = VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS);
65 if (RT_FAILURE(rc))
66 VBClFatalError(("Failed to unset graphics capability, rc=%Rrc.\n", rc));
67 rc = VbglR3SetMouseStatus(VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
68 if (RT_FAILURE(rc))
69 VBClFatalError(("Failed to unset mouse status, rc=%Rrc.\n", rc));
70 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED
71 | VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST);
72 if (RT_FAILURE(rc))
73 VBClFatalError(("Failed to unset filter mask, rc=%Rrc.\n", rc));
74 return VINF_SUCCESS;
75}
76
77/** Tell the VBoxGuest driver which events we want and tell the host which
78 * capabilities we support. */
79static int enableEventsAndCaps()
80{
81 int rc = VbglR3CtlFilterMask( VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED
82 | VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
83 if (RT_FAILURE(rc))
84 VBClFatalError(("Failed to set filter mask, rc=%Rrc.\n", rc));
85 rc = VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0);
86 if (RT_FAILURE(rc))
87 VBClFatalError(("Failed to set graphics capability, rc=%Rrc.\n", rc));
88 rc = VbglR3SetMouseStatus(0);
89 if (RT_FAILURE(rc))
90 VBClFatalError(("Failed to set mouse status, rc=%Rrc.\n", rc));
91 return VINF_SUCCESS;
92}
93
94static int initX11(struct x11State *pState)
95{
96 char szCommand[256];
97 int status;
98
99 pState->pDisplay = XOpenDisplay(NULL);
100 if (!pState->pDisplay)
101 return VERR_NOT_FOUND;
102 pState->fHaveRandR12 = false;
103 pState->pcszXrandr = "xrandr";
104 if (RTFileExists("/usr/X11/bin/xrandr"))
105 pState->pcszXrandr = "/usr/X11/bin/xrandr";
106 status = system(pState->pcszXrandr);
107 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
108 VBClFatalError(("Failed to execute the xrandr utility.\n"));
109 RTStrPrintf(szCommand, sizeof(szCommand), "%s --q12", pState->pcszXrandr);
110 status = system(szCommand);
111 if (WEXITSTATUS(status) == 0)
112 pState->fHaveRandR12 = true;
113 pState->cSizeHints = 0;
114 pState->paSizeHints = NULL;
115 return VINF_SUCCESS;
116}
117
118static void setModeX11(struct x11State *pState, unsigned cx, unsigned cy,
119 unsigned cBPP, unsigned iDisplay, unsigned x,
120 unsigned y, bool fEnabled, bool fChangeOrigin)
121{
122 char szCommand[256];
123 uint32_t i;
124
125 /** @note The xrandr command can fail if something else accesses RandR at
126 * the same time. We just ignore failure for now and let the user try
127 * again as we do not know what someone else is doing. */
128 if (iDisplay >= pState->cSizeHints)
129 {
130 pState->paSizeHints = (long *)RTMemRealloc(pState->paSizeHints,
131 (iDisplay + 1)
132 * sizeof(*pState->paSizeHints));
133 if (!pState->paSizeHints)
134 VBClFatalError(("Failed to re-allocate size hint memory.\n"));
135 for (i = pState->cSizeHints; i < iDisplay + 1; ++i)
136 pState->paSizeHints[i] = 0;
137 pState->cSizeHints = iDisplay + 1;
138 }
139 if (!fEnabled || (cx != 0 && cy != 0))
140 {
141 pState->paSizeHints[iDisplay] = (cx & 0x8fff) << 16 | (cy & 0x8fff);
142 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),
143 XInternAtom(pState->pDisplay, "VBOX_SIZE_HINTS", 0),
144 XA_INTEGER, 32, PropModeReplace,
145 (unsigned char *)pState->paSizeHints,
146 pState->cSizeHints);
147 XFlush(pState->pDisplay);
148 }
149 if (!pState->fHaveRandR12)
150 {
151 RTStrPrintf(szCommand, sizeof(szCommand),
152 "%s -s %ux%u", pState->pcszXrandr, cx, cy);
153 system(szCommand);
154 }
155 else
156 {
157 if (fChangeOrigin && fEnabled)
158 {
159 /* Extended Display support possible . Secondary monitor
160 * position supported */
161 RTStrPrintf(szCommand, sizeof(szCommand),
162 "%s --output VGA-%u --auto --pos %ux%u",
163 pState->pcszXrandr, iDisplay, x, y);
164 system(szCommand);
165 }
166 if ((!fChangeOrigin || fEnabled) && cx != 0 && cy != 0)
167 {
168 RTStrPrintf(szCommand, sizeof(szCommand),
169 "%s --output VGA-%u --preferred",
170 pState->pcszXrandr, iDisplay);
171 system(szCommand);
172 }
173 if (!fEnabled)
174 {
175 /* disable the virtual monitor */
176 RTStrPrintf(szCommand, sizeof(szCommand),
177 "%s --output VGA-%u --off",
178 pState->pcszXrandr, iDisplay);
179 system(szCommand);
180 }
181 }
182}
183
184/**
185 * Display change request monitor thread function.
186 * Before entering the loop, we re-read the last request
187 * received, and if the first one received inside the
188 * loop is identical we ignore it, because it is probably
189 * stale.
190 */
191static void runDisplay(struct x11State *pState)
192{
193 int status, rc;
194 unsigned i, cScreens;
195 char szCommand[256];
196 Cursor hClockCursor = XCreateFontCursor(pState->pDisplay, XC_watch);
197 Cursor hArrowCursor = XCreateFontCursor(pState->pDisplay, XC_left_ptr);
198
199 LogRelFlowFunc(("\n"));
200 rc = VbglR3VideoModeGetHighestSavedScreen(&cScreens);
201 if (rc != VINF_SUCCESS && rc != VERR_NOT_SUPPORTED)
202 VBClFatalError(("Failed to get the number of saved screen modes, rc=%Rrc\n",
203 rc));
204 if (rc == VINF_SUCCESS)
205 /* The "8" is to sanity test that VbglR3VideoModeGetHighestSavedScreen()
206 * worked right. */
207 for (i = 0; i < RT_MAX(cScreens + 1, 8); ++i)
208 {
209 unsigned cx = 0, cy = 0, cBPP = 0, x = 0, y = 0;
210 bool fEnabled = true;
211
212 rc = VbglR3RetrieveVideoMode(i, &cx, &cy, &cBPP, &x, &y,
213 &fEnabled);
214 /* Sanity test. */
215 if (i > cScreens && rc != VERR_NOT_FOUND)
216 VBClFatalError(("Internal error retrieving the number of saved screen modes.\n"));
217 if (rc == VINF_SUCCESS)
218 setModeX11(pState, cx, cy, cBPP, i, x, y, fEnabled,
219 true);
220 }
221 while (true)
222 {
223 uint32_t fEvents;
224 do
225 rc = VbglR3WaitEvent( VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
226 | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED,
227 RT_INDEFINITE_WAIT, &fEvents);
228 while(rc == VERR_INTERRUPTED);
229 if (RT_FAILURE(rc)) /* VERR_NO_MEMORY? */
230 VBClFatalError(("event wait failed, rc=%Rrc\n", rc));
231 if (fEvents & VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED)
232 {
233 /* Jiggle the mouse pointer to trigger a switch to a software
234 * cursor if necessary. */
235 XGrabPointer(pState->pDisplay,
236 DefaultRootWindow(pState->pDisplay), true, 0,
237 GrabModeAsync, GrabModeAsync, None, hClockCursor,
238 CurrentTime);
239 XFlush(pState->pDisplay);
240 XGrabPointer(pState->pDisplay,
241 DefaultRootWindow(pState->pDisplay), true, 0,
242 GrabModeAsync, GrabModeAsync, None, hArrowCursor,
243 CurrentTime);
244 XFlush(pState->pDisplay);
245 XUngrabPointer(pState->pDisplay, CurrentTime);
246 XFlush(pState->pDisplay);
247 }
248 /* And if it is a size hint, set the new size. */
249 if (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
250 {
251 uint32_t cx = 0, cy = 0, cBPP = 0, iDisplay = 0, x = 0, y = 0;
252 bool fEnabled = true, fChangeOrigin = true;
253 VMMDevSeamlessMode Mode;
254
255 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBPP, &iDisplay,
256 &x, &y, &fEnabled,
257 &fChangeOrigin, true);
258 /* Extended display version not supported on host */
259 if (rc != VINF_SUCCESS)
260 VBClFatalError(("Failed to get display change request, rc=%Rrc\n",
261 rc));
262 else
263 LogRelFlowFunc(("Got size hint from host cx=%d, cy=%d, bpp=%d, iDisplay=%d, x=%d, y=%d fEnabled=%d\n",
264 cx, cy, cBPP, iDisplay, x, y,
265 fEnabled));
266 if (iDisplay > INT32_MAX)
267 VBClFatalError(("Received a size hint for too high display number %u\n",
268 (unsigned) iDisplay));
269 rc = VbglR3SeamlessGetLastEvent(&Mode);
270 if (RT_FAILURE(rc))
271 VBClFatalError(("Failed to check seamless mode, rc=%Rrc\n", rc));
272 if (Mode == VMMDev_Seamless_Disabled)
273 {
274 rc = VbglR3SaveVideoMode(iDisplay, cx, cy, cBPP, x, y,
275 fEnabled);
276 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED)
277 VBClFatalError(("Failed to save size hint, rc=%Rrc\n", rc));
278 }
279 setModeX11(pState, cx, cy, cBPP, iDisplay, x, y, fEnabled,
280 fChangeOrigin);
281 }
282 }
283}
284
285/** Display magic number, start of a UUID. */
286#define DISPLAYSERVICE_MAGIC 0xf0029993
287
288/** VBoxClient service class wrapping the logic for the display service while
289 * the main VBoxClient code provides the daemon logic needed by all services.
290 */
291struct DISPLAYSERVICE
292{
293 /** The service interface. */
294 struct VBCLSERVICE *pInterface;
295 /** Magic number for sanity checks. */
296 uint32_t magic;
297 /** State related to the X server. */
298 struct x11State mState;
299 /** Are we initialised yet? */
300 bool mfInit;
301};
302
303static const char *getPidFilePath()
304{
305 return ".vboxclient-display.pid";
306}
307
308static struct DISPLAYSERVICE *getClassFromInterface(struct VBCLSERVICE **
309 ppInterface)
310{
311 struct DISPLAYSERVICE *pSelf = (struct DISPLAYSERVICE *)ppInterface;
312 if (pSelf->magic != DISPLAYSERVICE_MAGIC)
313 VBClFatalError(("Bad display service object!\n"));
314 return pSelf;
315}
316
317static int init(struct VBCLSERVICE **ppInterface)
318{
319 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
320 int rc;
321
322 if (pSelf->mfInit)
323 return VERR_WRONG_ORDER;
324 rc = initX11(&pSelf->mState);
325 if (RT_FAILURE(rc))
326 return rc;
327 rc = enableEventsAndCaps();
328 if (RT_SUCCESS(rc))
329 pSelf->mfInit = true;
330 return rc;
331}
332
333static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
334{
335 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
336 int rc;
337
338 if (!pSelf->mfInit)
339 return VERR_WRONG_ORDER;
340 rc = VBClStartVTMonitor();
341 if (RT_FAILURE(rc))
342 VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
343 runDisplay(&pSelf->mState);
344 return VERR_INTERNAL_ERROR; /* "Should never reach here." */
345}
346
347static int pause(struct VBCLSERVICE **ppInterface)
348{
349 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
350
351 if (!pSelf->mfInit)
352 return VERR_WRONG_ORDER;
353 return disableEventsAndCaps();
354}
355
356static int resume(struct VBCLSERVICE **ppInterface)
357{
358 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
359
360 if (!pSelf->mfInit)
361 return VERR_WRONG_ORDER;
362 return enableEventsAndCaps();
363}
364
365static void cleanup(struct VBCLSERVICE **ppInterface)
366{
367 NOREF(ppInterface);
368 disableEventsAndCaps();
369}
370
371struct VBCLSERVICE vbclDisplayInterface =
372{
373 getPidFilePath,
374 init,
375 run,
376 pause,
377 resume,
378 cleanup
379};
380
381struct VBCLSERVICE **VBClGetDisplayService()
382{
383 struct DISPLAYSERVICE *pService =
384 (struct DISPLAYSERVICE *)RTMemAlloc(sizeof(*pService));
385
386 if (!pService)
387 VBClFatalError(("Out of memory\n"));
388 pService->pInterface = &vbclDisplayInterface;
389 pService->magic = DISPLAYSERVICE_MAGIC;
390 pService->mfInit = false;
391 return &pService->pInterface;
392}
Note: See TracBrowser for help on using the repository browser.

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