VirtualBox

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

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

bugref:9637. build fix

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