VirtualBox

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

Last change on this file since 83911 was 83911, 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: 41.4 KB
Line 
1/* $Id: display-svga-x11.cpp 83911 2020-04-21 19:38:05Z 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 * - The following assumptions are done and should be taken into account when reading/chaning the code:
39 * # The order of the outputs (monitors) is assumed to be the same in RANDROUTPUT array and
40 XRRScreenResources.outputs array.
41 */
42#include <stdio.h>
43#include <dlfcn.h>
44#include "VBoxClient.h"
45
46#include <VBox/VBoxGuestLib.h>
47
48#include <iprt/asm.h>
49#include <iprt/assert.h>
50#include <iprt/err.h>
51#include <iprt/file.h>
52#include <iprt/string.h>
53#include <iprt/thread.h>
54
55#include <X11/Xlibint.h>
56#include <X11/extensions/Xrandr.h>
57#include <X11/extensions/panoramiXproto.h>
58
59#define MILLIS_PER_INCH (25.4)
60#define DEFAULT_DPI (96.0)
61
62
63/** Maximum number of supported screens. DRM and X11 both limit this to 32. */
64/** @todo if this ever changes, dynamically allocate resizeable arrays in the
65 * context structure. */
66#define VMW_MAX_HEADS 32
67/** Monitor positions array. Allocated here and deallocated in the class descructor. */
68RTPOINT *mpMonitorPositions;
69/** Thread to listen to some of the X server events. */
70RTTHREAD mX11MonitorThread = NIL_RTTHREAD;
71/** Shutdown indicator for the monitor thread. */
72static bool g_fMonitorThreadShutdown = false;
73
74
75typedef struct {
76 CARD8 reqType; /* always X_VMwareCtrlReqCode */
77 CARD8 VMwareCtrlReqType; /* always X_VMwareCtrlSetTopology */
78 CARD16 length B16;
79 CARD32 screen B32;
80 CARD32 number B32;
81 CARD32 pad1 B32;
82} xVMwareCtrlSetTopologyReq;
83#define sz_xVMwareCtrlSetTopologyReq 16
84
85#define X_VMwareCtrlSetTopology 2
86
87typedef struct {
88 BYTE type; /* X_Reply */
89 BYTE pad1;
90 CARD16 sequenceNumber B16;
91 CARD32 length B32;
92 CARD32 screen B32;
93 CARD32 pad2 B32;
94 CARD32 pad3 B32;
95 CARD32 pad4 B32;
96 CARD32 pad5 B32;
97 CARD32 pad6 B32;
98} xVMwareCtrlSetTopologyReply;
99#define sz_xVMwareCtrlSetTopologyReply 32
100
101struct X11VMWRECT
102{
103 int16_t x;
104 int16_t y;
105 uint16_t w;
106 uint16_t h;
107};
108AssertCompileSize(struct X11VMWRECT, 8);
109
110struct X11CONTEXT
111{
112 Display *pDisplay;
113 Window rootWindow;
114 int iDefaultScreen;
115 XRRScreenResources *pScreenResources;
116 int hRandRMajor;
117 int hVMWCtrlMajorOpCode;
118 int hRandRMinor;
119 int hRandREventBase;
120 int hRandRErrorBase;
121 int hEventMask;
122 /** The number of outputs (monitors, including disconnect ones) xrandr reports. */
123 int hOutputCount;
124 void *pRandLibraryHandle;
125 /** Function pointers we used if we dlopen libXrandr instead of linking. */
126 void (*pXRRSelectInput) (Display *, Window, int);
127 Bool (*pXRRQueryExtension) (Display *, int *, int *);
128 Status (*pXRRQueryVersion) (Display *, int *, int*);
129 XRRMonitorInfo* (*pXRRGetMonitors)(Display *, Window, Bool, int *);
130 XRRScreenResources* (*pXRRGetScreenResources)(Display *, Window);
131 Status (*pXRRSetCrtcConfig)(Display *, XRRScreenResources *, RRCrtc,
132 Time, int, int, RRMode, Rotation, RROutput *, int);
133 void (*pXRRFreeMonitors)(XRRMonitorInfo *);
134 void (*pXRRFreeScreenResources)(XRRScreenResources *);
135 void (*pXRRFreeModeInfo)(XRRModeInfo *);
136 void (*pXRRFreeOutputInfo)(XRROutputInfo *);
137 void (*pXRRSetScreenSize)(Display *, Window, int, int, int, int);
138 int (*pXRRUpdateConfiguration)(XEvent *event);
139 XRRModeInfo* (*pXRRAllocModeInfo)(_Xconst char *, int);
140 RRMode (*pXRRCreateMode) (Display *, Window, XRRModeInfo *);
141 XRROutputInfo* (*pXRRGetOutputInfo) (Display *, XRRScreenResources *, RROutput);
142 void (*pXRRAddOutputMode)(Display *, RROutput, RRMode);
143};
144
145static X11CONTEXT x11Context;
146
147#define MAX_MODE_NAME_LEN 64
148#define MAX_COMMAND_LINE_LEN 512
149#define MAX_MODE_LINE_LEN 512
150
151struct RANDROUTPUT
152{
153 int32_t x;
154 int32_t y;
155 uint32_t width;
156 uint32_t height;
157 bool fEnabled;
158};
159
160struct DisplayModeR {
161
162 /* These are the values that the user sees/provides */
163 int Clock; /* pixel clock freq (kHz) */
164 int HDisplay; /* horizontal timing */
165 int HSyncStart;
166 int HSyncEnd;
167 int HTotal;
168 int HSkew;
169 int VDisplay; /* vertical timing */
170 int VSyncStart;
171 int VSyncEnd;
172 int VTotal;
173 int VScan;
174 float HSync;
175 float VRefresh;
176};
177
178/** Forward declarations. */
179static void x11Connect();
180static int determineOutputCount();
181
182#define checkFunctionPtr(pFunction) \
183 do{ \
184 if (!pFunction) \
185 { \
186 VBClLogFatalError("Could not find symbol address\n"); \
187 dlclose(x11Context.pRandLibraryHandle); \
188 x11Context.pRandLibraryHandle = NULL; \
189 return VERR_NOT_FOUND; \
190 } \
191 }while(0)
192
193
194/** A slightly modified version of the xf86CVTMode function from xf86cvt.c
195 * from the xserver source code. Computes several parameters of a display mode
196 * out of horizontal and vertical resolutions. Replicated here to avoid further
197 * dependencies. */
198DisplayModeR f86CVTMode(int HDisplay, int VDisplay, float VRefresh, Bool Reduced,
199 Bool Interlaced)
200{
201 DisplayModeR Mode;
202
203 /* 1) top/bottom margin size (% of height) - default: 1.8 */
204#define CVT_MARGIN_PERCENTAGE 1.8
205
206 /* 2) character cell horizontal granularity (pixels) - default 8 */
207#define CVT_H_GRANULARITY 8
208
209 /* 4) Minimum vertical porch (lines) - default 3 */
210#define CVT_MIN_V_PORCH 3
211
212 /* 4) Minimum number of vertical back porch lines - default 6 */
213#define CVT_MIN_V_BPORCH 6
214
215 /* Pixel Clock step (kHz) */
216#define CVT_CLOCK_STEP 250
217
218 Bool Margins = false;
219 float VFieldRate, HPeriod;
220 int HDisplayRnd, HMargin;
221 int VDisplayRnd, VMargin, VSync;
222 float Interlace; /* Please rename this */
223
224 /* CVT default is 60.0Hz */
225 if (!VRefresh)
226 VRefresh = 60.0;
227
228 /* 1. Required field rate */
229 if (Interlaced)
230 VFieldRate = VRefresh * 2;
231 else
232 VFieldRate = VRefresh;
233
234 /* 2. Horizontal pixels */
235 HDisplayRnd = HDisplay - (HDisplay % CVT_H_GRANULARITY);
236
237 /* 3. Determine left and right borders */
238 if (Margins) {
239 /* right margin is actually exactly the same as left */
240 HMargin = (((float) HDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0);
241 HMargin -= HMargin % CVT_H_GRANULARITY;
242 }
243 else
244 HMargin = 0;
245
246 /* 4. Find total active pixels */
247 Mode.HDisplay = HDisplayRnd + 2 * HMargin;
248
249 /* 5. Find number of lines per field */
250 if (Interlaced)
251 VDisplayRnd = VDisplay / 2;
252 else
253 VDisplayRnd = VDisplay;
254
255 /* 6. Find top and bottom margins */
256 /* nope. */
257 if (Margins)
258 /* top and bottom margins are equal again. */
259 VMargin = (((float) VDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0);
260 else
261 VMargin = 0;
262
263 Mode.VDisplay = VDisplay + 2 * VMargin;
264
265 /* 7. Interlace */
266 if (Interlaced)
267 Interlace = 0.5;
268 else
269 Interlace = 0.0;
270
271 /* Determine VSync Width from aspect ratio */
272 if (!(VDisplay % 3) && ((VDisplay * 4 / 3) == HDisplay))
273 VSync = 4;
274 else if (!(VDisplay % 9) && ((VDisplay * 16 / 9) == HDisplay))
275 VSync = 5;
276 else if (!(VDisplay % 10) && ((VDisplay * 16 / 10) == HDisplay))
277 VSync = 6;
278 else if (!(VDisplay % 4) && ((VDisplay * 5 / 4) == HDisplay))
279 VSync = 7;
280 else if (!(VDisplay % 9) && ((VDisplay * 15 / 9) == HDisplay))
281 VSync = 7;
282 else /* Custom */
283 VSync = 10;
284
285 if (!Reduced) { /* simplified GTF calculation */
286
287 /* 4) Minimum time of vertical sync + back porch interval (µs)
288 * default 550.0 */
289#define CVT_MIN_VSYNC_BP 550.0
290
291 /* 3) Nominal HSync width (% of line period) - default 8 */
292#define CVT_HSYNC_PERCENTAGE 8
293
294 float HBlankPercentage;
295 int VSyncAndBackPorch, VBackPorch;
296 int HBlank;
297
298 /* 8. Estimated Horizontal period */
299 HPeriod = ((float) (1000000.0 / VFieldRate - CVT_MIN_VSYNC_BP)) /
300 (VDisplayRnd + 2 * VMargin + CVT_MIN_V_PORCH + Interlace);
301
302 /* 9. Find number of lines in sync + backporch */
303 if (((int) (CVT_MIN_VSYNC_BP / HPeriod) + 1) <
304 (VSync + CVT_MIN_V_PORCH))
305 VSyncAndBackPorch = VSync + CVT_MIN_V_PORCH;
306 else
307 VSyncAndBackPorch = (int) (CVT_MIN_VSYNC_BP / HPeriod) + 1;
308
309 /* 10. Find number of lines in back porch */
310 VBackPorch = VSyncAndBackPorch - VSync;
311 (void) VBackPorch;
312
313 /* 11. Find total number of lines in vertical field */
314 Mode.VTotal = VDisplayRnd + 2 * VMargin + VSyncAndBackPorch + Interlace
315 + CVT_MIN_V_PORCH;
316
317 /* 5) Definition of Horizontal blanking time limitation */
318 /* Gradient (%/kHz) - default 600 */
319#define CVT_M_FACTOR 600
320
321 /* Offset (%) - default 40 */
322#define CVT_C_FACTOR 40
323
324 /* Blanking time scaling factor - default 128 */
325#define CVT_K_FACTOR 128
326
327 /* Scaling factor weighting - default 20 */
328#define CVT_J_FACTOR 20
329
330#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256
331#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
332 CVT_J_FACTOR
333
334 /* 12. Find ideal blanking duty cycle from formula */
335 HBlankPercentage = CVT_C_PRIME - CVT_M_PRIME * HPeriod / 1000.0;
336
337 /* 13. Blanking time */
338 if (HBlankPercentage < 20)
339 HBlankPercentage = 20;
340
341 HBlank = Mode.HDisplay * HBlankPercentage / (100.0 - HBlankPercentage);
342 HBlank -= HBlank % (2 * CVT_H_GRANULARITY);
343
344 /* 14. Find total number of pixels in a line. */
345 Mode.HTotal = Mode.HDisplay + HBlank;
346
347 /* Fill in HSync values */
348 Mode.HSyncEnd = Mode.HDisplay + HBlank / 2;
349
350 Mode.HSyncStart = Mode.HSyncEnd -
351 (Mode.HTotal * CVT_HSYNC_PERCENTAGE) / 100;
352 Mode.HSyncStart += CVT_H_GRANULARITY -
353 Mode.HSyncStart % CVT_H_GRANULARITY;
354
355 /* Fill in VSync values */
356 Mode.VSyncStart = Mode.VDisplay + CVT_MIN_V_PORCH;
357 Mode.VSyncEnd = Mode.VSyncStart + VSync;
358
359 }
360 else { /* Reduced blanking */
361 /* Minimum vertical blanking interval time (µs) - default 460 */
362#define CVT_RB_MIN_VBLANK 460.0
363
364 /* Fixed number of clocks for horizontal sync */
365#define CVT_RB_H_SYNC 32.0
366
367 /* Fixed number of clocks for horizontal blanking */
368#define CVT_RB_H_BLANK 160.0
369
370 /* Fixed number of lines for vertical front porch - default 3 */
371#define CVT_RB_VFPORCH 3
372
373 int VBILines;
374
375 /* 8. Estimate Horizontal period. */
376 HPeriod = ((float) (1000000.0 / VFieldRate - CVT_RB_MIN_VBLANK)) /
377 (VDisplayRnd + 2 * VMargin);
378
379 /* 9. Find number of lines in vertical blanking */
380 VBILines = ((float) CVT_RB_MIN_VBLANK) / HPeriod + 1;
381
382 /* 10. Check if vertical blanking is sufficient */
383 if (VBILines < (CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH))
384 VBILines = CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH;
385
386 /* 11. Find total number of lines in vertical field */
387 Mode.VTotal = VDisplayRnd + 2 * VMargin + Interlace + VBILines;
388
389 /* 12. Find total number of pixels in a line */
390 Mode.HTotal = Mode.HDisplay + CVT_RB_H_BLANK;
391
392 /* Fill in HSync values */
393 Mode.HSyncEnd = Mode.HDisplay + CVT_RB_H_BLANK / 2;
394 Mode.HSyncStart = Mode.HSyncEnd - CVT_RB_H_SYNC;
395
396 /* Fill in VSync values */
397 Mode.VSyncStart = Mode.VDisplay + CVT_RB_VFPORCH;
398 Mode.VSyncEnd = Mode.VSyncStart + VSync;
399 }
400 /* 15/13. Find pixel clock frequency (kHz for xf86) */
401 Mode.Clock = Mode.HTotal * 1000.0 / HPeriod;
402 Mode.Clock -= Mode.Clock % CVT_CLOCK_STEP;
403
404 /* 16/14. Find actual Horizontal Frequency (kHz) */
405 Mode.HSync = ((float) Mode.Clock) / ((float) Mode.HTotal);
406
407 /* 17/15. Find actual Field rate */
408 Mode.VRefresh = (1000.0 * ((float) Mode.Clock)) /
409 ((float) (Mode.HTotal * Mode.VTotal));
410
411 /* 18/16. Find actual vertical frame frequency */
412 /* ignore - just set the mode flag for interlaced */
413 if (Interlaced)
414 Mode.VTotal *= 2;
415 return Mode;
416}
417
418
419bool VMwareCtrlSetTopology(Display *dpy, int hExtensionMajorOpcode,
420 int screen, xXineramaScreenInfo extents[], int number)
421{
422 xVMwareCtrlSetTopologyReply rep;
423 xVMwareCtrlSetTopologyReq *req;
424
425 long len;
426
427 LockDisplay(dpy);
428
429 GetReq(VMwareCtrlSetTopology, req);
430 req->reqType = hExtensionMajorOpcode;
431 req->VMwareCtrlReqType = X_VMwareCtrlSetTopology;
432 req->screen = screen;
433 req->number = number;
434
435 len = ((long) number) << 1;
436 SetReqLen(req, len, len);
437 len <<= 2;
438 _XSend(dpy, (char *)extents, len);
439
440 if (!_XReply(dpy, (xReply *)&rep,
441 (SIZEOF(xVMwareCtrlSetTopologyReply) - SIZEOF(xReply)) >> 2,
442 xFalse))
443 {
444 UnlockDisplay(dpy);
445 SyncHandle();
446 return false;
447 }
448 UnlockDisplay(dpy);
449 SyncHandle();
450 return true;
451}
452
453/** This function assumes monitors are named as from Virtual1 to VirtualX. */
454static int getMonitorIdFromName(const char *sMonitorName)
455{
456 if (!sMonitorName)
457 return -1;
458 int iLen = strlen(sMonitorName);
459 if (iLen <= 0)
460 return -1;
461 int iBase = 10;
462 int iResult = 0;
463 for (int i = iLen - 1; i >= 0; --i)
464 {
465 /* Stop upon seeing the first non-numeric char. */
466 if (sMonitorName[i] < 48 || sMonitorName[i] > 57)
467 break;
468 iResult += (sMonitorName[i] - 48) * iBase / 10;
469 iBase *= 10;
470 }
471 return iResult;
472}
473
474static void sendMonitorPositions(RTPOINT *pPositions, size_t cPositions)
475{
476 if (cPositions && !pPositions)
477 {
478 VBClLogError(("Monitor position update called with NULL pointer!\n"));
479 return;
480 }
481 int rc = VbglR3SeamlessSendMonitorPositions(cPositions, pPositions);
482 if (RT_SUCCESS(rc))
483 VBClLogError("Sending monitor positions (%u of them) to the host: %Rrc\n", cPositions, rc);
484 else
485 VBClLogError("Error during sending monitor positions (%u of them) to the host: %Rrc\n", cPositions, rc);
486}
487
488static void queryMonitorPositions()
489{
490 static const int iSentinelPosition = -1;
491 if (mpMonitorPositions)
492 {
493 free(mpMonitorPositions);
494 mpMonitorPositions = NULL;
495 }
496
497 int iMonitorCount = 0;
498 XRRMonitorInfo *pMonitorInfo = NULL;
499#ifdef WITH_DISTRO_XRAND_XINERAMA
500 pMonitorInfo = XRRGetMonitors(x11Context.pDisplay, DefaultRootWindow(x11Context.pDisplay), true, &iMonitorCount);
501#else
502 if (x11Context.pXRRGetMonitors)
503 pMonitorInfo = x11Context.pXRRGetMonitors(x11Context.pDisplay, DefaultRootWindow(x11Context.pDisplay), true, &iMonitorCount);
504#endif
505 if (!pMonitorInfo)
506 return;
507 if (iMonitorCount == -1)
508 VBClLogError("Could not get monitor info\n");
509 else
510 {
511 mpMonitorPositions = (RTPOINT*)malloc(x11Context.hOutputCount * sizeof(RTPOINT));
512 /** @todo memset? */
513 for (int i = 0; i < x11Context.hOutputCount; ++i)
514 {
515 mpMonitorPositions[i].x = iSentinelPosition;
516 mpMonitorPositions[i].y = iSentinelPosition;
517 }
518 for (int i = 0; i < iMonitorCount; ++i)
519 {
520 int iMonitorID = getMonitorIdFromName(XGetAtomName(x11Context.pDisplay, pMonitorInfo[i].name)) - 1;
521 if (iMonitorID >= x11Context.hOutputCount || iMonitorID == -1)
522 continue;
523 VBClLogInfo("Monitor %d (w,h)=(%d,%d) (x,y)=(%d,%d)\n",
524 i,
525 pMonitorInfo[i].width, pMonitorInfo[i].height,
526 pMonitorInfo[i].x, pMonitorInfo[i].y);
527 mpMonitorPositions[iMonitorID].x = pMonitorInfo[i].x;
528 mpMonitorPositions[iMonitorID].y = pMonitorInfo[i].y;
529 }
530 if (iMonitorCount > 0)
531 sendMonitorPositions(mpMonitorPositions, x11Context.hOutputCount);
532 }
533#ifdef WITH_DISTRO_XRAND_XINERAMA
534 XRRFreeMonitors(pMonitorInfo);
535#else
536 if (x11Context.pXRRFreeMonitors)
537 x11Context.pXRRFreeMonitors(pMonitorInfo);
538#endif
539}
540
541static void monitorRandREvents()
542{
543 XEvent event;
544 XNextEvent(x11Context.pDisplay, &event);
545 int eventTypeOffset = event.type - x11Context.hRandREventBase;
546 switch (eventTypeOffset)
547 {
548 case RRScreenChangeNotify:
549 VBClLogInfo("RRScreenChangeNotify\n");
550 queryMonitorPositions();
551 break;
552 case RRNotify:
553 VBClLogInfo("RRNotify\n");
554 break;
555 default:
556 VBClLogInfo("Unknown RR event: %d\n", event.type);
557 break;
558 }
559}
560
561/**
562 * @callback_method_impl{FNRTTHREAD}
563 */
564static DECLCALLBACK(int) x11MonitorThreadFunction(RTTHREAD ThreadSelf, void *pvUser)
565{
566 RT_NOREF(ThreadSelf, pvUser);
567 while (!ASMAtomicReadBool(&g_fMonitorThreadShutdown))
568 {
569 monitorRandREvents();
570 }
571 return 0;
572}
573
574static int startX11MonitorThread()
575{
576 return 0;
577 int rc;
578
579 Assert(g_fMonitorThreadShutdown == false);
580 if (mX11MonitorThread == NIL_RTTHREAD)
581 {
582 rc = RTThreadCreate(&mX11MonitorThread, x11MonitorThreadFunction, NULL /*pvUser*/, 0 /*cbStack*/,
583 RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "X11 events");
584 if (RT_FAILURE(rc))
585 VBClLogFatalError("Warning: failed to start X11 monitor thread (VBoxClient) rc=%Rrc!\n", rc);
586 }
587 else
588 rc = VINF_ALREADY_INITIALIZED;
589 return rc;
590}
591
592static int stopX11MonitorThread(void)
593{
594 int rc;
595 if (mX11MonitorThread != NIL_RTTHREAD)
596 {
597 ASMAtomicWriteBool(&g_fMonitorThreadShutdown, true);
598 /** @todo Send event to thread to get it out of XNextEvent. */
599 //????????
600 //mX11Monitor.interruptEventWait();
601 rc = RTThreadWait(mX11MonitorThread, RT_MS_1SEC, NULL /*prc*/);
602 if (RT_SUCCESS(rc))
603 {
604 mX11MonitorThread = NIL_RTTHREAD;
605 g_fMonitorThreadShutdown = false;
606 }
607 else
608 VBClLogError("Failed to stop X11 monitor thread, rc=%Rrc!\n", rc);
609 }
610 return rc;
611}
612
613static bool callVMWCTRL()
614{
615 const int hHeight = 600;
616 const int hWidth = 800;
617
618 xXineramaScreenInfo *extents = (xXineramaScreenInfo *)malloc(x11Context.hOutputCount * sizeof(xXineramaScreenInfo));
619 if (!extents)
620 return false;
621 int hRunningOffset = 0;
622 for (int i = 0; i < x11Context.hOutputCount; ++i)
623 {
624 extents[i].x_org = hRunningOffset;
625 extents[i].y_org = 0;
626 extents[i].width = hWidth;
627 extents[i].height = hHeight;
628 hRunningOffset += hWidth;
629 }
630 return VMwareCtrlSetTopology(x11Context.pDisplay, x11Context.hVMWCtrlMajorOpCode,
631 DefaultScreen(x11Context.pDisplay),
632 extents, x11Context.hOutputCount);
633}
634
635static bool init()
636{
637 x11Connect();
638 if (x11Context.pDisplay == NULL)
639 return false;
640 callVMWCTRL();
641 if (RT_FAILURE(startX11MonitorThread()))
642 return false;
643 return true;
644}
645
646static void cleanup()
647{
648 if (mpMonitorPositions)
649 {
650 free(mpMonitorPositions);
651 mpMonitorPositions = NULL;
652 }
653 stopX11MonitorThread();
654 if (x11Context.pRandLibraryHandle)
655 {
656 dlclose(x11Context.pRandLibraryHandle);
657 x11Context.pRandLibraryHandle = NULL;
658 }
659#ifdef WITH_DISTRO_XRAND_XINERAMA
660 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
661 XRRFreeScreenResources(x11Context.pScreenResources);
662#else
663 if (x11Context.pXRRSelectInput)
664 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
665 if (x11Context.pXRRFreeScreenResources)
666 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
667#endif
668 XCloseDisplay(x11Context.pDisplay);
669}
670
671#ifndef WITH_DISTRO_XRAND_XINERAMA
672static int openLibRandR()
673{
674 x11Context.pRandLibraryHandle = dlopen("libXrandr.so", RTLD_LAZY /*| RTLD_LOCAL */);
675 if (!x11Context.pRandLibraryHandle)
676 x11Context.pRandLibraryHandle = dlopen("libXrandr.so.2", RTLD_LAZY /*| RTLD_LOCAL */);
677 if (!x11Context.pRandLibraryHandle)
678 x11Context.pRandLibraryHandle = dlopen("libXrandr.so.2.2.0", RTLD_LAZY /*| RTLD_LOCAL */);
679
680 if (!x11Context.pRandLibraryHandle)
681 {
682 VBClLogFatalError("Could not locate libXrandr for dlopen\n");
683 return VERR_NOT_FOUND;
684 }
685
686 *(void **)(&x11Context.pXRRSelectInput) = dlsym(x11Context.pRandLibraryHandle, "XRRSelectInput");
687 checkFunctionPtr(x11Context.pXRRSelectInput);
688
689 *(void **)(&x11Context.pXRRQueryExtension) = dlsym(x11Context.pRandLibraryHandle, "XRRQueryExtension");
690 checkFunctionPtr(x11Context.pXRRQueryExtension);
691
692 *(void **)(&x11Context.pXRRQueryVersion) = dlsym(x11Context.pRandLibraryHandle, "XRRQueryVersion");
693 checkFunctionPtr(x11Context.pXRRQueryVersion);
694
695 *(void **)(&x11Context.pXRRGetMonitors) = dlsym(x11Context.pRandLibraryHandle, "XRRGetMonitors");
696 checkFunctionPtr(x11Context.pXRRGetMonitors);
697
698 *(void **)(&x11Context.pXRRGetScreenResources) = dlsym(x11Context.pRandLibraryHandle, "XRRGetScreenResources");
699 checkFunctionPtr(x11Context.pXRRGetScreenResources);
700
701 *(void **)(&x11Context.pXRRSetCrtcConfig) = dlsym(x11Context.pRandLibraryHandle, "XRRSetCrtcConfig");
702 checkFunctionPtr(x11Context.pXRRSetCrtcConfig);
703
704 *(void **)(&x11Context.pXRRFreeMonitors) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeMonitors");
705 checkFunctionPtr(x11Context.pXRRFreeMonitors);
706
707 *(void **)(&x11Context.pXRRFreeScreenResources) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeScreenResources");
708 checkFunctionPtr(x11Context.pXRRFreeMonitors);
709
710 *(void **)(&x11Context.pXRRFreeModeInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeModeInfo");
711 checkFunctionPtr(x11Context.pXRRFreeModeInfo);
712
713 *(void **)(&x11Context.pXRRFreeOutputInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeOutputInfo");
714 checkFunctionPtr(x11Context.pXRRFreeOutputInfo);
715
716 *(void **)(&x11Context.pXRRSetScreenSize) = dlsym(x11Context.pRandLibraryHandle, "XRRSetScreenSize");
717 checkFunctionPtr(x11Context.pXRRSetScreenSize);
718
719 *(void **)(&x11Context.pXRRUpdateConfiguration) = dlsym(x11Context.pRandLibraryHandle, "XRRUpdateConfiguration");
720 checkFunctionPtr(x11Context.pXRRUpdateConfiguration);
721
722 *(void **)(&x11Context.pXRRAllocModeInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRUpdateConfiguration");
723 checkFunctionPtr(x11Context.pXRRAllocModeInfo);
724
725 *(void **)(&x11Context.pXRRAllocModeInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRUpdateConfiguration");
726 checkFunctionPtr(x11Context.pXRRAllocModeInfo);
727
728 *(void **)(&x11Context.pXRRCreateMode) = dlsym(x11Context.pRandLibraryHandle, "XRRCreateMode");
729 checkFunctionPtr(x11Context.pXRRCreateMode);
730
731 *(void **)(&x11Context.pXRRGetOutputInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRGetOutputInfo");
732 checkFunctionPtr(x11Context.pXRRGetOutputInfo);
733
734 return VINF_SUCCESS;
735}
736#endif
737
738static void x11Connect()
739{
740 x11Context.pScreenResources = NULL;
741 x11Context.pXRRSelectInput = NULL;
742 x11Context.pRandLibraryHandle = NULL;
743 x11Context.pXRRQueryExtension = NULL;
744 x11Context.pXRRQueryVersion = NULL;
745 x11Context.pXRRGetMonitors = NULL;
746 x11Context.pXRRGetScreenResources = NULL;
747 x11Context.pXRRSetCrtcConfig = NULL;
748 x11Context.pXRRFreeMonitors = NULL;
749 x11Context.pXRRFreeScreenResources = NULL;
750 x11Context.pXRRFreeOutputInfo = NULL;
751 x11Context.pXRRFreeModeInfo = NULL;
752 x11Context.pXRRSetScreenSize = NULL;
753 x11Context.pXRRUpdateConfiguration = NULL;
754 x11Context.pXRRAllocModeInfo = NULL;
755 x11Context.pXRRCreateMode = NULL;
756 x11Context.pXRRGetOutputInfo = NULL;
757 x11Context.pXRRAddOutputMode = NULL;
758
759 int dummy;
760 if (x11Context.pDisplay != NULL)
761 VBClLogFatalError("%s called with bad argument\n", __func__);
762 x11Context.pDisplay = XOpenDisplay(NULL);
763 if (x11Context.pDisplay == NULL)
764 return;
765#ifndef WITH_DISTRO_XRAND_XINERAMA
766 if (openLibRandR() != VINF_SUCCESS)
767 {
768 XCloseDisplay(x11Context.pDisplay);
769 x11Context.pDisplay = NULL;
770 return;
771 }
772#endif
773 if (!XQueryExtension(x11Context.pDisplay, "VMWARE_CTRL",
774 &x11Context.hVMWCtrlMajorOpCode, &dummy, &dummy))
775 {
776 XCloseDisplay(x11Context.pDisplay);
777 x11Context.pDisplay = NULL;
778 return;
779 }
780 bool fSuccess = false;
781#ifdef WITH_DISTRO_XRAND_XINERAMA
782 fSuccess = XRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase);
783#else
784 if (x11Context.pXRRQueryExtension)
785 fSuccess = x11Context.pXRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase);
786#endif
787 if (fSuccess)
788 {
789 fSuccess = false;
790#ifdef WITH_DISTRO_XRAND_XINERAMA
791 fSuccess = XRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor);
792#else
793 if (x11Context.pXRRQueryVersion)
794 fSuccess = x11Context.pXRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor);
795#endif
796 if (!fSuccess)
797 {
798 XCloseDisplay(x11Context.pDisplay);
799 x11Context.pDisplay = NULL;
800 return;
801 }
802 }
803 x11Context.rootWindow = DefaultRootWindow(x11Context.pDisplay);
804 x11Context.hEventMask = RRScreenChangeNotifyMask;
805 if (x11Context.hRandRMinor >= 2)
806 x11Context.hEventMask |= RRCrtcChangeNotifyMask
807 | RROutputChangeNotifyMask
808 | RROutputPropertyNotifyMask;
809
810 /* Select the XEvent types we want to listen to. */
811#ifdef WITH_DISTRO_XRAND_XINERAMA
812 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, x11Context.hEventMask);
813#else
814 if (x11Context.pXRRSelectInput)
815 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, x11Context.hEventMask);
816#endif
817#ifdef WITH_DISTRO_XRAND_XINERAMA
818 x11Context.pScreenResources = XRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
819#else
820 if (x11Context.pXRRGetScreenResources)
821 x11Context.pScreenResources = x11Context.pXRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
822#endif
823 x11Context.iDefaultScreen = DefaultScreen(x11Context.pDisplay);
824 x11Context.hOutputCount = determineOutputCount();
825}
826
827static int determineOutputCount()
828{
829 if (!x11Context.pScreenResources)
830 return 0;
831 return x11Context.pScreenResources->noutput;
832}
833
834static int findExistingModeIndex(unsigned iXRes, unsigned iYRes)
835{
836 if (!x11Context.pScreenResources)
837 return -1;
838 for (int i = 0; i < x11Context.pScreenResources->nmode; ++i)
839 {
840 if (x11Context.pScreenResources->modes[i].width == iXRes && x11Context.pScreenResources->modes[i].height == iYRes)
841 return i;
842 }
843 return -1;
844}
845
846static bool disableCRTC(RRCrtc crtcID)
847{
848 Status ret = Success;
849#ifdef WITH_DISTRO_XRAND_XINERAMA
850 ret = XRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcID,
851 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
852#else
853 if (x11Context.pXRRSetCrtcConfig)
854 ret = x11Context.pXRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcID,
855 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
856#endif
857 if (ret == Success)
858 return false;
859 else
860 {
861 VBClLogFatalError("Crtc disable failed %lu\n", crtcID);
862 return false;
863 }
864}
865
866static XRRScreenSize currentSize()
867{
868 XRRScreenSize cSize;
869 cSize.width = DisplayWidth(x11Context.pDisplay, x11Context.iDefaultScreen);
870 cSize.mwidth = DisplayWidthMM(x11Context.pDisplay, x11Context.iDefaultScreen);
871 cSize.height = DisplayHeight(x11Context.pDisplay, x11Context.iDefaultScreen);
872 cSize.mheight = DisplayHeightMM(x11Context.pDisplay, x11Context.iDefaultScreen);
873 return cSize;
874}
875
876static unsigned int computeDpi(unsigned int pixels, unsigned int mm)
877{
878 unsigned int dpi = 0;
879 if (mm > 0) {
880 dpi = (unsigned int)((double)pixels * MILLIS_PER_INCH /
881 (double)mm + 0.5);
882 printf("computed dpi %u\n", dpi);
883 }
884 return (dpi > 0) ? dpi : DEFAULT_DPI;
885}
886
887static bool resizeFrameBuffer(struct RANDROUTPUT *paOutputs)
888{
889 unsigned int iXRes = 0;
890 unsigned int iYRes = 0;
891 /* Don't care about the output positions for now. */
892 for (int i = 0; i < VMW_MAX_HEADS; ++i)
893 {
894 if (!paOutputs[i].fEnabled)
895 continue;
896 iXRes += paOutputs[i].width;
897 iYRes = iYRes < paOutputs[i].height ? paOutputs[i].height : iYRes;
898 }
899 XRRScreenSize cSize= currentSize();
900 unsigned int xdpi = computeDpi(cSize.width, cSize.mwidth);
901 unsigned int ydpi = computeDpi(cSize.height, cSize.mheight);
902 unsigned int xmm;
903 unsigned int ymm;
904 xmm = (int)(MILLIS_PER_INCH * iXRes / ((double)xdpi) + 0.5);
905 ymm = (int)(MILLIS_PER_INCH * iYRes / ((double)ydpi) + 0.5);
906#ifdef WITH_DISTRO_XRAND_XINERAMA
907 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, RRScreenChangeNotifyMask);
908 XRRSetScreenSize(x11Context.pDisplay, x11Context.rootWindow, iXRes, iYRes, xmm, ymm);
909#else
910 if (x11Context.pXRRSelectInput)
911 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, RRScreenChangeNotifyMask);
912 if (x11Context.pXRRSetScreenSize)
913 x11Context.pXRRSetScreenSize(x11Context.pDisplay, x11Context.rootWindow, iXRes, iYRes, xmm, ymm);
914#endif
915 XSync(x11Context.pDisplay, False);
916 XEvent configEvent;
917 bool event = false;
918 while (XCheckTypedEvent(x11Context.pDisplay, RRScreenChangeNotify + x11Context.hRandREventBase, &configEvent))
919 {
920#ifdef WITH_DISTRO_XRAND_XINERAMA
921 XRRUpdateConfiguration(&configEvent);
922#else
923 if (x11Context.pXRRUpdateConfiguration)
924 x11Context.pXRRUpdateConfiguration(&configEvent);
925#endif
926 event = true;
927 }
928#ifdef WITH_DISTRO_XRAND_XINERAMA
929 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
930#else
931 if (x11Context.pXRRSelectInput)
932 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
933#endif
934 XRRScreenSize newSize = currentSize();
935 /** @todo In case of unsuccesful frame buffer resize we have to revert frame buffer size and crtc sizes. */
936 if (!event || newSize.width != (int)iXRes || newSize.height != (int)iYRes)
937 {
938 VBClLogError("Resizing frame buffer to %d %d has failed\n", iXRes, iYRes);
939 return false;
940 }
941 return true;
942}
943
944static XRRModeInfo *createMode(int iXRes, int iYRes)
945{
946 XRRModeInfo *pModeInfo = NULL;
947 char sModeName[126];
948 sprintf(sModeName, "%dx%d_vbox", iXRes, iYRes);
949#ifdef WITH_DISTRO_XRAND_XINERAMA
950 pModeInfo = XRRAllocModeInfo(sModeName, strlen(sModeName));
951#else
952 if (x11Context.pXRRAllocModeInfo)
953 pModeInfo = x11Context.pXRRAllocModeInfo(sModeName, strlen(sModeName));
954#endif
955 pModeInfo->width = iXRes;
956 pModeInfo->height = iYRes;
957
958 DisplayModeR mode = f86CVTMode(iXRes, iYRes, 60 /*VRefresh */, true /*Reduced */, false /* Interlaced */);
959
960 pModeInfo->dotClock = mode.Clock;
961 pModeInfo->hSyncStart = mode.HSyncStart;
962 pModeInfo->hSyncEnd = mode.HSyncEnd;
963 pModeInfo->hTotal = mode.HTotal;
964 pModeInfo->hSkew = mode.HSkew;
965 pModeInfo->vSyncStart = mode.VSyncStart;
966 pModeInfo->vSyncEnd = mode.VSyncEnd;
967 pModeInfo->vTotal = mode.VTotal;
968
969 RRMode newMode = None;
970#ifdef WITH_DISTRO_XRAND_XINERAMA
971 newMode = XRRCreateMode(x11Context.pDisplay, x11Context.rootWindow, pModeInfo);
972#else
973 if (x11Context.pXRRCreateMode)
974 newMode = x11Context.pXRRCreateMode(x11Context.pDisplay, x11Context.rootWindow, pModeInfo);
975#endif
976 if (newMode == None)
977 {
978#ifdef WITH_DISTRO_XRAND_XINERAMA
979 XRRFreeModeInfo(pModeInfo);
980#else
981 if (x11Context.pXRRFreeModeInfo)
982 x11Context.pXRRFreeModeInfo(pModeInfo);
983#endif
984 return NULL;
985 }
986 pModeInfo->id = newMode;
987 return pModeInfo;
988}
989
990static bool configureOutput(int iOutputIndex, struct RANDROUTPUT *paOutputs)
991{
992 if (iOutputIndex >= x11Context.hOutputCount)
993 {
994 VBClLogError("Output index %d is greater than # of oputputs %d\n", iOutputIndex, x11Context.hOutputCount);
995 return false;
996 }
997 RROutput outputId = x11Context.pScreenResources->outputs[iOutputIndex];
998 XRROutputInfo *pOutputInfo = NULL;
999#ifdef WITH_DISTRO_XRAND_XINERAMA
1000 pOutputInfo = XRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, outputId);
1001#else
1002 if (x11Context.pXRRGetOutputInfo)
1003 pOutputInfo = x11Context.pXRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, outputId);
1004#endif
1005
1006 XRRModeInfo *pModeInfo = NULL;
1007 bool fNewMode = false;
1008 /* Index of the mode within the XRRScreenResources.modes array. -1 if such a mode with required resolution does not exists*/
1009 int iModeIndex = findExistingModeIndex(paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1010 if (iModeIndex != -1 && iModeIndex < x11Context.hOutputCount)
1011 pModeInfo = &(x11Context.pScreenResources->modes[iModeIndex]);
1012 else
1013 {
1014 /* A mode with required size was not found. Create a new one. */
1015 pModeInfo = createMode(paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1016 fNewMode = true;
1017 }
1018 if (!pModeInfo)
1019 {
1020 VBClLogError("Could not create mode for the resolution (%d, %d)\n",
1021 paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1022 return false;
1023 }
1024
1025#ifdef WITH_DISTRO_XRAND_XINERAMA
1026 XRRAddOutputMode(x11Context.pDisplay, outputId, pModeInfo->id);
1027#else
1028 if (x11Context.pXRRAddOutputMode)
1029 x11Context.pXRRAddOutputMode(x11Context.pDisplay, outputId, pModeInfo->id);
1030#endif
1031 /* Make sure outputs crtc is set. */
1032 pOutputInfo->crtc = pOutputInfo->crtcs[0];
1033
1034 RRCrtc crtcId = pOutputInfo->crtcs[0];
1035 Status ret = Success;
1036#ifdef WITH_DISTRO_XRAND_XINERAMA
1037 ret = XRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcId, CurrentTime,
1038 paOutputs[iOutputIndex].x, paOutputs[iOutputIndex].y,
1039 pModeInfo->id, RR_Rotate_0, &(outputId), 1 /*int noutputs*/);
1040#else
1041 if (x11Context.pXRRSetCrtcConfig)
1042 ret = x11Context.pXRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcId, CurrentTime,
1043 paOutputs[iOutputIndex].x, paOutputs[iOutputIndex].y,
1044 pModeInfo->id, RR_Rotate_0, &(outputId), 1 /*int noutputs*/);
1045#endif
1046 if (ret != Success)
1047 VBClLogError("crtc set config failed for output %d\n", iOutputIndex);
1048
1049#ifdef WITH_DISTRO_XRAND_XINERAMA
1050 XRRFreeOutputInfo(pOutputInfo);
1051#else
1052 if (x11Context.pXRRFreeOutputInfo)
1053 x11Context.pXRRFreeOutputInfo(pOutputInfo);
1054#endif
1055
1056 if (fNewMode)
1057 {
1058#ifdef WITH_DISTRO_XRAND_XINERAMA
1059 XRRFreeModeInfo(pModeInfo);
1060#else
1061 if (x11Context.pXRRFreeModeInfo)
1062 x11Context.pXRRFreeModeInfo(pModeInfo);
1063#endif
1064 }
1065 return true;
1066}
1067
1068/** Construct the xrandr command which sets the whole monitor topology each time. */
1069static void setXrandrTopology(struct RANDROUTPUT *paOutputs)
1070{
1071 XGrabServer(x11Context.pDisplay);
1072 (void*)paOutputs;
1073 if (!x11Context.pScreenResources)
1074 return;
1075 /* Disable all crtcs. */
1076 for (int i = 0; i , x11Context.pScreenResources->ncrtc; ++i)
1077 {
1078 if (!disableCRTC(x11Context.pScreenResources->crtcs[i]))
1079 {
1080 XUngrabServer(x11Context.pDisplay);
1081 return;
1082 }
1083 }
1084 /* Resize the frame buffer. */
1085 resizeFrameBuffer(paOutputs);
1086
1087 /* Configure the outputs. */
1088 for (int i = 0; i < x11Context.hOutputCount; ++i)
1089 {
1090 /* be paranoid. */
1091 if (i >= x11Context.pScreenResources->noutput)
1092 break;
1093 if (!paOutputs[i].fEnabled)
1094 continue;
1095 configureOutput(i, paOutputs);
1096 }
1097 XUngrabServer(x11Context.pDisplay);
1098}
1099
1100static const char *getName()
1101{
1102 return "Display SVGA X11";
1103}
1104
1105static const char *getPidFilePath()
1106{
1107 return ".vboxclient-display-svga-x11.pid";
1108}
1109
1110static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
1111{
1112 RT_NOREF(ppInterface, fDaemonised);
1113 int rc;
1114 uint32_t events;
1115 /* Do not acknowledge the first event we query for to pick up old events,
1116 * e.g. from before a guest reboot. */
1117 bool fAck = false;
1118
1119 if (!init())
1120 return VINF_SUCCESS;
1121 static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS];
1122
1123 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
1124 if (RT_FAILURE(rc))
1125 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
1126 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
1127 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
1128 return VINF_SUCCESS;
1129 if (RT_FAILURE(rc))
1130 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
1131
1132 for (;;)
1133 {
1134 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
1135 uint32_t cDisplaysOut;
1136 /* Query the first size without waiting. This lets us e.g. pick up
1137 * the last event before a guest reboot when we start again after. */
1138 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
1139 fAck = true;
1140 if (RT_FAILURE(rc))
1141 VBClLogFatalError("Failed to get display change request, rc=%Rrc\n", rc);
1142 if (cDisplaysOut > VMW_MAX_HEADS)
1143 VBClLogFatalError("Display change request contained, rc=%Rrc\n", rc);
1144 if (cDisplaysOut > 0)
1145 {
1146 for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
1147 {
1148 uint32_t idDisplay = aDisplays[i].idDisplay;
1149 if (idDisplay >= VMW_MAX_HEADS)
1150 continue;
1151 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags;
1152 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
1153 {
1154 if (idDisplay == 0 || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
1155 {
1156 aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin;
1157 aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin;
1158 } else {
1159 aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx;
1160 aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin;
1161 }
1162 aMonitors[idDisplay].cx = aDisplays[i].cx;
1163 aMonitors[idDisplay].cy = aDisplays[i].cy;
1164 }
1165 }
1166 /* Create a whole topology and send it to xrandr. */
1167 struct RANDROUTPUT aOutputs[VMW_MAX_HEADS];
1168 int iRunningX = 0;
1169 for (int j = 0; j < x11Context.hOutputCount; ++j)
1170 {
1171 aOutputs[j].x = iRunningX;
1172 aOutputs[j].y = aMonitors[j].yOrigin;
1173 aOutputs[j].width = aMonitors[j].cx;
1174 aOutputs[j].height = aMonitors[j].cy;
1175 aOutputs[j].fEnabled = !(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED);
1176 if (aOutputs[j].fEnabled)
1177 iRunningX += aOutputs[j].width;
1178 }
1179 setXrandrTopology(aOutputs);
1180 }
1181 do
1182 {
1183 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
1184 } while (rc == VERR_INTERRUPTED);
1185 if (RT_FAILURE(rc))
1186 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
1187 }
1188 cleanup();
1189}
1190
1191static struct VBCLSERVICE interface =
1192{
1193 getName,
1194 getPidFilePath,
1195 VBClServiceDefaultHandler, /* Init */
1196 run,
1197 VBClServiceDefaultCleanup
1198}, *pInterface = &interface;
1199
1200struct VBCLSERVICE **VBClDisplaySVGAX11Service()
1201{
1202 return &pInterface;
1203}
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