VirtualBox

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

Last change on this file since 84008 was 83935, checked in by vboxsync, 5 years ago

bugref:9637. Some comment corrections.

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

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