VirtualBox

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

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

Additions/VBoxClient/VMSVGA: use VbglR3GetDisplayChangeRequestMulti.
Change log: Additions/linux: do not forget the last size hint on guest reboot.
For VBoxClient with guests using VMSVGA, switch to using
VbglR3GetDisplayChangeRequestMulti to retrieve guest hints and query hints
before waiting for change notifications. This makes sure that hints send
before a guest reboot will keep their effect over the reboot.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.0 KB
Line 
1/* $Id: display-svga.cpp 79028 2019-06-06 15:05:36Z 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#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 VBClFatalError(("%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 VBClFatalError(("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 VBClFatalError(("%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 VBClFatalError(("Failure updating layout, rc=%Rrc\n", rc));
167}
168
169static const char *getPidFilePath()
170{
171 return ".vboxclient-display-svga.pid";
172}
173
174static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
175{
176 (void)ppInterface;
177 (void)fDaemonised;
178 struct DRMCONTEXT drmContext = { NIL_RTFILE };
179 unsigned i;
180 int rc;
181 struct DRMVMWRECT aRects[VMW_MAX_HEADS];
182 unsigned cHeads;
183 /* Do not acknowledge the first event we query for to pick up old events,
184 * e.g. from before a guest reboot. */
185 bool fAck = false;
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 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
206 uint32_t cDisplaysOut;
207
208 /* Query the first size without waiting. This lets us e.g. pick up
209 * the last event before a guest reboot when we start again after. */
210 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
211 fAck = true;
212 if (RT_FAILURE(rc))
213 VBClFatalError(("Failed to get display change request, rc=%Rrc\n", rc));
214 if (cDisplaysOut > VMW_MAX_HEADS)
215 VBClFatalError(("Display change request contained, rc=%Rrc\n", rc));
216 for (i = 0, cHeads = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
217 {
218 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
219 {
220 if ((i == 0) || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
221 {
222 aRects[cHeads].x = aDisplays[i].xOrigin;
223 aRects[cHeads].y = aDisplays[i].yOrigin;
224 } else {
225 aRects[cHeads].x = aRects[cHeads - 1].x + aRects[cHeads - 1].w;
226 aRects[cHeads].y = aRects[cHeads - 1].y;
227 }
228 aRects[cHeads].w = aDisplays[i].cx;
229 aRects[cHeads].h = aDisplays[i].cy;
230 ++cHeads;
231 }
232 }
233 for (i = 0; i < cHeads; ++i)
234 printf("Head %u: %dx%d, (%d, %d)\n", i, (int)aRects[i].w, (int)aRects[i].h,
235 (int)aRects[i].x, (int)aRects[i].y);
236 drmSendHints(&drmContext, aRects, cHeads);
237 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
238 if (RT_FAILURE(rc))
239 VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc));
240 }
241}
242
243static struct VBCLSERVICE interface =
244{
245 getPidFilePath,
246 VBClServiceDefaultHandler, /* Init */
247 run,
248 VBClServiceDefaultCleanup
249}, *pInterface = &interface;
250
251struct VBCLSERVICE **VBClDisplaySVGAService()
252{
253 return &pInterface;
254}
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