VirtualBox

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

Last change on this file since 67320 was 67320, checked in by vboxsync, 7 years ago

bugref:8533: Additions/x11: fully support VMSVGA
Use AcquireGuestCaps in the vmsvga VBoxClient service and do not create a pidfile.

This change makes the VBoxClient vmsvga service use the new AcquireGuestCaps
function in the R3 guest library, and stop creating a pidfile, but still
daemonise. The pidfile is no longer needed to prevent multiple copies of
the service from running due to the use of AcquireGuestCaps, and was not very
clean either, as the service running as root created it in the root home. If
we do decide to go back to creating pidfiles this needs to be fixed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: display-svga.cpp 67320 2017-06-09 13:18:12Z vboxsync $ */
2/** @file
3 * X11 guest client - VMSVGA emulation resize event pass-through to guest
4 * driver.
5 */
6
7/*
8 * Copyright (C) 2016 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 or
27 * Unity or KDE at the log-in screen and after log-in.
28 * - On other guests (than Linux 4.6 or later) running X.Org Server 1.3 or
29 * later, VBoxClient --vmsvga should never be running as root, and should run
30 * (and dynamic resizing and screen enable/disable should work for all
31 * screens) whenever a user is logged in to a supported desktop environment.
32 * - On guests running X.Org Server 1.2 or older, VBoxClient --vmsvga should
33 * never run as root and should run whenever a user is logged in to a
34 * supported desktop environment. Dynamic resizing should work for the first
35 * screen, and enabling others should not be possible.
36 * - When VMSVGA is not enabled, VBoxClient --vmsvga should never stay running.
37 */
38
39#include "VBoxClient.h"
40
41#include <VBox/VBoxGuestLib.h>
42
43#include <iprt/assert.h>
44#include <iprt/file.h>
45#include <iprt/string.h>
46
47/** Maximum number of supported screens. DRM and X11 both limit this to 32. */
48#define VMW_MAX_HEADS 32
49
50/* VMWare kernel driver control parts definitions. */
51
52#ifdef RT_OS_LINUX
53# include <sys/ioctl.h>
54#else /* Solaris and BSDs, in case they ever adopt the DRM driver. */
55# include <sys/ioccom.h>
56#endif
57
58#define DRM_DRIVER_NAME "vmwgfx"
59
60/** DRM version structure. */
61struct DRMVERSION
62{
63 int cMajor;
64 int cMinor;
65 int cPatchLevel;
66 size_t cbName;
67 char *pszName;
68 size_t cbDate;
69 char *pszDate;
70 size_t cbDescription;
71 char *pszDescription;
72};
73AssertCompileSize(struct DRMVERSION, 8 + 7 * sizeof(void *));
74
75/** Rectangle structure for geometry of a single screen. */
76struct DRMVMWRECT
77{
78 int32_t x;
79 int32_t y;
80 uint32_t w;
81 uint32_t h;
82};
83AssertCompileSize(struct DRMVMWRECT, 16);
84
85#define DRM_IOCTL_VERSION _IOWR('d', 0x00, struct DRMVERSION)
86
87struct DRMCONTEXT
88{
89 RTFILE hDevice;
90};
91
92static void drmConnect(struct DRMCONTEXT *pContext)
93{
94 unsigned i;
95 RTFILE hDevice;
96
97 if (pContext->hDevice != NIL_RTFILE)
98 VBClFatalError(("%s called with bad argument\n", __func__));
99 /* Try to open the SVGA DRM device. */
100 for (i = 0; i < 64; ++i)
101 {
102 char szPath[64];
103 struct DRMVERSION version;
104 char szName[sizeof(DRM_DRIVER_NAME)];
105 int rc;
106
107 /* Control devices for drm graphics driver control devices go from
108 * controlD64 to controlD127. The driver takes resize hints via the
109 * control device. */
110 rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/controlD%u", i + 64);
111 if (RT_FAILURE(rc))
112 VBClFatalError(("RTStrPrintf of device path failed, rc=%Rrc\n", rc));
113 rc = RTFileOpen(&hDevice, szPath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
114 if (RT_FAILURE(rc))
115 continue;
116 RT_ZERO(version);
117 version.cbName = sizeof(szName);
118 version.pszName = szName;
119 rc = RTFileIoCtl(hDevice, DRM_IOCTL_VERSION, &version, sizeof(version), NULL);
120 if ( RT_SUCCESS(rc)
121 && !strncmp(szName, DRM_DRIVER_NAME, sizeof(DRM_DRIVER_NAME) - 1)
122 && ( version.cMajor > 2
123 || (version.cMajor == 2 && version.cMinor > 9)))
124 break;
125 hDevice = NIL_RTFILE;
126 }
127 pContext->hDevice = hDevice;
128}
129
130/** Preferred screen layout information for DRM_VMW_UPDATE_LAYOUT IoCtl. The
131 * rects argument is a cast pointer to an array of drm_vmw_rect. */
132struct DRMVMWUPDATELAYOUT {
133 uint32_t cOutputs;
134 uint32_t u32Pad;
135 uint64_t ptrRects;
136};
137AssertCompileSize(struct DRMVMWUPDATELAYOUT, 16);
138
139#define DRM_IOCTL_VMW_UPDATE_LAYOUT \
140 _IOW('d', 0x40 + 20, struct DRMVMWUPDATELAYOUT)
141
142static void drmSendHints(struct DRMCONTEXT *pContext, struct DRMVMWRECT *paRects,
143 unsigned cHeads)
144{
145 int rc;
146 struct DRMVMWUPDATELAYOUT ioctlLayout;
147
148 if (pContext->hDevice == NIL_RTFILE)
149 VBClFatalError(("%s bad device argument.\n", __func__));
150 ioctlLayout.cOutputs = cHeads;
151 ioctlLayout.ptrRects = (uint64_t)paRects;
152 rc = RTFileIoCtl(pContext->hDevice, DRM_IOCTL_VMW_UPDATE_LAYOUT,
153 &ioctlLayout, sizeof(ioctlLayout), NULL);
154 if (RT_FAILURE(rc) && rc != VERR_INVALID_PARAMETER)
155 VBClFatalError(("Failure updating layout, rc=%Rrc\n", rc));
156}
157
158/* VMWare X.Org driver control parts definitions. */
159
160#include <X11/Xlibint.h>
161
162struct X11CONTEXT
163{
164 Display *pDisplay;
165 int hRandRMajor;
166 int hVMWMajor;
167};
168
169static void x11Connect(struct X11CONTEXT *pContext)
170{
171 int dummy;
172
173 if (pContext->pDisplay != NULL)
174 VBClFatalError(("%s called with bad argument\n", __func__));
175 pContext->pDisplay = XOpenDisplay(NULL);
176 if (pContext->pDisplay == NULL)
177 return;
178 if ( !XQueryExtension(pContext->pDisplay, "RANDR",
179 &pContext->hRandRMajor, &dummy, &dummy)
180 || !XQueryExtension(pContext->pDisplay, "VMWARE_CTRL",
181 &pContext->hVMWMajor, &dummy, &dummy))
182 {
183 XCloseDisplay(pContext->pDisplay);
184 pContext->pDisplay = NULL;
185 }
186}
187
188#define X11_VMW_TOPOLOGY_REQ 2
189struct X11VMWRECT /* xXineramaScreenInfo in Xlib headers. */
190{
191 int16_t x;
192 int16_t y;
193 uint16_t w;
194 uint16_t h;
195};
196AssertCompileSize(struct X11VMWRECT, 8);
197
198struct X11REQHEADER
199{
200 uint8_t hMajor;
201 uint8_t idType;
202 uint16_t cd;
203};
204
205struct X11VMWTOPOLOGYREQ
206{
207 struct X11REQHEADER header;
208 uint32_t idX11Screen;
209 uint32_t cScreens;
210 uint32_t u32Pad;
211 struct X11VMWRECT aRects[1];
212};
213AssertCompileSize(struct X11VMWTOPOLOGYREQ, 24);
214
215#define X11_VMW_TOPOLOGY_REPLY_SIZE 32
216
217#define X11_VMW_RESOLUTION_REQUEST 1
218struct X11VMWRESOLUTIONREQ
219{
220 struct X11REQHEADER header;
221 uint32_t idX11Screen;
222 uint32_t x;
223 uint32_t y;
224};
225AssertCompileSize(struct X11VMWRESOLUTIONREQ, 16);
226
227#define X11_VMW_RESOLUTION_REPLY_SIZE 32
228
229#define X11_RANDR_GET_SCREEN_REQUEST 5
230struct X11RANDRGETSCREENREQ
231{
232 struct X11REQHEADER header;
233 uint32_t hWindow;
234};
235AssertCompileSize(struct X11RANDRGETSCREENREQ, 8);
236
237#define X11_RANDR_GET_SCREEN_REPLY_SIZE 32
238
239/* This was a macro in old Xlib versions and a function in newer ones; the
240 * display members touched by the macro were declared as ABI for compatibility
241 * reasons. To simplify building with different generations, we duplicate the
242 * code. */
243static void x11GetRequest(struct X11CONTEXT *pContext, uint8_t hMajor,
244 uint8_t idType, size_t cb, struct X11REQHEADER **ppReq)
245{
246 if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax)
247 _XFlush(pContext->pDisplay);
248 if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax)
249 VBClFatalError(("%s display buffer overflow.\n", __func__));
250 if (cb % 4 != 0)
251 VBClFatalError(("%s bad parameter.\n", __func__));
252 pContext->pDisplay->last_req = pContext->pDisplay->bufptr;
253 *ppReq = (struct X11REQHEADER *)pContext->pDisplay->bufptr;
254 (*ppReq)->hMajor = hMajor;
255 (*ppReq)->idType = idType;
256 (*ppReq)->cd = cb / 4;
257 pContext->pDisplay->bufptr += cb;
258 pContext->pDisplay->request++;
259}
260
261static void x11SendHints(struct X11CONTEXT *pContext, struct DRMVMWRECT *pRects,
262 unsigned cRects)
263{
264 unsigned i;
265 struct X11VMWTOPOLOGYREQ *pReqTopology;
266 uint8_t repTopology[X11_VMW_TOPOLOGY_REPLY_SIZE];
267 struct X11VMWRESOLUTIONREQ *pReqResolution;
268 uint8_t repResolution[X11_VMW_RESOLUTION_REPLY_SIZE];
269
270 if (!VALID_PTR(pContext->pDisplay))
271 VBClFatalError(("%s bad display argument.\n", __func__));
272 if (cRects == 0)
273 return;
274 /* Try a topology (multiple screen) request. */
275 x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_TOPOLOGY_REQ,
276 sizeof(struct X11VMWTOPOLOGYREQ)
277 + sizeof(struct X11VMWRECT) * (cRects - 1),
278 (struct X11REQHEADER **)&pReqTopology);
279 pReqTopology->idX11Screen = DefaultScreen(pContext->pDisplay);
280 pReqTopology->cScreens = cRects;
281 for (i = 0; i < cRects; ++i)
282 {
283 pReqTopology->aRects[i].x = pRects[i].x;
284 pReqTopology->aRects[i].y = pRects[i].y;
285 pReqTopology->aRects[i].w = pRects[i].w;
286 pReqTopology->aRects[i].h = pRects[i].h;
287 }
288 _XSend(pContext->pDisplay, NULL, 0);
289 if (_XReply(pContext->pDisplay, (xReply *)&repTopology, 0, xTrue))
290 return;
291 /* That failed, so try the old single screen set resolution. We prefer
292 * simpler code to negligeably improved efficiency, so we just always try
293 * both requests instead of doing version checks or caching. */
294 x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_RESOLUTION_REQUEST,
295 sizeof(struct X11VMWTOPOLOGYREQ),
296 (struct X11REQHEADER **)&pReqResolution);
297 pReqResolution->idX11Screen = DefaultScreen(pContext->pDisplay);
298 pReqResolution->x = pRects[0].x;
299 pReqResolution->y = pRects[0].y;
300 if (_XReply(pContext->pDisplay, (xReply *)&repResolution, 0, xTrue))
301 return;
302 /* What now? */
303 VBClFatalError(("%s failed to set resolution\n", __func__));
304}
305
306/** Call RRGetScreenInfo to wake up the server to the new modes. */
307static void x11GetScreenInfo(struct X11CONTEXT *pContext)
308{
309 struct X11RANDRGETSCREENREQ *pReqGetScreen;
310 uint8_t repGetScreen[X11_RANDR_GET_SCREEN_REPLY_SIZE];
311
312 if (!VALID_PTR(pContext->pDisplay))
313 VBClFatalError(("%s bad display argument.\n", __func__));
314 x11GetRequest(pContext, pContext->hRandRMajor, X11_RANDR_GET_SCREEN_REQUEST,
315 sizeof(struct X11RANDRGETSCREENREQ),
316 (struct X11REQHEADER **)&pReqGetScreen);
317 pReqGetScreen->hWindow = DefaultRootWindow(pContext->pDisplay);
318 _XSend(pContext->pDisplay, NULL, 0);
319 if (!_XReply(pContext->pDisplay, (xReply *)&repGetScreen, 0, xTrue))
320 VBClFatalError(("%s failed to set resolution\n", __func__));
321}
322
323static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
324{
325 (void)ppInterface;
326 (void)fDaemonised;
327 struct DRMCONTEXT drmContext = { NIL_RTFILE };
328 struct X11CONTEXT x11Context = { NULL };
329 unsigned i;
330 int rc;
331 uint32_t acx[VMW_MAX_HEADS] = { 0 };
332 uint32_t acy[VMW_MAX_HEADS] = { 0 };
333 uint32_t adx[VMW_MAX_HEADS] = { 0 };
334 uint32_t ady[VMW_MAX_HEADS] = { 0 };
335 uint32_t afEnabled[VMW_MAX_HEADS] = { false };
336 struct DRMVMWRECT aRects[VMW_MAX_HEADS];
337 unsigned cHeads;
338
339 drmConnect(&drmContext);
340 if (drmContext.hDevice == NIL_RTFILE)
341 {
342 x11Connect(&x11Context);
343 if (x11Context.pDisplay == NULL)
344 return VINF_SUCCESS;
345 }
346 /* Initialise the guest library. */
347 rc = VbglR3InitUser();
348 if (RT_FAILURE(rc))
349 VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
350 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
351 if (RT_FAILURE(rc))
352 VBClFatalError(("Failed to request display change events, rc=%Rrc\n", rc));
353 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
354 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
355 return VINF_SUCCESS;
356 if (RT_FAILURE(rc))
357 VBClFatalError(("Failed to register resizing support, rc=%Rrc\n", rc));
358 for (;;)
359 {
360 uint32_t events;
361
362 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
363 if (RT_FAILURE(rc))
364 VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc));
365 while (rc != VERR_TIMEOUT)
366 {
367 uint32_t cx, cy, cBits, dx, dy, idx;
368 bool fEnabled, fChangeOrigin;
369
370 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBits, &idx, &dx, &dy, &fEnabled, &fChangeOrigin, true);
371 if (RT_FAILURE(rc))
372 VBClFatalError(("Failed to get display change request, rc=%Rrc\n", rc));
373 if (idx < VMW_MAX_HEADS)
374 {
375 acx[idx] = cx;
376 acy[idx] = cy;
377 if (fChangeOrigin)
378 adx[idx] = dx < INT32_MAX ? dx : 0;
379 if (fChangeOrigin)
380 ady[idx] = dy < INT32_MAX ? dy : 0;
381 afEnabled[idx] = fEnabled;
382 }
383 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0, &events);
384 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
385 VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc));
386 }
387 for (i = 0, cHeads = 0; i < VMW_MAX_HEADS; ++i)
388 {
389 if (afEnabled[i])
390 {
391 aRects[cHeads].x = (int32_t)adx[i];
392 aRects[cHeads].y = (int32_t)ady[i];
393 aRects[cHeads].w = acx[i];
394 aRects[cHeads].h = acy[i];
395 ++cHeads;
396 }
397 }
398 if (drmContext.hDevice != NIL_RTFILE)
399 drmSendHints(&drmContext, aRects, cHeads);
400 else
401 {
402 x11SendHints(&x11Context, aRects, cHeads);
403 x11GetScreenInfo(&x11Context);
404 }
405 }
406}
407
408struct VBCLSERVICE interface =
409{
410 NULL, /* No pidfile needed, as we use acquire capability for exclusion. */
411 VBClServiceDefaultHandler, /* Init */
412 run,
413 VBClServiceDefaultCleanup,
414 true /* fDaemonise */
415}, *pInterface = &interface;
416
417struct VBCLSERVICE **VBClDisplaySVGAService()
418{
419 return &pInterface;
420}
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