VirtualBox

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

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.2 KB
Line 
1/* $Id: display-svga.cpp 76553 2019-01-01 01:45:53Z 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-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/file.h>
46#include <iprt/err.h>
47#include <iprt/string.h>
48
49/** Maximum number of supported screens. DRM and X11 both limit this to 32. */
50/** @todo if this ever changes, dynamically allocate resizeable arrays in the
51 * context structure. */
52#define VMW_MAX_HEADS 32
53
54/* VMWare kernel driver control parts definitions. */
55
56#ifdef RT_OS_LINUX
57# include <sys/ioctl.h>
58#else /* Solaris and BSDs, in case they ever adopt the DRM driver. */
59# include <sys/ioccom.h>
60#endif
61
62#define DRM_DRIVER_NAME "vmwgfx"
63
64/** DRM version structure. */
65struct DRMVERSION
66{
67 int cMajor;
68 int cMinor;
69 int cPatchLevel;
70 size_t cbName;
71 char *pszName;
72 size_t cbDate;
73 char *pszDate;
74 size_t cbDescription;
75 char *pszDescription;
76};
77AssertCompileSize(struct DRMVERSION, 8 + 7 * sizeof(void *));
78
79/** Rectangle structure for geometry of a single screen. */
80struct DRMVMWRECT
81{
82 int32_t x;
83 int32_t y;
84 uint32_t w;
85 uint32_t h;
86};
87AssertCompileSize(struct DRMVMWRECT, 16);
88
89#define DRM_IOCTL_VERSION _IOWR('d', 0x00, struct DRMVERSION)
90
91struct DRMCONTEXT
92{
93 RTFILE hDevice;
94};
95
96static void drmConnect(struct DRMCONTEXT *pContext)
97{
98 unsigned i;
99 RTFILE hDevice;
100
101 if (pContext->hDevice != NIL_RTFILE)
102 VBClFatalError(("%s called with bad argument\n", __func__));
103 /* Try to open the SVGA DRM device. */
104 for (i = 0; i < 128; ++i)
105 {
106 char szPath[64];
107 struct DRMVERSION version;
108 char szName[sizeof(DRM_DRIVER_NAME)];
109 int rc;
110
111 /* Control devices for drm graphics driver control devices go from
112 * controlD64 to controlD127. Render node devices go from renderD128
113 * to renderD192. The driver takes resize hints via the control device
114 * on pre-4.10 kernels and on the render device on newer ones. Try
115 * both types. */
116 if (i % 2 == 0)
117 rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/renderD%u", i / 2 + 128);
118 else
119 rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/controlD%u", i / 2 + 64);
120 if (RT_FAILURE(rc))
121 VBClFatalError(("RTStrPrintf of device path failed, rc=%Rrc\n", rc));
122 rc = RTFileOpen(&hDevice, szPath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
123 if (RT_FAILURE(rc))
124 continue;
125 RT_ZERO(version);
126 version.cbName = sizeof(szName);
127 version.pszName = szName;
128 rc = RTFileIoCtl(hDevice, DRM_IOCTL_VERSION, &version, sizeof(version), NULL);
129 if ( RT_SUCCESS(rc)
130 && !strncmp(szName, DRM_DRIVER_NAME, sizeof(DRM_DRIVER_NAME) - 1)
131 && ( version.cMajor > 2
132 || (version.cMajor == 2 && version.cMinor > 9)))
133 break;
134 hDevice = NIL_RTFILE;
135 }
136 pContext->hDevice = hDevice;
137}
138
139/** Preferred screen layout information for DRM_VMW_UPDATE_LAYOUT IoCtl. The
140 * rects argument is a cast pointer to an array of drm_vmw_rect. */
141struct DRMVMWUPDATELAYOUT {
142 uint32_t cOutputs;
143 uint32_t u32Pad;
144 uint64_t ptrRects;
145};
146AssertCompileSize(struct DRMVMWUPDATELAYOUT, 16);
147
148#define DRM_IOCTL_VMW_UPDATE_LAYOUT \
149 _IOW('d', 0x40 + 20, struct DRMVMWUPDATELAYOUT)
150
151static void drmSendHints(struct DRMCONTEXT *pContext, struct DRMVMWRECT *paRects,
152 unsigned cHeads)
153{
154 int rc;
155 struct DRMVMWUPDATELAYOUT ioctlLayout;
156
157 if (pContext->hDevice == NIL_RTFILE)
158 VBClFatalError(("%s bad device argument.\n", __func__));
159 ioctlLayout.cOutputs = cHeads;
160 ioctlLayout.ptrRects = (uint64_t)paRects;
161 rc = RTFileIoCtl(pContext->hDevice, DRM_IOCTL_VMW_UPDATE_LAYOUT,
162 &ioctlLayout, sizeof(ioctlLayout), NULL);
163 if (RT_FAILURE(rc) && rc != VERR_INVALID_PARAMETER)
164 VBClFatalError(("Failure updating layout, rc=%Rrc\n", rc));
165}
166
167static const char *getPidFilePath()
168{
169 return ".vboxclient-display-svga.pid";
170}
171
172static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
173{
174 (void)ppInterface;
175 (void)fDaemonised;
176 struct DRMCONTEXT drmContext = { NIL_RTFILE };
177 unsigned i;
178 int rc;
179 uint32_t acx[VMW_MAX_HEADS] = { 0 };
180 uint32_t acy[VMW_MAX_HEADS] = { 0 };
181 uint32_t adx[VMW_MAX_HEADS] = { 0 };
182 uint32_t ady[VMW_MAX_HEADS] = { 0 };
183 uint32_t afEnabled[VMW_MAX_HEADS] = { false };
184 struct DRMVMWRECT aRects[VMW_MAX_HEADS];
185 unsigned cHeads;
186
187 drmConnect(&drmContext);
188 if (drmContext.hDevice == NIL_RTFILE)
189 return VINF_SUCCESS;
190 /* Initialise the guest library. */
191 rc = VbglR3InitUser();
192 if (RT_FAILURE(rc))
193 VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
194 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
195 if (RT_FAILURE(rc))
196 VBClFatalError(("Failed to request display change events, rc=%Rrc\n", rc));
197 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
198 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
199 return VINF_SUCCESS;
200 if (RT_FAILURE(rc))
201 VBClFatalError(("Failed to register resizing support, rc=%Rrc\n", rc));
202 for (;;)
203 {
204 uint32_t events;
205
206 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
207 if (RT_FAILURE(rc))
208 VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc));
209 while (rc != VERR_TIMEOUT)
210 {
211 uint32_t cx, cy, cBits, dx, dy, idx;
212 bool fEnabled, fChangeOrigin;
213
214 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBits, &idx, &dx, &dy, &fEnabled, &fChangeOrigin, true);
215 if (RT_FAILURE(rc))
216 VBClFatalError(("Failed to get display change request, rc=%Rrc\n", rc));
217 if (idx < VMW_MAX_HEADS)
218 {
219 acx[idx] = cx;
220 acy[idx] = cy;
221 if (fChangeOrigin)
222 adx[idx] = dx < INT32_MAX ? dx : 0;
223 if (fChangeOrigin)
224 ady[idx] = dy < INT32_MAX ? dy : 0;
225 afEnabled[idx] = fEnabled;
226 }
227 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0, &events);
228 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT && rc != VERR_INTERRUPTED)
229 VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc));
230 }
231 for (i = 0, cHeads = 0; i < VMW_MAX_HEADS; ++i)
232 {
233 if (afEnabled[i])
234 {
235 if ((i == 0) || (adx[i] || ady[i]))
236 {
237 aRects[cHeads].x = (int32_t)adx[i];
238 aRects[cHeads].y = (int32_t)ady[i];
239 } else {
240 aRects[cHeads].x = (int32_t)(adx[i - 1] + acx[i - 1]);
241 aRects[cHeads].y = (int32_t)ady[i - 1];
242 }
243 aRects[cHeads].w = acx[i];
244 aRects[cHeads].h = acy[i];
245 ++cHeads;
246 }
247 }
248 drmSendHints(&drmContext, aRects, cHeads);
249 }
250}
251
252static struct VBCLSERVICE interface =
253{
254 getPidFilePath,
255 VBClServiceDefaultHandler, /* Init */
256 run,
257 VBClServiceDefaultCleanup
258}, *pInterface = &interface;
259
260struct VBCLSERVICE **VBClDisplaySVGAService()
261{
262 return &pInterface;
263}
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