VirtualBox

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

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

Additions: X11: VBoxClient --vmsvga: Use proper index when caching video modes, bugref:10222.

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