VirtualBox

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

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

Additions: X11: VMSVGA: prevent memory leak on CRTC disable, 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 89609 2021-06-10 16:15:40Z 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 * - 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.pXRRAddOutputMode = NULL;
873 x11Context.fWmwareCtrlExtention = false;
874 x11Context.fMonitorInfoAvailable = false;
875 x11Context.hRandRMajor = 0;
876 x11Context.hRandRMinor = 0;
877
878 int dummy;
879 if (x11Context.pDisplay != NULL)
880 VBClLogFatalError("%s called with bad argument\n", __func__);
881 x11Context.pDisplay = XOpenDisplay(NULL);
882 x11Context.pDisplayRandRMonitoring = XOpenDisplay(NULL);
883 if (x11Context.pDisplay == NULL)
884 return;
885#ifndef WITH_DISTRO_XRAND_XINERAMA
886 if (openLibRandR() != VINF_SUCCESS)
887 {
888 XCloseDisplay(x11Context.pDisplay);
889 XCloseDisplay(x11Context.pDisplayRandRMonitoring);
890 x11Context.pDisplay = NULL;
891 x11Context.pDisplayRandRMonitoring = NULL;
892 return;
893 }
894#endif
895
896 x11Context.fWmwareCtrlExtention = XQueryExtension(x11Context.pDisplay, "VMWARE_CTRL",
897 &x11Context.hVMWCtrlMajorOpCode, &dummy, &dummy);
898 if (!x11Context.fWmwareCtrlExtention)
899 VBClLogError("VMWARE's ctrl extension is not available! Multi monitor management is not possible\n");
900 else
901 VBClLogInfo("VMWARE's ctrl extension is available. Major Opcode is %d.\n", x11Context.hVMWCtrlMajorOpCode);
902
903 /* Check Xrandr stuff. */
904 bool fSuccess = false;
905#ifdef WITH_DISTRO_XRAND_XINERAMA
906 fSuccess = XRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase);
907#else
908 if (x11Context.pXRRQueryExtension)
909 fSuccess = x11Context.pXRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase);
910#endif
911 if (fSuccess)
912 {
913 fSuccess = false;
914#ifdef WITH_DISTRO_XRAND_XINERAMA
915 fSuccess = XRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor);
916#else
917 if (x11Context.pXRRQueryVersion)
918 fSuccess = x11Context.pXRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor);
919#endif
920 if (!fSuccess)
921 {
922 XCloseDisplay(x11Context.pDisplay);
923 x11Context.pDisplay = NULL;
924 return;
925 }
926 if (x11Context.hRandRMajor < 1 || x11Context.hRandRMinor <= 3)
927 {
928 VBClLogFatalError("Resizing service requires libXrandr Version >= 1.4. Detected version is %d.%d\n", x11Context.hRandRMajor, x11Context.hRandRMinor);
929 XCloseDisplay(x11Context.pDisplay);
930 x11Context.pDisplay = NULL;
931 return;
932 }
933 }
934 x11Context.rootWindow = DefaultRootWindow(x11Context.pDisplay);
935 x11Context.hEventMask = RRScreenChangeNotifyMask;
936
937 /* Select the XEvent types we want to listen to. */
938#ifdef WITH_DISTRO_XRAND_XINERAMA
939 XRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, x11Context.hEventMask);
940#else
941 if (x11Context.pXRRSelectInput)
942 x11Context.pXRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, x11Context.hEventMask);
943#endif
944 x11Context.iDefaultScreen = DefaultScreen(x11Context.pDisplay);
945
946#ifdef WITH_DISTRO_XRAND_XINERAMA
947 x11Context.pScreenResources = XRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
948#else
949 if (x11Context.pXRRGetScreenResources)
950 x11Context.pScreenResources = x11Context.pXRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
951#endif
952 /* Currently without the VMWARE_CTRL extension we cannot connect outputs and set outputs' preferred mode.
953 * So we set the output count to 1 to get the 1st output position correct. */
954 x11Context.hOutputCount = x11Context.fWmwareCtrlExtention ? determineOutputCount() : 1;
955#ifdef WITH_DISTRO_XRAND_XINERAMA
956 XRRFreeScreenResources(x11Context.pScreenResources);
957#else
958 if (x11Context.pXRRFreeScreenResources)
959 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
960#endif
961}
962
963static int determineOutputCount()
964{
965 if (!x11Context.pScreenResources)
966 return 0;
967 return x11Context.pScreenResources->noutput;
968}
969
970static int findExistingModeIndex(unsigned iXRes, unsigned iYRes)
971{
972 if (!x11Context.pScreenResources)
973 return -1;
974 for (int i = 0; i < x11Context.pScreenResources->nmode; ++i)
975 {
976 if (x11Context.pScreenResources->modes[i].width == iXRes && x11Context.pScreenResources->modes[i].height == iYRes)
977 return i;
978 }
979 return -1;
980}
981
982static bool disableCRTC(RRCrtc crtcID)
983{
984 XRRCrtcInfo *pCrctInfo = NULL;
985
986#ifdef WITH_DISTRO_XRAND_XINERAMA
987 pCrctInfo = XRRGetCrtcInfo(x11Context.pDisplay, x11Context.pScreenResources, crtcID);
988#else
989 if (x11Context.pXRRGetCrtcInfo)
990 pCrctInfo = x11Context.pXRRGetCrtcInfo(x11Context.pDisplay, x11Context.pScreenResources, crtcID);
991#endif
992
993 if (!pCrctInfo)
994 return false;
995
996 Status ret = Success;
997#ifdef WITH_DISTRO_XRAND_XINERAMA
998 ret = XRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcID,
999 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
1000#else
1001 if (x11Context.pXRRSetCrtcConfig)
1002 ret = x11Context.pXRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcID,
1003 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
1004#endif
1005
1006#ifdef WITH_DISTRO_XRAND_XINERAMA
1007 XRRFreeCrtcInfo(pCrctInfo);
1008#else
1009 if (x11Context.pXRRFreeCrtcInfo)
1010 x11Context.pXRRFreeCrtcInfo(pCrctInfo);
1011#endif
1012
1013 /** @todo In case of unsuccesful crtc config set we have to revert frame buffer size and crtc sizes. */
1014 if (ret == Success)
1015 return true;
1016 else
1017 return false;
1018}
1019
1020static XRRScreenSize currentSize()
1021{
1022 XRRScreenSize cSize;
1023 cSize.width = DisplayWidth(x11Context.pDisplay, x11Context.iDefaultScreen);
1024 cSize.mwidth = DisplayWidthMM(x11Context.pDisplay, x11Context.iDefaultScreen);
1025 cSize.height = DisplayHeight(x11Context.pDisplay, x11Context.iDefaultScreen);
1026 cSize.mheight = DisplayHeightMM(x11Context.pDisplay, x11Context.iDefaultScreen);
1027 return cSize;
1028}
1029
1030static unsigned int computeDpi(unsigned int pixels, unsigned int mm)
1031{
1032 unsigned int dpi = 0;
1033 if (mm > 0) {
1034 dpi = (unsigned int)((double)pixels * MILLIS_PER_INCH /
1035 (double)mm + 0.5);
1036 }
1037 return (dpi > 0) ? dpi : DEFAULT_DPI;
1038}
1039
1040static bool resizeFrameBuffer(struct RANDROUTPUT *paOutputs)
1041{
1042 unsigned int iXRes = 0;
1043 unsigned int iYRes = 0;
1044 /* Don't care about the output positions for now. */
1045 for (int i = 0; i < x11Context.hOutputCount; ++i)
1046 {
1047 if (!paOutputs[i].fEnabled)
1048 continue;
1049 iXRes += paOutputs[i].width;
1050 iYRes = iYRes < paOutputs[i].height ? paOutputs[i].height : iYRes;
1051 }
1052 XRRScreenSize cSize= currentSize();
1053 unsigned int xdpi = computeDpi(cSize.width, cSize.mwidth);
1054 unsigned int ydpi = computeDpi(cSize.height, cSize.mheight);
1055 unsigned int xmm;
1056 unsigned int ymm;
1057 xmm = (int)(MILLIS_PER_INCH * iXRes / ((double)xdpi) + 0.5);
1058 ymm = (int)(MILLIS_PER_INCH * iYRes / ((double)ydpi) + 0.5);
1059#ifdef WITH_DISTRO_XRAND_XINERAMA
1060 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, RRScreenChangeNotifyMask);
1061 XRRSetScreenSize(x11Context.pDisplay, x11Context.rootWindow, iXRes, iYRes, xmm, ymm);
1062#else
1063 if (x11Context.pXRRSelectInput)
1064 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, RRScreenChangeNotifyMask);
1065 if (x11Context.pXRRSetScreenSize)
1066 x11Context.pXRRSetScreenSize(x11Context.pDisplay, x11Context.rootWindow, iXRes, iYRes, xmm, ymm);
1067#endif
1068 XSync(x11Context.pDisplay, False);
1069 XEvent configEvent;
1070 bool event = false;
1071 while (XCheckTypedEvent(x11Context.pDisplay, RRScreenChangeNotify + x11Context.hRandREventBase, &configEvent))
1072 {
1073#ifdef WITH_DISTRO_XRAND_XINERAMA
1074 XRRUpdateConfiguration(&configEvent);
1075#else
1076 if (x11Context.pXRRUpdateConfiguration)
1077 x11Context.pXRRUpdateConfiguration(&configEvent);
1078#endif
1079 event = true;
1080 }
1081#ifdef WITH_DISTRO_XRAND_XINERAMA
1082 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
1083#else
1084 if (x11Context.pXRRSelectInput)
1085 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
1086#endif
1087 XRRScreenSize newSize = currentSize();
1088
1089 if (!event || newSize.width != (int)iXRes || newSize.height != (int)iYRes)
1090 {
1091 VBClLogError("Resizing frame buffer to %d %d has failed, current mode %d %d\n",
1092 iXRes, iYRes, newSize.width, newSize.height);
1093 return false;
1094 }
1095 return true;
1096}
1097
1098static XRRModeInfo *createMode(int iXRes, int iYRes)
1099{
1100 XRRModeInfo *pModeInfo = NULL;
1101 char sModeName[126];
1102 sprintf(sModeName, "%dx%d_vbox", iXRes, iYRes);
1103#ifdef WITH_DISTRO_XRAND_XINERAMA
1104 pModeInfo = XRRAllocModeInfo(sModeName, strlen(sModeName));
1105#else
1106 if (x11Context.pXRRAllocModeInfo)
1107 pModeInfo = x11Context.pXRRAllocModeInfo(sModeName, strlen(sModeName));
1108#endif
1109 pModeInfo->width = iXRes;
1110 pModeInfo->height = iYRes;
1111
1112 DisplayModeR mode = f86CVTMode(iXRes, iYRes, 60 /*VRefresh */, true /*Reduced */, false /* Interlaced */);
1113
1114 pModeInfo->dotClock = mode.Clock;
1115 pModeInfo->hSyncStart = mode.HSyncStart;
1116 pModeInfo->hSyncEnd = mode.HSyncEnd;
1117 pModeInfo->hTotal = mode.HTotal;
1118 pModeInfo->hSkew = mode.HSkew;
1119 pModeInfo->vSyncStart = mode.VSyncStart;
1120 pModeInfo->vSyncEnd = mode.VSyncEnd;
1121 pModeInfo->vTotal = mode.VTotal;
1122
1123 RRMode newMode = None;
1124#ifdef WITH_DISTRO_XRAND_XINERAMA
1125 newMode = XRRCreateMode(x11Context.pDisplay, x11Context.rootWindow, pModeInfo);
1126#else
1127 if (x11Context.pXRRCreateMode)
1128 newMode = x11Context.pXRRCreateMode(x11Context.pDisplay, x11Context.rootWindow, pModeInfo);
1129#endif
1130 if (newMode == None)
1131 {
1132#ifdef WITH_DISTRO_XRAND_XINERAMA
1133 XRRFreeModeInfo(pModeInfo);
1134#else
1135 if (x11Context.pXRRFreeModeInfo)
1136 x11Context.pXRRFreeModeInfo(pModeInfo);
1137#endif
1138 return NULL;
1139 }
1140 pModeInfo->id = newMode;
1141 return pModeInfo;
1142}
1143
1144static bool configureOutput(int iOutputIndex, struct RANDROUTPUT *paOutputs)
1145{
1146 if (iOutputIndex >= x11Context.hOutputCount)
1147 {
1148 VBClLogError("Output index %d is greater than # of oputputs %d\n", iOutputIndex, x11Context.hOutputCount);
1149 return false;
1150 }
1151 RROutput outputId = x11Context.pScreenResources->outputs[iOutputIndex];
1152 XRROutputInfo *pOutputInfo = NULL;
1153#ifdef WITH_DISTRO_XRAND_XINERAMA
1154 pOutputInfo = XRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, outputId);
1155#else
1156 if (x11Context.pXRRGetOutputInfo)
1157 pOutputInfo = x11Context.pXRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, outputId);
1158#endif
1159 if (!pOutputInfo)
1160 return false;
1161 XRRModeInfo *pModeInfo = NULL;
1162 bool fNewMode = false;
1163 /* Index of the mode within the XRRScreenResources.modes array. -1 if such a mode with required resolution does not exists*/
1164 int iModeIndex = findExistingModeIndex(paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1165 if (iModeIndex != -1 && iModeIndex < x11Context.pScreenResources->nmode)
1166 pModeInfo = &(x11Context.pScreenResources->modes[iModeIndex]);
1167 else
1168 {
1169 /* A mode with required size was not found. Create a new one. */
1170 pModeInfo = createMode(paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1171 fNewMode = true;
1172 }
1173 if (!pModeInfo)
1174 {
1175 VBClLogError("Could not create mode for the resolution (%d, %d)\n",
1176 paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1177 return false;
1178 }
1179
1180#ifdef WITH_DISTRO_XRAND_XINERAMA
1181 XRRAddOutputMode(x11Context.pDisplay, outputId, pModeInfo->id);
1182#else
1183 if (x11Context.pXRRAddOutputMode)
1184 x11Context.pXRRAddOutputMode(x11Context.pDisplay, outputId, pModeInfo->id);
1185#endif
1186 /* Make sure outputs crtc is set. */
1187 pOutputInfo->crtc = pOutputInfo->crtcs[0];
1188
1189 RRCrtc crtcId = pOutputInfo->crtcs[0];
1190 Status ret = Success;
1191#ifdef WITH_DISTRO_XRAND_XINERAMA
1192 ret = XRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcId, CurrentTime,
1193 paOutputs[iOutputIndex].x, paOutputs[iOutputIndex].y,
1194 pModeInfo->id, RR_Rotate_0, &(outputId), 1 /*int noutputs*/);
1195#else
1196 if (x11Context.pXRRSetCrtcConfig)
1197 ret = x11Context.pXRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcId, CurrentTime,
1198 paOutputs[iOutputIndex].x, paOutputs[iOutputIndex].y,
1199 pModeInfo->id, RR_Rotate_0, &(outputId), 1 /*int noutputs*/);
1200#endif
1201 if (ret != Success)
1202 VBClLogError("crtc set config failed for output %d\n", iOutputIndex);
1203
1204#ifdef WITH_DISTRO_XRAND_XINERAMA
1205 XRRFreeOutputInfo(pOutputInfo);
1206#else
1207 if (x11Context.pXRRFreeOutputInfo)
1208 x11Context.pXRRFreeOutputInfo(pOutputInfo);
1209#endif
1210
1211 if (fNewMode)
1212 {
1213#ifdef WITH_DISTRO_XRAND_XINERAMA
1214 XRRFreeModeInfo(pModeInfo);
1215#else
1216 if (x11Context.pXRRFreeModeInfo)
1217 x11Context.pXRRFreeModeInfo(pModeInfo);
1218#endif
1219 }
1220 return true;
1221}
1222
1223/** Construct the xrandr command which sets the whole monitor topology each time. */
1224static void setXrandrTopology(struct RANDROUTPUT *paOutputs)
1225{
1226 XGrabServer(x11Context.pDisplay);
1227 if (x11Context.fWmwareCtrlExtention)
1228 callVMWCTRL(paOutputs);
1229
1230#ifdef WITH_DISTRO_XRAND_XINERAMA
1231 x11Context.pScreenResources = XRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
1232#else
1233 if (x11Context.pXRRGetScreenResources)
1234 x11Context.pScreenResources = x11Context.pXRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
1235#endif
1236 x11Context.hOutputCount = x11Context.fWmwareCtrlExtention ? determineOutputCount() : 1;
1237
1238 if (!x11Context.pScreenResources)
1239 {
1240 XUngrabServer(x11Context.pDisplay);
1241 XFlush(x11Context.pDisplay);
1242 return;
1243 }
1244
1245 /* Disable crtcs. */
1246 for (int i = 0; i < x11Context.pScreenResources->noutput; ++i)
1247 {
1248 XRROutputInfo *pOutputInfo = NULL;
1249#ifdef WITH_DISTRO_XRAND_XINERAMA
1250 pOutputInfo = XRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, x11Context.pScreenResources->outputs[i]);
1251#else
1252 if (x11Context.pXRRGetOutputInfo)
1253 pOutputInfo = x11Context.pXRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, x11Context.pScreenResources->outputs[i]);
1254#endif
1255 if (!pOutputInfo)
1256 continue;
1257 if (pOutputInfo->crtc == None)
1258 continue;
1259
1260 if (!disableCRTC(pOutputInfo->crtc))
1261 {
1262 VBClLogFatalError("Crtc disable failed %lu\n", pOutputInfo->crtc);
1263 XUngrabServer(x11Context.pDisplay);
1264 XSync(x11Context.pDisplay, False);
1265#ifdef WITH_DISTRO_XRAND_XINERAMA
1266 XRRFreeScreenResources(x11Context.pScreenResources);
1267#else
1268 if (x11Context.pXRRFreeScreenResources)
1269 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1270#endif
1271 XFlush(x11Context.pDisplay);
1272 return;
1273 }
1274#ifdef WITH_DISTRO_XRAND_XINERAMA
1275 XRRFreeOutputInfo(pOutputInfo);
1276#else
1277 if (x11Context.pXRRFreeOutputInfo)
1278 x11Context.pXRRFreeOutputInfo(pOutputInfo);
1279#endif
1280 }
1281 /* Resize the frame buffer. */
1282 if (!resizeFrameBuffer(paOutputs))
1283 {
1284 XUngrabServer(x11Context.pDisplay);
1285 XSync(x11Context.pDisplay, False);
1286#ifdef WITH_DISTRO_XRAND_XINERAMA
1287 XRRFreeScreenResources(x11Context.pScreenResources);
1288#else
1289 if (x11Context.pXRRFreeScreenResources)
1290 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1291#endif
1292 XFlush(x11Context.pDisplay);
1293 return;
1294 }
1295
1296 /* Configure the outputs. */
1297 for (int i = 0; i < x11Context.hOutputCount; ++i)
1298 {
1299 /* be paranoid. */
1300 if (i >= x11Context.pScreenResources->noutput)
1301 break;
1302 if (!paOutputs[i].fEnabled)
1303 continue;
1304 configureOutput(i, paOutputs);
1305 }
1306 XSync(x11Context.pDisplay, False);
1307#ifdef WITH_DISTRO_XRAND_XINERAMA
1308 XRRFreeScreenResources(x11Context.pScreenResources);
1309#else
1310 if (x11Context.pXRRFreeScreenResources)
1311 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1312#endif
1313 XUngrabServer(x11Context.pDisplay);
1314 XFlush(x11Context.pDisplay);
1315}
1316
1317/**
1318 * @interface_method_impl{VBCLSERVICE,pfnWorker}
1319 */
1320static DECLCALLBACK(int) vbclSVGAWorker(bool volatile *pfShutdown)
1321{
1322 RT_NOREF(pfShutdown);
1323
1324 /* Do not acknowledge the first event we query for to pick up old events,
1325 * e.g. from before a guest reboot. */
1326 bool fAck = false;
1327 bool fFirstRun = true;
1328 static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS];
1329
1330 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
1331 if (RT_FAILURE(rc))
1332 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
1333 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
1334 if (RT_FAILURE(rc))
1335 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
1336 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
1337 return VERR_RESOURCE_BUSY;
1338
1339 /* Let the main thread know that it can continue spawning services. */
1340 RTThreadUserSignal(RTThreadSelf());
1341
1342 for (;;)
1343 {
1344 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
1345 uint32_t cDisplaysOut;
1346 /* Query the first size without waiting. This lets us e.g. pick up
1347 * the last event before a guest reboot when we start again after. */
1348 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
1349 fAck = true;
1350 if (RT_FAILURE(rc))
1351 VBClLogFatalError("Failed to get display change request, rc=%Rrc\n", rc);
1352 if (cDisplaysOut > VMW_MAX_HEADS)
1353 VBClLogFatalError("Display change request contained, rc=%Rrc\n", rc);
1354 if (cDisplaysOut > 0)
1355 {
1356 for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
1357 {
1358 uint32_t idDisplay = aDisplays[i].idDisplay;
1359 if (idDisplay >= VMW_MAX_HEADS)
1360 continue;
1361 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags;
1362 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
1363 {
1364 if (idDisplay == 0 || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
1365 {
1366 aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin;
1367 aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin;
1368 } else {
1369 aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx;
1370 aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin;
1371 }
1372 aMonitors[idDisplay].cx = aDisplays[i].cx;
1373 aMonitors[idDisplay].cy = aDisplays[i].cy;
1374 }
1375 }
1376 /* Create a whole topology and send it to xrandr. */
1377 struct RANDROUTPUT aOutputs[VMW_MAX_HEADS];
1378 int iRunningX = 0;
1379 for (int j = 0; j < x11Context.hOutputCount; ++j)
1380 {
1381 aOutputs[j].x = iRunningX;
1382 aOutputs[j].y = aMonitors[j].yOrigin;
1383 aOutputs[j].width = aMonitors[j].cx;
1384 aOutputs[j].height = aMonitors[j].cy;
1385 aOutputs[j].fEnabled = !(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED);
1386 if (aOutputs[j].fEnabled)
1387 iRunningX += aOutputs[j].width;
1388 }
1389 /* In 32-bit guests GAs build on our release machines causes an xserver lock during vmware_ctrl extention
1390 if we do the call withing XGrab. We make the call the said extension only once (to connect the outputs)
1391 rather than at each resize iteration. */
1392#if ARCH_BITS == 32
1393 if (fFirstRun)
1394 callVMWCTRL(aOutputs);
1395#endif
1396 setXrandrTopology(aOutputs);
1397 /* Wait for some seconds and set toplogy again after the boot. In some desktop environments (cinnamon) where
1398 DE get into our resizing our first resize is reverted by the DE. Sleeping for some secs. helps. Setting
1399 topology a 2nd time resolves the black screen I get after resizing.*/
1400 if (fFirstRun)
1401 {
1402 sleep(4);
1403 setXrandrTopology(aOutputs);
1404 fFirstRun = false;
1405 }
1406 }
1407 uint32_t events;
1408 do
1409 {
1410 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
1411 } while (rc == VERR_INTERRUPTED);
1412 if (RT_FAILURE(rc))
1413 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
1414 }
1415}
1416
1417VBCLSERVICE g_SvcDisplaySVGA =
1418{
1419 "dp-svga-x11", /* szName */
1420 "SVGA X11 display", /* pszDescription */
1421 ".vboxclient-display-svga-x11.pid", /* pszPidFilePath */
1422 NULL, /* pszUsage */
1423 NULL, /* pszOptions */
1424 NULL, /* pfnOption */
1425 vbclSVGAInit, /* pfnInit */
1426 vbclSVGAWorker, /* pfnWorker */
1427 vbclSVGAStop, /* pfnStop*/
1428 NULL /* pfnTerm */
1429};
1430
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