VirtualBox

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

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

Additions/VBoxClient: Big revamp of the internal service handling and termination fixes. A service now runs as part of a worker thread, while the main thread is used for initialization / shutdown and signal handling [build fix].

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

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