VirtualBox

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

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

Additions/VBoxDRMClient: Removed code duplication between VBoxClient / VBoxService by refactoring the VBoxDRMClient routines into VbglR3. Also improved error checking here and there, added @todos. bugref:9637

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