VirtualBox

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

Last change on this file since 89635 was 89635, checked in by vboxsync, 4 years ago

Additions: X11: VMSVGA: add missing initialization piece, bugref#10028.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.0 KB
Line 
1/* $Id: display-svga-x11.cpp 89635 2021-06-11 18:10:55Z 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.215389.xyz. 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 * - This code does 2 related but separate things: 1- It resizes and enables/disables monitors upon host's
42 requests (see the infinite loop in run()). 2- it listens to RandR events (caused by this or any other X11 client)
43 on a different thread and notifies host about the new monitor positions. See sendMonitorPositions(...). This is
44 mainly a work around since we have realized that vmsvga does not convey correct monitor positions thru FIFO.
45 */
46#include <stdio.h>
47#include <dlfcn.h>
48/** For sleep(..) */
49#include <unistd.h>
50#include "VBoxClient.h"
51
52#include <VBox/VBoxGuestLib.h>
53
54#include <iprt/asm.h>
55#include <iprt/assert.h>
56#include <iprt/err.h>
57#include <iprt/file.h>
58#include <iprt/mem.h>
59#include <iprt/path.h>
60#include <iprt/string.h>
61#include <iprt/thread.h>
62
63#include <X11/Xlibint.h>
64#include <X11/extensions/Xrandr.h>
65#include <X11/extensions/panoramiXproto.h>
66
67#define MILLIS_PER_INCH (25.4)
68#define DEFAULT_DPI (96.0)
69
70/** Maximum number of supported screens. DRM and X11 both limit this to 32. */
71/** @todo if this ever changes, dynamically allocate resizeable arrays in the
72 * context structure. */
73#define VMW_MAX_HEADS 32
74/** Monitor positions array. Allocated here and deallocated in the class descructor. */
75RTPOINT *mpMonitorPositions;
76/** Thread to listen to some of the X server events. */
77RTTHREAD mX11MonitorThread = NIL_RTTHREAD;
78/** Shutdown indicator for the monitor thread. */
79static bool g_fMonitorThreadShutdown = false;
80
81
82typedef struct {
83 CARD8 reqType; /* always X_VMwareCtrlReqCode */
84 CARD8 VMwareCtrlReqType; /* always X_VMwareCtrlSetTopology */
85 CARD16 length B16;
86 CARD32 screen B32;
87 CARD32 number B32;
88 CARD32 pad1 B32;
89} xVMwareCtrlSetTopologyReq;
90#define sz_xVMwareCtrlSetTopologyReq 16
91
92#define X_VMwareCtrlSetTopology 2
93
94typedef struct {
95 BYTE type; /* X_Reply */
96 BYTE pad1;
97 CARD16 sequenceNumber B16;
98 CARD32 length B32;
99 CARD32 screen B32;
100 CARD32 pad2 B32;
101 CARD32 pad3 B32;
102 CARD32 pad4 B32;
103 CARD32 pad5 B32;
104 CARD32 pad6 B32;
105} xVMwareCtrlSetTopologyReply;
106#define sz_xVMwareCtrlSetTopologyReply 32
107
108struct X11VMWRECT
109{
110 int16_t x;
111 int16_t y;
112 uint16_t w;
113 uint16_t h;
114};
115AssertCompileSize(struct X11VMWRECT, 8);
116
117struct X11CONTEXT
118{
119 Display *pDisplay;
120 /* We use a separate connection for randr event listening since sharing a
121 single display object with resizing (main) and event listening threads ends up having a deadlock.*/
122 Display *pDisplayRandRMonitoring;
123 Window rootWindow;
124 int iDefaultScreen;
125 XRRScreenResources *pScreenResources;
126 int hRandRMajor;
127 int hRandRMinor;
128 int hRandREventBase;
129 int hRandRErrorBase;
130 int hEventMask;
131 bool fMonitorInfoAvailable;
132 /** The number of outputs (monitors, including disconnect ones) xrandr reports. */
133 int hOutputCount;
134 void *pRandLibraryHandle;
135 bool fWmwareCtrlExtention;
136 int hVMWCtrlMajorOpCode;
137 /** Function pointers we used if we dlopen libXrandr instead of linking. */
138 void (*pXRRSelectInput) (Display *, Window, int);
139 Bool (*pXRRQueryExtension) (Display *, int *, int *);
140 Status (*pXRRQueryVersion) (Display *, int *, int*);
141 XRRMonitorInfo* (*pXRRGetMonitors)(Display *, Window, Bool, int *);
142 XRRScreenResources* (*pXRRGetScreenResources)(Display *, Window);
143 Status (*pXRRSetCrtcConfig)(Display *, XRRScreenResources *, RRCrtc,
144 Time, int, int, RRMode, Rotation, RROutput *, int);
145 void (*pXRRFreeMonitors)(XRRMonitorInfo *);
146 void (*pXRRFreeScreenResources)(XRRScreenResources *);
147 void (*pXRRFreeModeInfo)(XRRModeInfo *);
148 void (*pXRRFreeOutputInfo)(XRROutputInfo *);
149 void (*pXRRSetScreenSize)(Display *, Window, int, int, int, int);
150 int (*pXRRUpdateConfiguration)(XEvent *event);
151 XRRModeInfo* (*pXRRAllocModeInfo)(_Xconst char *, int);
152 RRMode (*pXRRCreateMode) (Display *, Window, XRRModeInfo *);
153 XRROutputInfo* (*pXRRGetOutputInfo) (Display *, XRRScreenResources *, RROutput);
154 XRRCrtcInfo* (*pXRRGetCrtcInfo) (Display *, XRRScreenResources *, RRCrtc crtc);
155 void (*pXRRFreeCrtcInfo)(XRRCrtcInfo *);
156 void (*pXRRAddOutputMode)(Display *, RROutput, RRMode);
157};
158
159static X11CONTEXT x11Context;
160
161struct RANDROUTPUT
162{
163 int32_t x;
164 int32_t y;
165 uint32_t width;
166 uint32_t height;
167 bool fEnabled;
168};
169
170struct DisplayModeR {
171
172 /* These are the values that the user sees/provides */
173 int Clock; /* pixel clock freq (kHz) */
174 int HDisplay; /* horizontal timing */
175 int HSyncStart;
176 int HSyncEnd;
177 int HTotal;
178 int HSkew;
179 int VDisplay; /* vertical timing */
180 int VSyncStart;
181 int VSyncEnd;
182 int VTotal;
183 int VScan;
184 float HSync;
185 float VRefresh;
186};
187
188/** Forward declarations. */
189static void x11Connect();
190static int determineOutputCount();
191
192#define checkFunctionPtrReturn(pFunction) \
193 do { \
194 if (!pFunction) \
195 { \
196 VBClLogFatalError("Could not find symbol address (%s)\n", #pFunction); \
197 dlclose(x11Context.pRandLibraryHandle); \
198 x11Context.pRandLibraryHandle = NULL; \
199 return VERR_NOT_FOUND; \
200 } \
201 } while (0)
202
203#define checkFunctionPtr(pFunction) \
204 do { \
205 if (!pFunction) \
206 VBClLogFatalError("Could not find symbol address (%s)\n", #pFunction);\
207 } while (0)
208
209
210/** A slightly modified version of the xf86CVTMode function from xf86cvt.c
211 * from the xserver source code. Computes several parameters of a display mode
212 * out of horizontal and vertical resolutions. Replicated here to avoid further
213 * dependencies. */
214DisplayModeR f86CVTMode(int HDisplay, int VDisplay, float VRefresh /* Herz */, Bool Reduced,
215 Bool Interlaced)
216{
217 DisplayModeR Mode;
218
219 /* 1) top/bottom margin size (% of height) - default: 1.8 */
220#define CVT_MARGIN_PERCENTAGE 1.8
221
222 /* 2) character cell horizontal granularity (pixels) - default 8 */
223#define CVT_H_GRANULARITY 8
224
225 /* 4) Minimum vertical porch (lines) - default 3 */
226#define CVT_MIN_V_PORCH 3
227
228 /* 4) Minimum number of vertical back porch lines - default 6 */
229#define CVT_MIN_V_BPORCH 6
230
231 /* Pixel Clock step (kHz) */
232#define CVT_CLOCK_STEP 250
233
234 Bool Margins = false;
235 float VFieldRate, HPeriod;
236 int HDisplayRnd, HMargin;
237 int VDisplayRnd, VMargin, VSync;
238 float Interlace; /* Please rename this */
239
240 /* CVT default is 60.0Hz */
241 if (!VRefresh)
242 VRefresh = 60.0;
243
244 /* 1. Required field rate */
245 if (Interlaced)
246 VFieldRate = VRefresh * 2;
247 else
248 VFieldRate = VRefresh;
249
250 /* 2. Horizontal pixels */
251 HDisplayRnd = HDisplay - (HDisplay % CVT_H_GRANULARITY);
252
253 /* 3. Determine left and right borders */
254 if (Margins) {
255 /* right margin is actually exactly the same as left */
256 HMargin = (((float) HDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0);
257 HMargin -= HMargin % CVT_H_GRANULARITY;
258 }
259 else
260 HMargin = 0;
261
262 /* 4. Find total active pixels */
263 Mode.HDisplay = HDisplayRnd + 2 * HMargin;
264
265 /* 5. Find number of lines per field */
266 if (Interlaced)
267 VDisplayRnd = VDisplay / 2;
268 else
269 VDisplayRnd = VDisplay;
270
271 /* 6. Find top and bottom margins */
272 /* nope. */
273 if (Margins)
274 /* top and bottom margins are equal again. */
275 VMargin = (((float) VDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0);
276 else
277 VMargin = 0;
278
279 Mode.VDisplay = VDisplay + 2 * VMargin;
280
281 /* 7. Interlace */
282 if (Interlaced)
283 Interlace = 0.5;
284 else
285 Interlace = 0.0;
286
287 /* Determine VSync Width from aspect ratio */
288 if (!(VDisplay % 3) && ((VDisplay * 4 / 3) == HDisplay))
289 VSync = 4;
290 else if (!(VDisplay % 9) && ((VDisplay * 16 / 9) == HDisplay))
291 VSync = 5;
292 else if (!(VDisplay % 10) && ((VDisplay * 16 / 10) == HDisplay))
293 VSync = 6;
294 else if (!(VDisplay % 4) && ((VDisplay * 5 / 4) == HDisplay))
295 VSync = 7;
296 else if (!(VDisplay % 9) && ((VDisplay * 15 / 9) == HDisplay))
297 VSync = 7;
298 else /* Custom */
299 VSync = 10;
300
301 if (!Reduced) { /* simplified GTF calculation */
302
303 /* 4) Minimum time of vertical sync + back porch interval (µs)
304 * default 550.0 */
305#define CVT_MIN_VSYNC_BP 550.0
306
307 /* 3) Nominal HSync width (% of line period) - default 8 */
308#define CVT_HSYNC_PERCENTAGE 8
309
310 float HBlankPercentage;
311 int VSyncAndBackPorch, VBackPorch;
312 int HBlank;
313
314 /* 8. Estimated Horizontal period */
315 HPeriod = ((float) (1000000.0 / VFieldRate - CVT_MIN_VSYNC_BP)) /
316 (VDisplayRnd + 2 * VMargin + CVT_MIN_V_PORCH + Interlace);
317
318 /* 9. Find number of lines in sync + backporch */
319 if (((int) (CVT_MIN_VSYNC_BP / HPeriod) + 1) <
320 (VSync + CVT_MIN_V_PORCH))
321 VSyncAndBackPorch = VSync + CVT_MIN_V_PORCH;
322 else
323 VSyncAndBackPorch = (int) (CVT_MIN_VSYNC_BP / HPeriod) + 1;
324
325 /* 10. Find number of lines in back porch */
326 VBackPorch = VSyncAndBackPorch - VSync;
327 (void) VBackPorch;
328
329 /* 11. Find total number of lines in vertical field */
330 Mode.VTotal = VDisplayRnd + 2 * VMargin + VSyncAndBackPorch + Interlace
331 + CVT_MIN_V_PORCH;
332
333 /* 5) Definition of Horizontal blanking time limitation */
334 /* Gradient (%/kHz) - default 600 */
335#define CVT_M_FACTOR 600
336
337 /* Offset (%) - default 40 */
338#define CVT_C_FACTOR 40
339
340 /* Blanking time scaling factor - default 128 */
341#define CVT_K_FACTOR 128
342
343 /* Scaling factor weighting - default 20 */
344#define CVT_J_FACTOR 20
345
346#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256
347#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
348 CVT_J_FACTOR
349
350 /* 12. Find ideal blanking duty cycle from formula */
351 HBlankPercentage = CVT_C_PRIME - CVT_M_PRIME * HPeriod / 1000.0;
352
353 /* 13. Blanking time */
354 if (HBlankPercentage < 20)
355 HBlankPercentage = 20;
356
357 HBlank = Mode.HDisplay * HBlankPercentage / (100.0 - HBlankPercentage);
358 HBlank -= HBlank % (2 * CVT_H_GRANULARITY);
359
360 /* 14. Find total number of pixels in a line. */
361 Mode.HTotal = Mode.HDisplay + HBlank;
362
363 /* Fill in HSync values */
364 Mode.HSyncEnd = Mode.HDisplay + HBlank / 2;
365
366 Mode.HSyncStart = Mode.HSyncEnd -
367 (Mode.HTotal * CVT_HSYNC_PERCENTAGE) / 100;
368 Mode.HSyncStart += CVT_H_GRANULARITY -
369 Mode.HSyncStart % CVT_H_GRANULARITY;
370
371 /* Fill in VSync values */
372 Mode.VSyncStart = Mode.VDisplay + CVT_MIN_V_PORCH;
373 Mode.VSyncEnd = Mode.VSyncStart + VSync;
374
375 }
376 else { /* Reduced blanking */
377 /* Minimum vertical blanking interval time (µs) - default 460 */
378#define CVT_RB_MIN_VBLANK 460.0
379
380 /* Fixed number of clocks for horizontal sync */
381#define CVT_RB_H_SYNC 32.0
382
383 /* Fixed number of clocks for horizontal blanking */
384#define CVT_RB_H_BLANK 160.0
385
386 /* Fixed number of lines for vertical front porch - default 3 */
387#define CVT_RB_VFPORCH 3
388
389 int VBILines;
390
391 /* 8. Estimate Horizontal period. */
392 HPeriod = ((float) (1000000.0 / VFieldRate - CVT_RB_MIN_VBLANK)) /
393 (VDisplayRnd + 2 * VMargin);
394
395 /* 9. Find number of lines in vertical blanking */
396 VBILines = ((float) CVT_RB_MIN_VBLANK) / HPeriod + 1;
397
398 /* 10. Check if vertical blanking is sufficient */
399 if (VBILines < (CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH))
400 VBILines = CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH;
401
402 /* 11. Find total number of lines in vertical field */
403 Mode.VTotal = VDisplayRnd + 2 * VMargin + Interlace + VBILines;
404
405 /* 12. Find total number of pixels in a line */
406 Mode.HTotal = Mode.HDisplay + CVT_RB_H_BLANK;
407
408 /* Fill in HSync values */
409 Mode.HSyncEnd = Mode.HDisplay + CVT_RB_H_BLANK / 2;
410 Mode.HSyncStart = Mode.HSyncEnd - CVT_RB_H_SYNC;
411
412 /* Fill in VSync values */
413 Mode.VSyncStart = Mode.VDisplay + CVT_RB_VFPORCH;
414 Mode.VSyncEnd = Mode.VSyncStart + VSync;
415 }
416 /* 15/13. Find pixel clock frequency (kHz for xf86) */
417 Mode.Clock = Mode.HTotal * 1000.0 / HPeriod;
418 Mode.Clock -= Mode.Clock % CVT_CLOCK_STEP;
419
420 /* 16/14. Find actual Horizontal Frequency (kHz) */
421 Mode.HSync = ((float) Mode.Clock) / ((float) Mode.HTotal);
422
423 /* 17/15. Find actual Field rate */
424 Mode.VRefresh = (1000.0 * ((float) Mode.Clock)) /
425 ((float) (Mode.HTotal * Mode.VTotal));
426
427 /* 18/16. Find actual vertical frame frequency */
428 /* ignore - just set the mode flag for interlaced */
429 if (Interlaced)
430 Mode.VTotal *= 2;
431 return Mode;
432}
433
434/** Makes a call to vmwarectrl extension. This updates the
435 * connection information and possible resolutions (modes)
436 * of each monitor on the driver. Also sets the preferred mode
437 * of each output (monitor) to currently selected one. */
438bool VMwareCtrlSetTopology(Display *dpy, int hExtensionMajorOpcode,
439 int screen, xXineramaScreenInfo extents[], int number)
440{
441 xVMwareCtrlSetTopologyReply rep;
442 xVMwareCtrlSetTopologyReq *req;
443
444 long len;
445
446 LockDisplay(dpy);
447
448 GetReq(VMwareCtrlSetTopology, req);
449 req->reqType = hExtensionMajorOpcode;
450 req->VMwareCtrlReqType = X_VMwareCtrlSetTopology;
451 req->screen = screen;
452 req->number = number;
453
454 len = ((long) number) << 1;
455 SetReqLen(req, len, len);
456 len <<= 2;
457 _XSend(dpy, (char *)extents, len);
458
459 if (!_XReply(dpy, (xReply *)&rep,
460 (SIZEOF(xVMwareCtrlSetTopologyReply) - SIZEOF(xReply)) >> 2,
461 xFalse))
462 {
463 UnlockDisplay(dpy);
464 SyncHandle();
465 return false;
466 }
467 UnlockDisplay(dpy);
468 SyncHandle();
469 return true;
470}
471
472/** This function assumes monitors are named as from Virtual1 to VirtualX. */
473static int getMonitorIdFromName(const char *sMonitorName)
474{
475 if (!sMonitorName)
476 return -1;
477 int iLen = strlen(sMonitorName);
478 if (iLen <= 0)
479 return -1;
480 int iBase = 10;
481 int iResult = 0;
482 for (int i = iLen - 1; i >= 0; --i)
483 {
484 /* Stop upon seeing the first non-numeric char. */
485 if (sMonitorName[i] < 48 || sMonitorName[i] > 57)
486 break;
487 iResult += (sMonitorName[i] - 48) * iBase / 10;
488 iBase *= 10;
489 }
490 return iResult;
491}
492
493static void sendMonitorPositions(RTPOINT *pPositions, size_t cPositions)
494{
495 if (cPositions && !pPositions)
496 {
497 VBClLogError(("Monitor position update called with NULL pointer!\n"));
498 return;
499 }
500 int rc = VbglR3SeamlessSendMonitorPositions(cPositions, pPositions);
501 if (RT_SUCCESS(rc))
502 VBClLogInfo("Sending monitor positions (%u of them) to the host: %Rrc\n", cPositions, rc);
503 else
504 VBClLogError("Error during sending monitor positions (%u of them) to the host: %Rrc\n", cPositions, rc);
505}
506
507static void queryMonitorPositions()
508{
509 static const int iSentinelPosition = -1;
510 if (mpMonitorPositions)
511 {
512 free(mpMonitorPositions);
513 mpMonitorPositions = NULL;
514 }
515
516 int iMonitorCount = 0;
517 XRRMonitorInfo *pMonitorInfo = NULL;
518#ifdef WITH_DISTRO_XRAND_XINERAMA
519 pMonitorInfo = XRRGetMonitors(x11Context.pDisplayRandRMonitoring,
520 DefaultRootWindow(x11Context.pDisplayRandRMonitoring), true, &iMonitorCount);
521#else
522 if (x11Context.pXRRGetMonitors)
523 pMonitorInfo = x11Context.pXRRGetMonitors(x11Context.pDisplayRandRMonitoring,
524 DefaultRootWindow(x11Context.pDisplayRandRMonitoring), true, &iMonitorCount);
525#endif
526 if (!pMonitorInfo)
527 return;
528 if (iMonitorCount == -1)
529 VBClLogError("Could not get monitor info\n");
530 else
531 {
532 mpMonitorPositions = (RTPOINT*)malloc(x11Context.hOutputCount * sizeof(RTPOINT));
533 /** @todo memset? */
534 for (int i = 0; i < x11Context.hOutputCount; ++i)
535 {
536 mpMonitorPositions[i].x = iSentinelPosition;
537 mpMonitorPositions[i].y = iSentinelPosition;
538 }
539 for (int i = 0; i < iMonitorCount; ++i)
540 {
541 char *pszMonitorName = XGetAtomName(x11Context.pDisplayRandRMonitoring, pMonitorInfo[i].name);
542 if (!pszMonitorName)
543 {
544 VBClLogError("queryMonitorPositions: skip monitor with unknown name %d\n", i);
545 continue;
546 }
547
548 int iMonitorID = getMonitorIdFromName(pszMonitorName);
549 XFree((void *)pszMonitorName);
550 pszMonitorName = NULL;
551
552 if (iMonitorID >= x11Context.hOutputCount || iMonitorID == -1)
553 {
554 VBClLogInfo("queryMonitorPositions: skip monitor %d (id %d) (w,h)=(%d,%d) (x,y)=(%d,%d)\n",
555 i, iMonitorID,
556 pMonitorInfo[i].width, pMonitorInfo[i].height,
557 pMonitorInfo[i].x, pMonitorInfo[i].y);
558 continue;
559 }
560 VBClLogInfo("Monitor %d (w,h)=(%d,%d) (x,y)=(%d,%d)\n",
561 i,
562 pMonitorInfo[i].width, pMonitorInfo[i].height,
563 pMonitorInfo[i].x, pMonitorInfo[i].y);
564 mpMonitorPositions[iMonitorID].x = pMonitorInfo[i].x;
565 mpMonitorPositions[iMonitorID].y = pMonitorInfo[i].y;
566 }
567 if (iMonitorCount > 0)
568 sendMonitorPositions(mpMonitorPositions, x11Context.hOutputCount);
569 }
570#ifdef WITH_DISTRO_XRAND_XINERAMA
571 XRRFreeMonitors(pMonitorInfo);
572#else
573 if (x11Context.pXRRFreeMonitors)
574 x11Context.pXRRFreeMonitors(pMonitorInfo);
575#endif
576}
577
578static void monitorRandREvents()
579{
580 XEvent event;
581 XNextEvent(x11Context.pDisplayRandRMonitoring, &event);
582 int eventTypeOffset = event.type - x11Context.hRandREventBase;
583 switch (eventTypeOffset)
584 {
585 case RRScreenChangeNotify:
586 VBClLogInfo("RRScreenChangeNotify event received\n");
587 queryMonitorPositions();
588 break;
589 default:
590 break;
591 }
592}
593
594/**
595 * @callback_method_impl{FNRTTHREAD}
596 */
597static DECLCALLBACK(int) x11MonitorThreadFunction(RTTHREAD ThreadSelf, void *pvUser)
598{
599 RT_NOREF(ThreadSelf, pvUser);
600 while (!ASMAtomicReadBool(&g_fMonitorThreadShutdown))
601 {
602 monitorRandREvents();
603 }
604 return 0;
605}
606
607static int startX11MonitorThread()
608{
609 int rc;
610 Assert(g_fMonitorThreadShutdown == false);
611 if (mX11MonitorThread == NIL_RTTHREAD)
612 {
613 rc = RTThreadCreate(&mX11MonitorThread, x11MonitorThreadFunction, NULL /*pvUser*/, 0 /*cbStack*/,
614 RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "X11 events");
615 if (RT_FAILURE(rc))
616 VBClLogFatalError("Warning: failed to start X11 monitor thread (VBoxClient) rc=%Rrc!\n", rc);
617 }
618 else
619 rc = VINF_ALREADY_INITIALIZED;
620 return rc;
621}
622
623static int stopX11MonitorThread(void)
624{
625 int rc;
626 if (mX11MonitorThread != NIL_RTTHREAD)
627 {
628 ASMAtomicWriteBool(&g_fMonitorThreadShutdown, true);
629 /** @todo Send event to thread to get it out of XNextEvent. */
630 //????????
631 //mX11Monitor.interruptEventWait();
632 rc = RTThreadWait(mX11MonitorThread, RT_MS_1SEC, NULL /*prc*/);
633 if (RT_SUCCESS(rc))
634 {
635 mX11MonitorThread = NIL_RTTHREAD;
636 g_fMonitorThreadShutdown = false;
637 }
638 else
639 VBClLogError("Failed to stop X11 monitor thread, rc=%Rrc!\n", rc);
640 }
641 return rc;
642}
643
644static bool callVMWCTRL(struct RANDROUTPUT *paOutputs)
645{
646 int hHeight = 600;
647 int hWidth = 800;
648
649 xXineramaScreenInfo *extents = (xXineramaScreenInfo *)malloc(x11Context.hOutputCount * sizeof(xXineramaScreenInfo));
650 if (!extents)
651 return false;
652 int hRunningOffset = 0;
653 for (int i = 0; i < x11Context.hOutputCount; ++i)
654 {
655 if (paOutputs[i].fEnabled)
656 {
657 hHeight = paOutputs[i].height;
658 hWidth = paOutputs[i].width;
659 }
660 else
661 {
662 hHeight = 0;
663 hWidth = 0;
664 }
665 extents[i].x_org = hRunningOffset;
666 extents[i].y_org = 0;
667 extents[i].width = hWidth;
668 extents[i].height = hHeight;
669 hRunningOffset += hWidth;
670 }
671 bool fResult = VMwareCtrlSetTopology(x11Context.pDisplay, x11Context.hVMWCtrlMajorOpCode,
672 DefaultScreen(x11Context.pDisplay),
673 extents, x11Context.hOutputCount);
674 free(extents);
675 return fResult;
676}
677
678/**
679 * Tries to determine if the session parenting this process is of Xwayland.
680 * NB: XDG_SESSION_TYPE is a systemd(1) environment variable and is unlikely
681 * set in non-systemd environments or remote logins.
682 * Therefore we check the Wayland specific display environment variable first.
683 */
684static bool isXwayland(void)
685{
686 const char *const pDisplayType = getenv("WAYLAND_DISPLAY");
687 const char *pSessionType;
688
689 if (pDisplayType != NULL)
690 return true;
691
692 pSessionType = getenv("XDG_SESSION_TYPE"); /** @todo r=andy Use RTEnv API. */
693 if ((pSessionType != NULL) && (RTStrIStartsWith(pSessionType, "wayland")))
694 return true;
695
696 return false;
697}
698
699/**
700 * @interface_method_impl{VBCLSERVICE,pfnInit}
701 */
702static DECLCALLBACK(int) vbclSVGAInit(void)
703{
704 int rc;
705
706 /* In 32-bit guests GAs build on our release machines causes an xserver hang.
707 * So for 32-bit GAs we use our DRM client. */
708#if ARCH_BITS == 32
709 rc = VbglR3DrmClientStart();
710 if (RT_FAILURE(rc))
711 VBClLogError("Starting DRM resizing client (32-bit) failed with %Rrc\n", rc);
712 return VERR_NOT_AVAILABLE; /** @todo r=andy Why ignoring rc here? */
713#endif
714
715 /* If DRM client is already running don't start this service. */
716 if (VbglR3DrmClientIsRunning())
717 {
718 VBClLogInfo("DRM resizing is already running. Exiting this service\n");
719 return VERR_NOT_AVAILABLE;
720 }
721
722 if (isXwayland())
723 {
724 rc = VbglR3DrmClientStart();
725 if (RT_FAILURE(rc))
726 VBClLogError("Starting DRM resizing client failed with %Rrc\n", rc);
727 return rc;
728 }
729
730 x11Connect();
731
732 if (x11Context.pDisplay == NULL)
733 return VERR_NOT_AVAILABLE;
734
735 /* don't start the monitoring thread if related randr functionality is not available. */
736 if (x11Context.fMonitorInfoAvailable)
737 {
738 if (RT_FAILURE(startX11MonitorThread()))
739 return VERR_NOT_AVAILABLE;
740 }
741
742 return VINF_SUCCESS;
743}
744
745/**
746 * @interface_method_impl{VBCLSERVICE,pfnStop}
747 */
748static DECLCALLBACK(void) vbclSVGAStop(void)
749{
750 if (mpMonitorPositions)
751 {
752 free(mpMonitorPositions);
753 mpMonitorPositions = NULL;
754 }
755
756 stopX11MonitorThread(); /** @todo r=andy We ignore rc!? */
757
758 if (x11Context.pRandLibraryHandle)
759 {
760 dlclose(x11Context.pRandLibraryHandle);
761 x11Context.pRandLibraryHandle = NULL;
762 }
763#ifdef WITH_DISTRO_XRAND_XINERAMA
764 XRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, 0);
765 XRRFreeScreenResources(x11Context.pScreenResources);
766#else
767 if (x11Context.pXRRSelectInput)
768 x11Context.pXRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, 0);
769 if (x11Context.pXRRFreeScreenResources)
770 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
771#endif
772 XCloseDisplay(x11Context.pDisplay);
773 XCloseDisplay(x11Context.pDisplayRandRMonitoring);
774}
775
776#ifndef WITH_DISTRO_XRAND_XINERAMA
777static int openLibRandR()
778{
779 x11Context.pRandLibraryHandle = dlopen("libXrandr.so", RTLD_LAZY /*| RTLD_LOCAL */);
780 if (!x11Context.pRandLibraryHandle)
781 x11Context.pRandLibraryHandle = dlopen("libXrandr.so.2", RTLD_LAZY /*| RTLD_LOCAL */);
782 if (!x11Context.pRandLibraryHandle)
783 x11Context.pRandLibraryHandle = dlopen("libXrandr.so.2.2.0", RTLD_LAZY /*| RTLD_LOCAL */);
784
785 if (!x11Context.pRandLibraryHandle)
786 {
787 VBClLogFatalError("Could not locate libXrandr for dlopen\n");
788 return VERR_NOT_FOUND;
789 }
790
791 *(void **)(&x11Context.pXRRSelectInput) = dlsym(x11Context.pRandLibraryHandle, "XRRSelectInput");
792 checkFunctionPtrReturn(x11Context.pXRRSelectInput);
793
794 *(void **)(&x11Context.pXRRQueryExtension) = dlsym(x11Context.pRandLibraryHandle, "XRRQueryExtension");
795 checkFunctionPtrReturn(x11Context.pXRRQueryExtension);
796
797 *(void **)(&x11Context.pXRRQueryVersion) = dlsym(x11Context.pRandLibraryHandle, "XRRQueryVersion");
798 checkFunctionPtrReturn(x11Context.pXRRQueryVersion);
799
800 /* Don't bail out when XRRGetMonitors XRRFreeMonitors are missing as in Oracle Solaris 10. It is not crucial esp. for single monitor. */
801 *(void **)(&x11Context.pXRRGetMonitors) = dlsym(x11Context.pRandLibraryHandle, "XRRGetMonitors");
802 checkFunctionPtr(x11Context.pXRRGetMonitors);
803
804 *(void **)(&x11Context.pXRRFreeMonitors) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeMonitors");
805 checkFunctionPtr(x11Context.pXRRFreeMonitors);
806
807 x11Context.fMonitorInfoAvailable = x11Context.pXRRGetMonitors && x11Context.pXRRFreeMonitors;
808
809 *(void **)(&x11Context.pXRRGetScreenResources) = dlsym(x11Context.pRandLibraryHandle, "XRRGetScreenResources");
810 checkFunctionPtrReturn(x11Context.pXRRGetScreenResources);
811
812 *(void **)(&x11Context.pXRRSetCrtcConfig) = dlsym(x11Context.pRandLibraryHandle, "XRRSetCrtcConfig");
813 checkFunctionPtrReturn(x11Context.pXRRSetCrtcConfig);
814
815 *(void **)(&x11Context.pXRRFreeScreenResources) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeScreenResources");
816 checkFunctionPtrReturn(x11Context.pXRRFreeScreenResources);
817
818 *(void **)(&x11Context.pXRRFreeModeInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeModeInfo");
819 checkFunctionPtrReturn(x11Context.pXRRFreeModeInfo);
820
821 *(void **)(&x11Context.pXRRFreeOutputInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeOutputInfo");
822 checkFunctionPtrReturn(x11Context.pXRRFreeOutputInfo);
823
824 *(void **)(&x11Context.pXRRSetScreenSize) = dlsym(x11Context.pRandLibraryHandle, "XRRSetScreenSize");
825 checkFunctionPtrReturn(x11Context.pXRRSetScreenSize);
826
827 *(void **)(&x11Context.pXRRUpdateConfiguration) = dlsym(x11Context.pRandLibraryHandle, "XRRUpdateConfiguration");
828 checkFunctionPtrReturn(x11Context.pXRRUpdateConfiguration);
829
830 *(void **)(&x11Context.pXRRAllocModeInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRAllocModeInfo");
831 checkFunctionPtrReturn(x11Context.pXRRAllocModeInfo);
832
833 *(void **)(&x11Context.pXRRCreateMode) = dlsym(x11Context.pRandLibraryHandle, "XRRCreateMode");
834 checkFunctionPtrReturn(x11Context.pXRRCreateMode);
835
836 *(void **)(&x11Context.pXRRGetOutputInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRGetOutputInfo");
837 checkFunctionPtrReturn(x11Context.pXRRGetOutputInfo);
838
839 *(void **)(&x11Context.pXRRGetCrtcInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRGetCrtcInfo");
840 checkFunctionPtrReturn(x11Context.pXRRGetCrtcInfo);
841
842 *(void **)(&x11Context.pXRRFreeCrtcInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeCrtcInfo");
843 checkFunctionPtrReturn(x11Context.pXRRFreeCrtcInfo);
844
845 *(void **)(&x11Context.pXRRAddOutputMode) = dlsym(x11Context.pRandLibraryHandle, "XRRAddOutputMode");
846 checkFunctionPtrReturn(x11Context.pXRRAddOutputMode);
847
848 return VINF_SUCCESS;
849}
850#endif
851
852static void x11Connect()
853{
854 x11Context.pScreenResources = NULL;
855 x11Context.pXRRSelectInput = NULL;
856 x11Context.pRandLibraryHandle = NULL;
857 x11Context.pXRRQueryExtension = NULL;
858 x11Context.pXRRQueryVersion = NULL;
859 x11Context.pXRRGetMonitors = NULL;
860 x11Context.pXRRGetScreenResources = NULL;
861 x11Context.pXRRSetCrtcConfig = NULL;
862 x11Context.pXRRFreeMonitors = NULL;
863 x11Context.pXRRFreeScreenResources = NULL;
864 x11Context.pXRRFreeOutputInfo = NULL;
865 x11Context.pXRRFreeModeInfo = NULL;
866 x11Context.pXRRSetScreenSize = NULL;
867 x11Context.pXRRUpdateConfiguration = NULL;
868 x11Context.pXRRAllocModeInfo = NULL;
869 x11Context.pXRRCreateMode = NULL;
870 x11Context.pXRRGetOutputInfo = NULL;
871 x11Context.pXRRGetCrtcInfo = NULL;
872 x11Context.pXRRFreeCrtcInfo = NULL;
873 x11Context.pXRRAddOutputMode = NULL;
874 x11Context.fWmwareCtrlExtention = false;
875 x11Context.fMonitorInfoAvailable = false;
876 x11Context.hRandRMajor = 0;
877 x11Context.hRandRMinor = 0;
878
879 int dummy;
880 if (x11Context.pDisplay != NULL)
881 VBClLogFatalError("%s called with bad argument\n", __func__);
882 x11Context.pDisplay = XOpenDisplay(NULL);
883 x11Context.pDisplayRandRMonitoring = XOpenDisplay(NULL);
884 if (x11Context.pDisplay == NULL)
885 return;
886#ifndef WITH_DISTRO_XRAND_XINERAMA
887 if (openLibRandR() != VINF_SUCCESS)
888 {
889 XCloseDisplay(x11Context.pDisplay);
890 XCloseDisplay(x11Context.pDisplayRandRMonitoring);
891 x11Context.pDisplay = NULL;
892 x11Context.pDisplayRandRMonitoring = NULL;
893 return;
894 }
895#endif
896
897 x11Context.fWmwareCtrlExtention = XQueryExtension(x11Context.pDisplay, "VMWARE_CTRL",
898 &x11Context.hVMWCtrlMajorOpCode, &dummy, &dummy);
899 if (!x11Context.fWmwareCtrlExtention)
900 VBClLogError("VMWARE's ctrl extension is not available! Multi monitor management is not possible\n");
901 else
902 VBClLogInfo("VMWARE's ctrl extension is available. Major Opcode is %d.\n", x11Context.hVMWCtrlMajorOpCode);
903
904 /* Check Xrandr stuff. */
905 bool fSuccess = false;
906#ifdef WITH_DISTRO_XRAND_XINERAMA
907 fSuccess = XRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase);
908#else
909 if (x11Context.pXRRQueryExtension)
910 fSuccess = x11Context.pXRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase);
911#endif
912 if (fSuccess)
913 {
914 fSuccess = false;
915#ifdef WITH_DISTRO_XRAND_XINERAMA
916 fSuccess = XRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor);
917#else
918 if (x11Context.pXRRQueryVersion)
919 fSuccess = x11Context.pXRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor);
920#endif
921 if (!fSuccess)
922 {
923 XCloseDisplay(x11Context.pDisplay);
924 x11Context.pDisplay = NULL;
925 return;
926 }
927 if (x11Context.hRandRMajor < 1 || x11Context.hRandRMinor <= 3)
928 {
929 VBClLogFatalError("Resizing service requires libXrandr Version >= 1.4. Detected version is %d.%d\n", x11Context.hRandRMajor, x11Context.hRandRMinor);
930 XCloseDisplay(x11Context.pDisplay);
931 x11Context.pDisplay = NULL;
932 return;
933 }
934 }
935 x11Context.rootWindow = DefaultRootWindow(x11Context.pDisplay);
936 x11Context.hEventMask = RRScreenChangeNotifyMask;
937
938 /* Select the XEvent types we want to listen to. */
939#ifdef WITH_DISTRO_XRAND_XINERAMA
940 XRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, x11Context.hEventMask);
941#else
942 if (x11Context.pXRRSelectInput)
943 x11Context.pXRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, x11Context.hEventMask);
944#endif
945 x11Context.iDefaultScreen = DefaultScreen(x11Context.pDisplay);
946
947#ifdef WITH_DISTRO_XRAND_XINERAMA
948 x11Context.pScreenResources = XRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
949#else
950 if (x11Context.pXRRGetScreenResources)
951 x11Context.pScreenResources = x11Context.pXRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
952#endif
953 /* Currently without the VMWARE_CTRL extension we cannot connect outputs and set outputs' preferred mode.
954 * So we set the output count to 1 to get the 1st output position correct. */
955 x11Context.hOutputCount = x11Context.fWmwareCtrlExtention ? determineOutputCount() : 1;
956#ifdef WITH_DISTRO_XRAND_XINERAMA
957 XRRFreeScreenResources(x11Context.pScreenResources);
958#else
959 if (x11Context.pXRRFreeScreenResources)
960 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
961#endif
962}
963
964static int determineOutputCount()
965{
966 if (!x11Context.pScreenResources)
967 return 0;
968 return x11Context.pScreenResources->noutput;
969}
970
971static int findExistingModeIndex(unsigned iXRes, unsigned iYRes)
972{
973 if (!x11Context.pScreenResources)
974 return -1;
975 for (int i = 0; i < x11Context.pScreenResources->nmode; ++i)
976 {
977 if (x11Context.pScreenResources->modes[i].width == iXRes && x11Context.pScreenResources->modes[i].height == iYRes)
978 return i;
979 }
980 return -1;
981}
982
983static bool disableCRTC(RRCrtc crtcID)
984{
985 XRRCrtcInfo *pCrctInfo = NULL;
986
987#ifdef WITH_DISTRO_XRAND_XINERAMA
988 pCrctInfo = XRRGetCrtcInfo(x11Context.pDisplay, x11Context.pScreenResources, crtcID);
989#else
990 if (x11Context.pXRRGetCrtcInfo)
991 pCrctInfo = x11Context.pXRRGetCrtcInfo(x11Context.pDisplay, x11Context.pScreenResources, crtcID);
992#endif
993
994 if (!pCrctInfo)
995 return false;
996
997 Status ret = Success;
998#ifdef WITH_DISTRO_XRAND_XINERAMA
999 ret = XRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcID,
1000 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
1001#else
1002 if (x11Context.pXRRSetCrtcConfig)
1003 ret = x11Context.pXRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcID,
1004 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
1005#endif
1006
1007#ifdef WITH_DISTRO_XRAND_XINERAMA
1008 XRRFreeCrtcInfo(pCrctInfo);
1009#else
1010 if (x11Context.pXRRFreeCrtcInfo)
1011 x11Context.pXRRFreeCrtcInfo(pCrctInfo);
1012#endif
1013
1014 /** @todo In case of unsuccesful crtc config set we have to revert frame buffer size and crtc sizes. */
1015 if (ret == Success)
1016 return true;
1017 else
1018 return false;
1019}
1020
1021static XRRScreenSize currentSize()
1022{
1023 XRRScreenSize cSize;
1024 cSize.width = DisplayWidth(x11Context.pDisplay, x11Context.iDefaultScreen);
1025 cSize.mwidth = DisplayWidthMM(x11Context.pDisplay, x11Context.iDefaultScreen);
1026 cSize.height = DisplayHeight(x11Context.pDisplay, x11Context.iDefaultScreen);
1027 cSize.mheight = DisplayHeightMM(x11Context.pDisplay, x11Context.iDefaultScreen);
1028 return cSize;
1029}
1030
1031static unsigned int computeDpi(unsigned int pixels, unsigned int mm)
1032{
1033 unsigned int dpi = 0;
1034 if (mm > 0) {
1035 dpi = (unsigned int)((double)pixels * MILLIS_PER_INCH /
1036 (double)mm + 0.5);
1037 }
1038 return (dpi > 0) ? dpi : DEFAULT_DPI;
1039}
1040
1041static bool resizeFrameBuffer(struct RANDROUTPUT *paOutputs)
1042{
1043 unsigned int iXRes = 0;
1044 unsigned int iYRes = 0;
1045 /* Don't care about the output positions for now. */
1046 for (int i = 0; i < x11Context.hOutputCount; ++i)
1047 {
1048 if (!paOutputs[i].fEnabled)
1049 continue;
1050 iXRes += paOutputs[i].width;
1051 iYRes = iYRes < paOutputs[i].height ? paOutputs[i].height : iYRes;
1052 }
1053 XRRScreenSize cSize= currentSize();
1054 unsigned int xdpi = computeDpi(cSize.width, cSize.mwidth);
1055 unsigned int ydpi = computeDpi(cSize.height, cSize.mheight);
1056 unsigned int xmm;
1057 unsigned int ymm;
1058 xmm = (int)(MILLIS_PER_INCH * iXRes / ((double)xdpi) + 0.5);
1059 ymm = (int)(MILLIS_PER_INCH * iYRes / ((double)ydpi) + 0.5);
1060#ifdef WITH_DISTRO_XRAND_XINERAMA
1061 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, RRScreenChangeNotifyMask);
1062 XRRSetScreenSize(x11Context.pDisplay, x11Context.rootWindow, iXRes, iYRes, xmm, ymm);
1063#else
1064 if (x11Context.pXRRSelectInput)
1065 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, RRScreenChangeNotifyMask);
1066 if (x11Context.pXRRSetScreenSize)
1067 x11Context.pXRRSetScreenSize(x11Context.pDisplay, x11Context.rootWindow, iXRes, iYRes, xmm, ymm);
1068#endif
1069 XSync(x11Context.pDisplay, False);
1070 XEvent configEvent;
1071 bool event = false;
1072 while (XCheckTypedEvent(x11Context.pDisplay, RRScreenChangeNotify + x11Context.hRandREventBase, &configEvent))
1073 {
1074#ifdef WITH_DISTRO_XRAND_XINERAMA
1075 XRRUpdateConfiguration(&configEvent);
1076#else
1077 if (x11Context.pXRRUpdateConfiguration)
1078 x11Context.pXRRUpdateConfiguration(&configEvent);
1079#endif
1080 event = true;
1081 }
1082#ifdef WITH_DISTRO_XRAND_XINERAMA
1083 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
1084#else
1085 if (x11Context.pXRRSelectInput)
1086 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
1087#endif
1088 XRRScreenSize newSize = currentSize();
1089
1090 if (!event || newSize.width != (int)iXRes || newSize.height != (int)iYRes)
1091 {
1092 VBClLogError("Resizing frame buffer to %d %d has failed, current mode %d %d\n",
1093 iXRes, iYRes, newSize.width, newSize.height);
1094 return false;
1095 }
1096 return true;
1097}
1098
1099static XRRModeInfo *createMode(int iXRes, int iYRes)
1100{
1101 XRRModeInfo *pModeInfo = NULL;
1102 char sModeName[126];
1103 sprintf(sModeName, "%dx%d_vbox", iXRes, iYRes);
1104#ifdef WITH_DISTRO_XRAND_XINERAMA
1105 pModeInfo = XRRAllocModeInfo(sModeName, strlen(sModeName));
1106#else
1107 if (x11Context.pXRRAllocModeInfo)
1108 pModeInfo = x11Context.pXRRAllocModeInfo(sModeName, strlen(sModeName));
1109#endif
1110 pModeInfo->width = iXRes;
1111 pModeInfo->height = iYRes;
1112
1113 DisplayModeR mode = f86CVTMode(iXRes, iYRes, 60 /*VRefresh */, true /*Reduced */, false /* Interlaced */);
1114
1115 pModeInfo->dotClock = mode.Clock;
1116 pModeInfo->hSyncStart = mode.HSyncStart;
1117 pModeInfo->hSyncEnd = mode.HSyncEnd;
1118 pModeInfo->hTotal = mode.HTotal;
1119 pModeInfo->hSkew = mode.HSkew;
1120 pModeInfo->vSyncStart = mode.VSyncStart;
1121 pModeInfo->vSyncEnd = mode.VSyncEnd;
1122 pModeInfo->vTotal = mode.VTotal;
1123
1124 RRMode newMode = None;
1125#ifdef WITH_DISTRO_XRAND_XINERAMA
1126 newMode = XRRCreateMode(x11Context.pDisplay, x11Context.rootWindow, pModeInfo);
1127#else
1128 if (x11Context.pXRRCreateMode)
1129 newMode = x11Context.pXRRCreateMode(x11Context.pDisplay, x11Context.rootWindow, pModeInfo);
1130#endif
1131 if (newMode == None)
1132 {
1133#ifdef WITH_DISTRO_XRAND_XINERAMA
1134 XRRFreeModeInfo(pModeInfo);
1135#else
1136 if (x11Context.pXRRFreeModeInfo)
1137 x11Context.pXRRFreeModeInfo(pModeInfo);
1138#endif
1139 return NULL;
1140 }
1141 pModeInfo->id = newMode;
1142 return pModeInfo;
1143}
1144
1145static bool configureOutput(int iOutputIndex, struct RANDROUTPUT *paOutputs)
1146{
1147 if (iOutputIndex >= x11Context.hOutputCount)
1148 {
1149 VBClLogError("Output index %d is greater than # of oputputs %d\n", iOutputIndex, x11Context.hOutputCount);
1150 return false;
1151 }
1152 RROutput outputId = x11Context.pScreenResources->outputs[iOutputIndex];
1153 XRROutputInfo *pOutputInfo = NULL;
1154#ifdef WITH_DISTRO_XRAND_XINERAMA
1155 pOutputInfo = XRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, outputId);
1156#else
1157 if (x11Context.pXRRGetOutputInfo)
1158 pOutputInfo = x11Context.pXRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, outputId);
1159#endif
1160 if (!pOutputInfo)
1161 return false;
1162 XRRModeInfo *pModeInfo = NULL;
1163 bool fNewMode = false;
1164 /* Index of the mode within the XRRScreenResources.modes array. -1 if such a mode with required resolution does not exists*/
1165 int iModeIndex = findExistingModeIndex(paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1166 if (iModeIndex != -1 && iModeIndex < x11Context.pScreenResources->nmode)
1167 pModeInfo = &(x11Context.pScreenResources->modes[iModeIndex]);
1168 else
1169 {
1170 /* A mode with required size was not found. Create a new one. */
1171 pModeInfo = createMode(paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1172 fNewMode = true;
1173 }
1174 if (!pModeInfo)
1175 {
1176 VBClLogError("Could not create mode for the resolution (%d, %d)\n",
1177 paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1178 return false;
1179 }
1180
1181#ifdef WITH_DISTRO_XRAND_XINERAMA
1182 XRRAddOutputMode(x11Context.pDisplay, outputId, pModeInfo->id);
1183#else
1184 if (x11Context.pXRRAddOutputMode)
1185 x11Context.pXRRAddOutputMode(x11Context.pDisplay, outputId, pModeInfo->id);
1186#endif
1187 /* Make sure outputs crtc is set. */
1188 pOutputInfo->crtc = pOutputInfo->crtcs[0];
1189
1190 RRCrtc crtcId = pOutputInfo->crtcs[0];
1191 Status ret = Success;
1192#ifdef WITH_DISTRO_XRAND_XINERAMA
1193 ret = XRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcId, CurrentTime,
1194 paOutputs[iOutputIndex].x, paOutputs[iOutputIndex].y,
1195 pModeInfo->id, RR_Rotate_0, &(outputId), 1 /*int noutputs*/);
1196#else
1197 if (x11Context.pXRRSetCrtcConfig)
1198 ret = x11Context.pXRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcId, CurrentTime,
1199 paOutputs[iOutputIndex].x, paOutputs[iOutputIndex].y,
1200 pModeInfo->id, RR_Rotate_0, &(outputId), 1 /*int noutputs*/);
1201#endif
1202 if (ret != Success)
1203 VBClLogError("crtc set config failed for output %d\n", iOutputIndex);
1204
1205#ifdef WITH_DISTRO_XRAND_XINERAMA
1206 XRRFreeOutputInfo(pOutputInfo);
1207#else
1208 if (x11Context.pXRRFreeOutputInfo)
1209 x11Context.pXRRFreeOutputInfo(pOutputInfo);
1210#endif
1211
1212 if (fNewMode)
1213 {
1214#ifdef WITH_DISTRO_XRAND_XINERAMA
1215 XRRFreeModeInfo(pModeInfo);
1216#else
1217 if (x11Context.pXRRFreeModeInfo)
1218 x11Context.pXRRFreeModeInfo(pModeInfo);
1219#endif
1220 }
1221 return true;
1222}
1223
1224/** Construct the xrandr command which sets the whole monitor topology each time. */
1225static void setXrandrTopology(struct RANDROUTPUT *paOutputs)
1226{
1227 XGrabServer(x11Context.pDisplay);
1228 if (x11Context.fWmwareCtrlExtention)
1229 callVMWCTRL(paOutputs);
1230
1231#ifdef WITH_DISTRO_XRAND_XINERAMA
1232 x11Context.pScreenResources = XRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
1233#else
1234 if (x11Context.pXRRGetScreenResources)
1235 x11Context.pScreenResources = x11Context.pXRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
1236#endif
1237 x11Context.hOutputCount = x11Context.fWmwareCtrlExtention ? determineOutputCount() : 1;
1238
1239 if (!x11Context.pScreenResources)
1240 {
1241 XUngrabServer(x11Context.pDisplay);
1242 XFlush(x11Context.pDisplay);
1243 return;
1244 }
1245
1246 /* Disable crtcs. */
1247 for (int i = 0; i < x11Context.pScreenResources->noutput; ++i)
1248 {
1249 XRROutputInfo *pOutputInfo = NULL;
1250#ifdef WITH_DISTRO_XRAND_XINERAMA
1251 pOutputInfo = XRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, x11Context.pScreenResources->outputs[i]);
1252#else
1253 if (x11Context.pXRRGetOutputInfo)
1254 pOutputInfo = x11Context.pXRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, x11Context.pScreenResources->outputs[i]);
1255#endif
1256 if (!pOutputInfo)
1257 continue;
1258 if (pOutputInfo->crtc == None)
1259 continue;
1260
1261 if (!disableCRTC(pOutputInfo->crtc))
1262 {
1263 VBClLogFatalError("Crtc disable failed %lu\n", pOutputInfo->crtc);
1264 XUngrabServer(x11Context.pDisplay);
1265 XSync(x11Context.pDisplay, False);
1266#ifdef WITH_DISTRO_XRAND_XINERAMA
1267 XRRFreeScreenResources(x11Context.pScreenResources);
1268#else
1269 if (x11Context.pXRRFreeScreenResources)
1270 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1271#endif
1272 XFlush(x11Context.pDisplay);
1273 return;
1274 }
1275#ifdef WITH_DISTRO_XRAND_XINERAMA
1276 XRRFreeOutputInfo(pOutputInfo);
1277#else
1278 if (x11Context.pXRRFreeOutputInfo)
1279 x11Context.pXRRFreeOutputInfo(pOutputInfo);
1280#endif
1281 }
1282 /* Resize the frame buffer. */
1283 if (!resizeFrameBuffer(paOutputs))
1284 {
1285 XUngrabServer(x11Context.pDisplay);
1286 XSync(x11Context.pDisplay, False);
1287#ifdef WITH_DISTRO_XRAND_XINERAMA
1288 XRRFreeScreenResources(x11Context.pScreenResources);
1289#else
1290 if (x11Context.pXRRFreeScreenResources)
1291 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1292#endif
1293 XFlush(x11Context.pDisplay);
1294 return;
1295 }
1296
1297 /* Configure the outputs. */
1298 for (int i = 0; i < x11Context.hOutputCount; ++i)
1299 {
1300 /* be paranoid. */
1301 if (i >= x11Context.pScreenResources->noutput)
1302 break;
1303 if (!paOutputs[i].fEnabled)
1304 continue;
1305 configureOutput(i, paOutputs);
1306 }
1307 XSync(x11Context.pDisplay, False);
1308#ifdef WITH_DISTRO_XRAND_XINERAMA
1309 XRRFreeScreenResources(x11Context.pScreenResources);
1310#else
1311 if (x11Context.pXRRFreeScreenResources)
1312 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1313#endif
1314 XUngrabServer(x11Context.pDisplay);
1315 XFlush(x11Context.pDisplay);
1316}
1317
1318/**
1319 * @interface_method_impl{VBCLSERVICE,pfnWorker}
1320 */
1321static DECLCALLBACK(int) vbclSVGAWorker(bool volatile *pfShutdown)
1322{
1323 RT_NOREF(pfShutdown);
1324
1325 /* Do not acknowledge the first event we query for to pick up old events,
1326 * e.g. from before a guest reboot. */
1327 bool fAck = false;
1328 bool fFirstRun = true;
1329 static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS];
1330
1331 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
1332 if (RT_FAILURE(rc))
1333 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
1334 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
1335 if (RT_FAILURE(rc))
1336 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
1337 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
1338 return VERR_RESOURCE_BUSY;
1339
1340 /* Let the main thread know that it can continue spawning services. */
1341 RTThreadUserSignal(RTThreadSelf());
1342
1343 for (;;)
1344 {
1345 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
1346 uint32_t cDisplaysOut;
1347 /* Query the first size without waiting. This lets us e.g. pick up
1348 * the last event before a guest reboot when we start again after. */
1349 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
1350 fAck = true;
1351 if (RT_FAILURE(rc))
1352 VBClLogFatalError("Failed to get display change request, rc=%Rrc\n", rc);
1353 if (cDisplaysOut > VMW_MAX_HEADS)
1354 VBClLogFatalError("Display change request contained, rc=%Rrc\n", rc);
1355 if (cDisplaysOut > 0)
1356 {
1357 for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
1358 {
1359 uint32_t idDisplay = aDisplays[i].idDisplay;
1360 if (idDisplay >= VMW_MAX_HEADS)
1361 continue;
1362 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags;
1363 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
1364 {
1365 if (idDisplay == 0 || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
1366 {
1367 aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin;
1368 aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin;
1369 } else {
1370 aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx;
1371 aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin;
1372 }
1373 aMonitors[idDisplay].cx = aDisplays[i].cx;
1374 aMonitors[idDisplay].cy = aDisplays[i].cy;
1375 }
1376 }
1377 /* Create a whole topology and send it to xrandr. */
1378 struct RANDROUTPUT aOutputs[VMW_MAX_HEADS];
1379 int iRunningX = 0;
1380 for (int j = 0; j < x11Context.hOutputCount; ++j)
1381 {
1382 aOutputs[j].x = iRunningX;
1383 aOutputs[j].y = aMonitors[j].yOrigin;
1384 aOutputs[j].width = aMonitors[j].cx;
1385 aOutputs[j].height = aMonitors[j].cy;
1386 aOutputs[j].fEnabled = !(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED);
1387 if (aOutputs[j].fEnabled)
1388 iRunningX += aOutputs[j].width;
1389 }
1390 /* In 32-bit guests GAs build on our release machines causes an xserver lock during vmware_ctrl extention
1391 if we do the call withing XGrab. We make the call the said extension only once (to connect the outputs)
1392 rather than at each resize iteration. */
1393#if ARCH_BITS == 32
1394 if (fFirstRun)
1395 callVMWCTRL(aOutputs);
1396#endif
1397 setXrandrTopology(aOutputs);
1398 /* Wait for some seconds and set toplogy again after the boot. In some desktop environments (cinnamon) where
1399 DE get into our resizing our first resize is reverted by the DE. Sleeping for some secs. helps. Setting
1400 topology a 2nd time resolves the black screen I get after resizing.*/
1401 if (fFirstRun)
1402 {
1403 sleep(4);
1404 setXrandrTopology(aOutputs);
1405 fFirstRun = false;
1406 }
1407 }
1408 uint32_t events;
1409 do
1410 {
1411 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
1412 } while (rc == VERR_INTERRUPTED);
1413 if (RT_FAILURE(rc))
1414 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
1415 }
1416}
1417
1418VBCLSERVICE g_SvcDisplaySVGA =
1419{
1420 "dp-svga-x11", /* szName */
1421 "SVGA X11 display", /* pszDescription */
1422 ".vboxclient-display-svga-x11.pid", /* pszPidFilePath */
1423 NULL, /* pszUsage */
1424 NULL, /* pszOptions */
1425 NULL, /* pfnOption */
1426 vbclSVGAInit, /* pfnInit */
1427 vbclSVGAWorker, /* pfnWorker */
1428 vbclSVGAStop, /* pfnStop*/
1429 NULL /* pfnTerm */
1430};
1431
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette