VirtualBox

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

Last change on this file since 75836 was 75836, checked in by vboxsync, 6 years ago

Additions/x11/VBoxClient: do not start on Linux 4.6 and later.
bugref:8533: Additions/x11: fully support VMSVGA
On Linux 4.6 and later we can send resize information straight to the kernel
driver, so we do not need to go through X11.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.2 KB
Line 
1/* $Id: display-svga-x11.cpp 75836 2018-11-30 10:52:50Z 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 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/file.h>
46#include <iprt/string.h>
47
48#include <sys/utsname.h>
49
50/** Maximum number of supported screens. DRM and X11 both limit this to 32. */
51/** @todo if this ever changes, dynamically allocate resizeable arrays in the
52 * context structure. */
53#define VMW_MAX_HEADS 32
54
55/* VMWare X.Org driver control parts definitions. */
56
57#include <X11/Xlibint.h>
58
59static bool checkRecentLinuxKernel(void)
60{
61 struct utsname name;
62
63 if (uname(&name))
64 VBClFatalError(("Failed to get kernel name.\n"));
65 if (strcmp(name.sysname, "Linux"))
66 return false;
67 return (RTStrVersionCompare(name.release, "4.6") >= 0);
68}
69
70struct X11CONTEXT
71{
72 Display *pDisplay;
73 int hRandRMajor;
74 int hVMWMajor;
75};
76
77static void x11Connect(struct X11CONTEXT *pContext)
78{
79 int dummy;
80
81 if (pContext->pDisplay != NULL)
82 VBClFatalError(("%s called with bad argument\n", __func__));
83 pContext->pDisplay = XOpenDisplay(NULL);
84 if (pContext->pDisplay == NULL)
85 return;
86 if ( !XQueryExtension(pContext->pDisplay, "RANDR",
87 &pContext->hRandRMajor, &dummy, &dummy)
88 || !XQueryExtension(pContext->pDisplay, "VMWARE_CTRL",
89 &pContext->hVMWMajor, &dummy, &dummy))
90 {
91 XCloseDisplay(pContext->pDisplay);
92 pContext->pDisplay = NULL;
93 }
94}
95
96#define X11_VMW_TOPOLOGY_REQ 2
97struct X11VMWRECT /* xXineramaScreenInfo in Xlib headers. */
98{
99 int16_t x;
100 int16_t y;
101 uint16_t w;
102 uint16_t h;
103};
104AssertCompileSize(struct X11VMWRECT, 8);
105
106struct X11REQHEADER
107{
108 uint8_t hMajor;
109 uint8_t idType;
110 uint16_t cd;
111};
112
113struct X11VMWTOPOLOGYREQ
114{
115 struct X11REQHEADER header;
116 uint32_t idX11Screen;
117 uint32_t cScreens;
118 uint32_t u32Pad;
119 struct X11VMWRECT aRects[1];
120};
121AssertCompileSize(struct X11VMWTOPOLOGYREQ, 24);
122
123#define X11_VMW_TOPOLOGY_REPLY_SIZE 32
124
125#define X11_VMW_RESOLUTION_REQUEST 1
126struct X11VMWRESOLUTIONREQ
127{
128 struct X11REQHEADER header;
129 uint32_t idX11Screen;
130 uint32_t w;
131 uint32_t h;
132};
133AssertCompileSize(struct X11VMWRESOLUTIONREQ, 16);
134
135#define X11_VMW_RESOLUTION_REPLY_SIZE 32
136
137#define X11_RANDR_GET_SCREEN_REQUEST 5
138struct X11RANDRGETSCREENREQ
139{
140 struct X11REQHEADER header;
141 uint32_t hWindow;
142};
143AssertCompileSize(struct X11RANDRGETSCREENREQ, 8);
144
145#define X11_RANDR_GET_SCREEN_REPLY_SIZE 32
146
147/* This was a macro in old Xlib versions and a function in newer ones; the
148 * display members touched by the macro were declared as ABI for compatibility
149 * reasons. To simplify building with different generations, we duplicate the
150 * code. */
151static void x11GetRequest(struct X11CONTEXT *pContext, uint8_t hMajor,
152 uint8_t idType, size_t cb, struct X11REQHEADER **ppReq)
153{
154 if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax)
155 _XFlush(pContext->pDisplay);
156 if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax)
157 VBClFatalError(("%s display buffer overflow.\n", __func__));
158 if (cb % 4 != 0)
159 VBClFatalError(("%s bad parameter.\n", __func__));
160 pContext->pDisplay->last_req = pContext->pDisplay->bufptr;
161 *ppReq = (struct X11REQHEADER *)pContext->pDisplay->bufptr;
162 (*ppReq)->hMajor = hMajor;
163 (*ppReq)->idType = idType;
164 (*ppReq)->cd = cb / 4;
165 pContext->pDisplay->bufptr += cb;
166 pContext->pDisplay->request++;
167}
168
169static void x11SendHints(struct X11CONTEXT *pContext, struct X11VMWRECT *pRects,
170 unsigned cRects)
171{
172 unsigned i;
173 struct X11VMWTOPOLOGYREQ *pReqTopology;
174 uint8_t repTopology[X11_VMW_TOPOLOGY_REPLY_SIZE];
175 struct X11VMWRESOLUTIONREQ *pReqResolution;
176 uint8_t repResolution[X11_VMW_RESOLUTION_REPLY_SIZE];
177
178 if (!VALID_PTR(pContext->pDisplay))
179 VBClFatalError(("%s bad display argument.\n", __func__));
180 if (cRects == 0)
181 return;
182 /* Try a topology (multiple screen) request. */
183 x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_TOPOLOGY_REQ,
184 sizeof(struct X11VMWTOPOLOGYREQ)
185 + sizeof(struct X11VMWRECT) * (cRects - 1),
186 (struct X11REQHEADER **)&pReqTopology);
187 pReqTopology->idX11Screen = DefaultScreen(pContext->pDisplay);
188 pReqTopology->cScreens = cRects;
189 for (i = 0; i < cRects; ++i)
190 pReqTopology->aRects[i] = pRects[i];
191 _XSend(pContext->pDisplay, NULL, 0);
192 if (_XReply(pContext->pDisplay, (xReply *)&repTopology, 0, xTrue))
193 return;
194 /* That failed, so try the old single screen set resolution. We prefer
195 * simpler code to negligeably improved efficiency, so we just always try
196 * both requests instead of doing version checks or caching. */
197 x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_RESOLUTION_REQUEST,
198 sizeof(struct X11VMWTOPOLOGYREQ),
199 (struct X11REQHEADER **)&pReqResolution);
200 pReqResolution->idX11Screen = DefaultScreen(pContext->pDisplay);
201 pReqResolution->w = pRects[0].w;
202 pReqResolution->h = pRects[0].h;
203 if (_XReply(pContext->pDisplay, (xReply *)&repResolution, 0, xTrue))
204 return;
205 /* What now? */
206 VBClFatalError(("%s failed to set resolution\n", __func__));
207}
208
209/** Call RRGetScreenInfo to wake up the server to the new modes. */
210static void x11GetScreenInfo(struct X11CONTEXT *pContext)
211{
212 struct X11RANDRGETSCREENREQ *pReqGetScreen;
213 uint8_t repGetScreen[X11_RANDR_GET_SCREEN_REPLY_SIZE];
214
215 if (!VALID_PTR(pContext->pDisplay))
216 VBClFatalError(("%s bad display argument.\n", __func__));
217 x11GetRequest(pContext, pContext->hRandRMajor, X11_RANDR_GET_SCREEN_REQUEST,
218 sizeof(struct X11RANDRGETSCREENREQ),
219 (struct X11REQHEADER **)&pReqGetScreen);
220 pReqGetScreen->hWindow = DefaultRootWindow(pContext->pDisplay);
221 _XSend(pContext->pDisplay, NULL, 0);
222 if (!_XReply(pContext->pDisplay, (xReply *)&repGetScreen, 0, xTrue))
223 VBClFatalError(("%s failed to set resolution\n", __func__));
224}
225
226static const char *getPidFilePath()
227{
228 return ".vboxclient-display-svga-x11.pid";
229}
230
231static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
232{
233 (void)ppInterface;
234 (void)fDaemonised;
235 struct X11CONTEXT x11Context = { NULL };
236 unsigned i;
237 int rc;
238 uint32_t acx[VMW_MAX_HEADS] = { 0 };
239 uint32_t acy[VMW_MAX_HEADS] = { 0 };
240 uint32_t adx[VMW_MAX_HEADS] = { 0 };
241 uint32_t ady[VMW_MAX_HEADS] = { 0 };
242 uint32_t afEnabled[VMW_MAX_HEADS] = { false };
243 struct X11VMWRECT aRects[VMW_MAX_HEADS];
244 unsigned cHeads;
245
246 if (checkRecentLinuxKernel())
247 return VINF_SUCCESS;
248 x11Connect(&x11Context);
249 if (x11Context.pDisplay == NULL)
250 return VINF_SUCCESS;
251 /* Initialise the guest library. */
252 rc = VbglR3InitUser();
253 if (RT_FAILURE(rc))
254 VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
255 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
256 if (RT_FAILURE(rc))
257 VBClFatalError(("Failed to request display change events, rc=%Rrc\n", rc));
258 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
259 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
260 return VINF_SUCCESS;
261 if (RT_FAILURE(rc))
262 VBClFatalError(("Failed to register resizing support, rc=%Rrc\n", rc));
263 for (;;)
264 {
265 uint32_t events;
266
267 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
268 if (RT_FAILURE(rc))
269 VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc));
270 while (rc != VERR_TIMEOUT)
271 {
272 uint32_t cx, cy, cBits, dx, dy, idx;
273 bool fEnabled, fChangeOrigin;
274
275 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBits, &idx, &dx, &dy, &fEnabled, &fChangeOrigin, true);
276 if (RT_FAILURE(rc))
277 VBClFatalError(("Failed to get display change request, rc=%Rrc\n", rc));
278 if (idx < VMW_MAX_HEADS)
279 {
280 acx[idx] = cx;
281 acy[idx] = cy;
282 if (fChangeOrigin)
283 adx[idx] = dx < INT32_MAX ? dx : 0;
284 if (fChangeOrigin)
285 ady[idx] = dy < INT32_MAX ? dy : 0;
286 afEnabled[idx] = fEnabled;
287 }
288 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0, &events);
289 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT && rc != VERR_INTERRUPTED)
290 VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc));
291 }
292 for (i = 0, cHeads = 0; i < VMW_MAX_HEADS; ++i)
293 {
294 if (afEnabled[i])
295 {
296 aRects[cHeads].x = (int16_t)adx[i];
297 aRects[cHeads].y = (int16_t)ady[i];
298 aRects[cHeads].w = (uint16_t)acx[i];
299 aRects[cHeads].h = (uint16_t)acy[i];
300 ++cHeads;
301 }
302 }
303 x11SendHints(&x11Context, aRects, cHeads);
304 x11GetScreenInfo(&x11Context);
305 }
306}
307
308static struct VBCLSERVICE interface =
309{
310 getPidFilePath,
311 VBClServiceDefaultHandler, /* Init */
312 run,
313 VBClServiceDefaultCleanup
314}, *pInterface = &interface;
315
316struct VBCLSERVICE **VBClDisplaySVGAX11Service()
317{
318 return &pInterface;
319}
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