VirtualBox

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

Last change on this file since 95478 was 94422, checked in by vboxsync, 3 years ago

X11/VBoxClient: Doxygen fix (use RT_NOREF) + todo. bugref:10199

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.2 KB
Line 
1/* $Id: display.cpp 94422 2022-03-31 21:59:39Z vboxsync $ */
2/** @file
3 * X11 guest client - display management.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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#include "VBoxClient.h"
19
20#include <iprt/errcore.h>
21#include <iprt/file.h>
22#include <iprt/mem.h>
23#include <iprt/string.h>
24
25#include <X11/Xlib.h>
26#include <X11/Xatom.h>
27#include <X11/extensions/Xrandr.h>
28
29/** @todo this should probably be replaced by something IPRT */
30/* For system() and WEXITSTATUS() */
31#include <stdlib.h>
32#include <sys/types.h>
33#include <sys/wait.h>
34#include <errno.h>
35#include <limits.h>
36#include <poll.h>
37#include <time.h>
38#include <dlfcn.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/** State information needed for the service. The main VBoxClient code provides
47 * the daemon logic needed by all services. */
48struct DISPLAYSTATE
49{
50 /** Are we initialised yet? */
51 bool mfInit;
52 /** The connection to the server. */
53 Display *pDisplay;
54 /** The RandR extension base event number. */
55 int cRREventBase;
56 /** Can we use version 1.2 or later of the RandR protocol here? */
57 bool fHaveRandR12;
58 /** The command argument to use for the xrandr binary. Currently only
59 * used to support the non-standard location on some Solaris systems -
60 * would it make sense to use absolute paths on all systems? */
61 const char *pcszXrandr;
62 /** Was there a recent mode hint with no following root window resize, and
63 * if so, have we waited for a reasonable time? */
64 time_t timeLastModeHint;
65 /** Handle to libXrandr. */
66 void *pRandLibraryHandle;
67 /** Handle to pXRRSelectInput. */
68 void (*pXRRSelectInput) (Display *, Window, int);
69 /** Handle to pXRRQueryExtension. */
70 Bool (*pXRRQueryExtension) (Display *, int *, int *);
71};
72
73static struct DISPLAYSTATE g_DisplayState;
74
75static unsigned char *getRootProperty(struct DISPLAYSTATE *pState, const char *pszName,
76 long cItems, Atom type)
77{
78 Atom actualType = None;
79 int iFormat = 0;
80 unsigned long cReturned = 0;
81 unsigned long cAfter = 0;
82 unsigned char *pData = 0;
83
84 if (XGetWindowProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),
85 XInternAtom(pState->pDisplay, pszName, 0), 0, cItems,
86 False /* delete */, type, &actualType, &iFormat,
87 &cReturned, &cAfter, &pData))
88 return NULL;
89 return pData;
90}
91
92static void doResize(struct DISPLAYSTATE *pState)
93{
94 /** @note The xrandr command can fail if something else accesses RandR at
95 * the same time. We just ignore failure for now as we do not know what
96 * someone else is doing. */
97 if (!pState->fHaveRandR12)
98 {
99 char szCommand[256];
100 unsigned char *pData;
101
102 pData = getRootProperty(pState, "VBOXVIDEO_PREFERRED_MODE", 1, XA_INTEGER);
103 if (pData != NULL)
104 {
105 RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u",
106 pState->pcszXrandr, ((unsigned long *)pData)[0] >> 16, ((unsigned long *)pData)[0] & 0xFFFF);
107 int rcShutUpGcc = system(szCommand); RT_NOREF_PV(rcShutUpGcc);
108 XFree(pData);
109 }
110 }
111 else
112 {
113 const char szCommandBase[] =
114 "%s --output VGA-0 --auto --output VGA-1 --auto --right-of VGA-0 "
115 "--output VGA-2 --auto --right-of VGA-1 --output VGA-3 --auto --right-of VGA-2 "
116 "--output VGA-4 --auto --right-of VGA-3 --output VGA-5 --auto --right-of VGA-4 "
117 "--output VGA-6 --auto --right-of VGA-5 --output VGA-7 --auto --right-of VGA-6 "
118 "--output VGA-8 --auto --right-of VGA-7 --output VGA-9 --auto --right-of VGA-8 "
119 "--output VGA-10 --auto --right-of VGA-9 --output VGA-11 --auto --right-of VGA-10 "
120 "--output VGA-12 --auto --right-of VGA-11 --output VGA-13 --auto --right-of VGA-12 "
121 "--output VGA-14 --auto --right-of VGA-13 --output VGA-15 --auto --right-of VGA-14 "
122 "--output VGA-16 --auto --right-of VGA-15 --output VGA-17 --auto --right-of VGA-16 "
123 "--output VGA-18 --auto --right-of VGA-17 --output VGA-19 --auto --right-of VGA-18 "
124 "--output VGA-20 --auto --right-of VGA-19 --output VGA-21 --auto --right-of VGA-20 "
125 "--output VGA-22 --auto --right-of VGA-21 --output VGA-23 --auto --right-of VGA-22 "
126 "--output VGA-24 --auto --right-of VGA-23 --output VGA-25 --auto --right-of VGA-24 "
127 "--output VGA-26 --auto --right-of VGA-25 --output VGA-27 --auto --right-of VGA-26 "
128 "--output VGA-28 --auto --right-of VGA-27 --output VGA-29 --auto --right-of VGA-28 "
129 "--output VGA-30 --auto --right-of VGA-29 --output VGA-31 --auto --right-of VGA-30";
130 char szCommand[sizeof(szCommandBase) + 256];
131 RTStrPrintf(szCommand, sizeof(szCommand), szCommandBase, pState->pcszXrandr);
132 int rcShutUpGcc = system(szCommand); RT_NOREF_PV(rcShutUpGcc);
133 }
134}
135
136/** Main loop: handle display hot-plug events, property updates (which can
137 * signal VT switches hot-plug in old X servers). */
138static void runDisplay(struct DISPLAYSTATE *pState)
139{
140 Display *pDisplay = pState->pDisplay;
141 long cValue = 1;
142
143 /* One way or another we want the preferred mode at server start-up. */
144 doResize(pState);
145 XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask | StructureNotifyMask);
146 if (pState->fHaveRandR12)
147 pState->pXRRSelectInput(pDisplay, DefaultRootWindow(pDisplay), RRScreenChangeNotifyMask);
148 /* Semantics: when VBOXCLIENT_STARTED is set, pre-1.3 X.Org Server driver
149 * assumes that a client capable of handling mode hints will be present for the
150 * rest of the X session. If we crash things will not work as they should.
151 * I thought that preferable to implementing complex crash-handling logic.
152 */
153 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOXCLIENT_STARTED", 0),
154 XA_INTEGER, 32, PropModeReplace, (unsigned char *)&cValue, 1);
155 /* Interrupting this cleanly will be more work than making it robust
156 * against spontaneous termination, especially as it will never get
157 * properly tested, so I will go for the second. */
158 while (true)
159 {
160 XEvent event;
161 struct pollfd PollFd;
162 int pollTimeOut = -1;
163 int cFds;
164
165 /* Do not handle overflow. */
166 if (pState->timeLastModeHint > 0 && pState->timeLastModeHint < INT_MAX - 2)
167 pollTimeOut = 2 - (time(0) - pState->timeLastModeHint);
168 PollFd.fd = ConnectionNumber(pDisplay);
169 PollFd.events = POLLIN; /* Hang-up is always reported. */
170 XFlush(pDisplay);
171 cFds = poll(&PollFd, 1, pollTimeOut >= 0 ? pollTimeOut * 1000 : -1);
172 while (XPending(pDisplay))
173 {
174 XNextEvent(pDisplay, &event);
175 /* This property is deleted when the server regains the virtual
176 * terminal. Force the main thread to call xrandr again, as old X
177 * servers could not handle it while switched out. */
178 if ( !pState->fHaveRandR12
179 && event.type == PropertyNotify
180 && event.xproperty.state == PropertyDelete
181 && event.xproperty.window == DefaultRootWindow(pDisplay)
182 && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_NO_VT", False))
183 doResize(pState);
184 if ( !pState->fHaveRandR12
185 && event.type == PropertyNotify
186 && event.xproperty.state == PropertyNewValue
187 && event.xproperty.window == DefaultRootWindow(pDisplay)
188 && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_PREFERRED_MODE", False))
189 doResize(pState);
190 if ( pState->fHaveRandR12
191 && event.type == pState->cRREventBase + RRScreenChangeNotify)
192 pState->timeLastModeHint = time(0);
193 if ( event.type == ConfigureNotify
194 && event.xproperty.window == DefaultRootWindow(pDisplay))
195 pState->timeLastModeHint = 0;
196 }
197 if (cFds == 0 && pState->timeLastModeHint > 0)
198 doResize(pState);
199 }
200}
201
202static int initDisplay(struct DISPLAYSTATE *pState)
203{
204 char szCommand[256];
205 int status;
206
207 pState->pRandLibraryHandle = dlopen("libXrandr.so", RTLD_LAZY /*| RTLD_LOCAL */);
208 if (!pState->pRandLibraryHandle)
209 pState->pRandLibraryHandle = dlopen("libXrandr.so.2", RTLD_LAZY /*| RTLD_LOCAL */);
210 if (!pState->pRandLibraryHandle)
211 pState->pRandLibraryHandle = dlopen("libXrandr.so.2.2.0", RTLD_LAZY /*| RTLD_LOCAL */);
212
213 if (!RT_VALID_PTR(pState->pRandLibraryHandle))
214 {
215 VBClLogFatalError("Could not locate libXrandr for dlopen\n");
216 return VERR_NOT_FOUND;
217 }
218
219 *(void **)(&pState->pXRRSelectInput) = dlsym(pState->pRandLibraryHandle, "XRRSelectInput");
220 *(void **)(&pState->pXRRQueryExtension) = dlsym(pState->pRandLibraryHandle, "XRRQueryExtension");
221
222 if ( !RT_VALID_PTR(pState->pXRRSelectInput)
223 || !RT_VALID_PTR(pState->pXRRQueryExtension))
224 {
225 VBClLogFatalError("Could not load required libXrandr symbols\n");
226 dlclose(pState->pRandLibraryHandle);
227 pState->pRandLibraryHandle = NULL;
228 return VERR_NOT_FOUND;
229 }
230
231 pState->pDisplay = XOpenDisplay(NULL);
232 if (!pState->pDisplay)
233 return VERR_NOT_FOUND;
234 if (!pState->pXRRQueryExtension(pState->pDisplay, &pState->cRREventBase, &status))
235 return VERR_NOT_FOUND;
236 pState->fHaveRandR12 = false;
237 pState->pcszXrandr = "xrandr";
238 if (RTFileExists("/usr/X11/bin/xrandr"))
239 pState->pcszXrandr = "/usr/X11/bin/xrandr";
240 status = system(pState->pcszXrandr);
241 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
242 VBClLogFatalError("Failed to execute the xrandr utility\n");
243 RTStrPrintf(szCommand, sizeof(szCommand), "%s --q12", pState->pcszXrandr);
244 status = system(szCommand);
245 if (WEXITSTATUS(status) == 0)
246 pState->fHaveRandR12 = true;
247 return VINF_SUCCESS;
248}
249
250/**
251 * @interface_method_impl{VBCLSERVICE,pfnInit}
252 */
253static DECLCALLBACK(int) init(void)
254{
255 struct DISPLAYSTATE *pSelf = &g_DisplayState;
256 int rc;
257
258 if (pSelf->mfInit)
259 return VERR_WRONG_ORDER;
260 rc = initDisplay(pSelf);
261 if (RT_FAILURE(rc))
262 return rc;
263 if (RT_SUCCESS(rc))
264 pSelf->mfInit = true;
265 return rc;
266}
267
268/**
269 * @interface_method_impl{VBCLSERVICE,pfnWorker}
270 */
271static DECLCALLBACK(int) run(bool volatile *pfShutdown)
272{
273 RT_NOREF(pfShutdown); /** @todo Probably very wrong not to check pfShutdown... Especially given no pfnStop implementation. */
274 struct DISPLAYSTATE *pSelf = &g_DisplayState;
275
276 if (!pSelf->mfInit)
277 return VERR_WRONG_ORDER;
278 runDisplay(pSelf);
279 return VERR_INTERNAL_ERROR; /* "Should never reach here." */
280}
281
282VBCLSERVICE g_SvcDisplayLegacy =
283{
284 "dp-legacy-x11", /* szName */
285 "Legacy display assistant", /* pszDescription */
286 ".vboxclient-display.pid", /* pszPidFilePath (no pid file lock) */
287 NULL, /* pszUsage */
288 NULL, /* pszOptions */
289 NULL, /* pfnOption */
290 init, /* pfnInit */
291 run, /* pfnWorker */
292 NULL, /* pfnStop */
293 NULL, /* pfnTerm */
294};
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