VirtualBox

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

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

Additions/x11: unused variables

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 KB
Line 
1/* $Id: display.cpp 56211 2015-06-03 08:48:19Z 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
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(bool fDisableEvents)
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 if (fDisableEvents)
97 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED | 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 long *paSizeHints = (long *)RTMemTmpAllocZ(pState->cScreensTracked * sizeof(long) * 2);
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 paSizeHints[2 * i] = (pState->paScreenInformation[i].cx & 0x8fff) << 16
185 | (pState->paScreenInformation[i].cy & 0x8fff);
186 else if (pState->paScreenInformation[i].cx != 0 && pState->paScreenInformation[i].cy != 0)
187 paSizeHints[2 * i] = -1;
188 if ( pState->paScreenInformation[i].fEnabled
189 && pState->paScreenInformation[i].fUpdatePosition)
190 paSizeHints[2 * i + 1] = (pState->paScreenInformation[i].x & 0x8fff) << 16
191 | (pState->paScreenInformation[i].y & 0x8fff);
192 else
193 paSizeHints[2 * i + 1] = -1;
194 }
195 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOX_SIZE_HINTS", 0),
196 XA_INTEGER, 32, PropModeReplace, (unsigned char *)paSizeHints, pState->cScreensTracked * 2);
197 XFlush(pState->pDisplay);
198 RTMemTmpFree(paSizeHints);
199}
200
201static void notifyXServerRandR11(struct DISPLAYSTATE *pState)
202{
203 char szCommand[256];
204
205 /** @note The xrandr command can fail if something else accesses RandR at
206 * the same time. We just ignore failure for now and let the user try
207 * again as we do not know what someone else is doing. */
208 if ( pState->paScreenInformation[0].fUpdateSize
209 && pState->paScreenInformation[0].cx > 0 && pState->paScreenInformation[0].cy > 0)
210 {
211 RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u",
212 pState->pcszXrandr, pState->paScreenInformation[0].cx, pState->paScreenInformation[0].cy);
213 system(szCommand);
214 pState->paScreenInformation[0].fUpdateSize = false;
215 }
216}
217
218static void updateMouseCapabilities(struct DISPLAYSTATE *pState)
219{
220 uint32_t fFeatures = 0;
221 int rc;
222
223 rc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
224
225 if (rc != VINF_SUCCESS)
226 VBClFatalError(("Failed to get mouse status, rc=%Rrc\n", rc));
227 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),
228 XInternAtom(pState->pDisplay, "VBOX_MOUSE_CAPABILITIES", 0), XA_INTEGER, 32, PropModeReplace,
229 (unsigned char *)&fFeatures, 1);
230 XFlush(pState->pDisplay);
231}
232
233/**
234 * Display change request monitor thread function.
235 */
236static void runDisplay(struct DISPLAYSTATE *pState)
237{
238 int rc;
239 unsigned i, cScreensTracked;
240
241 LogRelFlowFunc(("\n"));
242 rc = VbglR3VideoModeGetHighestSavedScreen(&cScreensTracked);
243 if (rc != VINF_SUCCESS && rc != VERR_NOT_SUPPORTED)
244 VBClFatalError(("Failed to get the number of saved screen modes, rc=%Rrc\n", rc));
245 /* Make sure that we have an entry for screen 1 at least. */
246 updateScreenInformation(pState, 1024, 768, 0, 1, 0, 0, true, false);
247 if (rc == VINF_SUCCESS)
248 {
249 /* The "8" is for the sanity test below. */
250 for (i = 0; i < RT_MAX(cScreensTracked + 1, 8); ++i)
251 {
252 unsigned cx = 0, cy = 0, cBPP = 0, x = 0, y = 0;
253 bool fEnabled = true;
254
255 rc = VbglR3RetrieveVideoMode(i, &cx, &cy, &cBPP, &x, &y,
256 &fEnabled);
257 /* Sanity test for VbglR3VideoModeGetHighestSavedScreen(). */
258 if (i > cScreensTracked && rc != VERR_NOT_FOUND)
259 VBClFatalError(("Internal error retrieving the number of saved screen modes.\n"));
260 if (rc == VINF_SUCCESS)
261 updateScreenInformation(pState, cx, cy, cBPP, i, x, y, fEnabled, true);
262 }
263 }
264 while (true)
265 {
266 uint32_t fEvents;
267 updateMouseCapabilities(pState);
268 updateSizeHintsProperty(pState);
269 if (!pState->fHaveRandR12)
270 notifyXServerRandR11(pState);
271 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED, RT_INDEFINITE_WAIT,
272 &fEvents);
273 /* Interrupted is used to re-set the mode without changing it. */
274 if (rc == VERR_INTERRUPTED)
275 rc = VINF_SUCCESS;
276 if (RT_FAILURE(rc)) /* VERR_NO_MEMORY? */
277 VBClFatalError(("event wait failed, rc=%Rrc\n", rc));
278 /* If it is a size hint, set the new size. */
279 if (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
280 {
281 uint32_t cx = 0, cy = 0, cBPP = 0, iDisplay = 0, x = 0, y = 0;
282 bool fEnabled = true, fUpdatePosition = true;
283 VMMDevSeamlessMode Mode;
284
285 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBPP, &iDisplay,
286 &x, &y, &fEnabled,
287 &fUpdatePosition, true);
288 if (rc != VINF_SUCCESS)
289 VBClFatalError(("Failed to get display change request, rc=%Rrc\n",
290 rc));
291 else
292 LogRelFlowFunc(("Got size hint from host cx=%d, cy=%d, bpp=%d, iDisplay=%d, x=%d, y=%d fEnabled=%d\n",
293 cx, cy, cBPP, iDisplay, x, y, fEnabled));
294 if (iDisplay > INT32_MAX)
295 VBClFatalError(("Received a size hint for too high display number %u\n",
296 (unsigned) iDisplay));
297 updateScreenInformation(pState, cx, cy, cBPP, iDisplay, x, y, fEnabled, fUpdatePosition);
298 rc = VbglR3SeamlessGetLastEvent(&Mode);
299 if (RT_FAILURE(rc))
300 VBClFatalError(("Failed to check seamless mode, rc=%Rrc\n", rc));
301 if (Mode == VMMDev_Seamless_Disabled)
302 {
303 rc = VbglR3SaveVideoMode(iDisplay, cx, cy, cBPP, x, y,
304 fEnabled);
305 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED)
306 VBClFatalError(("Failed to save size hint, rc=%Rrc\n", rc));
307 }
308 }
309 }
310}
311
312static const char *getPidFilePath()
313{
314 return ".vboxclient-display.pid";
315}
316
317static struct DISPLAYSTATE *getStateFromInterface(struct VBCLSERVICE **ppInterface)
318{
319 struct DISPLAYSTATE *pSelf = (struct DISPLAYSTATE *)ppInterface;
320 if (pSelf->magic != DISPLAYSTATE_MAGIC)
321 VBClFatalError(("Bad display service object!\n"));
322 return pSelf;
323}
324
325static int init(struct VBCLSERVICE **ppInterface)
326{
327 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
328 int rc;
329
330 if (pSelf->mfInit)
331 return VERR_WRONG_ORDER;
332 rc = initDisplay(pSelf);
333 if (RT_FAILURE(rc))
334 return rc;
335 rc = enableEventsAndCaps();
336 if (RT_SUCCESS(rc))
337 pSelf->mfInit = true;
338 return rc;
339}
340
341static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
342{
343 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
344 int rc;
345
346 if (!pSelf->mfInit)
347 return VERR_WRONG_ORDER;
348 rc = VBClStartVTMonitor();
349 if (RT_FAILURE(rc))
350 VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
351 runDisplay(pSelf);
352 return VERR_INTERNAL_ERROR; /* "Should never reach here." */
353}
354
355static int pause(struct VBCLSERVICE **ppInterface)
356{
357 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
358
359 if (!pSelf->mfInit)
360 return VERR_WRONG_ORDER;
361 return disableEventsAndCaps(false);
362}
363
364static int resume(struct VBCLSERVICE **ppInterface)
365{
366 struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface);
367 int rc;
368
369 if (!pSelf->mfInit)
370 return VERR_WRONG_ORDER;
371 rc = enableEventsAndCaps();
372 /* RandR 1.1-based drivers only let us change mode when we are not switched
373 * out, so interrupt the wait when we switch in and re-set it. */
374 VbglR3InterruptEventWaits();
375 return rc;
376}
377
378static void cleanup(struct VBCLSERVICE **ppInterface)
379{
380 NOREF(ppInterface);
381 disableEventsAndCaps(true);
382 VbglR3Term();
383}
384
385struct VBCLSERVICE vbclDisplayInterface =
386{
387 getPidFilePath,
388 init,
389 run,
390 pause,
391 resume,
392 cleanup
393};
394
395struct VBCLSERVICE **VBClGetDisplayService()
396{
397 struct DISPLAYSTATE *pService = (struct DISPLAYSTATE *)RTMemAlloc(sizeof(*pService));
398
399 if (!pService)
400 VBClFatalError(("Out of memory\n"));
401 pService->pInterface = &vbclDisplayInterface;
402 pService->magic = DISPLAYSTATE_MAGIC;
403 pService->mfInit = false;
404 return &pService->pInterface;
405}
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