VirtualBox

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

Last change on this file since 90218 was 90218, checked in by vboxsync, 3 years ago

Add/Lnx/VBoxClient/vmsvga: Try avoid closing the same display structure more than once by setting the members to NULL afte closing. Don't call XRRSelectInput with a NULL display pointer. (Background: Saw Ubuntu complaing once about a double free with XCloseDisplay and the signal handler on the stack.

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