Fix minor spelling errors.
[vd_agent.git] / src / vdagent / mutter.c
blob70998bb5b2e7ec5b0f66883fe9dda30dcf171df2
1 /* mutter.c - implements the DBUS interface to mutter
3 Copyright 2020 Red Hat, Inc.
5 Red Hat Authors:
6 Julien Ropé <jrope@redhat.com>
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <config.h>
24 #include <glib.h>
25 #include <gio/gio.h>
27 #include <syslog.h>
29 #include "vdagentd-proto.h"
30 #include "mutter.h"
32 // MUTTER DBUS FORMAT STRINGS
33 #define MODE_BASE_FORMAT "siiddad"
34 #define MODE_FORMAT "(" MODE_BASE_FORMAT "a{sv})"
35 #define MODES_FORMAT "a" MODE_FORMAT
36 #define MONITOR_SPEC_FORMAT "(ssss)"
37 #define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})"
38 #define MONITORS_FORMAT "a" MONITOR_FORMAT
40 #define LOGICAL_MONITOR_MONITORS_FORMAT "a" MONITOR_SPEC_FORMAT
41 #define LOGICAL_MONITOR_FORMAT "(iidub" LOGICAL_MONITOR_MONITORS_FORMAT "a{sv})"
42 #define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT
44 #define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "a{sv})"
47 struct VDAgentMutterDBus {
48 GDBusProxy *dbus_proxy;
49 GHashTable *connector_mapping;
52 /**
53 * Initialise a communication to Mutter through its DBUS interface.
55 * Errors can indicate that another compositor is used. This is not a blocker, and we should default
56 * to use a different API then.
58 * Returns:
59 * An initialise VDAgentMutterDBus structure if successful.
60 * NULL if an error occurred.
62 VDAgentMutterDBus *vdagent_mutter_create(GHashTable *connector_mapping)
64 GError *error = NULL;
65 VDAgentMutterDBus *mutter = g_new0(VDAgentMutterDBus, 1);
67 mutter->connector_mapping = g_hash_table_ref(connector_mapping);
69 GDBusProxyFlags flags = (G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START
70 | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
71 | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS);
73 mutter->dbus_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
74 flags,
75 NULL,
76 "org.gnome.Mutter.DisplayConfig",
77 "/org/gnome/Mutter/DisplayConfig",
78 "org.gnome.Mutter.DisplayConfig",
79 NULL,
80 &error);
81 if (!mutter->dbus_proxy) {
82 syslog(LOG_WARNING, "display: failed to create dbus proxy: %s", error->message);
83 g_clear_error(&error);
84 vdagent_mutter_destroy(mutter);
85 return NULL;
88 return mutter;
92 void vdagent_mutter_destroy(VDAgentMutterDBus *mutter)
94 g_clear_object(&mutter->dbus_proxy);
95 g_hash_table_unref(mutter->connector_mapping);
96 g_free(mutter);
99 /** Look through a list of logical monitor to find the one provided.
100 * Returns the corresponding x and y position of the monitor on the desktop.
101 * This function is a helper to vdagent_mutter_get_resolution().
103 * Parameters:
104 * logical_monitor: initialized GVariant iterator. It will be copied to look through the items
105 * so that its original position is not modified.
106 * connector: name of the connector that must be found
107 * x and y: will received the found position
110 static void vdagent_mutter_get_monitor_position(GVariantIter *logical_monitors,
111 const gchar *connector, int *x, int *y)
113 GVariant *logical_monitor = NULL;
114 GVariantIter *logical_monitor_iterator = g_variant_iter_copy(logical_monitors);
115 while (g_variant_iter_next(logical_monitor_iterator, "@"LOGICAL_MONITOR_FORMAT,
116 &logical_monitor)) {
117 GVariantIter *tmp_monitors = NULL;
119 g_variant_get_child(logical_monitor, 0, "i", x);
120 g_variant_get_child(logical_monitor, 1, "i", y);
121 g_variant_get_child(logical_monitor, 5, LOGICAL_MONITOR_MONITORS_FORMAT, &tmp_monitors);
123 g_variant_unref(logical_monitor);
125 GVariant *tmp_monitor = NULL;
126 gboolean found = FALSE;
127 while (!found && g_variant_iter_next(tmp_monitors, "@"MONITOR_SPEC_FORMAT, &tmp_monitor)) {
128 const gchar *tmp_connector;
130 g_variant_get_child(tmp_monitor, 0, "&s", &tmp_connector);
132 if (g_strcmp0(connector, tmp_connector) == 0) {
133 found = TRUE;
135 g_variant_unref(tmp_monitor);
138 g_variant_iter_free(tmp_monitors);
140 if (found) {
141 break;
143 *x = *y = 0;
145 g_variant_iter_free(logical_monitor_iterator);
148 GArray *vdagent_mutter_get_resolutions(VDAgentMutterDBus *mutter,
149 int *desktop_width, int *desktop_height, int *screen_count)
151 GError *error = NULL;
152 GArray *res_array = NULL;
154 // keep track of monitors we find and are not mapped to SPICE displays
155 // we will map them back later (assuming display ID == monitor index)
156 // this prevents the need from looping twice on all DBUS items
157 GArray *not_found_array = NULL;
159 if (!mutter) {
160 return res_array;
163 GVariant *values = g_dbus_proxy_call_sync(mutter->dbus_proxy,
164 "GetCurrentState",
165 NULL,
166 G_DBUS_CALL_FLAGS_NONE,
167 -1, // use proxy default timeout
168 NULL,
169 &error);
170 if (!values) {
171 syslog(LOG_WARNING, "display: failed to call GetCurrentState from mutter over DBUS");
172 if (error != NULL) {
173 syslog(LOG_WARNING, " error message: %s", error->message);
174 g_clear_error(&error);
176 return res_array;
179 res_array = g_array_new(FALSE, FALSE, sizeof(struct vdagentd_guest_xorg_resolution));
180 not_found_array = g_array_new(FALSE, FALSE, sizeof(struct vdagentd_guest_xorg_resolution));
182 GVariantIter *monitors = NULL;
183 GVariantIter *logical_monitors = NULL;
185 g_variant_get_child(values, 1, MONITORS_FORMAT, &monitors);
186 g_variant_get_child(values, 2, LOGICAL_MONITORS_FORMAT, &logical_monitors);
188 // list monitors
189 GVariant *monitor = NULL;
190 *screen_count = g_variant_iter_n_children(monitors);
192 while (g_variant_iter_next(monitors, "@"MONITOR_FORMAT, &monitor)) {
194 const gchar *connector = NULL;
195 GVariantIter *modes = NULL;
196 GVariant *monitor_specs = NULL;
198 g_variant_get_child(monitor, 0, "@"MONITOR_SPEC_FORMAT, &monitor_specs);
199 g_variant_get_child(monitor_specs, 0, "&s", &connector);
200 g_variant_get_child(monitor, 1, MODES_FORMAT, &modes);
202 g_variant_unref(monitor_specs);
203 g_variant_unref(monitor);
205 // list modes
206 GVariant *mode = NULL;
207 while (g_variant_iter_next(modes, "@"MODE_FORMAT, &mode)) {
208 GVariant *properties = NULL;
209 gboolean is_current;
211 g_variant_get_child(mode, 6, "@a{sv}", &properties);
212 if (!g_variant_lookup(properties, "is-current", "b", &is_current)) {
213 is_current = FALSE;
215 g_variant_unref(properties);
217 if (!is_current) {
218 g_variant_unref(mode);
219 continue;
222 struct vdagentd_guest_xorg_resolution curr;
223 vdagent_mutter_get_monitor_position(logical_monitors, connector, &curr.x, &curr.y);
224 g_variant_get_child(mode, 1, "i", &curr.width);
225 g_variant_get_child(mode, 2, "i", &curr.height);
226 g_variant_unref(mode);
228 // compute the size of the desktop based on the dimension of the monitors
229 if (curr.x + curr.width > *desktop_width) {
230 *desktop_width = curr.x + curr.width;
232 if (curr.y + curr.height > *desktop_height) {
233 *desktop_height = curr.y + curr.height;
236 gpointer value;
237 if (g_hash_table_lookup_extended(mutter->connector_mapping, connector, NULL, &value)) {
238 curr.display_id = GPOINTER_TO_UINT(value);
239 syslog(LOG_DEBUG,
240 "Found monitor %s with geometry %dx%d+%d-%d - associating it to SPICE display #%d",
241 connector, curr.width, curr.height, curr.x, curr.y, curr.display_id);
242 g_array_append_val(res_array, curr);
243 } else {
244 syslog(LOG_DEBUG, "No SPICE display found for connector %s", connector);
245 g_array_append_val(not_found_array, curr);
248 break;
250 g_variant_iter_free(modes);
253 g_variant_iter_free(logical_monitors);
254 g_variant_iter_free(monitors);
256 int i;
258 if (res_array->len == 0) {
259 syslog(LOG_DEBUG, "%s: No Spice display ID matching - assuming display ID == Monitor index",
260 __FUNCTION__);
261 g_array_free(res_array, TRUE);
262 res_array = not_found_array;
264 struct vdagentd_guest_xorg_resolution *res;
265 res = (struct vdagentd_guest_xorg_resolution*)res_array->data;
266 for (i = 0; i < res_array->len; i++) {
267 res[i].display_id = i;
270 else {
271 g_array_free(not_found_array, TRUE);
274 g_variant_unref(values);
275 return res_array;