VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/display-svga-x11.cpp@ 81052

Last change on this file since 81052 was 81052, checked in by vboxsync, 5 years ago

Additions/VBoxClient: Added proper logfile handling (and rotation support, like for VBoxService), together with additional information. Uses VBOXCLIENT_ log prefix, e.g. VBOXCLIENT_[RELEASE_]LOG.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.1 KB
Line 
1/* $Id: display-svga-x11.cpp 81052 2019-09-27 12:44:45Z vboxsync $ */
2/** @file
3 * X11 guest client - VMSVGA emulation resize event pass-through to X.Org
4 * guest driver.
5 */
6
7/*
8 * Copyright (C) 2017-2019 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/*
20 * Known things to test when changing this code. All assume a guest with VMSVGA
21 * active and controlled by X11 or Wayland, and Guest Additions installed and
22 * running, unless otherwise stated.
23 * - On Linux 4.6 and later guests, VBoxClient --vmsvga should be running as
24 * root and not as the logged-in user. Dynamic resizing should work for all
25 * screens in any environment which handles kernel resize notifications,
26 * including at log-in screens. Test GNOME Shell Wayland and GNOME Shell
27 * under X.Org or Unity or KDE at the log-in screen and after log-in.
28 * - Linux 4.10 changed the user-kernel-ABI introduced in 4.6: test both.
29 * - On other guests (than Linux 4.6 or later) running X.Org Server 1.3 or
30 * later, VBoxClient --vmsvga should never be running as root, and should run
31 * (and dynamic resizing and screen enable/disable should work for all
32 * screens) whenever a user is logged in to a supported desktop environment.
33 * - On guests running X.Org Server 1.2 or older, VBoxClient --vmsvga should
34 * never run as root and should run whenever a user is logged in to a
35 * supported desktop environment. Dynamic resizing should work for the first
36 * screen, and enabling others should not be possible.
37 * - When VMSVGA is not enabled, VBoxClient --vmsvga should never stay running.
38 */
39
40#include "VBoxClient.h"
41
42#include <VBox/VBoxGuestLib.h>
43
44#include <iprt/assert.h>
45#include <iprt/err.h>
46#include <iprt/file.h>
47#include <iprt/string.h>
48
49#include <sys/utsname.h>
50
51/** Maximum number of supported screens. DRM and X11 both limit this to 32. */
52/** @todo if this ever changes, dynamically allocate resizeable arrays in the
53 * context structure. */
54#define VMW_MAX_HEADS 32
55
56/* VMWare X.Org driver control parts definitions. */
57
58#include <X11/Xlibint.h>
59
60static bool checkRecentLinuxKernel(void)
61{
62 struct utsname name;
63
64 if (uname(&name) == -1)
65 VBClLogFatalError("Failed to get kernel name\n");
66 if (strcmp(name.sysname, "Linux"))
67 return false;
68 return (RTStrVersionCompare(name.release, "4.6") >= 0);
69}
70
71struct X11CONTEXT
72{
73 Display *pDisplay;
74 int hRandRMajor;
75 int hVMWMajor;
76};
77
78static void x11Connect(struct X11CONTEXT *pContext)
79{
80 int dummy;
81
82 if (pContext->pDisplay != NULL)
83 VBClLogFatalError("%s called with bad argument\n", __func__);
84 pContext->pDisplay = XOpenDisplay(NULL);
85 if (pContext->pDisplay == NULL)
86 return;
87 if ( !XQueryExtension(pContext->pDisplay, "RANDR",
88 &pContext->hRandRMajor, &dummy, &dummy)
89 || !XQueryExtension(pContext->pDisplay, "VMWARE_CTRL",
90 &pContext->hVMWMajor, &dummy, &dummy))
91 {
92 XCloseDisplay(pContext->pDisplay);
93 pContext->pDisplay = NULL;
94 }
95}
96
97#define X11_VMW_TOPOLOGY_REQ 2
98struct X11VMWRECT /* xXineramaScreenInfo in Xlib headers. */
99{
100 int16_t x;
101 int16_t y;
102 uint16_t w;
103 uint16_t h;
104};
105AssertCompileSize(struct X11VMWRECT, 8);
106
107struct X11REQHEADER
108{
109 uint8_t hMajor;
110 uint8_t idType;
111 uint16_t cd;
112};
113
114struct X11VMWTOPOLOGYREQ
115{
116 struct X11REQHEADER header;
117 uint32_t idX11Screen;
118 uint32_t cScreens;
119 uint32_t u32Pad;
120 struct X11VMWRECT aRects[1];
121};
122AssertCompileSize(struct X11VMWTOPOLOGYREQ, 24);
123
124#define X11_VMW_TOPOLOGY_REPLY_SIZE 32
125
126#define X11_VMW_RESOLUTION_REQUEST 1
127struct X11VMWRESOLUTIONREQ
128{
129 struct X11REQHEADER header;
130 uint32_t idX11Screen;
131 uint32_t w;
132 uint32_t h;
133};
134AssertCompileSize(struct X11VMWRESOLUTIONREQ, 16);
135
136#define X11_VMW_RESOLUTION_REPLY_SIZE 32
137
138#define X11_RANDR_GET_SCREEN_REQUEST 5
139struct X11RANDRGETSCREENREQ
140{
141 struct X11REQHEADER header;
142 uint32_t hWindow;
143};
144AssertCompileSize(struct X11RANDRGETSCREENREQ, 8);
145
146#define X11_RANDR_GET_SCREEN_REPLY_SIZE 32
147
148/* This was a macro in old Xlib versions and a function in newer ones; the
149 * display members touched by the macro were declared as ABI for compatibility
150 * reasons. To simplify building with different generations, we duplicate the
151 * code. */
152static void x11GetRequest(struct X11CONTEXT *pContext, uint8_t hMajor,
153 uint8_t idType, size_t cb, struct X11REQHEADER **ppReq)
154{
155 if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax)
156 _XFlush(pContext->pDisplay);
157 if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax)
158 VBClLogFatalError("%s display buffer overflow\n", __func__);
159 if (cb % 4 != 0)
160 VBClLogFatalError("%s bad parameter\n", __func__);
161 pContext->pDisplay->last_req = pContext->pDisplay->bufptr;
162 *ppReq = (struct X11REQHEADER *)pContext->pDisplay->bufptr;
163 (*ppReq)->hMajor = hMajor;
164 (*ppReq)->idType = idType;
165 (*ppReq)->cd = cb / 4;
166 pContext->pDisplay->bufptr += cb;
167 pContext->pDisplay->request++;
168}
169
170static void x11SendHints(struct X11CONTEXT *pContext, struct X11VMWRECT *pRects,
171 unsigned cRects)
172{
173 unsigned i;
174 struct X11VMWTOPOLOGYREQ *pReqTopology;
175 uint8_t repTopology[X11_VMW_TOPOLOGY_REPLY_SIZE];
176 struct X11VMWRESOLUTIONREQ *pReqResolution;
177 uint8_t repResolution[X11_VMW_RESOLUTION_REPLY_SIZE];
178
179 if (!VALID_PTR(pContext->pDisplay))
180 VBClLogFatalError("%s bad display argument\n", __func__);
181 if (cRects == 0)
182 return;
183 /* Try a topology (multiple screen) request. */
184 x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_TOPOLOGY_REQ,
185 sizeof(struct X11VMWTOPOLOGYREQ)
186 + sizeof(struct X11VMWRECT) * (cRects - 1),
187 (struct X11REQHEADER **)&pReqTopology);
188 pReqTopology->idX11Screen = DefaultScreen(pContext->pDisplay);
189 pReqTopology->cScreens = cRects;
190 for (i = 0; i < cRects; ++i)
191 pReqTopology->aRects[i] = pRects[i];
192 _XSend(pContext->pDisplay, NULL, 0);
193 if (_XReply(pContext->pDisplay, (xReply *)&repTopology, 0, xTrue))
194 return;
195 /* That failed, so try the old single screen set resolution. We prefer
196 * simpler code to negligeably improved efficiency, so we just always try
197 * both requests instead of doing version checks or caching. */
198 x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_RESOLUTION_REQUEST,
199 sizeof(struct X11VMWRESOLUTIONREQ),
200 (struct X11REQHEADER **)&pReqResolution);
201 pReqResolution->idX11Screen = DefaultScreen(pContext->pDisplay);
202 pReqResolution->w = pRects[0].w;
203 pReqResolution->h = pRects[0].h;
204 if (_XReply(pContext->pDisplay, (xReply *)&repResolution, 0, xTrue))
205 return;
206 /* What now? */
207 VBClLogFatalError("%s failed to set resolution\n", __func__);
208}
209
210/** Call RRGetScreenInfo to wake up the server to the new modes. */
211static void x11GetScreenInfo(struct X11CONTEXT *pContext)
212{
213 struct X11RANDRGETSCREENREQ *pReqGetScreen;
214 uint8_t repGetScreen[X11_RANDR_GET_SCREEN_REPLY_SIZE];
215
216 if (!VALID_PTR(pContext->pDisplay))
217 VBClLogFatalError("%s bad display argument\n", __func__);
218 x11GetRequest(pContext, pContext->hRandRMajor, X11_RANDR_GET_SCREEN_REQUEST,
219 sizeof(struct X11RANDRGETSCREENREQ),
220 (struct X11REQHEADER **)&pReqGetScreen);
221 pReqGetScreen->hWindow = DefaultRootWindow(pContext->pDisplay);
222 _XSend(pContext->pDisplay, NULL, 0);
223 if (!_XReply(pContext->pDisplay, (xReply *)&repGetScreen, 0, xTrue))
224 VBClLogFatalError("%s failed to set resolution\n", __func__);
225}
226
227static const char *getName()
228{
229 return "Display SVGA X11";
230}
231
232static const char *getPidFilePath()
233{
234 return ".vboxclient-display-svga-x11.pid";
235}
236
237static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
238{
239 (void)ppInterface;
240 (void)fDaemonised;
241 struct X11CONTEXT x11Context = { NULL };
242 unsigned i;
243 int rc;
244 struct X11VMWRECT aRects[VMW_MAX_HEADS];
245 unsigned cHeads;
246
247 if (checkRecentLinuxKernel())
248 return VINF_SUCCESS;
249 x11Connect(&x11Context);
250 if (x11Context.pDisplay == NULL)
251 return VINF_SUCCESS;
252 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
253 if (RT_FAILURE(rc))
254 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
255 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
256 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
257 return VINF_SUCCESS;
258 if (RT_FAILURE(rc))
259 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
260 for (;;)
261 {
262 uint32_t events;
263 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
264 uint32_t cDisplaysOut;
265
266 /* Query the first size without waiting. This lets us e.g. pick up
267 * the last event before a guest reboot when we start again after. */
268 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, true);
269 if (RT_FAILURE(rc))
270 VBClLogFatalError("Failed to get display change request, rc=%Rrc\n", rc);
271 if (cDisplaysOut > VMW_MAX_HEADS)
272 VBClLogFatalError("Display change request contained, rc=%Rrc\n", rc);
273 for (i = 0, cHeads = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
274 {
275 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
276 {
277 if ((i == 0) || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
278 {
279 aRects[cHeads].x = aDisplays[i].xOrigin < INT16_MAX
280 ? (int16_t)aDisplays[i].xOrigin : 0;
281 aRects[cHeads].y = aDisplays[i].yOrigin < INT16_MAX
282 ? (int16_t)aDisplays[i].yOrigin : 0;
283 } else {
284 aRects[cHeads].x = aRects[cHeads - 1].x + aRects[cHeads - 1].w;
285 aRects[cHeads].y = aRects[cHeads - 1].y;
286 }
287 aRects[cHeads].w = (int16_t)RT_MIN(aDisplays[i].cx, INT16_MAX);
288 aRects[cHeads].h = (int16_t)RT_MIN(aDisplays[i].cy, INT16_MAX);
289 ++cHeads;
290 }
291 }
292 x11SendHints(&x11Context, aRects, cHeads);
293 x11GetScreenInfo(&x11Context);
294 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
295 if (RT_FAILURE(rc))
296 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
297 }
298}
299
300static struct VBCLSERVICE interface =
301{
302 getName,
303 getPidFilePath,
304 VBClServiceDefaultHandler, /* Init */
305 run,
306 VBClServiceDefaultCleanup
307}, *pInterface = &interface;
308
309struct VBCLSERVICE **VBClDisplaySVGAX11Service()
310{
311 return &pInterface;
312}
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