VirtualBox

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

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

Additions: X11: VMSVGA: gracefully exit VBoxClient process if has been VBoxDRMClient started, bugref:9637.

This commit fixes regression introduced in r141338. VBoxClient (--vmsvga) and VBoxDRMClient
processes cannot ran in parallel. Only one process is allowed to do VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS).
Therefore, once VBoxDRMClient takes over, VBoxClient should be terminated.

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