VirtualBox

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

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

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1/* $Id: display-svga.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * X11 guest client - VMSVGA emulation resize event pass-through to drm guest
4 * driver.
5 */
6
7/*
8 * Copyright (C) 2016-2020 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/err.h>
47#include <iprt/string.h>
48
49#include <stdio.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 kernel driver control parts definitions. */
57
58#ifdef RT_OS_LINUX
59# include <sys/ioctl.h>
60#else /* Solaris and BSDs, in case they ever adopt the DRM driver. */
61# include <sys/ioccom.h>
62#endif
63
64#define DRM_DRIVER_NAME "vmwgfx"
65
66/** DRM version structure. */
67struct DRMVERSION
68{
69 int cMajor;
70 int cMinor;
71 int cPatchLevel;
72 size_t cbName;
73 char *pszName;
74 size_t cbDate;
75 char *pszDate;
76 size_t cbDescription;
77 char *pszDescription;
78};
79AssertCompileSize(struct DRMVERSION, 8 + 7 * sizeof(void *));
80
81/** Rectangle structure for geometry of a single screen. */
82struct DRMVMWRECT
83{
84 int32_t x;
85 int32_t y;
86 uint32_t w;
87 uint32_t h;
88};
89AssertCompileSize(struct DRMVMWRECT, 16);
90
91#define DRM_IOCTL_VERSION _IOWR('d', 0x00, struct DRMVERSION)
92
93struct DRMCONTEXT
94{
95 RTFILE hDevice;
96};
97
98static void drmConnect(struct DRMCONTEXT *pContext)
99{
100 unsigned i;
101 RTFILE hDevice;
102
103 if (pContext->hDevice != NIL_RTFILE)
104 VBClLogFatalError("%s called with bad argument\n", __func__);
105 /* Try to open the SVGA DRM device. */
106 for (i = 0; i < 128; ++i)
107 {
108 char szPath[64];
109 struct DRMVERSION version;
110 char szName[sizeof(DRM_DRIVER_NAME)];
111 int rc;
112
113 /* Control devices for drm graphics driver control devices go from
114 * controlD64 to controlD127. Render node devices go from renderD128
115 * to renderD192. The driver takes resize hints via the control device
116 * on pre-4.10 kernels and on the render device on newer ones. Try
117 * both types. */
118 if (i % 2 == 0)
119 rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/renderD%u", i / 2 + 128);
120 else
121 rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/controlD%u", i / 2 + 64);
122 if (RT_FAILURE(rc))
123 VBClLogFatalError("RTStrPrintf of device path failed, rc=%Rrc\n", rc);
124 rc = RTFileOpen(&hDevice, szPath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
125 if (RT_FAILURE(rc))
126 continue;
127 RT_ZERO(version);
128 version.cbName = sizeof(szName);
129 version.pszName = szName;
130 rc = RTFileIoCtl(hDevice, DRM_IOCTL_VERSION, &version, sizeof(version), NULL);
131 if ( RT_SUCCESS(rc)
132 && !strncmp(szName, DRM_DRIVER_NAME, sizeof(DRM_DRIVER_NAME) - 1)
133 && ( version.cMajor > 2
134 || (version.cMajor == 2 && version.cMinor > 9)))
135 break;
136 hDevice = NIL_RTFILE;
137 }
138 pContext->hDevice = hDevice;
139}
140
141/** Preferred screen layout information for DRM_VMW_UPDATE_LAYOUT IoCtl. The
142 * rects argument is a cast pointer to an array of drm_vmw_rect. */
143struct DRMVMWUPDATELAYOUT {
144 uint32_t cOutputs;
145 uint32_t u32Pad;
146 uint64_t ptrRects;
147};
148AssertCompileSize(struct DRMVMWUPDATELAYOUT, 16);
149
150#define DRM_IOCTL_VMW_UPDATE_LAYOUT \
151 _IOW('d', 0x40 + 20, struct DRMVMWUPDATELAYOUT)
152
153static void drmSendHints(struct DRMCONTEXT *pContext, struct DRMVMWRECT *paRects,
154 unsigned cHeads)
155{
156 int rc;
157 struct DRMVMWUPDATELAYOUT ioctlLayout;
158
159 if (pContext->hDevice == NIL_RTFILE)
160 VBClLogFatalError("%s bad device argument\n", __func__);
161 ioctlLayout.cOutputs = cHeads;
162 ioctlLayout.ptrRects = (uint64_t)paRects;
163 rc = RTFileIoCtl(pContext->hDevice, DRM_IOCTL_VMW_UPDATE_LAYOUT,
164 &ioctlLayout, sizeof(ioctlLayout), NULL);
165 if (RT_FAILURE(rc) && rc != VERR_INVALID_PARAMETER)
166 VBClLogFatalError("Failure updating layout, rc=%Rrc\n", rc);
167}
168
169static const char *getName()
170{
171 return "Display SVGA";
172}
173
174static const char *getPidFilePath()
175{
176 return ".vboxclient-display-svga.pid";
177}
178
179static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
180{
181 (void)ppInterface;
182 (void)fDaemonised;
183 struct DRMCONTEXT drmContext = { NIL_RTFILE };
184 static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS];
185 int rc;
186 unsigned cEnabledMonitors;
187 /* Do not acknowledge the first event we query for to pick up old events,
188 * e.g. from before a guest reboot. */
189 bool fAck = false;
190 drmConnect(&drmContext);
191 if (drmContext.hDevice == NIL_RTFILE)
192 return VINF_SUCCESS;
193 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
194 if (RT_FAILURE(rc))
195 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
196 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
197 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
198 return VINF_SUCCESS;
199 if (RT_FAILURE(rc))
200 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
201 for (;;)
202 {
203 uint32_t events;
204 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
205 uint32_t cDisplaysOut;
206 /* Query the first size without waiting. This lets us e.g. pick up
207 * the last event before a guest reboot when we start again after. */
208 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
209 fAck = true;
210 if (RT_FAILURE(rc))
211 VBClLogFatalError("Failed to get display change request, rc=%Rrc\n", rc);
212 if (cDisplaysOut > VMW_MAX_HEADS)
213 VBClLogFatalError("Display change request contained, rc=%Rrc\n", rc);
214 if (cDisplaysOut > 0)
215 {
216 for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
217 {
218 uint32_t idDisplay = aDisplays[i].idDisplay;
219 if (idDisplay >= VMW_MAX_HEADS)
220 continue;
221 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags;
222 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
223 {
224 if ((idDisplay == 0) || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
225 {
226 aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin;
227 aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin;
228 } else {
229 aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx;
230 aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin;
231 }
232 aMonitors[idDisplay].cx = aDisplays[i].cx;
233 aMonitors[idDisplay].cy = aDisplays[i].cy;
234 }
235 }
236 /* Create an dense (consisting of enabled monitors only) array to pass to DRM. */
237 cEnabledMonitors = 0;
238 struct DRMVMWRECT aEnabledMonitors[VMW_MAX_HEADS];
239 for (int j = 0; j < VMW_MAX_HEADS; ++j)
240 {
241 if (!(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
242 {
243 aEnabledMonitors[cEnabledMonitors].x = aMonitors[j].xOrigin;
244 aEnabledMonitors[cEnabledMonitors].y = aMonitors[j].yOrigin;
245 aEnabledMonitors[cEnabledMonitors].w = aMonitors[j].cx;
246 aEnabledMonitors[cEnabledMonitors].h = aMonitors[j].cy;
247 if (cEnabledMonitors > 0)
248 aEnabledMonitors[cEnabledMonitors].x = aEnabledMonitors[cEnabledMonitors - 1].x + aEnabledMonitors[cEnabledMonitors - 1].w;
249 ++cEnabledMonitors;
250 }
251 }
252 for (unsigned i = 0; i < cEnabledMonitors; ++i)
253 printf("Monitor %u: %dx%d, (%d, %d)\n", i, (int)aEnabledMonitors[i].w, (int)aEnabledMonitors[i].h,
254 (int)aEnabledMonitors[i].x, (int)aEnabledMonitors[i].y);
255 drmSendHints(&drmContext, aEnabledMonitors, cEnabledMonitors);
256 }
257 do
258 {
259 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
260 } while (rc == VERR_INTERRUPTED);
261 if (RT_FAILURE(rc))
262 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
263 }
264}
265
266static struct VBCLSERVICE interface =
267{
268 getName,
269 getPidFilePath,
270 VBClServiceDefaultHandler, /* Init */
271 run,
272 VBClServiceDefaultCleanup
273}, *pInterface = &interface;
274
275struct VBCLSERVICE **VBClDisplaySVGAService()
276{
277 return &pInterface;
278}
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