VirtualBox

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

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

Additions/x11/VBoxClient: remove invalid sanity test.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: display.cpp 53515 2014-12-12 09:48:56Z 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/** @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 int status;
124 uint32_t i;
125
126 if (iDisplay >= pState->cSizeHints)
127 {
128 pState->paSizeHints = (long *)RTMemRealloc(pState->paSizeHints,
129 (iDisplay + 1)
130 * sizeof(*pState->paSizeHints));
131 if (!pState->paSizeHints)
132 VBClFatalError(("Failed to re-allocate size hint memory.\n"));
133 for (i = pState->cSizeHints; i < iDisplay + 1; ++i)
134 pState->paSizeHints[i] = 0;
135 pState->cSizeHints = iDisplay + 1;
136 }
137 if (!fEnabled || (cx != 0 && cy != 0))
138 {
139 pState->paSizeHints[iDisplay] = (cx & 0x8fff) << 16 | (cy & 0x8fff);
140 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),
141 XInternAtom(pState->pDisplay, "VBOX_SIZE_HINTS", 0),
142 XA_INTEGER, 32, PropModeReplace,
143 (unsigned char *)pState->paSizeHints,
144 pState->cSizeHints);
145 XFlush(pState->pDisplay);
146 }
147 if (!pState->fHaveRandR12)
148 {
149 RTStrPrintf(szCommand, sizeof(szCommand),
150 "%s -s %ux%u", pState->pcszXrandr, cx, cy);
151 status = system(szCommand);
152 if (WEXITSTATUS(status) != 0)
153 VBClFatalError(("Failed to execute \\\"%s\\\".\n", 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 status = system(szCommand);
165 if (WEXITSTATUS(status) != 0)
166 VBClFatalError(("Failed to execute \\\"%s\\\".\n", szCommand));
167 }
168 if ((!fChangeOrigin || fEnabled) && cx != 0 && cy != 0)
169 {
170 RTStrPrintf(szCommand, sizeof(szCommand),
171 "%s --output VGA-%u --preferred",
172 pState->pcszXrandr, iDisplay);
173 status = system(szCommand);
174 if (WEXITSTATUS(status) != 0)
175 VBClFatalError(("Failed to execute \\\"%s\\\".\n", szCommand));
176 }
177 if (!fEnabled)
178 {
179 /* disable the virtual monitor */
180 RTStrPrintf(szCommand, sizeof(szCommand),
181 "%s --output VGA-%u --off",
182 pState->pcszXrandr, iDisplay);
183 status = system(szCommand);
184 if (WEXITSTATUS(status) != 0)
185 VBClFatalError(("Failed to execute \\\"%s\\\".\n", szCommand));
186 }
187 }
188}
189
190/**
191 * Display change request monitor thread function.
192 * Before entering the loop, we re-read the last request
193 * received, and if the first one received inside the
194 * loop is identical we ignore it, because it is probably
195 * stale.
196 */
197static void runDisplay(struct x11State *pState)
198{
199 int status, rc;
200 unsigned i, cScreens;
201 char szCommand[256];
202 Cursor hClockCursor = XCreateFontCursor(pState->pDisplay, XC_watch);
203 Cursor hArrowCursor = XCreateFontCursor(pState->pDisplay, XC_left_ptr);
204
205 LogRelFlowFunc(("\n"));
206 rc = VbglR3VideoModeGetHighestSavedScreen(&cScreens);
207 if (rc != VINF_SUCCESS && rc != VERR_NOT_SUPPORTED)
208 VBClFatalError(("Failed to get the number of saved screen modes, rc=%Rrc\n",
209 rc));
210 if (rc == VINF_SUCCESS)
211 /* The "8" is to sanity test that VbglR3VideoModeGetHighestSavedScreen()
212 * worked right. */
213 for (i = 0; i < RT_MAX(cScreens + 1, 8); ++i)
214 {
215 unsigned cx = 0, cy = 0, cBPP = 0, x = 0, y = 0;
216 bool fEnabled = true;
217
218 rc = VbglR3RetrieveVideoMode(i, &cx, &cy, &cBPP, &x, &y,
219 &fEnabled);
220 /* Sanity test. */
221 if (i > cScreens && rc != VERR_NOT_FOUND)
222 VBClFatalError(("Internal error retrieving the number of saved screen modes.\n"));
223 if (rc == VINF_SUCCESS)
224 setModeX11(pState, cx, cy, cBPP, i, x, y, fEnabled,
225 true);
226 }
227 while (true)
228 {
229 uint32_t fEvents;
230 do
231 rc = VbglR3WaitEvent( VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
232 | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED,
233 RT_INDEFINITE_WAIT, &fEvents);
234 while(rc == VERR_INTERRUPTED);
235 if (RT_FAILURE(rc)) /* VERR_NO_MEMORY? */
236 VBClFatalError(("event wait failed, rc=%Rrc\n", rc));
237 if (fEvents & VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED)
238 {
239 /* Jiggle the mouse pointer to trigger a switch to a software
240 * cursor if necessary. */
241 XGrabPointer(pState->pDisplay,
242 DefaultRootWindow(pState->pDisplay), true, 0,
243 GrabModeAsync, GrabModeAsync, None, hClockCursor,
244 CurrentTime);
245 XFlush(pState->pDisplay);
246 XGrabPointer(pState->pDisplay,
247 DefaultRootWindow(pState->pDisplay), true, 0,
248 GrabModeAsync, GrabModeAsync, None, hArrowCursor,
249 CurrentTime);
250 XFlush(pState->pDisplay);
251 XUngrabPointer(pState->pDisplay, CurrentTime);
252 XFlush(pState->pDisplay);
253 }
254 /* And if it is a size hint, set the new size. */
255 if (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
256 {
257 uint32_t cx = 0, cy = 0, cBPP = 0, iDisplay = 0, x = 0, y = 0;
258 bool fEnabled = true, fChangeOrigin = true;
259 VMMDevSeamlessMode Mode;
260
261 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBPP, &iDisplay,
262 &x, &y, &fEnabled,
263 &fChangeOrigin, true);
264 /* Extended display version not supported on host */
265 if (rc != VINF_SUCCESS)
266 VBClFatalError(("Failed to get display change request, rc=%Rrc\n",
267 rc));
268 else
269 LogRelFlowFunc(("Got size hint from host cx=%d, cy=%d, bpp=%d, iDisplay=%d, x=%d, y=%d fEnabled=%d\n",
270 cx, cy, cBPP, iDisplay, x, y,
271 fEnabled));
272 if (iDisplay > INT32_MAX)
273 VBClFatalError(("Received a size hint for too high display number %u\n",
274 (unsigned) iDisplay));
275 rc = VbglR3SeamlessGetLastEvent(&Mode);
276 if (RT_FAILURE(rc))
277 VBClFatalError(("Failed to check seamless mode, rc=%Rrc\n", rc));
278 if (Mode == VMMDev_Seamless_Disabled)
279 {
280 rc = VbglR3SaveVideoMode(iDisplay, cx, cy, cBPP, x, y,
281 fEnabled);
282 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED)
283 VBClFatalError(("Failed to save size hint, rc=%Rrc\n", rc));
284 }
285 setModeX11(pState, cx, cy, cBPP, iDisplay, x, y, fEnabled,
286 fChangeOrigin);
287 }
288 }
289}
290
291/** Display magic number, start of a UUID. */
292#define DISPLAYSERVICE_MAGIC 0xf0029993
293
294/** VBoxClient service class wrapping the logic for the display service while
295 * the main VBoxClient code provides the daemon logic needed by all services.
296 */
297struct DISPLAYSERVICE
298{
299 /** The service interface. */
300 struct VBCLSERVICE *pInterface;
301 /** Magic number for sanity checks. */
302 uint32_t magic;
303 /** State related to the X server. */
304 struct x11State mState;
305 /** Are we initialised yet? */
306 bool mfInit;
307};
308
309static const char *getPidFilePath()
310{
311 return ".vboxclient-display.pid";
312}
313
314static struct DISPLAYSERVICE *getClassFromInterface(struct VBCLSERVICE **
315 ppInterface)
316{
317 struct DISPLAYSERVICE *pSelf = (struct DISPLAYSERVICE *)ppInterface;
318 if (pSelf->magic != DISPLAYSERVICE_MAGIC)
319 VBClFatalError(("Bad display service object!\n"));
320 return pSelf;
321}
322
323static int init(struct VBCLSERVICE **ppInterface)
324{
325 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
326 int rc;
327
328 if (pSelf->mfInit)
329 return VERR_WRONG_ORDER;
330 rc = initX11(&pSelf->mState);
331 if (RT_FAILURE(rc))
332 return rc;
333 rc = enableEventsAndCaps();
334 if (RT_SUCCESS(rc))
335 pSelf->mfInit = true;
336 return rc;
337}
338
339static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
340{
341 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
342 int rc;
343
344 if (!pSelf->mfInit)
345 return VERR_WRONG_ORDER;
346 rc = VBClStartVTMonitor();
347 if (RT_FAILURE(rc))
348 VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
349 runDisplay(&pSelf->mState);
350 return VERR_INTERNAL_ERROR; /* "Should never reach here." */
351}
352
353static int pause(struct VBCLSERVICE **ppInterface)
354{
355 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
356
357 if (!pSelf->mfInit)
358 return VERR_WRONG_ORDER;
359 return disableEventsAndCaps();
360}
361
362static int resume(struct VBCLSERVICE **ppInterface)
363{
364 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
365
366 if (!pSelf->mfInit)
367 return VERR_WRONG_ORDER;
368 return enableEventsAndCaps();
369}
370
371static void cleanup(struct VBCLSERVICE **ppInterface)
372{
373 NOREF(ppInterface);
374 disableEventsAndCaps();
375}
376
377struct VBCLSERVICE vbclDisplayInterface =
378{
379 getPidFilePath,
380 init,
381 run,
382 pause,
383 resume,
384 cleanup
385};
386
387struct VBCLSERVICE **VBClGetDisplayService()
388{
389 struct DISPLAYSERVICE *pService =
390 (struct DISPLAYSERVICE *)RTMemAlloc(sizeof(*pService));
391
392 if (!pService)
393 VBClFatalError(("Out of memory\n"));
394 pService->pInterface = &vbclDisplayInterface;
395 pService->magic = DISPLAYSERVICE_MAGIC;
396 pService->mfInit = false;
397 return &pService->pInterface;
398}
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