VirtualBox

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

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

bugref:9637. Some comment correction.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.5 KB
Line 
1/* $Id: display-svga-x11.cpp 83039 2020-02-10 16:57:01Z 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-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#include "stdio.h"
40#include "VBoxClient.h"
41
42#include <VBox/VBoxGuestLib.h>
43
44#include <iprt/assert.h>
45#include <iprt/err.h>
46#include <iprt/file.h>
47#include <iprt/string.h>
48
49#include <sys/utsname.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 X.Org driver control parts definitions. */
57
58#include <X11/Xlibint.h>
59#include <X11/extensions/Xrandr.h>
60
61#ifdef RT_OS_LINUX
62# include <sys/ioctl.h>
63#else /* Solaris and BSDs, in case they ever adopt the DRM driver. */
64# include <sys/ioccom.h>
65#endif
66
67#define USE_XRANDR_BIN
68
69
70struct X11VMWRECT /* xXineramaScreenInfo in Xlib headers. */
71{
72 int16_t x;
73 int16_t y;
74 uint16_t w;
75 uint16_t h;
76};
77AssertCompileSize(struct X11VMWRECT, 8);
78
79struct X11CONTEXT
80{
81 Display *pDisplay;
82 int hRandRMajor;
83 int hVMWMajor;
84 int hRandRMinor;
85 int hRandREventBase;
86 int hRandRErrorBase;
87 Window rootWindow;
88};
89
90static void x11Connect(struct X11CONTEXT *pContext)
91{
92 int dummy;
93
94 if (pContext->pDisplay != NULL)
95 VBClLogFatalError("%s called with bad argument\n", __func__);
96 pContext->pDisplay = XOpenDisplay(NULL);
97 if (pContext->pDisplay == NULL)
98 return;
99 // if ( !XQueryExtension(pContext->pDisplay, "RANDR",
100 // &pContext->hRandRMajor, &dummy, &dummy)
101 if(!XQueryExtension(pContext->pDisplay, "VMWARE_CTRL",
102 &pContext->hVMWMajor, &dummy, &dummy))
103 {
104 XCloseDisplay(pContext->pDisplay);
105 pContext->pDisplay = NULL;
106 }
107 if (!XRRQueryExtension(pContext->pDisplay, &pContext->hRandREventBase, &pContext->hRandRErrorBase))
108 {
109 XCloseDisplay(pContext->pDisplay);
110 pContext->pDisplay = NULL;
111 }
112 if (!XRRQueryVersion(pContext->pDisplay, &pContext->hRandRMajor, &pContext->hRandRMinor))
113 {
114 XCloseDisplay(pContext->pDisplay);
115 pContext->pDisplay = NULL;
116 }
117 pContext->rootWindow = DefaultRootWindow(pContext->pDisplay);
118}
119
120#ifndef USE_XRANDR_BIN
121
122static bool checkRecentLinuxKernel(void)
123{
124 struct utsname name;
125
126 if (uname(&name) == -1)
127 VBClLogFatalError("Failed to get kernel name\n");
128 if (strcmp(name.sysname, "Linux"))
129 return false;
130 return (RTStrVersionCompare(name.release, "4.6") >= 0);
131}
132
133
134#define X11_VMW_TOPOLOGY_REQ 2
135
136struct X11REQHEADER
137{
138 uint8_t hMajor;
139 uint8_t idType;
140 uint16_t cd;
141};
142
143struct X11VMWTOPOLOGYREQ
144{
145 struct X11REQHEADER header;
146 uint32_t idX11Screen;
147 uint32_t cScreens;
148 uint32_t u32Pad;
149 struct X11VMWRECT aRects[1];
150};
151AssertCompileSize(struct X11VMWTOPOLOGYREQ, 24);
152
153#define X11_VMW_TOPOLOGY_REPLY_SIZE 32
154
155#define X11_VMW_RESOLUTION_REQUEST 1
156struct X11VMWRESOLUTIONREQ
157{
158 struct X11REQHEADER header;
159 uint32_t idX11Screen;
160 uint32_t w;
161 uint32_t h;
162};
163AssertCompileSize(struct X11VMWRESOLUTIONREQ, 16);
164
165#define X11_VMW_RESOLUTION_REPLY_SIZE 32
166
167#define X11_RANDR_GET_SCREEN_REQUEST 5
168struct X11RANDRGETSCREENREQ
169{
170 struct X11REQHEADER header;
171 uint32_t hWindow;
172};
173AssertCompileSize(struct X11RANDRGETSCREENREQ, 8);
174
175#define X11_RANDR_GET_SCREEN_REPLY_SIZE 32
176
177/* This was a macro in old Xlib versions and a function in newer ones; the
178 * display members touched by the macro were declared as ABI for compatibility
179 * reasons. To simplify building with different generations, we duplicate the
180 * code. */
181static void x11GetRequest(struct X11CONTEXT *pContext, uint8_t hMajor,
182 uint8_t idType, size_t cb, struct X11REQHEADER **ppReq)
183{
184 if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax)
185 _XFlush(pContext->pDisplay);
186 if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax)
187 VBClLogFatalError("%s display buffer overflow\n", __func__);
188 if (cb % 4 != 0)
189 VBClLogFatalError("%s bad parameter\n", __func__);
190 pContext->pDisplay->last_req = pContext->pDisplay->bufptr;
191 *ppReq = (struct X11REQHEADER *)pContext->pDisplay->bufptr;
192 (*ppReq)->hMajor = hMajor;
193 (*ppReq)->idType = idType;
194 (*ppReq)->cd = cb / 4;
195 pContext->pDisplay->bufptr += cb;
196 pContext->pDisplay->request++;
197}
198
199static void x11SendHints(struct X11CONTEXT *pContext, struct X11VMWRECT *pRects,
200 unsigned cRects)
201{
202 unsigned i;
203 struct X11VMWTOPOLOGYREQ *pReqTopology;
204 uint8_t repTopology[X11_VMW_TOPOLOGY_REPLY_SIZE];
205 struct X11VMWRESOLUTIONREQ *pReqResolution;
206 uint8_t repResolution[X11_VMW_RESOLUTION_REPLY_SIZE];
207
208 if (!VALID_PTR(pContext->pDisplay))
209 VBClLogFatalError("%s bad display argument\n", __func__);
210 if (cRects == 0)
211 return;
212 /* Try a topology (multiple screen) request. */
213 x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_TOPOLOGY_REQ,
214 sizeof(struct X11VMWTOPOLOGYREQ)
215 + sizeof(struct X11VMWRECT) * (cRects - 1),
216 (struct X11REQHEADER **)&pReqTopology);
217 pReqTopology->idX11Screen = DefaultScreen(pContext->pDisplay);
218 pReqTopology->cScreens = cRects;
219 for (i = 0; i < cRects; ++i)
220 pReqTopology->aRects[i] = pRects[i];
221 _XSend(pContext->pDisplay, NULL, 0);
222 if (_XReply(pContext->pDisplay, (xReply *)&repTopology, 0, xTrue))
223 return;
224 /* That failed, so try the old single screen set resolution. We prefer
225 * simpler code to negligeably improved efficiency, so we just always try
226 * both requests instead of doing version checks or caching. */
227 x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_RESOLUTION_REQUEST,
228 sizeof(struct X11VMWRESOLUTIONREQ),
229 (struct X11REQHEADER **)&pReqResolution);
230 pReqResolution->idX11Screen = DefaultScreen(pContext->pDisplay);
231 pReqResolution->w = pRects[0].w;
232 pReqResolution->h = pRects[0].h;
233 if (_XReply(pContext->pDisplay, (xReply *)&repResolution, 0, xTrue))
234 return;
235 /* What now? */
236 VBClLogFatalError("%s failed to set resolution\n", __func__);
237}
238
239/** Call RRGetScreenInfo to wake up the server to the new modes. */
240static void x11GetScreenInfo(struct X11CONTEXT *pContext)
241{
242 struct X11RANDRGETSCREENREQ *pReqGetScreen;
243 uint8_t repGetScreen[X11_RANDR_GET_SCREEN_REPLY_SIZE];
244
245 if (!VALID_PTR(pContext->pDisplay))
246 VBClLogFatalError("%s bad display argument\n", __func__);
247 x11GetRequest(pContext, pContext->hRandRMajor, X11_RANDR_GET_SCREEN_REQUEST,
248 sizeof(struct X11RANDRGETSCREENREQ),
249 (struct X11REQHEADER **)&pReqGetScreen);
250 pReqGetScreen->hWindow = DefaultRootWindow(pContext->pDisplay);
251 _XSend(pContext->pDisplay, NULL, 0);
252 if (!_XReply(pContext->pDisplay, (xReply *)&repGetScreen, 0, xTrue))
253 VBClLogFatalError("%s failed to set resolution\n", __func__);
254}
255
256#else //#fndef USE_XRANDR_BIN
257
258#define MAX_MODE_NAME_LEN 64
259#define MAX_COMMAND_LINE_LEN 512
260#define MAX_MODE_LINE_LEN 512
261
262static const char *szDefaultOutputNamePrefix = "Virtual";
263static const char *pcszXrandr = "xrandr";
264static const char *pcszCvt = "cvt";
265/** The number of outputs (monitors, including disconnect ones) xrandr reports. */
266static int iOutputCount = 0;
267
268struct DRMCONTEXT
269{
270 RTFILE hDevice;
271};
272
273#define DRM_DRIVER_NAME "vmwgfx"
274
275/** DRM version structure. */
276struct DRMVERSION
277{
278 int cMajor;
279 int cMinor;
280 int cPatchLevel;
281 size_t cbName;
282 char *pszName;
283 size_t cbDate;
284 char *pszDate;
285 size_t cbDescription;
286 char *pszDescription;
287};
288AssertCompileSize(struct DRMVERSION, 8 + 7 * sizeof(void *));
289
290#define DRM_IOCTL_VERSION _IOWR('d', 0x00, struct DRMVERSION)
291
292/** Rectangle structure for geometry of a single screen. */
293struct DRMVMWRECT
294{
295 int32_t x;
296 int32_t y;
297 uint32_t w;
298 uint32_t h;
299};
300AssertCompileSize(struct DRMVMWRECT, 16);
301
302struct DRMVMWUPDATELAYOUT {
303 uint32_t cOutputs;
304 uint32_t u32Pad;
305 uint64_t ptrRects;
306};
307AssertCompileSize(struct DRMVMWUPDATELAYOUT, 16);
308
309#define DRM_IOCTL_VMW_UPDATE_LAYOUT \
310 _IOW('d', 0x40 + 20, struct DRMVMWUPDATELAYOUT)
311
312struct RANDROUTPUT
313{
314 int32_t x;
315 int32_t y;
316 uint32_t width;
317 uint32_t height;
318 bool fEnabled;
319};
320
321/** run the xrandr command without options to get the total # of outputs (monitors) including
322 * disbaled ones. */
323static int determineOutputCount()
324{
325 int iCount = 0;
326 char szCommand[MAX_COMMAND_LINE_LEN];
327 RTStrPrintf(szCommand, sizeof(szCommand), "%s ", pcszXrandr);
328
329 FILE *pFile;
330 pFile = popen(szCommand, "r");
331 if (pFile == NULL)
332 {
333 VBClLogError("Failed to run %s\n", szCommand);
334 return VMW_MAX_HEADS;
335 }
336 char szModeLine[MAX_COMMAND_LINE_LEN];
337 while (fgets(szModeLine, sizeof(szModeLine), pFile) != NULL)
338 {
339 if (RTStrIStr(szModeLine, szDefaultOutputNamePrefix))
340 ++iCount;
341 }
342 if (iCount == 0)
343 iCount = VMW_MAX_HEADS;
344 return iCount;
345}
346
347/** Parse a single line of the output of xrandr command to extract Mode name.
348 * Assumed to be null terminated and in following format:
349 * e.g. 1016x559_vbox 59.70. where 1016x559_vbox is the mode name and at least one space in front
350 * Set mode name @p outPszModeName and return true if the name can be found, false otherwise.
351 * outPszModeNameis assumed to be of length MAX_MODE_NAME_LEN. */
352static bool parseModeLine(char *pszLine, char *outPszModeName)
353{
354 char *p = pszLine;
355 (void*)outPszModeName;
356 /* Copy chars to outPszModeName starting from the first non-space until outPszModeName ends with 'vbox'*/
357 size_t iNameIndex = 0;
358 bool fInitialSpace = true;
359
360 while(*p)
361 {
362 if (*p != ' ')
363 fInitialSpace = false;
364 if (!fInitialSpace && iNameIndex < MAX_MODE_NAME_LEN)
365 {
366 outPszModeName[iNameIndex] = *p;
367 ++iNameIndex;
368 if (iNameIndex >= 4)
369 {
370 if (outPszModeName[iNameIndex-1] == 'x' &&
371 outPszModeName[iNameIndex-2] == 'o' &&
372 outPszModeName[iNameIndex-3] == 'b')
373 break;
374 }
375 }
376 ++p;
377 }
378 outPszModeName[iNameIndex] = '\0';
379 return true;
380}
381
382/** Parse the output of the xrandr command to try to remove custom modes.
383 * This function assumes all the outputs are named as VirtualX. */
384static void removeCustomModesFromOutputs()
385{
386 char szCommand[MAX_COMMAND_LINE_LEN];
387 RTStrPrintf(szCommand, sizeof(szCommand), "%s ", pcszXrandr);
388
389 FILE *pFile;
390 pFile = popen(szCommand, "r");
391 if (pFile == NULL)
392 {
393 VBClLogError("Failed to run %s\n", szCommand);
394 return;
395 }
396 char szModeLine[MAX_COMMAND_LINE_LEN];
397 char szModeName[MAX_MODE_NAME_LEN];
398 int iCount = 0;
399 char szRmModeCommand[MAX_COMMAND_LINE_LEN];
400 char szDelModeCommand[MAX_COMMAND_LINE_LEN];
401
402 while (fgets(szModeLine, sizeof(szModeLine), pFile) != NULL)
403 {
404 if (RTStrIStr(szModeLine, szDefaultOutputNamePrefix))
405 {
406 ++iCount;
407 continue;
408 }
409 if (iCount > 0 && RTStrIStr(szModeLine, "_vbox"))
410 {
411 parseModeLine(szModeLine, szModeName);
412 if (strlen(szModeName) >= 4)
413 {
414 /* Delete the mode from the outout. this fails if the mode is currently in use. */
415 RTStrPrintf(szDelModeCommand, sizeof(szDelModeCommand), "%s --delmode Virtual%d %s", pcszXrandr, iCount, szModeName);
416 system(szDelModeCommand);
417 /* Delete the mode from the xserver. note that this will fail if some output has still has this mode (even if unused).
418 * thus this will fail most of the time. */
419 RTStrPrintf(szRmModeCommand, sizeof(szRmModeCommand), "%s --rmmode %s", pcszXrandr, szModeName);
420 system(szRmModeCommand);
421 }
422 }
423 }
424}
425
426static void getModeNameAndLineFromCVT(int iWidth, int iHeight, char *pszOutModeName, char *pszOutModeLine)
427{
428 char szCvtCommand[MAX_COMMAND_LINE_LEN];
429 const int iFreq = 60;
430 const int iMinNameLen = 4;
431 /* Make release builds happy. */
432 (void)iMinNameLen;
433 RTStrPrintf(szCvtCommand, sizeof(szCvtCommand), "%s %d %d %d", pcszCvt, iWidth, iHeight, iFreq);
434 FILE *pFile;
435 pFile = popen(szCvtCommand, "r");
436 if (pFile == NULL)
437 {
438 VBClLogError("Failed to run %s\n", szCvtCommand);
439 return;
440 }
441
442 char szModeLine[MAX_COMMAND_LINE_LEN];
443 while (fgets(szModeLine, sizeof(szModeLine), pFile) != NULL)
444 {
445 if (RTStrStr(szModeLine, "Modeline"))
446 {
447 if(szModeLine[strlen(szModeLine) - 1] == '\n')
448 szModeLine[strlen(szModeLine) - 1] = '\0';
449 size_t iFirstQu = RTStrOffCharOrTerm(szModeLine, '\"');
450 size_t iModeLineLen = strlen(szModeLine);
451 /* Make release builds happy. */
452 (void)iModeLineLen;
453 Assert(iFirstQu < iModeLineLen - iMinNameLen);
454
455 char *p = &(szModeLine[iFirstQu + 1]);
456 size_t iSecondQu = RTStrOffCharOrTerm(p, '_');
457 Assert(iSecondQu > iMinNameLen);
458 Assert(iSecondQu < MAX_MODE_NAME_LEN);
459 Assert(iSecondQu < iModeLineLen);
460
461 RTStrCopy(pszOutModeName, iSecondQu + 2, p);
462 RTStrCat(pszOutModeName, MAX_MODE_NAME_LEN, "vbox");
463 iSecondQu = RTStrOffCharOrTerm(p, '\"');
464 RTStrCopy(pszOutModeLine, MAX_MODE_LINE_LEN, &(szModeLine[iFirstQu + iSecondQu + 2]));
465 break;
466 }
467 }
468}
469
470/** Add a new mode to xserver and to the output */
471static void addMode(const char *pszModeName, const char *pszModeLine)
472{
473 char szNewModeCommand[MAX_COMMAND_LINE_LEN];
474 RTStrPrintf(szNewModeCommand, sizeof(szNewModeCommand), "%s --newmode \"%s\" %s", pcszXrandr, pszModeName, pszModeLine);
475 system(szNewModeCommand);
476
477 char szAddModeCommand[1024];
478 /* try to add the new mode to all possible outputs. we currently dont care if most the are disabled. */
479 for(int i = 0; i < iOutputCount; ++i)
480 {
481 RTStrPrintf(szAddModeCommand, sizeof(szAddModeCommand), "%s --addmode Virtual%d \"%s\"", pcszXrandr, i + 1, pszModeName);
482 system(szAddModeCommand);
483 }
484}
485
486static bool checkDefaultModes(struct RANDROUTPUT *pOutput, int iOutputIndex, char *pszModeName)
487{
488 const char szError[] = "cannot find mode";
489 char szXranrCommand[MAX_COMMAND_LINE_LEN];
490 RTStrPrintf(szXranrCommand, sizeof(szXranrCommand),
491 "%s --dryrun --output Virtual%u --mode %dx%d --pos %dx%d 2>/dev/stdout", pcszXrandr, iOutputIndex,
492 pOutput->width, pOutput->height, pOutput->x, pOutput->y);
493 RTStrPrintf(pszModeName, MAX_MODE_NAME_LEN, "%dx%d", pOutput->width, pOutput->height);
494 FILE *pFile;
495 pFile = popen(szXranrCommand, "r");
496 if (pFile == NULL)
497 {
498 VBClLogError("Failed to run %s\n", szXranrCommand);
499 return false;
500 }
501 char szResult[64];
502 if (fgets(szResult, sizeof(szResult), pFile) != NULL)
503 {
504 if (RTStrIStr(szResult, szError))
505 return false;
506 }
507 return true;
508}
509
510/** Construct the xrandr command which sets the whole monitor topology each time. */
511static void setXrandrModes(struct RANDROUTPUT *paOutputs)
512{
513 char szCommand[MAX_COMMAND_LINE_LEN];
514 RTStrPrintf(szCommand, sizeof(szCommand), "%s ", pcszXrandr);
515
516 for (int i = 0; i < iOutputCount; ++i)
517 {
518 char line[64];
519 if (!paOutputs[i].fEnabled)
520 RTStrPrintf(line, sizeof(line), "--output Virtual%u --off ", i + 1);
521 else
522 {
523 char szModeName[MAX_MODE_NAME_LEN];
524 char szModeLine[MAX_MODE_LINE_LEN];
525 /* Check if there is a default mode for the widthxheight and if not create and add a custom mode. */
526 if (!checkDefaultModes(&(paOutputs[i]), i + 1, szModeName))
527 {
528 getModeNameAndLineFromCVT(paOutputs[i].width, paOutputs[i].height, szModeName, szModeLine);
529 addMode(szModeName, szModeLine);
530 }
531 else
532 RTStrPrintf(szModeName, sizeof(szModeName), "%dx%d ", paOutputs[i].width, paOutputs[i].height);
533
534 RTStrPrintf(line, sizeof(line), "--output Virtual%u --mode %s --pos %dx%d ", i + 1,
535 szModeName, paOutputs[i].x, paOutputs[i].y);
536 }
537 RTStrCat(szCommand, sizeof(szCommand), line);
538 }
539 system(szCommand);
540 VBClLogInfo("=======xrandr topology command:=====\n%s\n", szCommand);
541 removeCustomModesFromOutputs();
542}
543
544static void drmConnect(struct DRMCONTEXT *pContext)
545{
546 unsigned i;
547 RTFILE hDevice;
548
549 if (pContext->hDevice != NIL_RTFILE)
550 VBClLogFatalError("%s called with bad argument\n", __func__);
551 /* Try to open the SVGA DRM device. */
552 for (i = 0; i < 128; ++i)
553 {
554 char szPath[64];
555 struct DRMVERSION version;
556 char szName[sizeof(DRM_DRIVER_NAME)];
557 int rc;
558
559 /* Control devices for drm graphics driver control devices go from
560 * controlD64 to controlD127. Render node devices go from renderD128
561 * to renderD192. The driver takes resize hints via the control device
562 * on pre-4.10 kernels and on the render device on newer ones. Try
563 * both types. */
564 if (i % 2 == 0)
565 rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/renderD%u", i / 2 + 128);
566 else
567 rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/controlD%u", i / 2 + 64);
568 if (RT_FAILURE(rc))
569 VBClLogFatalError("RTStrPrintf of device path failed, rc=%Rrc\n", rc);
570 rc = RTFileOpen(&hDevice, szPath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
571 if (RT_FAILURE(rc))
572 continue;
573 RT_ZERO(version);
574 version.cbName = sizeof(szName);
575 version.pszName = szName;
576 rc = RTFileIoCtl(hDevice, DRM_IOCTL_VERSION, &version, sizeof(version), NULL);
577 if ( RT_SUCCESS(rc)
578 && !strncmp(szName, DRM_DRIVER_NAME, sizeof(DRM_DRIVER_NAME) - 1)
579 && ( version.cMajor > 2
580 || (version.cMajor == 2 && version.cMinor >= 9)))
581 break;
582 hDevice = NIL_RTFILE;
583 }
584 pContext->hDevice = hDevice;
585}
586
587static void drmSendHints(struct DRMCONTEXT *pContext, struct DRMVMWRECT *paRects,
588 unsigned cHeads)
589{
590 int rc;
591 struct DRMVMWUPDATELAYOUT ioctlLayout;
592
593 if (pContext->hDevice == NIL_RTFILE)
594 VBClLogFatalError("%s bad device argument\n", __func__);
595 ioctlLayout.cOutputs = cHeads;
596 ioctlLayout.ptrRects = (uint64_t)paRects;
597 rc = RTFileIoCtl(pContext->hDevice, DRM_IOCTL_VMW_UPDATE_LAYOUT,
598 &ioctlLayout, sizeof(ioctlLayout), NULL);
599 if (RT_FAILURE(rc) && rc != VERR_INVALID_PARAMETER)
600 VBClLogFatalError("Failure updating layout, rc=%Rrc\n", rc);
601}
602
603#endif /* #ifndef USE_XRANDR_BIN */
604
605static const char *getName()
606{
607 return "Display SVGA X11";
608}
609
610static const char *getPidFilePath()
611{
612 return ".vboxclient-display-svga-x11.pid";
613}
614
615static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
616{
617 iOutputCount = determineOutputCount();
618 (void)ppInterface;
619 (void)fDaemonised;
620 int rc;
621 /* Do not acknowledge the first event we query for to pick up old events,
622 * e.g. from before a guest reboot. */
623 bool fAck = false;
624 struct X11CONTEXT x11Context = { NULL };
625 x11Connect(&x11Context);
626 if (x11Context.pDisplay == NULL)
627 return VINF_SUCCESS;
628
629#ifndef USE_XRANDR_BIN
630 unsigned cHeads;
631 struct X11VMWRECT aRects[VMW_MAX_HEADS];
632 if (checkRecentLinuxKernel())
633 return VINF_SUCCESS;
634#else //#ifndef USE_XRANDR_BIN
635 static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS];
636 struct DRMCONTEXT drmContext = { NIL_RTFILE };
637 drmConnect(&drmContext);
638 if (drmContext.hDevice == NIL_RTFILE)
639 return VINF_SUCCESS;
640 unsigned cEnabledMonitors = 0;
641#endif
642
643 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
644 if (RT_FAILURE(rc))
645 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
646 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
647 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
648 return VINF_SUCCESS;
649 if (RT_FAILURE(rc))
650 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
651
652 int eventMask = RRScreenChangeNotifyMask;
653 if (x11Context.hRandRMinor >= 2)
654 eventMask |= RRCrtcChangeNotifyMask |
655 RROutputChangeNotifyMask |
656 RROutputPropertyNotifyMask;
657 if (x11Context.hRandRMinor >= 4)
658 eventMask |= RRProviderChangeNotifyMask |
659 RRProviderPropertyNotifyMask |
660 RRResourceChangeNotifyMask;
661 // if (x11Context.hRandRMinor >= 6)
662 // eventMask |= RRLeaseNotifyMask;
663
664
665 for (;;)
666 {
667 uint32_t events;
668 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
669 uint32_t cDisplaysOut;
670
671 /* Query the first size without waiting. This lets us e.g. pick up
672 * the last event before a guest reboot when we start again after. */
673 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
674 fAck = true;
675
676 if (RT_FAILURE(rc))
677 VBClLogFatalError("Failed to get display change request, rc=%Rrc\n", rc);
678 if (cDisplaysOut > VMW_MAX_HEADS)
679 VBClLogFatalError("Display change request contained, rc=%Rrc\n", rc);
680#ifndef USE_XRANDR_BIN
681 for (unsigned i = 0, cHeads = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
682 {
683 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
684 {
685 if ((i == 0) || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
686 {
687 aRects[cHeads].x = aDisplays[i].xOrigin < INT16_MAX
688 ? (int16_t)aDisplays[i].xOrigin : 0;
689 aRects[cHeads].y = aDisplays[i].yOrigin < INT16_MAX
690 ? (int16_t)aDisplays[i].yOrigin : 0;
691 } else {
692 aRects[cHeads].x = aRects[cHeads - 1].x + aRects[cHeads - 1].w;
693 aRects[cHeads].y = aRects[cHeads - 1].y;
694 }
695 aRects[cHeads].w = (int16_t)RT_MIN(aDisplays[i].cx, INT16_MAX);
696 aRects[cHeads].h = (int16_t)RT_MIN(aDisplays[i].cy, INT16_MAX);
697 ++cHeads;
698 }
699 }
700 x11SendHints(&x11Context, aRects, cHeads);
701 x11GetScreenInfo(&x11Context);
702#else
703 if (cDisplaysOut > 0)
704 {
705 for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
706 {
707 uint32_t idDisplay = aDisplays[i].idDisplay;
708 if (idDisplay >= VMW_MAX_HEADS)
709 continue;
710 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags;
711 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
712 {
713 if ((idDisplay == 0) || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
714 {
715 aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin;
716 aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin;
717 } else {
718 aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx;
719 aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin;
720 }
721 aMonitors[idDisplay].cx = aDisplays[i].cx;
722 aMonitors[idDisplay].cy = aDisplays[i].cy;
723 }
724 }
725
726 /* Create a whole topology and send it to xrandr. */
727 struct RANDROUTPUT aOutputs[VMW_MAX_HEADS];
728 int iRunningX = 0;
729 for (int j = 0; j < iOutputCount; ++j)
730 {
731 aOutputs[j].x = iRunningX;
732 aOutputs[j].y = aMonitors[j].yOrigin;
733 aOutputs[j].width = aMonitors[j].cx;
734 aOutputs[j].height = aMonitors[j].cy;
735 aOutputs[j].fEnabled = !(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED);
736 if (aOutputs[j].fEnabled)
737 iRunningX += aOutputs[j].width;
738 }
739 setXrandrModes(aOutputs);
740 /* Disable this for now since we want to fix messed up monitor positions in some other way. */
741 if(0)
742 {
743 /* Additionally update the drm thru ioctl since vmwgfx messes up monitor positions it gets from xserver. */
744 /* Create an dense (consisting of enabled monitors only) array to pass to DRM. */
745 cEnabledMonitors = 0;
746 struct DRMVMWRECT aEnabledMonitors[VMW_MAX_HEADS];
747 for (int j = 0; j < VMW_MAX_HEADS; ++j)
748 {
749 if (!(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
750 {
751 aEnabledMonitors[cEnabledMonitors].x = aMonitors[j].xOrigin;
752 aEnabledMonitors[cEnabledMonitors].y = aMonitors[j].yOrigin;
753 aEnabledMonitors[cEnabledMonitors].w = aMonitors[j].cx;
754 aEnabledMonitors[cEnabledMonitors].h = aMonitors[j].cy;
755 if (cEnabledMonitors > 0)
756 aEnabledMonitors[cEnabledMonitors].x = aEnabledMonitors[cEnabledMonitors - 1].x + aEnabledMonitors[cEnabledMonitors - 1].w;
757 ++cEnabledMonitors;
758 }
759 }
760 bool fScreenChangeNotifyEvent = false;
761 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, eventMask);
762 XSync(x11Context.pDisplay, false);
763 XEvent event;
764 while (XCheckTypedEvent(x11Context.pDisplay, RRScreenChangeNotify + x11Context.hRandREventBase, &event) ||
765 XCheckTypedEvent(x11Context.pDisplay, RRNotify + x11Context.hRandREventBase, &event))
766 fScreenChangeNotifyEvent = true;
767 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
768
769 if (!fScreenChangeNotifyEvent)
770 VBClLogInfo("Did not receive any RRScreenChangeNotify events.\n");
771 else
772 drmSendHints(&drmContext, aEnabledMonitors, cEnabledMonitors);
773 }
774 }
775
776#endif
777 do
778 {
779 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
780 } while (rc == VERR_INTERRUPTED);
781 if (RT_FAILURE(rc))
782 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
783 }
784}
785
786static struct VBCLSERVICE interface =
787{
788 getName,
789 getPidFilePath,
790 VBClServiceDefaultHandler, /* Init */
791 run,
792 VBClServiceDefaultCleanup
793}, *pInterface = &interface;
794
795struct VBCLSERVICE **VBClDisplaySVGAX11Service()
796{
797 return &pInterface;
798}
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