VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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