VirtualBox

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

Last change on this file since 96875 was 96875, checked in by vboxsync, 2 years ago

Aditions: X11: Consolidate all the environment variables we use at one place, bugref:10134.

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