VirtualBox

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

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

Additions: X11: VBoxClient: VMSVGA: process graceful shutdown, bugref:10028.

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

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