VirtualBox

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

Last change on this file since 95478 was 94427, checked in by vboxsync, 3 years ago

X11/VBoxClient: doxygen fixes (don't duplicate/confuse docs, newer doxygen version dislikes it (sometimes)).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1/* $Id: display-helper-generic.cpp 94427 2022-03-31 23:58:08Z vboxsync $ */
2/** @file
3 * Guest Additions - Generic Desktop Environment helper.
4 *
5 * A generic helper for X11 Client which performs Desktop Environment
6 * specific actions utilizing libXrandr.
7 */
8
9/*
10 * Copyright (C) 2006-2022 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21#include "VBoxClient.h"
22#include "display-helper.h"
23
24#include <stdio.h>
25#include <stdlib.h>
26
27#include <VBox/log.h>
28#include <VBox/xrandr.h>
29
30#include <iprt/errcore.h>
31#include <iprt/asm.h>
32#include <iprt/thread.h>
33#include <iprt/mem.h>
34#include <iprt/list.h>
35
36/** Load libxrandr symbols needed for us. */
37#include <VBox/xrandr.h>
38/* Declarations of the functions that we need from libXrandr. */
39#define VBOX_XRANDR_GENERATE_BODY
40#include <VBox/xrandr-calls.h>
41
42#include <X11/Xlibint.h>
43
44/** Name of Display Change Monitor thread. */
45#define VBCL_HLP_DCM_THREAD_NAME "dcm-task"
46
47/** Display Change Monitor thread. */
48static RTTHREAD g_vbclHlpGenericDcmThread = NIL_RTTHREAD;
49
50/** Global flag which is triggered when service requested to shutdown. */
51static bool volatile g_fShutdown;
52
53/** Node of monitors info list. */
54typedef struct vbcl_hlp_generic_monitor_list_t
55{
56 /** List node. */
57 RTLISTNODE Node;
58 /** Pointer to xRandr monitor info. */
59 XRRMonitorInfo *pMonitorInfo;
60} vbcl_hlp_generic_monitor_list_t;
61
62/** Pointer to display change event notification callback (set by external function call). */
63static FNDISPLAYOFFSETCHANGE *g_pfnDisplayOffsetChangeCb;
64
65/**
66 * Determine monitor name strings order in a list of monitors which is sorted in ascending way.
67 *
68 * @return TRUE if first name should go first in a list, FALSE otherwise.
69 * @param pszName1 First monitor name.
70 * @param pszName2 Second monitor name.
71 */
72static bool vbcl_hlp_generic_order_names(char *pszName1, char *pszName2)
73{
74 AssertReturn(pszName1, false);
75 AssertReturn(pszName2, false);
76
77 char *pszFirst = pszName1;
78 char *pszSecond = pszName2;
79
80 while (*pszFirst && *pszSecond)
81 {
82 if (*pszFirst < *pszSecond)
83 return true;
84
85 pszFirst++;
86 pszSecond++;
87 }
88
89 return false;
90}
91
92/**
93 * Insert monitor info into the list sorted ascending.
94 *
95 * @return IPRT status code.
96 * @param pDisplay X11 display handle to fetch monitor name string from.
97 * @param pListHead Head of monitors info list.
98 * @param pMonitorInfo Monitor info ti be inserted into the list.
99 */
100static int vbcl_hlp_generic_monitor_list_insert_sorted(
101 Display *pDisplay, vbcl_hlp_generic_monitor_list_t *pListHead, XRRMonitorInfo *pMonitorInfo)
102{
103 vbcl_hlp_generic_monitor_list_t *pNode = (vbcl_hlp_generic_monitor_list_t *)RTMemAllocZ(sizeof(vbcl_hlp_generic_monitor_list_t));
104 vbcl_hlp_generic_monitor_list_t *pNodeIter;
105 char *pszMonitorName;
106
107 AssertReturn(pNode, VERR_NO_MEMORY);
108
109 pNode->pMonitorInfo = pMonitorInfo;
110
111 if (RTListIsEmpty(&pListHead->Node))
112 {
113 RTListNodeInsertAfter(&pListHead->Node, &pNode->Node);
114 return VINF_SUCCESS;
115 }
116
117 pszMonitorName = XGetAtomName(pDisplay, pMonitorInfo->name);
118 AssertReturn(pszMonitorName, VERR_NO_MEMORY);
119
120 RTListForEach(&pListHead->Node, pNodeIter, vbcl_hlp_generic_monitor_list_t, Node)
121 {
122 char *pszIterMonitorName = XGetAtomName(pDisplay, pNodeIter->pMonitorInfo->name);
123
124 if (vbcl_hlp_generic_order_names(pszMonitorName, pszIterMonitorName))
125 {
126 RTListNodeInsertBefore(&pNodeIter->Node, &pNode->Node);
127 XFree((void *)pszIterMonitorName);
128 XFree((void *)pszMonitorName);
129 return VINF_SUCCESS;
130 }
131
132 XFree((void *)pszIterMonitorName);
133 }
134
135 XFree((void *)pszMonitorName);
136
137 /* If we reached the end of the list, it means that monitor
138 * should be placed in the end (according to alphabetical sorting). */
139 RTListNodeInsertBefore(&pNodeIter->Node, &pNode->Node);
140
141 return VINF_SUCCESS;
142}
143
144/**
145 * Release monitors info list resources.
146 *
147 * @param pListHead List head.
148 */
149static void vbcl_hlp_generic_free_monitor_list(vbcl_hlp_generic_monitor_list_t *pListHead)
150{
151 vbcl_hlp_generic_monitor_list_t *pEntry, *pNextEntry;
152
153 RTListForEachSafe(&pListHead->Node, pEntry, pNextEntry, vbcl_hlp_generic_monitor_list_t, Node)
154 {
155 RTListNodeRemove(&pEntry->Node);
156 RTMemFree(pEntry);
157 }
158}
159
160/**
161 * Handle received RRScreenChangeNotify event.
162 *
163 * @param pDisplay X11 display handle.
164 */
165static void vbcl_hlp_generic_process_display_change_event(Display *pDisplay)
166{
167 int iCount;
168 uint32_t idxDisplay = 0;
169 XRRMonitorInfo *pMonitorsInfo = XRRGetMonitors(pDisplay, DefaultRootWindow(pDisplay), true, &iCount);
170 if (pMonitorsInfo && iCount > 0 && iCount < VBOX_DRMIPC_MONITORS_MAX)
171 {
172 int rc;
173 vbcl_hlp_generic_monitor_list_t pMonitorsInfoList, *pIter;
174 struct VBOX_DRMIPC_VMWRECT aDisplays[VBOX_DRMIPC_MONITORS_MAX];
175
176 RTListInit(&pMonitorsInfoList.Node);
177
178 /* Put monitors info into sorted (by monitor name) list. */
179 for (int i = 0; i < iCount; i++)
180 {
181 rc = vbcl_hlp_generic_monitor_list_insert_sorted(pDisplay, &pMonitorsInfoList, &pMonitorsInfo[i]);
182 if (RT_FAILURE(rc))
183 {
184 VBClLogError("unable to fill monitors info list, rc=%Rrc\n", rc);
185 break;
186 }
187 }
188
189 /* Now iterate over sorted list of monitor configurations. */
190 RTListForEach(&pMonitorsInfoList.Node, pIter, vbcl_hlp_generic_monitor_list_t, Node)
191 {
192 char *pszMonitorName = XGetAtomName(pDisplay, pIter->pMonitorInfo->name);
193
194 VBClLogVerbose(1, "reporting monitor %s offset: (%d, %d)\n",
195 pszMonitorName, pIter->pMonitorInfo->x, pIter->pMonitorInfo->y);
196
197 XFree((void *)pszMonitorName);
198
199 aDisplays[idxDisplay].x = pIter->pMonitorInfo->x;
200 aDisplays[idxDisplay].y = pIter->pMonitorInfo->y;
201 aDisplays[idxDisplay].w = pIter->pMonitorInfo->width;
202 aDisplays[idxDisplay].h = pIter->pMonitorInfo->height;
203
204 idxDisplay++;
205 }
206
207 vbcl_hlp_generic_free_monitor_list(&pMonitorsInfoList);
208
209 XRRFreeMonitors(pMonitorsInfo);
210
211 if (g_pfnDisplayOffsetChangeCb)
212 {
213 rc = g_pfnDisplayOffsetChangeCb(idxDisplay, aDisplays);
214 if (RT_FAILURE(rc))
215 VBClLogError("unable to notify subscriber about monitors info change, rc=%Rrc\n", rc);
216 }
217 }
218 else
219 VBClLogError("cannot get monitors info\n");
220}
221
222/** Worker thread for display change events monitoring. */
223static DECLCALLBACK(int) vbcl_hlp_generic_display_change_event_monitor_worker(RTTHREAD ThreadSelf, void *pvUser)
224{
225 int rc = VERR_GENERAL_FAILURE;
226
227 RT_NOREF(pvUser);
228
229 VBClLogVerbose(1, "vbcl_hlp_generic_display_change_event_monitor_worker started\n");
230
231 Display *pDisplay = XOpenDisplay(NULL);
232 if (pDisplay)
233 {
234 bool fSuccess;
235 int iEventBase, iErrorBase /* unused */, iMajor, iMinor;
236
237 fSuccess = XRRQueryExtension(pDisplay, &iEventBase, &iErrorBase);
238 fSuccess &= XRRQueryVersion(pDisplay, &iMajor, &iMinor);
239
240 if (fSuccess && iMajor >= 1 && iMinor > 3)
241 {
242 /* All required checks are now passed. Notify parent thread that we started. */
243 RTThreadUserSignal(ThreadSelf);
244
245 /* Only receive events we need. */
246 XRRSelectInput(pDisplay, DefaultRootWindow(pDisplay), RRScreenChangeNotifyMask);
247
248 /* Monitor main loop. */
249 while (!ASMAtomicReadBool(&g_fShutdown))
250 {
251 XEvent Event;
252
253 if (XPending(pDisplay) > 0)
254 {
255 XNextEvent(pDisplay, &Event);
256 switch (Event.type - iEventBase)
257 {
258 case RRScreenChangeNotify:
259 {
260 vbcl_hlp_generic_process_display_change_event(pDisplay);
261 break;
262 }
263
264 default:
265 break;
266 }
267 }
268 else
269 RTThreadSleep(RT_MS_1SEC / 2);
270 }
271 }
272 else
273 {
274 VBClLogError("dcm monitor cannot find XRandr 1.3+ extension\n");
275 rc = VERR_NOT_AVAILABLE;
276 }
277
278 XCloseDisplay(pDisplay);
279 }
280 else
281 {
282 VBClLogError("dcm monitor cannot open X Display\n");
283 rc = VERR_NOT_AVAILABLE;
284 }
285
286 VBClLogVerbose(1, "vbcl_hlp_generic_display_change_event_monitor_worker ended\n");
287
288 return rc;
289}
290
291static void vbcl_hlp_generic_start_display_change_monitor()
292{
293 int rc;
294
295 rc = RTXrandrLoadLib();
296 if (RT_SUCCESS(rc))
297 {
298 /* Start thread which will monitor display change events. */
299 rc = RTThreadCreate(&g_vbclHlpGenericDcmThread, vbcl_hlp_generic_display_change_event_monitor_worker, (void *)NULL, 0,
300 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, VBCL_HLP_DCM_THREAD_NAME);
301 if (RT_SUCCESS(rc))
302 {
303 rc = RTThreadUserWait(g_vbclHlpGenericDcmThread, RT_MS_5SEC);
304 }
305 else
306 g_vbclHlpGenericDcmThread = NIL_RTTHREAD;
307
308 VBClLogInfo("attempt to start display change monitor thread, rc=%Rrc\n", rc);
309
310 }
311 else
312 VBClLogInfo("libXrandr not available, will not monitor display change events, rc=%Rrc\n", rc);
313}
314
315/**
316 * @interface_method_impl{VBCLDISPLAYHELPER,pfnSetPrimaryDisplay}
317 */
318static DECLCALLBACK(int) vbcl_hlp_generic_set_primary_display(uint32_t idDisplay)
319{
320 XRRScreenResources *pScreenResources;
321 Display *pDisplay;
322
323 int rc = VERR_INVALID_PARAMETER;
324
325 pDisplay = XOpenDisplay(NULL);
326 if (pDisplay)
327 {
328 pScreenResources = XRRGetScreenResources(pDisplay, DefaultRootWindow(pDisplay));
329 if (pScreenResources)
330 {
331 if ((int)idDisplay < pScreenResources->noutput)
332 {
333 XRRSetOutputPrimary(pDisplay, DefaultRootWindow(pDisplay), pScreenResources->outputs[idDisplay]);
334 VBClLogInfo("display %u has been set as primary\n", idDisplay);
335 rc = VINF_SUCCESS;
336 }
337 else
338 VBClLogError("cannot set display %u as primary: index out of range\n", idDisplay);
339
340 XRRFreeScreenResources(pScreenResources);
341 }
342 else
343 VBClLogError("cannot set display %u as primary: libXrandr can not get screen resources\n", idDisplay);
344
345 XCloseDisplay(pDisplay);
346 }
347 else
348 VBClLogError("cannot set display %u as primary: cannot connect to X11\n", idDisplay);
349
350 return rc;
351}
352
353/**
354 * @interface_method_impl{VBCLDISPLAYHELPER,pfnProbe}
355 */
356static DECLCALLBACK(int) vbcl_hlp_generic_probe(void)
357{
358 /* Generic helper always supposed to return positive status on probe(). This
359 * helper is a fallback one in case all the other helpers were failed to detect
360 * their environments. */
361 return VINF_SUCCESS;
362}
363
364RTDECL(int) vbcl_hlp_generic_init(void)
365{
366 ASMAtomicWriteBool(&g_fShutdown, false);
367
368 /* Attempt to start display change events monitor. */
369 vbcl_hlp_generic_start_display_change_monitor();
370
371 /* Always return positive status for generic (fallback, last resort) helper. */
372 return VINF_SUCCESS;
373}
374
375RTDECL(int) vbcl_hlp_generic_term(void)
376{
377 int rc = VINF_SUCCESS;
378
379 if (g_vbclHlpGenericDcmThread != NIL_RTTHREAD)
380 {
381 /* Signal thread we are going to shutdown. */
382 ASMAtomicWriteBool(&g_fShutdown, true);
383
384 /* Wait for thread to terminate gracefully. */
385 rc = RTThreadWait(g_vbclHlpGenericDcmThread, RT_MS_5SEC, NULL);
386 }
387
388 return rc;
389}
390
391RTDECL(void) vbcl_hlp_generic_subscribe_display_offset_changed(FNDISPLAYOFFSETCHANGE *pfnCb)
392{
393 g_pfnDisplayOffsetChangeCb = pfnCb;
394}
395
396RTDECL(void) vbcl_hlp_generic_unsubscribe_display_offset_changed(void)
397{
398 g_pfnDisplayOffsetChangeCb = NULL;
399}
400
401/* Helper callbacks. */
402const VBCLDISPLAYHELPER g_DisplayHelperGeneric =
403{
404 "GENERIC", /* .pszName */
405 vbcl_hlp_generic_probe, /* .pfnProbe */
406 vbcl_hlp_generic_init, /* .pfnInit */
407 vbcl_hlp_generic_term, /* .pfnTerm */
408 vbcl_hlp_generic_set_primary_display, /* .pfnSetPrimaryDisplay */
409 vbcl_hlp_generic_subscribe_display_offset_changed, /* .pfnSubscribeDisplayOffsetChangeNotification */
410 vbcl_hlp_generic_unsubscribe_display_offset_changed, /* .pfnUnsubscribeDisplayOffsetChangeNotification */
411};
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