VirtualBox

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

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

Additions: Renaming (VbglR3DRMClient -> VbglR3DrmClient).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.7 KB
Line 
1/* $Id: display-svga-x11.cpp 86887 2020-11-16 10:49:34Z 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"); /** @todo r=andy Use RTEnv API. */
676 if ((pSessionType != NULL) && (RTStrIStartsWith(pSessionType, "wayland")))
677 return true;
678
679 return false;
680}
681
682/**
683 * @interface_method_impl{VBCLSERVICE,pfnInit}
684 */
685static DECLCALLBACK(int) vbclSVGAInit(void)
686{
687 int rc;
688
689 /* In 32-bit guests GAs build on our release machines causes an xserver hang.
690 * So for 32-bit GAs we use our DRM client. */
691#if ARCH_BITS == 32
692 rc = VbglR3DrmClientStart();
693 if (RT_FAILURE(rc))
694 VBClLogError("Starting DRM resizing client (32-bit) failed with %Rrc\n", rc);
695 return VERR_NOT_AVAILABLE; /** @todo r=andy Why ignoring rc here? */
696#endif
697
698 /* If DRM client is already running don't start this service. */
699 if (VbglR3DrmClientIsRunning())
700 {
701 VBClLogInfo("DRM resizing is already running. Exiting this service\n");
702 return VERR_NOT_AVAILABLE;
703 }
704
705 if (isXwayland())
706 {
707 rc = VbglR3DrmClientStart();
708 if (RT_FAILURE(rc))
709 VBClLogError("Starting DRM resizing client failed with %Rrc\n", rc);
710 return rc;
711 }
712
713 x11Connect();
714
715 if (x11Context.pDisplay == NULL)
716 return VERR_NOT_AVAILABLE;
717
718 /* don't start the monitoring thread if related randr functionality is not available. */
719 if (x11Context.fMonitorInfoAvailable)
720 {
721 if (RT_FAILURE(startX11MonitorThread()))
722 return VERR_NOT_AVAILABLE;
723 }
724
725 return VINF_SUCCESS;
726}
727
728/**
729 * @interface_method_impl{VBCLSERVICE,pfnStop}
730 */
731static DECLCALLBACK(void) vbclSVGAStop(void)
732{
733 if (mpMonitorPositions)
734 {
735 free(mpMonitorPositions);
736 mpMonitorPositions = NULL;
737 }
738
739 stopX11MonitorThread(); /** @todo r=andy We ignore rc!? */
740
741 if (x11Context.pRandLibraryHandle)
742 {
743 dlclose(x11Context.pRandLibraryHandle);
744 x11Context.pRandLibraryHandle = NULL;
745 }
746#ifdef WITH_DISTRO_XRAND_XINERAMA
747 XRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, 0);
748 XRRFreeScreenResources(x11Context.pScreenResources);
749#else
750 if (x11Context.pXRRSelectInput)
751 x11Context.pXRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, 0);
752 if (x11Context.pXRRFreeScreenResources)
753 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
754#endif
755 XCloseDisplay(x11Context.pDisplay);
756 XCloseDisplay(x11Context.pDisplayRandRMonitoring);
757}
758
759#ifndef WITH_DISTRO_XRAND_XINERAMA
760static int openLibRandR()
761{
762 x11Context.pRandLibraryHandle = dlopen("libXrandr.so", RTLD_LAZY /*| RTLD_LOCAL */);
763 if (!x11Context.pRandLibraryHandle)
764 x11Context.pRandLibraryHandle = dlopen("libXrandr.so.2", RTLD_LAZY /*| RTLD_LOCAL */);
765 if (!x11Context.pRandLibraryHandle)
766 x11Context.pRandLibraryHandle = dlopen("libXrandr.so.2.2.0", RTLD_LAZY /*| RTLD_LOCAL */);
767
768 if (!x11Context.pRandLibraryHandle)
769 {
770 VBClLogFatalError("Could not locate libXrandr for dlopen\n");
771 return VERR_NOT_FOUND;
772 }
773
774 *(void **)(&x11Context.pXRRSelectInput) = dlsym(x11Context.pRandLibraryHandle, "XRRSelectInput");
775 checkFunctionPtrReturn(x11Context.pXRRSelectInput);
776
777 *(void **)(&x11Context.pXRRQueryExtension) = dlsym(x11Context.pRandLibraryHandle, "XRRQueryExtension");
778 checkFunctionPtrReturn(x11Context.pXRRQueryExtension);
779
780 *(void **)(&x11Context.pXRRQueryVersion) = dlsym(x11Context.pRandLibraryHandle, "XRRQueryVersion");
781 checkFunctionPtrReturn(x11Context.pXRRQueryVersion);
782
783 /* Don't bail out when XRRGetMonitors XRRFreeMonitors are missing as in Oracle Solaris 10. It is not crucial esp. for single monitor. */
784 *(void **)(&x11Context.pXRRGetMonitors) = dlsym(x11Context.pRandLibraryHandle, "XRRGetMonitors");
785 checkFunctionPtr(x11Context.pXRRGetMonitors);
786
787 *(void **)(&x11Context.pXRRFreeMonitors) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeMonitors");
788 checkFunctionPtr(x11Context.pXRRFreeMonitors);
789
790 x11Context.fMonitorInfoAvailable = x11Context.pXRRGetMonitors && x11Context.pXRRFreeMonitors;
791
792 *(void **)(&x11Context.pXRRGetScreenResources) = dlsym(x11Context.pRandLibraryHandle, "XRRGetScreenResources");
793 checkFunctionPtrReturn(x11Context.pXRRGetScreenResources);
794
795 *(void **)(&x11Context.pXRRSetCrtcConfig) = dlsym(x11Context.pRandLibraryHandle, "XRRSetCrtcConfig");
796 checkFunctionPtrReturn(x11Context.pXRRSetCrtcConfig);
797
798 *(void **)(&x11Context.pXRRFreeScreenResources) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeScreenResources");
799 checkFunctionPtrReturn(x11Context.pXRRFreeScreenResources);
800
801 *(void **)(&x11Context.pXRRFreeModeInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeModeInfo");
802 checkFunctionPtrReturn(x11Context.pXRRFreeModeInfo);
803
804 *(void **)(&x11Context.pXRRFreeOutputInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRFreeOutputInfo");
805 checkFunctionPtrReturn(x11Context.pXRRFreeOutputInfo);
806
807 *(void **)(&x11Context.pXRRSetScreenSize) = dlsym(x11Context.pRandLibraryHandle, "XRRSetScreenSize");
808 checkFunctionPtrReturn(x11Context.pXRRSetScreenSize);
809
810 *(void **)(&x11Context.pXRRUpdateConfiguration) = dlsym(x11Context.pRandLibraryHandle, "XRRUpdateConfiguration");
811 checkFunctionPtrReturn(x11Context.pXRRUpdateConfiguration);
812
813 *(void **)(&x11Context.pXRRAllocModeInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRAllocModeInfo");
814 checkFunctionPtrReturn(x11Context.pXRRAllocModeInfo);
815
816 *(void **)(&x11Context.pXRRCreateMode) = dlsym(x11Context.pRandLibraryHandle, "XRRCreateMode");
817 checkFunctionPtrReturn(x11Context.pXRRCreateMode);
818
819 *(void **)(&x11Context.pXRRGetOutputInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRGetOutputInfo");
820 checkFunctionPtrReturn(x11Context.pXRRGetOutputInfo);
821
822 *(void **)(&x11Context.pXRRGetCrtcInfo) = dlsym(x11Context.pRandLibraryHandle, "XRRGetCrtcInfo");
823 checkFunctionPtrReturn(x11Context.pXRRGetCrtcInfo);
824
825 *(void **)(&x11Context.pXRRAddOutputMode) = dlsym(x11Context.pRandLibraryHandle, "XRRAddOutputMode");
826 checkFunctionPtrReturn(x11Context.pXRRAddOutputMode);
827
828 return VINF_SUCCESS;
829}
830#endif
831
832static void x11Connect()
833{
834 x11Context.pScreenResources = NULL;
835 x11Context.pXRRSelectInput = NULL;
836 x11Context.pRandLibraryHandle = NULL;
837 x11Context.pXRRQueryExtension = NULL;
838 x11Context.pXRRQueryVersion = NULL;
839 x11Context.pXRRGetMonitors = NULL;
840 x11Context.pXRRGetScreenResources = NULL;
841 x11Context.pXRRSetCrtcConfig = NULL;
842 x11Context.pXRRFreeMonitors = NULL;
843 x11Context.pXRRFreeScreenResources = NULL;
844 x11Context.pXRRFreeOutputInfo = NULL;
845 x11Context.pXRRFreeModeInfo = NULL;
846 x11Context.pXRRSetScreenSize = NULL;
847 x11Context.pXRRUpdateConfiguration = NULL;
848 x11Context.pXRRAllocModeInfo = NULL;
849 x11Context.pXRRCreateMode = NULL;
850 x11Context.pXRRGetOutputInfo = NULL;
851 x11Context.pXRRGetCrtcInfo = NULL;
852 x11Context.pXRRAddOutputMode = NULL;
853 x11Context.fWmwareCtrlExtention = false;
854 x11Context.fMonitorInfoAvailable = false;
855 x11Context.hRandRMajor = 0;
856 x11Context.hRandRMinor = 0;
857
858 int dummy;
859 if (x11Context.pDisplay != NULL)
860 VBClLogFatalError("%s called with bad argument\n", __func__);
861 x11Context.pDisplay = XOpenDisplay(NULL);
862 x11Context.pDisplayRandRMonitoring = XOpenDisplay(NULL);
863 if (x11Context.pDisplay == NULL)
864 return;
865#ifndef WITH_DISTRO_XRAND_XINERAMA
866 if (openLibRandR() != VINF_SUCCESS)
867 {
868 XCloseDisplay(x11Context.pDisplay);
869 XCloseDisplay(x11Context.pDisplayRandRMonitoring);
870 x11Context.pDisplay = NULL;
871 x11Context.pDisplayRandRMonitoring = NULL;
872 return;
873 }
874#endif
875
876 x11Context.fWmwareCtrlExtention = XQueryExtension(x11Context.pDisplay, "VMWARE_CTRL",
877 &x11Context.hVMWCtrlMajorOpCode, &dummy, &dummy);
878 if (!x11Context.fWmwareCtrlExtention)
879 VBClLogError("VMWARE's ctrl extension is not available! Multi monitor management is not possible\n");
880 else
881 VBClLogInfo("VMWARE's ctrl extension is available. Major Opcode is %d.\n", x11Context.hVMWCtrlMajorOpCode);
882
883 /* Check Xrandr stuff. */
884 bool fSuccess = false;
885#ifdef WITH_DISTRO_XRAND_XINERAMA
886 fSuccess = XRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase);
887#else
888 if (x11Context.pXRRQueryExtension)
889 fSuccess = x11Context.pXRRQueryExtension(x11Context.pDisplay, &x11Context.hRandREventBase, &x11Context.hRandRErrorBase);
890#endif
891 if (fSuccess)
892 {
893 fSuccess = false;
894#ifdef WITH_DISTRO_XRAND_XINERAMA
895 fSuccess = XRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor);
896#else
897 if (x11Context.pXRRQueryVersion)
898 fSuccess = x11Context.pXRRQueryVersion(x11Context.pDisplay, &x11Context.hRandRMajor, &x11Context.hRandRMinor);
899#endif
900 if (!fSuccess)
901 {
902 XCloseDisplay(x11Context.pDisplay);
903 x11Context.pDisplay = NULL;
904 return;
905 }
906 if (x11Context.hRandRMajor < 1 || x11Context.hRandRMinor <= 3)
907 {
908 VBClLogFatalError("Resizing service requires libXrandr Version >= 1.4. Detected version is %d.%d\n", x11Context.hRandRMajor, x11Context.hRandRMinor);
909 XCloseDisplay(x11Context.pDisplay);
910 x11Context.pDisplay = NULL;
911 return;
912 }
913 }
914 x11Context.rootWindow = DefaultRootWindow(x11Context.pDisplay);
915 x11Context.hEventMask = RRScreenChangeNotifyMask;
916
917 /* Select the XEvent types we want to listen to. */
918#ifdef WITH_DISTRO_XRAND_XINERAMA
919 XRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, x11Context.hEventMask);
920#else
921 if (x11Context.pXRRSelectInput)
922 x11Context.pXRRSelectInput(x11Context.pDisplayRandRMonitoring, x11Context.rootWindow, x11Context.hEventMask);
923#endif
924 x11Context.iDefaultScreen = DefaultScreen(x11Context.pDisplay);
925
926#ifdef WITH_DISTRO_XRAND_XINERAMA
927 x11Context.pScreenResources = XRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
928#else
929 if (x11Context.pXRRGetScreenResources)
930 x11Context.pScreenResources = x11Context.pXRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
931#endif
932 /* Currently without the VMWARE_CTRL extension we cannot connect outputs and set outputs' preferred mode.
933 * So we set the output count to 1 to get the 1st output position correct. */
934 x11Context.hOutputCount = x11Context.fWmwareCtrlExtention ? determineOutputCount() : 1;
935#ifdef WITH_DISTRO_XRAND_XINERAMA
936 XRRFreeScreenResources(x11Context.pScreenResources);
937#else
938 if (x11Context.pXRRFreeScreenResources)
939 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
940#endif
941}
942
943static int determineOutputCount()
944{
945 if (!x11Context.pScreenResources)
946 return 0;
947 return x11Context.pScreenResources->noutput;
948}
949
950static int findExistingModeIndex(unsigned iXRes, unsigned iYRes)
951{
952 if (!x11Context.pScreenResources)
953 return -1;
954 for (int i = 0; i < x11Context.pScreenResources->nmode; ++i)
955 {
956 if (x11Context.pScreenResources->modes[i].width == iXRes && x11Context.pScreenResources->modes[i].height == iYRes)
957 return i;
958 }
959 return -1;
960}
961
962static bool disableCRTC(RRCrtc crtcID)
963{
964 XRRCrtcInfo *pCrctInfo = NULL;
965
966#ifdef WITH_DISTRO_XRAND_XINERAMA
967 pCrctInfo = XRRGetCrtcInfo(x11Context.pDisplay, x11Context.pScreenResources, crtcID);
968#else
969 if (x11Context.pXRRGetCrtcInfo)
970 pCrctInfo = x11Context.pXRRGetCrtcInfo(x11Context.pDisplay, x11Context.pScreenResources, crtcID);
971#endif
972
973 if (!pCrctInfo)
974 return false;
975
976 Status ret = Success;
977#ifdef WITH_DISTRO_XRAND_XINERAMA
978 ret = XRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcID,
979 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
980#else
981 if (x11Context.pXRRSetCrtcConfig)
982 ret = x11Context.pXRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcID,
983 CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
984#endif
985 /** @todo In case of unsuccesful crtc config set we have to revert frame buffer size and crtc sizes. */
986 if (ret == Success)
987 return true;
988 else
989 return false;
990}
991
992static XRRScreenSize currentSize()
993{
994 XRRScreenSize cSize;
995 cSize.width = DisplayWidth(x11Context.pDisplay, x11Context.iDefaultScreen);
996 cSize.mwidth = DisplayWidthMM(x11Context.pDisplay, x11Context.iDefaultScreen);
997 cSize.height = DisplayHeight(x11Context.pDisplay, x11Context.iDefaultScreen);
998 cSize.mheight = DisplayHeightMM(x11Context.pDisplay, x11Context.iDefaultScreen);
999 return cSize;
1000}
1001
1002static unsigned int computeDpi(unsigned int pixels, unsigned int mm)
1003{
1004 unsigned int dpi = 0;
1005 if (mm > 0) {
1006 dpi = (unsigned int)((double)pixels * MILLIS_PER_INCH /
1007 (double)mm + 0.5);
1008 }
1009 return (dpi > 0) ? dpi : DEFAULT_DPI;
1010}
1011
1012static bool resizeFrameBuffer(struct RANDROUTPUT *paOutputs)
1013{
1014 unsigned int iXRes = 0;
1015 unsigned int iYRes = 0;
1016 /* Don't care about the output positions for now. */
1017 for (int i = 0; i < x11Context.hOutputCount; ++i)
1018 {
1019 if (!paOutputs[i].fEnabled)
1020 continue;
1021 iXRes += paOutputs[i].width;
1022 iYRes = iYRes < paOutputs[i].height ? paOutputs[i].height : iYRes;
1023 }
1024 XRRScreenSize cSize= currentSize();
1025 unsigned int xdpi = computeDpi(cSize.width, cSize.mwidth);
1026 unsigned int ydpi = computeDpi(cSize.height, cSize.mheight);
1027 unsigned int xmm;
1028 unsigned int ymm;
1029 xmm = (int)(MILLIS_PER_INCH * iXRes / ((double)xdpi) + 0.5);
1030 ymm = (int)(MILLIS_PER_INCH * iYRes / ((double)ydpi) + 0.5);
1031#ifdef WITH_DISTRO_XRAND_XINERAMA
1032 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, RRScreenChangeNotifyMask);
1033 XRRSetScreenSize(x11Context.pDisplay, x11Context.rootWindow, iXRes, iYRes, xmm, ymm);
1034#else
1035 if (x11Context.pXRRSelectInput)
1036 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, RRScreenChangeNotifyMask);
1037 if (x11Context.pXRRSetScreenSize)
1038 x11Context.pXRRSetScreenSize(x11Context.pDisplay, x11Context.rootWindow, iXRes, iYRes, xmm, ymm);
1039#endif
1040 XSync(x11Context.pDisplay, False);
1041 XEvent configEvent;
1042 bool event = false;
1043 while (XCheckTypedEvent(x11Context.pDisplay, RRScreenChangeNotify + x11Context.hRandREventBase, &configEvent))
1044 {
1045#ifdef WITH_DISTRO_XRAND_XINERAMA
1046 XRRUpdateConfiguration(&configEvent);
1047#else
1048 if (x11Context.pXRRUpdateConfiguration)
1049 x11Context.pXRRUpdateConfiguration(&configEvent);
1050#endif
1051 event = true;
1052 }
1053#ifdef WITH_DISTRO_XRAND_XINERAMA
1054 XRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
1055#else
1056 if (x11Context.pXRRSelectInput)
1057 x11Context.pXRRSelectInput(x11Context.pDisplay, x11Context.rootWindow, 0);
1058#endif
1059 XRRScreenSize newSize = currentSize();
1060
1061 if (!event || newSize.width != (int)iXRes || newSize.height != (int)iYRes)
1062 {
1063 VBClLogError("Resizing frame buffer to %d %d has failed\n", iXRes, iYRes);
1064 return false;
1065 }
1066 return true;
1067}
1068
1069static XRRModeInfo *createMode(int iXRes, int iYRes)
1070{
1071 XRRModeInfo *pModeInfo = NULL;
1072 char sModeName[126];
1073 sprintf(sModeName, "%dx%d_vbox", iXRes, iYRes);
1074#ifdef WITH_DISTRO_XRAND_XINERAMA
1075 pModeInfo = XRRAllocModeInfo(sModeName, strlen(sModeName));
1076#else
1077 if (x11Context.pXRRAllocModeInfo)
1078 pModeInfo = x11Context.pXRRAllocModeInfo(sModeName, strlen(sModeName));
1079#endif
1080 pModeInfo->width = iXRes;
1081 pModeInfo->height = iYRes;
1082
1083 DisplayModeR mode = f86CVTMode(iXRes, iYRes, 60 /*VRefresh */, true /*Reduced */, false /* Interlaced */);
1084
1085 pModeInfo->dotClock = mode.Clock;
1086 pModeInfo->hSyncStart = mode.HSyncStart;
1087 pModeInfo->hSyncEnd = mode.HSyncEnd;
1088 pModeInfo->hTotal = mode.HTotal;
1089 pModeInfo->hSkew = mode.HSkew;
1090 pModeInfo->vSyncStart = mode.VSyncStart;
1091 pModeInfo->vSyncEnd = mode.VSyncEnd;
1092 pModeInfo->vTotal = mode.VTotal;
1093
1094 RRMode newMode = None;
1095#ifdef WITH_DISTRO_XRAND_XINERAMA
1096 newMode = XRRCreateMode(x11Context.pDisplay, x11Context.rootWindow, pModeInfo);
1097#else
1098 if (x11Context.pXRRCreateMode)
1099 newMode = x11Context.pXRRCreateMode(x11Context.pDisplay, x11Context.rootWindow, pModeInfo);
1100#endif
1101 if (newMode == None)
1102 {
1103#ifdef WITH_DISTRO_XRAND_XINERAMA
1104 XRRFreeModeInfo(pModeInfo);
1105#else
1106 if (x11Context.pXRRFreeModeInfo)
1107 x11Context.pXRRFreeModeInfo(pModeInfo);
1108#endif
1109 return NULL;
1110 }
1111 pModeInfo->id = newMode;
1112 return pModeInfo;
1113}
1114
1115static bool configureOutput(int iOutputIndex, struct RANDROUTPUT *paOutputs)
1116{
1117 if (iOutputIndex >= x11Context.hOutputCount)
1118 {
1119 VBClLogError("Output index %d is greater than # of oputputs %d\n", iOutputIndex, x11Context.hOutputCount);
1120 return false;
1121 }
1122 RROutput outputId = x11Context.pScreenResources->outputs[iOutputIndex];
1123 XRROutputInfo *pOutputInfo = NULL;
1124#ifdef WITH_DISTRO_XRAND_XINERAMA
1125 pOutputInfo = XRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, outputId);
1126#else
1127 if (x11Context.pXRRGetOutputInfo)
1128 pOutputInfo = x11Context.pXRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, outputId);
1129#endif
1130 if (!pOutputInfo)
1131 return false;
1132 XRRModeInfo *pModeInfo = NULL;
1133 bool fNewMode = false;
1134 /* Index of the mode within the XRRScreenResources.modes array. -1 if such a mode with required resolution does not exists*/
1135 int iModeIndex = findExistingModeIndex(paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1136 if (iModeIndex != -1 && iModeIndex < x11Context.pScreenResources->nmode)
1137 pModeInfo = &(x11Context.pScreenResources->modes[iModeIndex]);
1138 else
1139 {
1140 /* A mode with required size was not found. Create a new one. */
1141 pModeInfo = createMode(paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1142 fNewMode = true;
1143 }
1144 if (!pModeInfo)
1145 {
1146 VBClLogError("Could not create mode for the resolution (%d, %d)\n",
1147 paOutputs[iOutputIndex].width, paOutputs[iOutputIndex].height);
1148 return false;
1149 }
1150
1151#ifdef WITH_DISTRO_XRAND_XINERAMA
1152 XRRAddOutputMode(x11Context.pDisplay, outputId, pModeInfo->id);
1153#else
1154 if (x11Context.pXRRAddOutputMode)
1155 x11Context.pXRRAddOutputMode(x11Context.pDisplay, outputId, pModeInfo->id);
1156#endif
1157 /* Make sure outputs crtc is set. */
1158 pOutputInfo->crtc = pOutputInfo->crtcs[0];
1159
1160 RRCrtc crtcId = pOutputInfo->crtcs[0];
1161 Status ret = Success;
1162#ifdef WITH_DISTRO_XRAND_XINERAMA
1163 ret = XRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcId, CurrentTime,
1164 paOutputs[iOutputIndex].x, paOutputs[iOutputIndex].y,
1165 pModeInfo->id, RR_Rotate_0, &(outputId), 1 /*int noutputs*/);
1166#else
1167 if (x11Context.pXRRSetCrtcConfig)
1168 ret = x11Context.pXRRSetCrtcConfig(x11Context.pDisplay, x11Context.pScreenResources, crtcId, CurrentTime,
1169 paOutputs[iOutputIndex].x, paOutputs[iOutputIndex].y,
1170 pModeInfo->id, RR_Rotate_0, &(outputId), 1 /*int noutputs*/);
1171#endif
1172 if (ret != Success)
1173 VBClLogError("crtc set config failed for output %d\n", iOutputIndex);
1174
1175#ifdef WITH_DISTRO_XRAND_XINERAMA
1176 XRRFreeOutputInfo(pOutputInfo);
1177#else
1178 if (x11Context.pXRRFreeOutputInfo)
1179 x11Context.pXRRFreeOutputInfo(pOutputInfo);
1180#endif
1181
1182 if (fNewMode)
1183 {
1184#ifdef WITH_DISTRO_XRAND_XINERAMA
1185 XRRFreeModeInfo(pModeInfo);
1186#else
1187 if (x11Context.pXRRFreeModeInfo)
1188 x11Context.pXRRFreeModeInfo(pModeInfo);
1189#endif
1190 }
1191 return true;
1192}
1193
1194/** Construct the xrandr command which sets the whole monitor topology each time. */
1195static void setXrandrTopology(struct RANDROUTPUT *paOutputs)
1196{
1197 XGrabServer(x11Context.pDisplay);
1198 if (x11Context.fWmwareCtrlExtention)
1199 callVMWCTRL(paOutputs);
1200
1201#ifdef WITH_DISTRO_XRAND_XINERAMA
1202 x11Context.pScreenResources = XRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
1203#else
1204 if (x11Context.pXRRGetScreenResources)
1205 x11Context.pScreenResources = x11Context.pXRRGetScreenResources(x11Context.pDisplay, x11Context.rootWindow);
1206#endif
1207 x11Context.hOutputCount = x11Context.fWmwareCtrlExtention ? determineOutputCount() : 1;
1208
1209 if (!x11Context.pScreenResources)
1210 {
1211 XUngrabServer(x11Context.pDisplay);
1212 return;
1213 }
1214
1215 /* Disable crtcs. */
1216 for (int i = 0; i < x11Context.pScreenResources->noutput; ++i)
1217 {
1218 XRROutputInfo *pOutputInfo = NULL;
1219#ifdef WITH_DISTRO_XRAND_XINERAMA
1220 pOutputInfo = XRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, x11Context.pScreenResources->outputs[i]);
1221#else
1222 if (x11Context.pXRRGetOutputInfo)
1223 pOutputInfo = x11Context.pXRRGetOutputInfo(x11Context.pDisplay, x11Context.pScreenResources, x11Context.pScreenResources->outputs[i]);
1224#endif
1225 if (!pOutputInfo)
1226 continue;
1227 if (pOutputInfo->crtc == None)
1228 continue;
1229
1230 if (!disableCRTC(pOutputInfo->crtc))
1231 {
1232 VBClLogFatalError("Crtc disable failed %lu\n", pOutputInfo->crtc);
1233 XUngrabServer(x11Context.pDisplay);
1234#ifdef WITH_DISTRO_XRAND_XINERAMA
1235 XRRFreeScreenResources(x11Context.pScreenResources);
1236#else
1237 if (x11Context.pXRRFreeScreenResources)
1238 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1239#endif
1240 return;
1241 }
1242#ifdef WITH_DISTRO_XRAND_XINERAMA
1243 XRRFreeOutputInfo(pOutputInfo);
1244#else
1245 if (x11Context.pXRRFreeOutputInfo)
1246 x11Context.pXRRFreeOutputInfo(pOutputInfo);
1247#endif
1248 }
1249 /* Resize the frame buffer. */
1250 if (!resizeFrameBuffer(paOutputs))
1251 {
1252 XUngrabServer(x11Context.pDisplay);
1253#ifdef WITH_DISTRO_XRAND_XINERAMA
1254 XRRFreeScreenResources(x11Context.pScreenResources);
1255#else
1256 if (x11Context.pXRRFreeScreenResources)
1257 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1258#endif
1259 return;
1260 }
1261
1262 /* Configure the outputs. */
1263 for (int i = 0; i < x11Context.hOutputCount; ++i)
1264 {
1265 /* be paranoid. */
1266 if (i >= x11Context.pScreenResources->noutput)
1267 break;
1268 if (!paOutputs[i].fEnabled)
1269 continue;
1270 configureOutput(i, paOutputs);
1271 }
1272 XSync(x11Context.pDisplay, False);
1273#ifdef WITH_DISTRO_XRAND_XINERAMA
1274 XRRFreeScreenResources(x11Context.pScreenResources);
1275#else
1276 if (x11Context.pXRRFreeScreenResources)
1277 x11Context.pXRRFreeScreenResources(x11Context.pScreenResources);
1278#endif
1279 XUngrabServer(x11Context.pDisplay);
1280 XFlush(x11Context.pDisplay);
1281}
1282
1283/**
1284 * @interface_method_impl{VBCLSERVICE,pfnWorker}
1285 */
1286static DECLCALLBACK(int) vbclSVGAWorker(bool volatile *pfShutdown)
1287{
1288 RT_NOREF(pfShutdown);
1289
1290 /* Do not acknowledge the first event we query for to pick up old events,
1291 * e.g. from before a guest reboot. */
1292 bool fAck = false;
1293 bool fFirstRun = true;
1294 static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS];
1295
1296 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
1297 if (RT_FAILURE(rc))
1298 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
1299 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
1300 if (RT_FAILURE(rc))
1301 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
1302 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */
1303 return VERR_RESOURCE_BUSY;
1304
1305 /* Let the main thread know that it can continue spawning services. */
1306 RTThreadUserSignal(RTThreadSelf());
1307
1308 for (;;)
1309 {
1310 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
1311 uint32_t cDisplaysOut;
1312 /* Query the first size without waiting. This lets us e.g. pick up
1313 * the last event before a guest reboot when we start again after. */
1314 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
1315 fAck = true;
1316 if (RT_FAILURE(rc))
1317 VBClLogFatalError("Failed to get display change request, rc=%Rrc\n", rc);
1318 if (cDisplaysOut > VMW_MAX_HEADS)
1319 VBClLogFatalError("Display change request contained, rc=%Rrc\n", rc);
1320 if (cDisplaysOut > 0)
1321 {
1322 for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
1323 {
1324 uint32_t idDisplay = aDisplays[i].idDisplay;
1325 if (idDisplay >= VMW_MAX_HEADS)
1326 continue;
1327 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags;
1328 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
1329 {
1330 if (idDisplay == 0 || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
1331 {
1332 aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin;
1333 aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin;
1334 } else {
1335 aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx;
1336 aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin;
1337 }
1338 aMonitors[idDisplay].cx = aDisplays[i].cx;
1339 aMonitors[idDisplay].cy = aDisplays[i].cy;
1340 }
1341 }
1342 /* Create a whole topology and send it to xrandr. */
1343 struct RANDROUTPUT aOutputs[VMW_MAX_HEADS];
1344 int iRunningX = 0;
1345 for (int j = 0; j < x11Context.hOutputCount; ++j)
1346 {
1347 aOutputs[j].x = iRunningX;
1348 aOutputs[j].y = aMonitors[j].yOrigin;
1349 aOutputs[j].width = aMonitors[j].cx;
1350 aOutputs[j].height = aMonitors[j].cy;
1351 aOutputs[j].fEnabled = !(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED);
1352 if (aOutputs[j].fEnabled)
1353 iRunningX += aOutputs[j].width;
1354 }
1355 /* In 32-bit guests GAs build on our release machines causes an xserver lock during vmware_ctrl extention
1356 if we do the call withing XGrab. We make the call the said extension only once (to connect the outputs)
1357 rather than at each resize iteration. */
1358#if ARCH_BITS == 32
1359 if (fFirstRun)
1360 callVMWCTRL(aOutputs);
1361#endif
1362 setXrandrTopology(aOutputs);
1363 /* Wait for some seconds and set toplogy again after the boot. In some desktop environments (cinnamon) where
1364 DE get into our resizing our first resize is reverted by the DE. Sleeping for some secs. helps. Setting
1365 topology a 2nd time resolves the black screen I get after resizing.*/
1366 if (fFirstRun)
1367 {
1368 sleep(4);
1369 setXrandrTopology(aOutputs);
1370 fFirstRun = false;
1371 }
1372 }
1373 uint32_t events;
1374 do
1375 {
1376 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events);
1377 } while (rc == VERR_INTERRUPTED);
1378 if (RT_FAILURE(rc))
1379 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
1380 }
1381}
1382
1383VBCLSERVICE g_SvcDisplaySVGA =
1384{
1385 "dp-svga-x11", /* szName */
1386 "SVGA X11 display", /* pszDescription */
1387 ".vboxclient-display-svga-x11.pid", /* pszPidFilePath */
1388 NULL, /* pszUsage */
1389 NULL, /* pszOptions */
1390 NULL, /* pfnOption */
1391 vbclSVGAInit, /* pfnInit */
1392 vbclSVGAWorker, /* pfnWorker */
1393 vbclSVGAStop, /* pfnStop*/
1394 NULL /* pfnTerm */
1395};
1396
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