VirtualBox

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

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

Additions/x11/VBoxClient: fix remembering the size of additional screens.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.7 KB
Line 
1/* $Id: display.cpp 54186 2015-02-12 21:37:23Z 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
28#include <iprt/assert.h>
29#include <iprt/err.h>
30#include <iprt/file.h>
31#include <iprt/mem.h>
32#include <iprt/string.h>
33#include <iprt/thread.h>
34#include <VBox/log.h>
35#include <VBox/VMMDev.h>
36#include <VBox/VBoxGuestLib.h>
37
38#include "VBoxClient.h"
39
40/* TESTING: Dynamic resizing and mouse integration toggling should work
41 * correctly with a range of X servers (pre-1.3, 1.3 and later under Linux, 1.3
42 * and later under Solaris) with Guest Additions installed. Switching to a
43 * virtual terminal while a user session is in place should disable dynamic
44 * resizing and cursor integration, switching back should re-enable them. */
45
46/** Most recent information received for a particular screen. */
47struct screenInformation
48{
49 unsigned cx;
50 unsigned cy;
51 unsigned cBPP;
52 unsigned x;
53 unsigned y;
54 bool fEnabled;
55 bool fUpdateSize;
56 bool fUpdatePosition;
57};
58
59/** Display magic number, start of a UUID. */
60#define DISPLAYSTATE_MAGIC UINT32_C(0xf0029993)
61
62/** State information needed for the service. The main VBoxClient code provides
63 * the daemon logic needed by all services. */
64struct DISPLAYSTATE
65{
66 /** The service interface. */
67 struct VBCLSERVICE *pInterface;
68 /** Magic number for sanity checks. */
69 uint32_t magic;
70 /** Are we initialised yet? */
71 bool mfInit;
72 /** The connection to the server. */
73 Display *pDisplay;
74 /** Can we use version 1.2 or later of the RandR protocol here? */
75 bool fHaveRandR12;
76 /** The command argument to use for the xrandr binary. Currently only
77 * used to support the non-standard location on some Solaris systems -
78 * would it make sense to use absolute paths on all systems? */
79 const char *pcszXrandr;
80 /** The number of screens we are currently aware of. */
81 unsigned cScreensTracked;
82 /** Array of information about different screens. */
83 struct screenInformation *paScreenInformation;
84};
85
86/** Tell the VBoxGuest driver we no longer want any events and tell the host
87 * we no longer support any capabilities. */
88static int disableEventsAndCaps()
89{
90 int rc = VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS);
91 if (RT_FAILURE(rc))
92 VBClFatalError(("Failed to unset graphics capability, rc=%Rrc.\n", rc));
93 rc = VbglR3SetMouseStatus(VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
94 if (RT_FAILURE(rc))
95 VBClFatalError(("Failed to unset mouse status, rc=%Rrc.\n", rc));
96 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED
97 | VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST);
98 if (RT_FAILURE(rc))
99 VBClFatalError(("Failed to unset filter mask, rc=%Rrc.\n", rc));
100 return VINF_SUCCESS;
101}
102
103/** Tell the VBoxGuest driver which events we want and tell the host which
104 * capabilities we support. */
105static int enableEventsAndCaps()
106{
107 int rc = VbglR3CtlFilterMask( VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED
108 | VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
109 if (RT_FAILURE(rc))
110 VBClFatalError(("Failed to set filter mask, rc=%Rrc.\n", rc));
111 rc = VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0);
112 if (RT_FAILURE(rc))
113 VBClFatalError(("Failed to set graphics capability, rc=%Rrc.\n", rc));
114 rc = VbglR3SetMouseStatus(0);
115 if (RT_FAILURE(rc))
116 VBClFatalError(("Failed to set mouse status, rc=%Rrc.\n", rc));
117 return VINF_SUCCESS;
118}
119
120static int initDisplay(struct DISPLAYSTATE *pState)
121{
122 char szCommand[256];
123 int status;
124
125 /* Initialise the guest library. */
126 int rc = VbglR3InitUser();
127 if (RT_FAILURE(rc))
128 VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
129 pState->pDisplay = XOpenDisplay(NULL);
130 if (!pState->pDisplay)
131 return VERR_NOT_FOUND;
132 pState->fHaveRandR12 = false;
133 pState->pcszXrandr = "xrandr";
134 if (RTFileExists("/usr/X11/bin/xrandr"))
135 pState->pcszXrandr = "/usr/X11/bin/xrandr";
136 status = system(pState->pcszXrandr);
137 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
138 VBClFatalError(("Failed to execute the xrandr utility.\n"));
139 RTStrPrintf(szCommand, sizeof(szCommand), "%s --q12", pState->pcszXrandr);
140 status = system(szCommand);
141 if (WEXITSTATUS(status) == 0)
142 pState->fHaveRandR12 = true;
143 pState->cScreensTracked = 0;
144 pState->paScreenInformation = NULL;
145 return VINF_SUCCESS;
146}
147
148static void updateScreenInformation(struct DISPLAYSTATE *pState, unsigned cx, unsigned cy, unsigned cBPP, unsigned iDisplay,
149 unsigned x, unsigned y, bool fEnabled, bool fUpdatePosition)
150{
151 uint32_t i;
152
153 if (iDisplay >= pState->cScreensTracked)
154 {
155 pState->paScreenInformation =
156 (struct screenInformation *)RTMemRealloc(pState->paScreenInformation,
157 (iDisplay + 1) * sizeof(*pState->paScreenInformation));
158 if (!pState->paScreenInformation)
159 VBClFatalError(("Failed to re-allocate screen information.\n"));
160 for (i = pState->cScreensTracked; i < iDisplay + 1; ++i)
161 RT_ZERO(pState->paScreenInformation[i]);
162 pState->cScreensTracked = iDisplay + 1;
163 }
164 pState->paScreenInformation[iDisplay].cx = cx;
165 pState->paScreenInformation[iDisplay].cy = cy;
166 pState->paScreenInformation[iDisplay].cBPP = cBPP;
167 pState->paScreenInformation[iDisplay].x = x;
168 pState->paScreenInformation[iDisplay].y = y;
169 pState->paScreenInformation[iDisplay].fEnabled = fEnabled;
170 pState->paScreenInformation[iDisplay].fUpdateSize = true;
171 pState->paScreenInformation[iDisplay].fUpdatePosition = fUpdatePosition;
172}
173
174static void updateSizeHintsProperty(struct DISPLAYSTATE *pState)
175{
176 unsigned long *paSizeHints = (unsigned long *)RTMemTmpAllocZ(pState->cScreensTracked * sizeof(unsigned long));
177 unsigned i;
178
179 if (paSizeHints == NULL)
180 VBClFatalError(("Failed to allocate size hint property memory.\n"));
181 for (i = 0; i < pState->cScreensTracked; ++i)
182 {
183 if ( pState->paScreenInformation[i].fEnabled
184 && pState->paScreenInformation[i].cx != 0 && pState->paScreenInformation[i].cy != 0)
185 paSizeHints[i] = (pState->paScreenInformation[i].cx & 0x8fff) << 16 | (pState->paScreenInformation[i].cy & 0x8fff);
186 else if (pState->paScreenInformation[i].cx != 0 && pState->paScreenInformation[i].cy != 0)
187 paSizeHints[i] = -1;
188 }
189 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOX_SIZE_HINTS", 0),
190 XA_INTEGER, 32, PropModeReplace, (unsigned char *)paSizeHints, pState->cScreensTracked);
191 XFlush(pState->pDisplay);
192 RTMemTmpFree(paSizeHints);
193}
194
195static void notifyXServer(struct DISPLAYSTATE *pState)
196{
197 char szCommand[256];
198 unsigned i;
199 bool fUpdateInformation = false;
200
201 /** @note The xrandr command can fail if something else accesses RandR at
202 * the same time. We just ignore failure for now and let the user try
203 * again as we do not know what someone else is doing. */
204 for (i = 0; i < pState->cScreensTracked; ++i)
205 if (pState->paScreenInformation[i].fUpdateSize)
206 fUpdateInformation = true;
207 if ( !pState->fHaveRandR12 && pState->paScreenInformation[0].fUpdateSize
208 && pState->paScreenInformation[0].cx > 0 && pState->paScreenInformation[0].cy > 0)
209 {
210 RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u",
211 pState->pcszXrandr, pState->paScreenInformation[0].cx, pState->paScreenInformation[0].cy);
212 system(szCommand);
213 pState->paScreenInformation[0].fUpdateSize = false;
214 }
215 else if (pState->fHaveRandR12 && fUpdateInformation)
216 for (i = 0; i < pState->cScreensTracked; ++i)
217 {
218 if (pState->paScreenInformation[i].fUpdateSize)
219 {
220 RTStrPrintf(szCommand, sizeof(szCommand), "%s --output VGA-%u --preferred", pState->pcszXrandr, i);
221 system(szCommand);
222 }
223 if (pState->paScreenInformation[i].fUpdatePosition)
224 {
225 RTStrPrintf(szCommand, sizeof(szCommand), "%s --output VGA-%u --auto --pos %ux%u",
226 pState->pcszXrandr, i, pState->paScreenInformation[i].x, pState->paScreenInformation[i].y);
227 system(szCommand);
228 }
229 pState->paScreenInformation[i].fUpdateSize = pState->paScreenInformation[i].fUpdatePosition = false;
230 }
231 else
232 {
233 RTStrPrintf(szCommand, sizeof(szCommand), "%s", pState->pcszXrandr);
234 system(szCommand);
235 }
236}
237
238static void updateMouseCapabilities(struct DISPLAYSTATE *pState)
239{
240 uint32_t fFeatures = 0;
241 int rc;
242 unsigned i;
243
244 rc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
245
246 if (rc != VINF_SUCCESS)
247 VBClFatalError(("Failed to get mouse status, rc=%Rrc\n", rc));
248 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),
249 XInternAtom(pState->pDisplay, "VBOX_MOUSE_CAPABILITIES", 0), XA_INTEGER, 32, PropModeReplace,
250 (unsigned char *)&fFeatures, 1);
251 XFlush(pState->pDisplay);
252 if (pState->fHaveRandR12)
253 for (i = 0; i < pState->cScreensTracked; ++i)
254 pState->paScreenInformation[i].fUpdateSize = true;
255 else
256 pState->paScreenInformation[0].fUpdateSize = true;
257}
258
259/**
260 * Display change request monitor thread function.
261 */
262static void runDisplay(struct DISPLAYSTATE *pState)
263{
264 int status, rc;
265 unsigned i, cScreensTracked;
266 char szCommand[256];
267
268 LogRelFlowFunc(("\n"));
269 rc = VbglR3VideoModeGetHighestSavedScreen(&cScreensTracked);
270 if (rc != VINF_SUCCESS && rc != VERR_NOT_SUPPORTED)
271 VBClFatalError(("Failed to get the number of saved screen modes, rc=%Rrc\n", rc));
272 /* Make sure that we have an entry for screen 1 at least. */
273 updateScreenInformation(pState, 1024, 768, 0, 1, 0, 0, true, false);
274 if (rc == VINF_SUCCESS)
275 {
276 /* The "8" is for the sanity test below. */
277 for (i = 0; i < RT_MAX(cScreensTracked + 1, 8); ++i)
278 {
279 unsigned cx = 0, cy = 0, cBPP = 0, x = 0, y = 0;
280 bool fEnabled = true;
281
282 rc = VbglR3RetrieveVideoMode(i, &cx, &cy, &cBPP, &x, &y,
283 &fEnabled);
284 /* Sanity test for VbglR3VideoModeGetHighestSavedScreen(). */
285 if (i > cScreensTracked && rc != VERR_NOT_FOUND)
286 VBClFatalError(("Internal error retrieving the number of saved screen modes.\n"));
287 if (rc == VINF_SUCCESS)
288 updateScreenInformation(pState, cx, cy, cBPP, i, x, y, fEnabled, true);
289 }
290 }
291 while (true)
292 {
293 uint32_t fEvents;
294 updateMouseCapabilities(pState);
295 updateSizeHintsProperty(pState);
296 notifyXServer(pState);
297 do
298 rc = VbglR3WaitEvent( VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
299 | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED,
300 RT_INDEFINITE_WAIT, &fEvents);
301 while(rc == VERR_INTERRUPTED);
302 if (RT_FAILURE(rc)) /* VERR_NO_MEMORY? */
303 VBClFatalError(("event wait failed, rc=%Rrc\n", rc));
304 /* If it is a size hint, set the new size. */
305 if (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
306 {
307 uint32_t cx = 0, cy = 0, cBPP = 0, iDisplay = 0, x = 0, y = 0;
308 bool fEnabled = true, fUpdatePosition = true;
309 VMMDevSeamlessMode Mode;
310
311 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBPP, &iDisplay,
312 &x, &y, &fEnabled,
313 &fUpdatePosition, true);
314 if (rc != VINF_SUCCESS)
315 VBClFatalError(("Failed to get display change request, rc=%Rrc\n",
316 rc));
317 else
318 LogRelFlowFunc(("Got size hint from host cx=%d, cy=%d, bpp=%d, iDisplay=%d, x=%d, y=%d fEnabled=%d\n",
319 cx, cy, cBPP, iDisplay, x, y, fEnabled));
320 if (iDisplay > INT32_MAX)
321 VBClFatalError(("Received a size hint for too high display number %u\n",
322 (unsigned) iDisplay));
323 updateScreenInformation(pState, cx, cy, cBPP, iDisplay, x, y, fEnabled, fUpdatePosition);
324 rc = VbglR3SeamlessGetLastEvent(&Mode);
325 if (RT_FAILURE(rc))
326 VBClFatalError(("Failed to check seamless mode, rc=%Rrc\n", rc));
327 if (Mode == VMMDev_Seamless_Disabled)
328 {
329 rc = VbglR3SaveVideoMode(iDisplay, cx, cy, cBPP, x, y,
330 fEnabled);
331 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED)
332 VBClFatalError(("Failed to save size hint, rc=%Rrc\n", rc));
333 }
334 }
335 }
336}
337
338static const char *getPidFilePath()
339{
340 return ".vboxclient-display.pid";
341}
342
343static struct DISPLAYSTATE *getStateFromInterface(struct VBCLSERVICE **ppInterface)
344{
345 struct DISPLAYSTATE *pSelf = (struct DISPLAYSTATE *)ppInterface;
346 if (pSelf->magic != DISPLAYSTATE_MAGIC)
347 VBClFatalError(("Bad display service object!\n"));
348 return pSelf;
349}
350
351static int init(struct VBCLSERVICE **ppInterface)
352{
353 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
354 int rc;
355
356 if (pSelf->mfInit)
357 return VERR_WRONG_ORDER;
358 rc = initDisplay(pSelf);
359 if (RT_FAILURE(rc))
360 return rc;
361 rc = enableEventsAndCaps();
362 if (RT_SUCCESS(rc))
363 pSelf->mfInit = true;
364 return rc;
365}
366
367static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
368{
369 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
370 int rc;
371
372 if (!pSelf->mfInit)
373 return VERR_WRONG_ORDER;
374 rc = VBClStartVTMonitor();
375 if (RT_FAILURE(rc))
376 VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
377 runDisplay(pSelf);
378 return VERR_INTERNAL_ERROR; /* "Should never reach here." */
379}
380
381static int pause(struct VBCLSERVICE **ppInterface)
382{
383 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
384
385 if (!pSelf->mfInit)
386 return VERR_WRONG_ORDER;
387 return disableEventsAndCaps();
388}
389
390static int resume(struct VBCLSERVICE **ppInterface)
391{
392 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
393
394 if (!pSelf->mfInit)
395 return VERR_WRONG_ORDER;
396 return enableEventsAndCaps();
397}
398
399static void cleanup(struct VBCLSERVICE **ppInterface)
400{
401 NOREF(ppInterface);
402 disableEventsAndCaps();
403 VbglR3Term();
404}
405
406struct VBCLSERVICE vbclDisplayInterface =
407{
408 getPidFilePath,
409 init,
410 run,
411 pause,
412 resume,
413 cleanup
414};
415
416struct VBCLSERVICE **VBClGetDisplayService()
417{
418 struct DISPLAYSTATE *pService = (struct DISPLAYSTATE *)RTMemAlloc(sizeof(*pService));
419
420 if (!pService)
421 VBClFatalError(("Out of memory\n"));
422 pService->pInterface = &vbclDisplayInterface;
423 pService->magic = DISPLAYSTATE_MAGIC;
424 pService->mfInit = false;
425 return &pService->pInterface;
426}
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