VirtualBox

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

Last change on this file since 57357 was 57357, checked in by vboxsync, 9 years ago

Additions/x11/VBoxClient: remove global VT switching handling again.

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