VirtualBox

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

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

bugref:9637. Adding randr event monitoring to display-svga client.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.0 KB
Line 
1/* $Id: display-svga-x11.cpp 83136 2020-02-21 14:36:52Z 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#include <iprt/thread.h>
49
50#include <X11/Xlib.h>
51#include <X11/Xutil.h>
52#include <X11/Xlibint.h>
53#include <X11/extensions/shape.h>
54#include <X11/extensions/Xrandr.h>
55
56/** Maximum number of supported screens. DRM and X11 both limit this to 32. */
57/** @todo if this ever changes, dynamically allocate resizeable arrays in the
58 * context structure. */
59#define VMW_MAX_HEADS 32
60/** Monitor positions array. Allocated here and deallocated in the class descructor. */
61RTPOINT *mpMonitorPositions;
62/** Thread to listen to some of the X server events. */
63RTTHREAD mX11MonitorThread = NIL_RTTHREAD;
64
65struct X11VMWRECT /* xXineramaScreenInfo in Xlib headers. */
66{
67 int16_t x;
68 int16_t y;
69 uint16_t w;
70 uint16_t h;
71};
72AssertCompileSize(struct X11VMWRECT, 8);
73
74struct X11CONTEXT
75{
76 Display *pDisplay;
77 int hRandRMajor;
78 int hVMWMajor;
79 int hRandRMinor;
80 int hRandREventBase;
81 int hRandRErrorBase;
82 int hEventMask;
83 /** The number of outputs (monitors, including disconnect ones) xrandr reports. */
84 int hOutputCount;
85 Window rootWindow;
86};
87
88static X11CONTEXT x11Context;
89
90#define MAX_MODE_NAME_LEN 64
91#define MAX_COMMAND_LINE_LEN 512
92#define MAX_MODE_LINE_LEN 512
93
94static const char *szDefaultOutputNamePrefix = "Virtual";
95static const char *pcszXrandr = "xrandr";
96static const char *pcszCvt = "cvt";
97
98struct DRMCONTEXT
99{
100 RTFILE hDevice;
101};
102
103struct RANDROUTPUT
104{
105 int32_t x;
106 int32_t y;
107 uint32_t width;
108 uint32_t height;
109 bool fEnabled;
110};
111
112/** Forward declarations. */
113static void x11Connect();
114static int determineOutputCount();
115
116/** This function assumes monitors are named as from Virtual1 to VirtualX. */
117static int getMonitorIdFromName(const char *sMonitorName)
118{
119 if (!sMonitorName)
120 return -1;
121 int iLen = strlen(sMonitorName);
122 if (iLen <= 0)
123 return -1;
124 int iBase = 10;
125 int iResult = 0;
126 for (int i = iLen - 1; i >= 0; --i)
127 {
128 /* Stop upon seeing the first non-numeric char. */
129 if (sMonitorName[i] < 48 || sMonitorName[i] > 57)
130 break;
131 iResult += (sMonitorName[i] - 48) * iBase / 10;
132 iBase *= 10;
133 }
134 return iResult;
135}
136
137static void queryMonitorPositions()
138{
139 printf("===========================================================================================================================================");
140 static const int iSentinelPosition = -1;
141 if (mpMonitorPositions)
142 {
143 free(mpMonitorPositions);
144 mpMonitorPositions = NULL;
145 }
146 // if (!mHostMonitorPositionSendCallback)
147 // {
148 // VBClLogFatalError("No monitor positions update callback\n");
149 // return;
150 // }
151
152 XRRScreenResources *pScreenResources = XRRGetScreenResources (x11Context.pDisplay, DefaultRootWindow(x11Context.pDisplay));
153 AssertReturnVoid(pScreenResources);
154 XRRFreeScreenResources (pScreenResources);
155
156 int iMonitorCount = 0;
157 XRRMonitorInfo *pMonitorInfo = XRRGetMonitors(x11Context.pDisplay, DefaultRootWindow(x11Context.pDisplay), true, &iMonitorCount);
158 if (iMonitorCount == -1)
159 VBClLogError("Could not get monitor info\n");
160 else
161 {
162 getMonitorIdFromName("virtual123");
163 mpMonitorPositions = (RTPOINT*)malloc(x11Context.hOutputCount * sizeof(RTPOINT));
164 /* todo: memset? */
165 for (int i = 0; i < x11Context.hOutputCount; ++i)
166 {
167 mpMonitorPositions[i].x = iSentinelPosition;
168 mpMonitorPositions[i].y = iSentinelPosition;
169 }
170 for (int i = 0; i < iMonitorCount; ++i)
171 {
172 int iMonitorID = getMonitorIdFromName(XGetAtomName(x11Context.pDisplay, pMonitorInfo[i].name)) - 1;
173 if (iMonitorID >= x11Context.hOutputCount || iMonitorID == -1)
174 continue;
175 VBClLogInfo("Monitor %d (w,h)=(%d,%d) (x,y)=(%d,%d)\n",
176 i,
177 pMonitorInfo[i].width, pMonitorInfo[i].height,
178 pMonitorInfo[i].x, pMonitorInfo[i].y);
179 mpMonitorPositions[iMonitorID].x = pMonitorInfo[i].x;
180 mpMonitorPositions[iMonitorID].y = pMonitorInfo[i].y;
181 }
182 // if (iMonitorCount > 0)
183 // mHostMonitorPositionSendCallback(mpMonitorPositions, x11Context.hOutputCount);
184 }
185 XRRFreeMonitors(pMonitorInfo);
186}
187
188static void monitorRandREvents()
189{
190 XEvent event;
191 XNextEvent(x11Context.pDisplay, &event);
192 int eventTypeOffset = event.type - x11Context.hRandREventBase;
193 switch (eventTypeOffset)
194 {
195 case RRScreenChangeNotify:
196 VBClLogInfo("RRScreenChangeNotify\n");
197 queryMonitorPositions();
198 break;
199 case RRNotify:
200 VBClLogInfo("RRNotify\n");
201 break;
202 default:
203 VBClLogInfo("Unknown RR event\n");
204 break;
205 }
206}
207
208int x11MonitorThreadFunction(RTTHREAD hThreadSelf, void *pvUser)
209{
210 (void)hThreadSelf;
211 (void*)pvUser;
212 while(1)
213 {
214 monitorRandREvents();
215 }
216 return 0;
217}
218
219static int startX11MonitorThread()
220{
221 int rc;
222
223 if (mX11MonitorThread == NIL_RTTHREAD)
224 {
225 rc = RTThreadCreate(&mX11MonitorThread, x11MonitorThreadFunction, 0, 0,
226 RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
227 "X11 events");
228 if (RT_FAILURE(rc))
229 VBClLogFatalError("Warning: failed to start X11 monitor thread (VBoxClient) rc=%Rrc!\n", rc);
230 }
231 return rc;
232}
233
234static int stopX11MonitorThread(void)
235{
236 int rc;
237 if (mX11MonitorThread != NIL_RTTHREAD)
238 {
239 //????????
240 //mX11Monitor.interruptEventWait();
241 rc = RTThreadWait(mX11MonitorThread, 1000, NULL);
242 if (RT_SUCCESS(rc))
243 mX11MonitorThread = NIL_RTTHREAD;
244 else
245 VBClLogError("Failed to stop X11 monitor thread, rc=%Rrc!\n", rc);
246 }
247 return rc;
248}
249
250static bool init()
251{
252 x11Connect();
253 if (x11Context.pDisplay == NULL)
254 return false;
255 if (RT_FAILURE(startX11MonitorThread()))
256 return false;
257 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, x11Context.hEventMask);
258 return true;
259}
260
261static void cleanup()
262{
263 if (mpMonitorPositions)
264 {
265 free(mpMonitorPositions);
266 mpMonitorPositions = NULL;
267 }
268 stopX11MonitorThread();
269 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
270 XCloseDisplay(x11Context.pDisplay);
271}
272
273static void x11Connect()
274{
275 int dummy;
276 if (x11Context.pDisplay != NULL)
277 VBClLogFatalError("%s called with bad argument\n", __func__);
278 x11Context.pDisplay = XOpenDisplay(NULL);
279 if (x11Context.pDisplay == NULL)
280 return;
281 if(!XQueryExtension(x11Context.pDisplay, "VMWARE_CTRL",
282 &x11Context.hVMWMajor, &dummy, &dummy))
283 {
284 XCloseDisplay(x11Context.pDisplay);
285 x11Context.pDisplay = NULL;
286 }
287 if (!XRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase))
288 {
289 XCloseDisplay(x11Context.pDisplay);
290 x11Context.pDisplay = NULL;
291 }
292 if (!XRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor))
293 {
294 XCloseDisplay(x11Context.pDisplay);
295 x11Context.pDisplay = NULL;
296 }
297 x11Context.hEventMask = RRScreenChangeNotifyMask;
298 if (x11Context.hRandRMinor >= 2)
299 x11Context.hEventMask |= RRCrtcChangeNotifyMask |
300 RROutputChangeNotifyMask |
301 RROutputPropertyNotifyMask;
302 x11Context.rootWindow = DefaultRootWindow(x11Context.pDisplay);
303 x11Context.hOutputCount = determineOutputCount();
304}
305
306/** run the xrandr command without options to get the total # of outputs (monitors) including
307 * disbaled ones. */
308static int determineOutputCount()
309{
310 int iCount = 0;
311 char szCommand[MAX_COMMAND_LINE_LEN];
312 RTStrPrintf(szCommand, sizeof(szCommand), "%s ", pcszXrandr);
313
314 FILE *pFile;
315 pFile = popen(szCommand, "r");
316 if (pFile == NULL)
317 {
318 VBClLogError("Failed to run %s\n", szCommand);
319 return VMW_MAX_HEADS;
320 }
321 char szModeLine[MAX_COMMAND_LINE_LEN];
322 while (fgets(szModeLine, sizeof(szModeLine), pFile) != NULL)
323 {
324 if (RTStrIStr(szModeLine, szDefaultOutputNamePrefix))
325 ++iCount;
326 }
327 if (iCount == 0)
328 iCount = VMW_MAX_HEADS;
329 return iCount;
330}
331
332/** Parse a single line of the output of xrandr command to extract Mode name.
333 * Assumed to be null terminated and in following format:
334 * e.g. 1016x559_vbox 59.70. where 1016x559_vbox is the mode name and at least one space in front
335 * Set mode name @p outPszModeName and return true if the name can be found, false otherwise.
336 * outPszModeNameis assumed to be of length MAX_MODE_NAME_LEN. */
337static bool parseModeLine(char *pszLine, char *outPszModeName)
338{
339 char *p = pszLine;
340 (void*)outPszModeName;
341 /* Copy chars to outPszModeName starting from the first non-space until outPszModeName ends with 'vbox'*/
342 size_t iNameIndex = 0;
343 bool fInitialSpace = true;
344
345 while(*p)
346 {
347 if (*p != ' ')
348 fInitialSpace = false;
349 if (!fInitialSpace && iNameIndex < MAX_MODE_NAME_LEN)
350 {
351 outPszModeName[iNameIndex] = *p;
352 ++iNameIndex;
353 if (iNameIndex >= 4)
354 {
355 if (outPszModeName[iNameIndex-1] == 'x' &&
356 outPszModeName[iNameIndex-2] == 'o' &&
357 outPszModeName[iNameIndex-3] == 'b')
358 break;
359 }
360 }
361 ++p;
362 }
363 outPszModeName[iNameIndex] = '\0';
364 return true;
365}
366
367/** Parse the output of the xrandr command to try to remove custom modes.
368 * This function assumes all the outputs are named as VirtualX. */
369static void removeCustomModesFromOutputs()
370{
371 char szCommand[MAX_COMMAND_LINE_LEN];
372 RTStrPrintf(szCommand, sizeof(szCommand), "%s ", pcszXrandr);
373
374 FILE *pFile;
375 pFile = popen(szCommand, "r");
376 if (pFile == NULL)
377 {
378 VBClLogError("Failed to run %s\n", szCommand);
379 return;
380 }
381 char szModeLine[MAX_COMMAND_LINE_LEN];
382 char szModeName[MAX_MODE_NAME_LEN];
383 int iCount = 0;
384 char szRmModeCommand[MAX_COMMAND_LINE_LEN];
385 char szDelModeCommand[MAX_COMMAND_LINE_LEN];
386
387 while (fgets(szModeLine, sizeof(szModeLine), pFile) != NULL)
388 {
389 if (RTStrIStr(szModeLine, szDefaultOutputNamePrefix))
390 {
391 ++iCount;
392 continue;
393 }
394 if (iCount > 0 && RTStrIStr(szModeLine, "_vbox"))
395 {
396 parseModeLine(szModeLine, szModeName);
397 if (strlen(szModeName) >= 4)
398 {
399 /* Delete the mode from the outout. this fails if the mode is currently in use. */
400 RTStrPrintf(szDelModeCommand, sizeof(szDelModeCommand), "%s --delmode Virtual%d %s", pcszXrandr, iCount, szModeName);
401 system(szDelModeCommand);
402 /* Delete the mode from the xserver. note that this will fail if some output has still has this mode (even if unused).
403 * thus this will fail most of the time. */
404 RTStrPrintf(szRmModeCommand, sizeof(szRmModeCommand), "%s --rmmode %s", pcszXrandr, szModeName);
405 system(szRmModeCommand);
406 }
407 }
408 }
409}
410
411static void getModeNameAndLineFromCVT(int iWidth, int iHeight, char *pszOutModeName, char *pszOutModeLine)
412{
413 char szCvtCommand[MAX_COMMAND_LINE_LEN];
414 const int iFreq = 60;
415 const int iMinNameLen = 4;
416 /* Make release builds happy. */
417 (void)iMinNameLen;
418 RTStrPrintf(szCvtCommand, sizeof(szCvtCommand), "%s %d %d %d", pcszCvt, iWidth, iHeight, iFreq);
419 FILE *pFile;
420 pFile = popen(szCvtCommand, "r");
421 if (pFile == NULL)
422 {
423 VBClLogError("Failed to run %s\n", szCvtCommand);
424 return;
425 }
426
427 char szModeLine[MAX_COMMAND_LINE_LEN];
428 while (fgets(szModeLine, sizeof(szModeLine), pFile) != NULL)
429 {
430 if (RTStrStr(szModeLine, "Modeline"))
431 {
432 if(szModeLine[strlen(szModeLine) - 1] == '\n')
433 szModeLine[strlen(szModeLine) - 1] = '\0';
434 size_t iFirstQu = RTStrOffCharOrTerm(szModeLine, '\"');
435 size_t iModeLineLen = strlen(szModeLine);
436 /* Make release builds happy. */
437 (void)iModeLineLen;
438 Assert(iFirstQu < iModeLineLen - iMinNameLen);
439
440 char *p = &(szModeLine[iFirstQu + 1]);
441 size_t iSecondQu = RTStrOffCharOrTerm(p, '_');
442 Assert(iSecondQu > iMinNameLen);
443 Assert(iSecondQu < MAX_MODE_NAME_LEN);
444 Assert(iSecondQu < iModeLineLen);
445
446 RTStrCopy(pszOutModeName, iSecondQu + 2, p);
447 RTStrCat(pszOutModeName, MAX_MODE_NAME_LEN, "vbox");
448 iSecondQu = RTStrOffCharOrTerm(p, '\"');
449 RTStrCopy(pszOutModeLine, MAX_MODE_LINE_LEN, &(szModeLine[iFirstQu + iSecondQu + 2]));
450 break;
451 }
452 }
453}
454
455/** Add a new mode to xserver and to the output */
456static void addMode(const char *pszModeName, const char *pszModeLine)
457{
458 char szNewModeCommand[MAX_COMMAND_LINE_LEN];
459 RTStrPrintf(szNewModeCommand, sizeof(szNewModeCommand), "%s --newmode \"%s\" %s", pcszXrandr, pszModeName, pszModeLine);
460 system(szNewModeCommand);
461
462 char szAddModeCommand[1024];
463 /* try to add the new mode to all possible outputs. we currently dont care if most the are disabled. */
464 for(int i = 0; i < x11Context.hOutputCount; ++i)
465 {
466 RTStrPrintf(szAddModeCommand, sizeof(szAddModeCommand), "%s --addmode Virtual%d \"%s\"", pcszXrandr, i + 1, pszModeName);
467 system(szAddModeCommand);
468 }
469}
470
471static bool checkDefaultModes(struct RANDROUTPUT *pOutput, int iOutputIndex, char *pszModeName)
472{
473 const char szError[] = "cannot find mode";
474 char szXranrCommand[MAX_COMMAND_LINE_LEN];
475 RTStrPrintf(szXranrCommand, sizeof(szXranrCommand),
476 "%s --dryrun --output Virtual%u --mode %dx%d --pos %dx%d 2>/dev/stdout", pcszXrandr, iOutputIndex,
477 pOutput->width, pOutput->height, pOutput->x, pOutput->y);
478 RTStrPrintf(pszModeName, MAX_MODE_NAME_LEN, "%dx%d", pOutput->width, pOutput->height);
479 FILE *pFile;
480 pFile = popen(szXranrCommand, "r");
481 if (pFile == NULL)
482 {
483 VBClLogError("Failed to run %s\n", szXranrCommand);
484 return false;
485 }
486 char szResult[64];
487 if (fgets(szResult, sizeof(szResult), pFile) != NULL)
488 {
489 if (RTStrIStr(szResult, szError))
490 return false;
491 }
492 return true;
493}
494
495/** Construct the xrandr command which sets the whole monitor topology each time. */
496static void setXrandrModes(struct RANDROUTPUT *paOutputs)
497{
498 char szCommand[MAX_COMMAND_LINE_LEN];
499 RTStrPrintf(szCommand, sizeof(szCommand), "%s ", pcszXrandr);
500
501 for (int i = 0; i < x11Context.hOutputCount; ++i)
502 {
503 char line[64];
504 if (!paOutputs[i].fEnabled)
505 RTStrPrintf(line, sizeof(line), "--output Virtual%u --off ", i + 1);
506 else
507 {
508 char szModeName[MAX_MODE_NAME_LEN];
509 char szModeLine[MAX_MODE_LINE_LEN];
510 /* Check if there is a default mode for the widthxheight and if not create and add a custom mode. */
511 if (!checkDefaultModes(&(paOutputs[i]), i + 1, szModeName))
512 {
513 getModeNameAndLineFromCVT(paOutputs[i].width, paOutputs[i].height, szModeName, szModeLine);
514 addMode(szModeName, szModeLine);
515 }
516 else
517 RTStrPrintf(szModeName, sizeof(szModeName), "%dx%d ", paOutputs[i].width, paOutputs[i].height);
518
519 RTStrPrintf(line, sizeof(line), "--output Virtual%u --mode %s --pos %dx%d ", i + 1,
520 szModeName, paOutputs[i].x, paOutputs[i].y);
521 }
522 RTStrCat(szCommand, sizeof(szCommand), line);
523 }
524 system(szCommand);
525 VBClLogInfo("=======xrandr topology command:=====\n%s\n", szCommand);
526 removeCustomModesFromOutputs();
527}
528
529static const char *getName()
530{
531 return "Display SVGA X11";
532}
533
534static const char *getPidFilePath()
535{
536 return ".vboxclient-display-svga-x11.pid";
537}
538
539static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
540{
541 (void)ppInterface;
542 (void)fDaemonised;
543 int rc;
544 uint32_t events;
545 /* Do not acknowledge the first event we query for to pick up old events,
546 * e.g. from before a guest reboot. */
547 bool fAck = false;
548
549 if (!init())
550 return VINF_SUCCESS;
551 static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS];
552
553 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
554 if (RT_FAILURE(rc))
555 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
556 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
557 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
558 return VINF_SUCCESS;
559 if (RT_FAILURE(rc))
560 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
561
562 int eventMask = RRScreenChangeNotifyMask;
563 if (x11Context.hRandRMinor >= 2)
564 eventMask |= RRCrtcChangeNotifyMask |
565 RROutputChangeNotifyMask |
566 RROutputPropertyNotifyMask;
567 if (x11Context.hRandRMinor >= 4)
568 eventMask |= RRProviderChangeNotifyMask |
569 RRProviderPropertyNotifyMask |
570 RRResourceChangeNotifyMask;
571 for (;;)
572 {
573 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
574 uint32_t cDisplaysOut;
575 /* Query the first size without waiting. This lets us e.g. pick up
576 * the last event before a guest reboot when we start again after. */
577 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
578 fAck = true;
579 if (RT_FAILURE(rc))
580 VBClLogFatalError("Failed to get display change request, rc=%Rrc\n", rc);
581 if (cDisplaysOut > VMW_MAX_HEADS)
582 VBClLogFatalError("Display change request contained, rc=%Rrc\n", rc);
583 if (cDisplaysOut > 0)
584 {
585 for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
586 {
587 uint32_t idDisplay = aDisplays[i].idDisplay;
588 if (idDisplay >= VMW_MAX_HEADS)
589 continue;
590 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags;
591 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
592 {
593 if ((idDisplay == 0) || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
594 {
595 aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin;
596 aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin;
597 } else {
598 aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx;
599 aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin;
600 }
601 aMonitors[idDisplay].cx = aDisplays[i].cx;
602 aMonitors[idDisplay].cy = aDisplays[i].cy;
603 }
604 }
605 /* Create a whole topology and send it to xrandr. */
606 struct RANDROUTPUT aOutputs[VMW_MAX_HEADS];
607 int iRunningX = 0;
608 for (int j = 0; j < x11Context.hOutputCount; ++j)
609 {
610 aOutputs[j].x = iRunningX;
611 aOutputs[j].y = aMonitors[j].yOrigin;
612 aOutputs[j].width = aMonitors[j].cx;
613 aOutputs[j].height = aMonitors[j].cy;
614 aOutputs[j].fEnabled = !(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED);
615 if (aOutputs[j].fEnabled)
616 iRunningX += aOutputs[j].width;
617 }
618 setXrandrModes(aOutputs);
619 }
620 do
621 {
622 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
623 } while (rc == VERR_INTERRUPTED);
624 if (RT_FAILURE(rc))
625 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
626 }
627 cleanup();
628}
629
630static struct VBCLSERVICE interface =
631{
632 getName,
633 getPidFilePath,
634 VBClServiceDefaultHandler, /* Init */
635 run,
636 VBClServiceDefaultCleanup
637}, *pInterface = &interface;
638
639struct VBCLSERVICE **VBClDisplaySVGAX11Service()
640{
641 return &pInterface;
642}
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