VirtualBox

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

Last change on this file since 84547 was 84479, checked in by vboxsync, 5 years ago

bugref:9637. Relax function pointer checks for XRRGetMonitors and XRRFreeMonitor.

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