VirtualBox

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

最後變更 在這個檔案從93375是 93370,由 vboxsync 提交於 3 年 前

Additions: Linux: introduce helpers for Desktop Environment specific actions to be triggered from VBoxClient, bugref:10134.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 35.9 KB
 
1/* $Id: display-helper-gnome3.cpp 93370 2022-01-20 17:44:36Z vboxsync $ */
2/** @file
3 * A helper for X11/Wayland Client which performs Gnome Desktop
4 * Environment specific actions.
5 */
6
7/*
8 * Copyright (C) 2006-2022 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.alldomusa.eu.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 * This helper implements communication protocol between gnome-settings-daemon
21 * and itself using interface defined in (revision e88467f9):
22 *
23 * https://gitlab.gnome.org/GNOME/mutter/-/blob/main/src/org.gnome.Mutter.DisplayConfig.xml
24 */
25
26#include "VBoxClient.h"
27#include "display-helper.h"
28
29#include <stdio.h>
30#include <stdlib.h>
31
32#include <VBox/log.h>
33#include <VBox/VBoxGuestLib.h>
34#include <VBox/dbus.h>
35
36#include <iprt/env.h>
37#include <iprt/initterm.h>
38#include <iprt/message.h>
39#include <iprt/dir.h>
40#include <iprt/err.h>
41#include <iprt/mem.h>
42#include <iprt/string.h>
43
44/** D-bus parameters for connecting to Gnome display service. */
45#define VBOXCLIENT_HELPER_DBUS_DESTINATION "org.gnome.Mutter.DisplayConfig"
46#define VBOXCLIENT_HELPER_DBUS_PATH "/org/gnome/Mutter/DisplayConfig"
47#define VBOXCLIENT_HELPER_DBUS_IFACE "org.gnome.Mutter.DisplayConfig"
48#define VBOXCLIENT_HELPER_DBUS_GET_METHOD "GetCurrentState"
49#define VBOXCLIENT_HELPER_DBUS_APPLY_METHOD "ApplyMonitorsConfig"
50
51/** D-bus communication timeout value, milliseconds.*/
52#define VBOXCLIENT_HELPER_DBUS_TIMEOUT_MS (1 * 1000)
53
54/** gnome-settings-daemon ApplyMonitorsConfig method:
55 * 0: verify - test if configuration can be applied and do not change anything,
56 * 1: temporary - apply configuration temporary, all will be reverted after re-login,
57 * 2: persistent - apply configuration permanently (asks for user confirmation).
58 */
59#define VBOXCLIENT_APPLY_DISPLAY_CONFIG_METHOD (1)
60
61/**
62 * Helper macro which is used in order to simplify code when batch of
63 * values needed to be parsed out of D-bus. Macro prevents execution
64 * of the 'next' command if 'previous' one was failed (tracked via
65 * local variable _ret). It is required that '_ret' should be initialized
66 * to TRUE before batch started.
67 *
68 * @param _ret Local variable which is used in order to track execution flow.
69 * @param _call A function (with full arguments) which returns 'dbus_bool_t'.
70 */
71#define VBCL_HLP_GNOME3_NEXT(_ret, _call) \
72 { _ret &= _ret ? _call : _ret; if (!ret) VBClLogError(__FILE__ ":%d: check fail here!\n", __LINE__); }
73
74/**
75 * This structure describes sub-part of physical monitor state
76 * required to compose a payload for calling ApplyMonitorsConfig method. */
77struct vbcl_hlp_gnome3_physical_display_state
78{
79 /** Physical display connector name string. */
80 char *connector;
81 /** Current mode name string for physical display. */
82 char *mode;
83};
84
85/**
86 * Verify if data represented by D-bus message iteration corresponds to given data type.
87 *
88 * @return True if D-bus message iteration corresponds to given data type, False otherwise.
89 * @param iter D-bus message iteration.
90 * @param type D-bus data type.
91 */
92static dbus_bool_t
93vbcl_hlp_gnome3_verify_data_type(DBusMessageIter *iter, int type)
94{
95 if (!iter)
96 return false;
97
98 if (dbus_message_iter_get_arg_type(iter) != type)
99 return false;
100
101 return true;
102}
103
104/**
105 * Verifies D-bus iterator signature.
106 *
107 * @return True if iterator signature matches to given one.
108 * @param iter D-bus iterator to check.
109 * @param signature Expected iterator signature.
110 */
111static dbus_bool_t
112vbcl_hlp_gnome3_check_iter_signature(DBusMessageIter *iter, const char *signature)
113{
114 char *iter_signature;
115 dbus_bool_t match;
116
117 if ( !iter
118 || !signature)
119 {
120 return false;
121 }
122
123 /* In case of dbus_message_iter_get_signature() returned memory should be freed by us. */
124 iter_signature = dbus_message_iter_get_signature(iter);
125 match = (strcmp(iter_signature, signature) == 0);
126
127 if (!match)
128 VBClLogError("iter signature mismatch: '%s' vs. '%s'\n", signature, iter_signature);
129
130 if (iter_signature)
131 dbus_free(iter_signature);
132
133 return match;
134}
135
136/**
137 * Verifies D-bus message signature.
138 *
139 * @return True if message signature matches to given one.
140 * @param iter D-bus message to check.
141 * @param signature Expected message signature.
142 */
143static dbus_bool_t
144vbcl_hlp_gnome3_check_message_signature(DBusMessage *message, const char *signature)
145{
146 char *message_signature;
147 dbus_bool_t match;
148
149 if ( !message
150 || !signature)
151 {
152 return false;
153 }
154
155 /* In case of dbus_message_get_signature() returned memory need NOT be freed by us. */
156 message_signature = dbus_message_get_signature(message);
157 match = (strcmp(message_signature, signature) == 0);
158
159 if (!match)
160 VBClLogError("message signature mismatch: '%s' vs. '%s'\n", signature, message_signature);
161
162 return match;
163}
164
165/**
166 * Jump into DBUS_TYPE_ARRAY iter container and initialize sub-iterator
167 * aimed to traverse container child nodes.
168 *
169 * @return True if operation was successful, False otherwise.
170 * @param iter D-bus iter of type DBUS_TYPE_ARRAY.
171 * @param array Returned sub-iterator.
172 */
173static dbus_bool_t
174vbcl_hlp_gnome3_iter_get_array(DBusMessageIter *iter, DBusMessageIter *array)
175{
176 if (!iter || !array)
177 return false;
178
179 if (vbcl_hlp_gnome3_verify_data_type(iter, DBUS_TYPE_ARRAY))
180 {
181 dbus_message_iter_recurse(iter, array);
182 /* Move to the next iter, returned value not important. */
183 dbus_message_iter_next(iter);
184 return true;
185 }
186 else
187 {
188 VBClLogError(
189 "cannot get array: argument signature '%s' does not match to type of array\n",
190 dbus_message_iter_get_signature(iter));
191 }
192
193 return false;
194}
195
196/**
197 * Get value of D-bus iter of specified simple type (numerals, strings).
198 *
199 * @return True if operation was successful, False otherwise.
200 * @param iter D-bus iter of type simple type.
201 * @param type D-bus data type.
202 * @param value Returned value.
203 */
204static dbus_bool_t
205vbcl_hlp_gnome3_iter_get_basic(DBusMessageIter *iter, int type, void *value)
206{
207 if (!iter || !value)
208 return false;
209
210 if (vbcl_hlp_gnome3_verify_data_type(iter, type))
211 {
212 dbus_message_iter_get_basic(iter, value);
213 /* Move to the next iter, returned value not important. */
214 dbus_message_iter_next(iter);
215 return true;
216 }
217 else
218 {
219 VBClLogError(
220 "cannot get value: argument signature '%s' does not match to specified type\n",
221 dbus_message_iter_get_signature(iter));
222 }
223
224 return false;
225}
226
227/**
228 * Lookup simple value (numeral, string, bool etc) in D-bus dictionary
229 * by given key and type.
230 *
231 * @return True value is found, False otherwise.
232 * @param dict D-bus iterator which represents dictionary.
233 * @param key_match Dictionary key.
234 * @param type Type of value.
235 * @param value Returning value.
236 */
237static dbus_bool_t
238vbcl_hlp_gnome3_lookup_dict(DBusMessageIter *dict, const char *key_match, int type, void *value)
239{
240 dbus_bool_t found = false;
241
242 if (!dict || !key_match)
243 return false;
244
245 if (!vbcl_hlp_gnome3_check_iter_signature(dict, "{sv}"))
246 return false;
247
248 do
249 {
250 dbus_bool_t ret = true;
251 DBusMessageIter iter;
252 char *key = NULL;
253
254 /* Proceed to part a{ > sv < } of a{sv}. */
255 dbus_message_iter_recurse(dict, &iter);
256
257 /* Should be TRUE in order to satisfy VBCL_HLP_GNOME3_NEXT() requirements. */
258 AssertReturn(ret, false);
259
260 /* Proceed to part a{ > s < v} of a{sv}. */
261 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&iter, DBUS_TYPE_STRING, &key));
262
263 /* Check if key matches. */
264 if (strcmp(key_match, key) == 0)
265 {
266 DBusMessageIter value_iter;
267
268 /* Proceed to part a{s > v < } of a{sv}. */
269 dbus_message_iter_recurse(&iter, &value_iter);
270 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&value_iter, type, value));
271
272 /* Make sure there are no more arguments. */
273 VBCL_HLP_GNOME3_NEXT(ret, !dbus_message_iter_has_next(&value_iter));
274
275 if (ret)
276 {
277 found = true;
278 break;
279 }
280 }
281 }
282 while (dbus_message_iter_next(dict));
283
284 return found;
285}
286
287/**
288 * Go through available modes and pick up the one which has property 'is-current' set.
289 * See GetCurrentState interface documentation for more details. Returned string memory
290 * must be freed by calling function.
291 *
292 * @return Mode name as a string if found, NULL otherwise.
293 * @param modes List of monitor modes.
294 */
295static char *
296vbcl_hlp_gnome3_lookup_monitor_current_mode(DBusMessageIter *modes)
297{
298 char *szCurrentMode = NULL;
299 DBusMessageIter modes_iter;
300
301 /* De-serialization parameters for 'modes': (siiddada{sv}). */
302 char *id = NULL;
303 int32_t width = 0;
304 int32_t height = 0;
305 double refresh_rate = 0;
306 double preferred_scale = 0;
307 DBusMessageIter supported_scales;
308 DBusMessageIter properties;
309
310 if (!modes)
311 return NULL;
312
313 if(!vbcl_hlp_gnome3_check_iter_signature(modes, "(siiddada{sv})"))
314 return NULL;
315
316 do
317 {
318 static const char *key_match = "is-current";
319 dbus_bool_t default_mode_found = false;
320 dbus_bool_t ret = true;
321
322 /* Should be TRUE in order to satisfy VBCL_HLP_GNOME3_NEXT() requirements. */
323 AssertReturn(ret, NULL);
324
325 /* Proceed to part a( > siiddada{sv} < ) of a(siiddada{sv}). */
326 dbus_message_iter_recurse(modes, &modes_iter);
327 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&modes_iter, DBUS_TYPE_STRING, &id));
328 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&modes_iter, DBUS_TYPE_INT32, &width));
329 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&modes_iter, DBUS_TYPE_INT32, &height));
330 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&modes_iter, DBUS_TYPE_DOUBLE, &refresh_rate));
331 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&modes_iter, DBUS_TYPE_DOUBLE, &preferred_scale));
332 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&modes_iter, &supported_scales));
333 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&modes_iter, &properties));
334
335 ret = vbcl_hlp_gnome3_lookup_dict(&properties, key_match, DBUS_TYPE_BOOLEAN, &default_mode_found);
336 if (ret && default_mode_found)
337 {
338 szCurrentMode = strdup(id);
339 break;
340 }
341 }
342 while (dbus_message_iter_next(modes));
343
344 return szCurrentMode;
345}
346
347/**
348 * Parse physical monitors list entry. See GetCurrentState interface documentation for more details.
349 *
350 * @return True if monitors list entry has been successfully parsed, False otherwise.
351 * @param physical_monitors_in D-bus iterator representing list of physical monitors.
352 * @param connector Connector name (out).
353 * @param vendor Vendor name (out).
354 * @param product Product name (out).
355 * @param physical_monitor_serial Serial number (out).
356 * @param modes List of monitor modes (out).
357 * @param physical_monitor_properties A D-bus dictionary containing monitor properties (out).
358 */
359static dbus_bool_t
360vbcl_hlp_gnome3_parse_physical_monitor_record(
361 DBusMessageIter *physical_monitors_in,
362 char **connector,
363 char **vendor,
364 char **product,
365 char **physical_monitor_serial,
366 DBusMessageIter *modes,
367 DBusMessageIter *physical_monitor_properties)
368{
369 dbus_bool_t ret = true;
370
371 DBusMessageIter physical_monitors_in_iter;
372 DBusMessageIter physical_monitors_in_description_iter;
373
374 if ( !physical_monitors_in
375 || !connector
376 || !vendor
377 || !product
378 || !physical_monitor_serial
379 || !modes
380 || !physical_monitor_properties)
381 {
382 return false;
383 }
384
385 /* Validate signature. */
386 if (!vbcl_hlp_gnome3_check_iter_signature(physical_monitors_in, "((ssss)a(siiddada{sv})a{sv})"))
387 return false;
388
389 /* Proceed to part ( > (ssss)a(siiddada{sv})a{sv} < ) of ((ssss)a(siiddada{sv})a{sv}). */
390 dbus_message_iter_recurse(physical_monitors_in, &physical_monitors_in_iter);
391
392 /* Should be TRUE in order to satisfy VBCL_HLP_GNOME3_NEXT() requirements. */
393 AssertReturn(ret, false);
394
395 /* Proceed to part ( > (ssss) < a(siiddada{sv})a{sv}) of ((ssss)a(siiddada{sv})a{sv}). */
396 dbus_message_iter_recurse(&physical_monitors_in_iter, &physical_monitors_in_description_iter);
397 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&physical_monitors_in_description_iter, DBUS_TYPE_STRING, connector));
398 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&physical_monitors_in_description_iter, DBUS_TYPE_STRING, vendor));
399 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&physical_monitors_in_description_iter, DBUS_TYPE_STRING, product));
400 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&physical_monitors_in_description_iter, DBUS_TYPE_STRING, physical_monitor_serial));
401
402 /* Proceed to part ((ssss) > a(siiddada{sv}) < a{sv}) of ((ssss)a(siiddada{sv})a{sv}). */
403 if (ret)
404 dbus_message_iter_next(&physical_monitors_in_iter);
405
406 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&physical_monitors_in_iter, modes));
407
408 /* Proceed to part ((ssss)a(siiddada{sv}) > a{sv} < ) of ((ssss)a(siiddada{sv})a{sv}). */
409 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&physical_monitors_in_iter, physical_monitor_properties));
410
411 /* Make sure there are no more arguments. */
412 VBCL_HLP_GNOME3_NEXT(ret, !dbus_message_iter_has_next(&physical_monitors_in_iter));
413
414 return ret;
415}
416
417/**
418 * Parse logical monitors list entry. See GetCurrentState interface documentation for more details.
419 *
420 * @return True if monitors list entry has been successfully parsed, False otherwise.
421 * @param logical_monitors_in D-bus iterator representing list of logical monitors.
422 * @param x Monitor X position (out).
423 * @param y Monitor Y position (out).
424 * @param scale Monitor scale factor (out).
425 * @param transform Current monitor transform (rotation) (out).
426 * @param primary A flag which indicates if monitor is set as primary (out).
427 * @param monitors List of physical monitors which are displaying this logical monitor (out).
428 * @param properties List of monitor properties (out).
429 */
430static dbus_bool_t
431vbcl_hlp_gnome3_parse_logical_monitor_record(
432 DBusMessageIter *logical_monitors_in,
433 int32_t *x,
434 int32_t *y,
435 double *scale,
436 uint32_t *transform,
437 dbus_bool_t *primary,
438 DBusMessageIter *monitors,
439 DBusMessageIter *properties)
440{
441 dbus_bool_t ret = true;
442
443 /* Iter used to traverse logical monitor parameters: @a(iiduba(ssss)a{sv}). */
444 DBusMessageIter logical_monitors_in_iter;
445
446 if ( !logical_monitors_in
447 || !x
448 || !y
449 || !scale
450 || !transform
451 || !primary
452 || !monitors
453 || !properties)
454
455 /* Should be TRUE in order to satisfy VBCL_HLP_GNOME3_NEXT() requirements. */
456 AssertReturn(ret, false);
457
458 /* Proceed to part @a( > iiduba(ssss)a{sv} < ) of @a(iiduba(ssss)a{sv}). */
459 dbus_message_iter_recurse(logical_monitors_in, &logical_monitors_in_iter);
460 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&logical_monitors_in_iter, DBUS_TYPE_INT32, x));
461 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&logical_monitors_in_iter, DBUS_TYPE_INT32, y));
462 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&logical_monitors_in_iter, DBUS_TYPE_DOUBLE, scale));
463 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&logical_monitors_in_iter, DBUS_TYPE_UINT32, transform));
464 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&logical_monitors_in_iter, DBUS_TYPE_BOOLEAN, primary));
465 /* Proceed to part @a(iidub > a(ssss) < a{sv}) of @a(iiduba(ssss)a{sv}). */
466 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&logical_monitors_in_iter, monitors));
467 /* Proceed to part @a(iiduba(ssss) > a{sv} < ) of @a(iiduba(ssss)a{sv}). */
468 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&logical_monitors_in_iter, properties));
469
470 /* Make sure there are no more arguments. */
471 VBCL_HLP_GNOME3_NEXT(ret, !dbus_message_iter_has_next(&logical_monitors_in_iter));
472
473 return ret;
474}
475
476/**
477 * Get list of physical monitors parameters from D-bus iterator.
478 *
479 * Once this function was traversed 'physical_monitors_in' iterator, we are in the
480 * end of the list of physical monitors parameters. So, it is important to do it once.
481 *
482 * @return True if monitors parameters were successfully discovered, False otherwise.
483 * @param physical_monitors_in D-bus iterator representing list of physical monitors.
484 * @param state Storage to put monitors state to.
485 * @param state_records_max Size of state storage.
486 * @param cPhysicalMonitors Actual number of physical displays parsed.
487 */
488static dbus_bool_t
489vbcl_hlp_gnome3_get_physical_monitors_state(
490 DBusMessageIter *physical_monitors_in,
491 vbcl_hlp_gnome3_physical_display_state *state,
492 uint32_t state_records_max,
493 uint32_t *cPhysicalMonitors)
494{
495 dbus_bool_t ret = true;
496 uint32_t iMonitor = 0;
497
498 if ( !physical_monitors_in
499 || !state
500 || !cPhysicalMonitors)
501 {
502 return false;
503 }
504
505 /* Validate signature. */
506 if (!vbcl_hlp_gnome3_check_iter_signature(physical_monitors_in, "((ssss)a(siiddada{sv})a{sv})"))
507 return false;
508
509 /* Should be TRUE in order to satisfy VBCL_HLP_GNOME3_NEXT() requirements. */
510 AssertReturn(ret, false);
511
512 do
513 {
514 char *connector = NULL;
515 char *vendor = NULL;
516 char *product = NULL;
517 char *physical_monitor_serial = NULL;
518 DBusMessageIter modes;
519 DBusMessageIter physical_monitor_properties;
520
521 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_parse_physical_monitor_record(
522 physical_monitors_in, &connector, &vendor, &product, &physical_monitor_serial,
523 &modes, &physical_monitor_properties));
524
525 if (iMonitor < state_records_max)
526 {
527 state[iMonitor].connector = connector;
528 state[iMonitor].mode = vbcl_hlp_gnome3_lookup_monitor_current_mode(&modes);
529
530 /* Check if both parameters were discovered successfully. */
531 VBCL_HLP_GNOME3_NEXT(ret, state[iMonitor].connector && state[iMonitor].mode);
532 }
533
534 iMonitor++;
535
536 }
537 while (ret && dbus_message_iter_next(physical_monitors_in));
538
539 if (iMonitor >= state_records_max)
540 {
541 VBClLogError("physical monitors list is too big (%u)\n", iMonitor);
542 ret = false;
543 }
544
545 *cPhysicalMonitors = iMonitor;
546
547 return ret;
548}
549
550/**
551 * Release monitors state resources.
552 *
553 * @param state Array of monitor states.
554 * @param cPhysicalMonitors Number of elements in array.
555 */
556static void
557vbcl_hlp_gnome3_free_physical_monitors_state(
558 vbcl_hlp_gnome3_physical_display_state *state,
559 uint32_t cPhysicalMonitors)
560{
561 if (!state || !cPhysicalMonitors)
562 return;
563
564 for (uint32_t i = 0; i < cPhysicalMonitors; i++)
565 {
566 /* Only free() what we allocated ourselves. */
567 if (state[i].mode)
568 free(state[i].mode);
569 }
570}
571
572/**
573 * This function is responsible for gathering current display
574 * information (via its helper functions), compose a payload
575 * for ApplyMonitorsConfig method and finally send configuration
576 * change to gnome-settings-daemon over D-bus.
577 *
578 * @return IPRT status code.
579 * @param connection Handle to D-bus connection.
580 * @param serial Serial number obtained from GetCurrentState interface,
581 * needs to be passed to ApplyMonitorsConfig.
582 * @param physical_monitors_in List of physical monitors (see GetCurrentState).
583 * @param logical_monitors_in List of logical monitors (see GetCurrentState).
584 * @param idPrimaryDisplay ID (number) of display which is requested to be set as primary.
585 */
586static int
587vbcl_hlp_gnome3_convert_and_apply_display_settings(
588 DBusConnection *connection,
589 uint32_t serial,
590 DBusMessageIter *physical_monitors_in,
591 DBusMessageIter *logical_monitors_in,
592 uint32_t idPrimaryDisplay)
593{
594 int rc = VERR_INVALID_PARAMETER;
595 uint32_t iLogicalMonitor = 0;
596 uint32_t cPhysicalMonitors = 0;
597 int32_t method = VBOXCLIENT_APPLY_DISPLAY_CONFIG_METHOD;
598
599 dbus_bool_t ret = true;
600 DBusError error;
601 DBusMessage *reply = NULL;;
602 DBusMessage *message = NULL;
603 DBusMessageIter message_iter;
604 DBusMessageIter logical_monitors_out_iter;
605 DBusMessageIter properties_out_iter;
606
607 struct vbcl_hlp_gnome3_physical_display_state
608 physical_monitors_state[VBOX_DRMIPC_MONITORS_MAX];
609
610 if ( !connection
611 || !physical_monitors_in
612 || !logical_monitors_in)
613 {
614 return VERR_INVALID_PARAMETER;
615 }
616
617 /* Important for error handling code path when dbus_message_iter_abandon_container_if_open() is in place. */
618 RT_ZERO(message_iter);
619 RT_ZERO(logical_monitors_out_iter);
620 RT_ZERO(properties_out_iter);
621
622 message = dbus_message_new_method_call(
623 VBOXCLIENT_HELPER_DBUS_DESTINATION,
624 VBOXCLIENT_HELPER_DBUS_PATH,
625 VBOXCLIENT_HELPER_DBUS_IFACE,
626 VBOXCLIENT_HELPER_DBUS_APPLY_METHOD);
627 if (!message)
628 {
629 VBClLogError("unable to apply monitors config: no memory\n");
630 return VERR_NO_MEMORY;
631 }
632
633 /* Start composing payload for ApplyMonitorsConfig method: (uu@a(iiduba(ssa{sv}))@a{sv}). */
634 dbus_message_iter_init_append(message, &message_iter);
635
636 /* Should be TRUE in order to satisfy VBCL_HLP_GNOME3_NEXT() requirements. */
637 AssertReturn(ret, false);
638
639 /* Get list of physical monitors parameters. */
640 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_get_physical_monitors_state(
641 physical_monitors_in, physical_monitors_state, VBOX_DRMIPC_MONITORS_MAX, &cPhysicalMonitors));
642
643 /* ( >u< u@a(iiduba(ssa{sv}))@a{sv}) of (uu@a(iiduba(ssa{sv}))@a{sv}). */
644 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_UINT32, &serial));
645 /* (u >u< @a(iiduba(ssa{sv}))@a{sv}) of (uu@a(iiduba(ssa{sv}))@a{sv}). */
646 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_UINT32, &method));
647
648 /* Parameter "monitors" of method ApplyMonitorsConfig.
649 * Part (uu >@a(iiduba(ssa{sv}))< @a{sv}) of (uu@a(iiduba(ssa{sv}))@a{sv}). */
650 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_open_container(&message_iter, DBUS_TYPE_ARRAY, "(iiduba(ssa{sv}))", &logical_monitors_out_iter));
651
652 /* Iterate over current configuration monitors (@logical_monitors
653 * parameter of GetCurrentState interface) and compose the rest part of message. */
654 do
655 {
656 /* De-serialization parameters for @logical_monitors data (see GetCurrentState interface documentation). */
657 int32_t x = 0;
658 int32_t y = 0;
659 double scale = 0;
660 uint32_t transform = 0;
661 dbus_bool_t primary = false;
662 dbus_bool_t isPrimary = false;
663 DBusMessageIter monitors;
664 DBusMessageIter properties;
665
666 /* These iterators are used in order to compose sub-containers of the message. */
667 DBusMessageIter sub_iter0;
668 DBusMessageIter sub_iter1;
669 DBusMessageIter sub_iter3;
670 DBusMessageIter sub_iter2;
671 /* Important for error handling code path when dbus_message_iter_abandon_container_if_open() is in place. */
672 RT_ZERO(sub_iter0);
673 RT_ZERO(sub_iter1);
674 RT_ZERO(sub_iter2);
675 RT_ZERO(sub_iter3);
676
677 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_parse_logical_monitor_record(
678 logical_monitors_in, &x, &y, &scale, &transform, &primary, &monitors, &properties));
679
680 if (ret)
681 {
682 /* Whether current display supposed to be set as primary. */
683 isPrimary = (iLogicalMonitor == idPrimaryDisplay);
684
685 /* Compose part (uu@a( > iiduba(ssa{sv}) < )@a{sv}) of (uu@a(iiduba(ssa{sv}))@a{sv}). */
686 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_open_container(&logical_monitors_out_iter, DBUS_TYPE_STRUCT, NULL, &sub_iter0));
687 {
688 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&sub_iter0, DBUS_TYPE_INT32, &x));
689 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&sub_iter0, DBUS_TYPE_INT32, &y));
690 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&sub_iter0, DBUS_TYPE_DOUBLE, &scale));
691 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&sub_iter0, DBUS_TYPE_UINT32, &transform));
692 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&sub_iter0, DBUS_TYPE_BOOLEAN, &isPrimary));
693
694 /* Compose part (uu@a(iidub > a(ssa{sv}) < )@a{sv}) of (uu@a(iiduba(ssa{sv}))@a{sv}). */
695 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_open_container(&sub_iter0, DBUS_TYPE_ARRAY, "(ssa{sv})", &sub_iter1));
696 {
697 /* Compose part (uu@a(iiduba > (ssa{sv}) < )@a{sv}) of (uu@a(iiduba(ssa{sv}))@a{sv}). */
698 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_open_container(&sub_iter1, DBUS_TYPE_STRUCT, NULL, &sub_iter2));
699 {
700 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&sub_iter2, DBUS_TYPE_STRING, &physical_monitors_state[iLogicalMonitor].connector));
701 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_append_basic(&sub_iter2, DBUS_TYPE_STRING, &physical_monitors_state[iLogicalMonitor].mode));
702
703 /* Compose part (uu@a(iiduba(ss > a{sv} < ))@a{sv}) of (uu@a(iiduba(ssa{sv}))@a{sv}) (empty dictionary). */
704 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_open_container(&sub_iter2, DBUS_TYPE_ARRAY, "{sv}", &sub_iter3));
705 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_close_container(&sub_iter2, &sub_iter3));
706 }
707 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_close_container(&sub_iter1, &sub_iter2));
708 }
709 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_close_container(&sub_iter0, &sub_iter1));
710 }
711 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_close_container(&logical_monitors_out_iter, &sub_iter0));
712
713 iLogicalMonitor++;
714
715 if (!ret)
716 {
717 dbus_message_iter_abandon_container_if_open(&sub_iter2, &sub_iter3);
718 dbus_message_iter_abandon_container_if_open(&sub_iter1, &sub_iter2);
719 dbus_message_iter_abandon_container_if_open(&sub_iter0, &sub_iter1);
720 dbus_message_iter_abandon_container_if_open(&logical_monitors_out_iter, &sub_iter0);
721 }
722 }
723 else
724 {
725 break;
726 }
727
728 }
729 while (ret && dbus_message_iter_next(logical_monitors_in));
730
731 /* Finish with parameter "monitors" of method ApplyMonitorsConfig. */
732 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_close_container(&message_iter, &logical_monitors_out_iter));
733
734 /* Parameter "properties" of method ApplyMonitorsConfig (empty dict).
735 * Part (uu@a(iiduba(ssa{sv})) >@a{sv}< ) of (uu@a(iiduba(ssa{sv}))@a{sv}).*/
736 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_open_container(&message_iter, DBUS_TYPE_ARRAY, "{sv}", &properties_out_iter));
737 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_close_container(&message_iter, &properties_out_iter));
738
739 if (ret)
740 {
741 dbus_error_init(&error);
742
743 reply = dbus_connection_send_with_reply_and_block(connection, message, VBOXCLIENT_HELPER_DBUS_TIMEOUT_MS, &error);
744 if (reply)
745 {
746 VBClLogInfo("display %d has been set as primary\n", idPrimaryDisplay);
747 dbus_message_unref(reply);
748 rc = VINF_SUCCESS;
749 }
750 else
751 {
752 VBClLogError("unable to apply monitors config: %s\n",
753 dbus_error_is_set(&error) ? error.message : "unknown error");
754 dbus_error_free(&error);
755 rc = VERR_INVALID_PARAMETER;
756 }
757 }
758 else
759 {
760 VBClLogError("unable to apply monitors config: cannot compose monitors config\n");
761
762 dbus_message_iter_abandon_container_if_open(&message_iter, &logical_monitors_out_iter);
763 dbus_message_iter_abandon_container_if_open(&message_iter, &properties_out_iter);
764
765 rc = VERR_INVALID_PARAMETER;
766 }
767
768 /* Clean physical monitors state. */
769 vbcl_hlp_gnome3_free_physical_monitors_state(physical_monitors_state, cPhysicalMonitors);
770
771 dbus_message_unref(message);
772
773 return rc;
774}
775
776/**
777 * This function parses GetCurrentState interface call reply and passes it for further processing.
778 *
779 * @return IPRT status code.
780 * @param connection Handle to D-bus connection.
781 * @param idPrimaryDisplay ID (number) of display which is requested to be set as primary.
782 * @param reply Reply message of GetCurrentState call.
783 */
784static int
785vbcl_hlp_gnome3_process_current_display_layout(
786 DBusConnection *connection, uint32_t idPrimaryDisplay, DBusMessage *reply)
787{
788 static const char *expected_signature = "ua((ssss)a(siiddada{sv})a{sv})a(iiduba(ssss)a{sv})a{sv}";
789
790 dbus_bool_t ret = true;
791 DBusMessageIter iter;
792 int rc = VERR_GENERAL_FAILURE;
793
794 uint32_t serial = 0;
795 DBusMessageIter monitors;
796 DBusMessageIter logical_monitors_in;
797 DBusMessageIter properties;
798
799 if (!reply)
800 {
801 return VERR_INVALID_PARAMETER;
802 }
803
804 /* Parse VBOXCLIENT_HELPER_DBUS_GET_METHOD reply payload:
805 *
806 * (u@a((ssss)a(siiddada{sv})a{sv})@a(iiduba(ssss)a{sv})@a{sv}).
807 *
808 * Method return the following arguments: monitors, logical_monitors, properties.
809 */
810
811 /* Should be TRUE in order to satisfy VBCL_HLP_GNOME3_NEXT() requirements. */
812 AssertReturn(ret, false);
813
814 /* Important: in order to avoid libdbus asserts during parsing, its signature should be verified at first. */
815 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_check_message_signature(reply, expected_signature));
816
817 VBCL_HLP_GNOME3_NEXT(ret, dbus_message_iter_init(reply, &iter));
818 if (ret)
819 {
820 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_basic(&iter, DBUS_TYPE_UINT32, &serial));
821 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&iter, &monitors));
822 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&iter, &logical_monitors_in));
823 VBCL_HLP_GNOME3_NEXT(ret, vbcl_hlp_gnome3_iter_get_array(&iter, &properties));
824
825 /* Make sure there are no more arguments. */
826 if (ret && !dbus_message_iter_has_next(&iter))
827 {
828 rc = vbcl_hlp_gnome3_convert_and_apply_display_settings(
829 connection, serial, &monitors, &logical_monitors_in, idPrimaryDisplay);
830 }
831 else
832 {
833 VBClLogError("cannot fetch current displays configuration: incorrect number of arguments\n");
834 rc = VERR_INVALID_PARAMETER;
835 }
836 }
837 else
838 {
839 VBClLogError("cannot fetch current displays configuration: no data\n");
840 rc = VERR_INVALID_PARAMETER;
841 }
842
843 return rc;
844}
845
846/**
847 * This function establishes D-bus connection, requests gnome-settings-daemon
848 * to provide current display configuration via GetCurrentState interface call
849 * and passes this information further to helper functions in order to set
850 * requested display as primary.
851 *
852 * @return IPRT status code.
853 * @param idPrimaryDisplay A display ID which is requested to be set as primary.
854 */
855static int
856vbcl_hlp_gnome3_set_primary_display(uint32_t idPrimaryDisplay)
857{
858 int rc = VERR_GENERAL_FAILURE;
859
860 DBusConnection *connection = NULL;
861 DBusMessage *message = NULL;
862 DBusError error;
863
864 rc = RTDBusLoadLib();
865 if (RT_FAILURE(rc))
866 {
867 VBClLogError("unable to load D-bus library\n");
868 return VERR_SYMBOL_NOT_FOUND;
869 }
870
871 dbus_error_init(&error);
872 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
873 if (!dbus_error_is_set(&error))
874 {
875 message = dbus_message_new_method_call(
876 VBOXCLIENT_HELPER_DBUS_DESTINATION,
877 VBOXCLIENT_HELPER_DBUS_PATH,
878 VBOXCLIENT_HELPER_DBUS_IFACE,
879 VBOXCLIENT_HELPER_DBUS_GET_METHOD);
880
881 if (message)
882 {
883 DBusMessage *reply;
884
885 reply = dbus_connection_send_with_reply_and_block(connection, message, VBOXCLIENT_HELPER_DBUS_TIMEOUT_MS, &error);
886 if (!dbus_error_is_set(&error))
887 {
888 rc = vbcl_hlp_gnome3_process_current_display_layout(connection, idPrimaryDisplay, reply);
889 dbus_message_unref(reply);
890 }
891 else
892 {
893 VBClLogError("unable to get current display configuration: %s\n", error.message);
894 dbus_error_free(&error);
895 rc = VERR_INVALID_PARAMETER;
896 }
897
898 dbus_message_unref(message);
899 }
900 else
901 {
902 VBClLogError("unable to get current display configuration: no memory\n");
903 rc = VERR_NO_MEMORY;
904 }
905
906 dbus_connection_flush(connection);
907 }
908 else
909 {
910 VBClLogError("unable to establish dbus connection: %s\n", error.message);
911 dbus_error_free(&error);
912 rc = VERR_INVALID_HANDLE;
913 }
914
915 return rc;
916}
917
918/**
919 * @interface_method_impl{VBCLDISPLAYHELPER,pfnProbe}
920 */
921static int
922vbcl_hlp_gnome3_probe(void)
923{
924 const char *pszCurrentDesktop = RTEnvGet(VBCL_HLP_ENV_XDG_CURRENT_DESKTOP);
925
926 /* GNOME3 identifies itself by XDG_CURRENT_DESKTOP environment variable.
927 * It can slightly vary for different distributions, but we assume that this
928 * variable should at least contain sub-string 'GNOME' in its value. */
929 if (pszCurrentDesktop && RTStrStr(pszCurrentDesktop, "GNOME"))
930 return VINF_SUCCESS;
931
932 return VERR_NOT_FOUND;
933}
934
935/**
936 * Detect if user is running on Wayland by checking corresponding environment variable.
937 *
938 * @returns True if Wayland has been detected, False otherwise.
939 */
940static bool
941vbcl_hlp_gnome3_has_wayland(void)
942{
943 return RTEnvGet(VBCL_HLP_ENV_WAYLAND_DISPLAY) != NULL;
944}
945
946/**
947 * @interface_method_impl{VBCLDISPLAYHELPER,pfnInit}
948 */
949static int
950vbcl_hlp_gnome3_init(void)
951{
952 int rc;
953
954 if (!vbcl_hlp_gnome3_has_wayland())
955 {
956 rc = vbcl_hlp_generic_init();
957 VBClLogInfo("attempt to start generic helper routines, rc=%Rrc\n", rc);
958 }
959
960 return VINF_SUCCESS;
961}
962
963/**
964 * @interface_method_impl{VBCLDISPLAYHELPER,pfnTerm}
965 */
966static int
967vbcl_hlp_gnome3_term(void)
968{
969 int rc;
970
971 if (!vbcl_hlp_gnome3_has_wayland())
972 {
973 rc = vbcl_hlp_generic_term();
974 VBClLogInfo("attempt to stop generic helper routines, rc=%Rrc\n", rc);
975 }
976
977 return VINF_SUCCESS;
978}
979
980/* Helper callbacks. */
981const VBCLDISPLAYHELPER g_DisplayHelperGnome3 =
982{
983 "GNOME3", /* .pszName */
984 vbcl_hlp_gnome3_probe, /* .pfnProbe */
985 vbcl_hlp_gnome3_init, /* .pfnInit */
986 vbcl_hlp_gnome3_term, /* .pfnTerm */
987 vbcl_hlp_gnome3_set_primary_display, /* .pfnSetPrimaryDisplay */
988 vbcl_hlp_generic_subscribe_display_offset_changed, /* .pfnSubscribeDisplayOffsetChangeNotification */
989 vbcl_hlp_generic_unsubscribe_display_offset_changed, /* .pfnUnsubscribeDisplayOffsetChangeNotification */
990};
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette