VirtualBox

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

最後變更 在這個檔案從53442是 53440,由 vboxsync 提交於 10 年 前

Additions/x11: fix recently re-introduced mode hint regression which caused guest screens to disappear (again).

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 14.3 KB
 
1/* $Id: display.cpp 53440 2014-12-03 21:06:24Z 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.alldomusa.eu.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 ( (rc != VINF_SUCCESS && rc != VERR_NOT_FOUND)
222 || (i > cScreens && rc != VERR_NOT_FOUND))
223 VBClFatalError(("Internal error retrieving the number of saved screen modes.\n"));
224 if (rc == VINF_SUCCESS)
225 setModeX11(pState, cx, cy, cBPP, i, x, y, fEnabled,
226 true);
227 }
228 while (true)
229 {
230 uint32_t fEvents;
231 do
232 rc = VbglR3WaitEvent( VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
233 | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED,
234 RT_INDEFINITE_WAIT, &fEvents);
235 while(rc == VERR_INTERRUPTED);
236 if (RT_FAILURE(rc)) /* VERR_NO_MEMORY? */
237 VBClFatalError(("event wait failed, rc=%Rrc\n", rc));
238 if (fEvents & VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED)
239 {
240 /* Jiggle the mouse pointer to trigger a switch to a software
241 * cursor if necessary. */
242 XGrabPointer(pState->pDisplay,
243 DefaultRootWindow(pState->pDisplay), true, 0,
244 GrabModeAsync, GrabModeAsync, None, hClockCursor,
245 CurrentTime);
246 XFlush(pState->pDisplay);
247 XGrabPointer(pState->pDisplay,
248 DefaultRootWindow(pState->pDisplay), true, 0,
249 GrabModeAsync, GrabModeAsync, None, hArrowCursor,
250 CurrentTime);
251 XFlush(pState->pDisplay);
252 XUngrabPointer(pState->pDisplay, CurrentTime);
253 XFlush(pState->pDisplay);
254 }
255 /* And if it is a size hint, set the new size. */
256 if (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
257 {
258 uint32_t cx = 0, cy = 0, cBPP = 0, iDisplay = 0, x = 0, y = 0;
259 bool fEnabled = true, fChangeOrigin = true;
260 VMMDevSeamlessMode Mode;
261
262 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBPP, &iDisplay,
263 &x, &y, &fEnabled,
264 &fChangeOrigin, true);
265 /* Extended display version not supported on host */
266 if (rc != VINF_SUCCESS)
267 VBClFatalError(("Failed to get display change request, rc=%Rrc\n",
268 rc));
269 else
270 LogRelFlowFunc(("Got size hint from host cx=%d, cy=%d, bpp=%d, iDisplay=%d, x=%d, y=%d fEnabled=%d\n",
271 cx, cy, cBPP, iDisplay, x, y,
272 fEnabled));
273 if (iDisplay > INT32_MAX)
274 VBClFatalError(("Received a size hint for too high display number %u\n",
275 (unsigned) iDisplay));
276 rc = VbglR3SeamlessGetLastEvent(&Mode);
277 if (RT_FAILURE(rc))
278 VBClFatalError(("Failed to check seamless mode, rc=%Rrc\n", rc));
279 if (Mode == VMMDev_Seamless_Disabled)
280 {
281 rc = VbglR3SaveVideoMode(iDisplay, cx, cy, cBPP, x, y,
282 fEnabled);
283 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED)
284 VBClFatalError(("Failed to save size hint, rc=%Rrc\n", rc));
285 }
286 setModeX11(pState, cx, cy, cBPP, iDisplay, x, y, fEnabled,
287 fChangeOrigin);
288 }
289 }
290}
291
292/** Display magic number, start of a UUID. */
293#define DISPLAYSERVICE_MAGIC 0xf0029993
294
295/** VBoxClient service class wrapping the logic for the display service while
296 * the main VBoxClient code provides the daemon logic needed by all services.
297 */
298struct DISPLAYSERVICE
299{
300 /** The service interface. */
301 struct VBCLSERVICE *pInterface;
302 /** Magic number for sanity checks. */
303 uint32_t magic;
304 /** State related to the X server. */
305 struct x11State mState;
306 /** Are we initialised yet? */
307 bool mfInit;
308};
309
310static const char *getPidFilePath()
311{
312 return ".vboxclient-display.pid";
313}
314
315static struct DISPLAYSERVICE *getClassFromInterface(struct VBCLSERVICE **
316 ppInterface)
317{
318 struct DISPLAYSERVICE *pSelf = (struct DISPLAYSERVICE *)ppInterface;
319 if (pSelf->magic != DISPLAYSERVICE_MAGIC)
320 VBClFatalError(("Bad display service object!\n"));
321 return pSelf;
322}
323
324static int init(struct VBCLSERVICE **ppInterface)
325{
326 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
327 int rc;
328
329 if (pSelf->mfInit)
330 return VERR_WRONG_ORDER;
331 rc = initX11(&pSelf->mState);
332 if (RT_FAILURE(rc))
333 return rc;
334 rc = enableEventsAndCaps();
335 if (RT_SUCCESS(rc))
336 pSelf->mfInit = true;
337 return rc;
338}
339
340static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
341{
342 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
343 int rc;
344
345 if (!pSelf->mfInit)
346 return VERR_WRONG_ORDER;
347 rc = VBClStartVTMonitor();
348 if (RT_FAILURE(rc))
349 VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
350 runDisplay(&pSelf->mState);
351 return VERR_INTERNAL_ERROR; /* "Should never reach here." */
352}
353
354static int pause(struct VBCLSERVICE **ppInterface)
355{
356 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
357
358 if (!pSelf->mfInit)
359 return VERR_WRONG_ORDER;
360 return disableEventsAndCaps();
361}
362
363static int resume(struct VBCLSERVICE **ppInterface)
364{
365 struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
366
367 if (!pSelf->mfInit)
368 return VERR_WRONG_ORDER;
369 return enableEventsAndCaps();
370}
371
372static void cleanup(struct VBCLSERVICE **ppInterface)
373{
374 NOREF(ppInterface);
375 disableEventsAndCaps();
376}
377
378struct VBCLSERVICE vbclDisplayInterface =
379{
380 getPidFilePath,
381 init,
382 run,
383 pause,
384 resume,
385 cleanup
386};
387
388struct VBCLSERVICE **VBClGetDisplayService()
389{
390 struct DISPLAYSERVICE *pService =
391 (struct DISPLAYSERVICE *)RTMemAlloc(sizeof(*pService));
392
393 if (!pService)
394 VBClFatalError(("Out of memory\n"));
395 pService->pInterface = &vbclDisplayInterface;
396 pService->magic = DISPLAYSERVICE_MAGIC;
397 pService->mfInit = false;
398 return &pService->pInterface;
399}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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