VirtualBox

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

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

bugref:9637. Calling vmware_ctrl before getting screen resources. that enables us reusing modes ctrl extension has already created.

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