VirtualBox

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

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

FE/Qt: bugref:9637. Using DRM client on 32 bit vms

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