VirtualBox

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

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

Additions/VBoxClient/svga: provide default screen layout information.
bugref:9170: Implement support for multihead display configurations in VMSVGA device.
When a multi-monitor guest is in use, the host and the guest have to agree
about how the pointer input device maps to screen layouts in order for input
to be properly usable. So if the host does not actively request a particular
screen layout we assume screens laid out horizontally starting at zero, which
matches the host default. I am not sure if the host code will correctly
inform the host input device code if it requests another layout, but if it
does not this works. And if the guest ignores the host request, e.g. if the
user asked the guest control panel for a different layout, then the input
mapping will break, but this is possibly a guest bug, as it breaks other
virtualisers too.

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