VirtualBox

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

Last change on this file since 85798 was 84832, checked in by vboxsync, 5 years ago

bugref:9637. freeing after return does not make much sense.

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