summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Ropé <jrope@redhat.com>2020-10-12 10:07:42 +0200
committerJulien Ropé <jrope@redhat.com>2020-11-19 15:40:02 +0100
commit73bf8367268e7ef5a00fd23674b0a8700d0e4a85 (patch)
treef9826f3392e990145060504fd931d10176a8b54e
parent21d355974f1be994d4b81933db0a7cb93433c1cf (diff)
Add implementation of the DBUS interface to Mutter.
This can be used to retrieve monitor resolutions directly from the Mutter compositor under Wayland. Under environments using a different compositor, the X11 lib will keep being used. Signed-off-by: Julien Ropé <jrope@redhat.com> Acked-by: Jakub Janků <jjanku@redhat.com>
-rw-r--r--Makefile.am2
-rw-r--r--src/vdagent/mutter.c276
-rw-r--r--src/vdagent/mutter.h33
3 files changed, 311 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 47d7820..e8fa4a6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,6 +49,8 @@ src_spice_vdagent_SOURCES = \
src/vdagent/display.h \
src/vdagent/file-xfers.c \
src/vdagent/file-xfers.h \
+ src/vdagent/mutter.c \
+ src/vdagent/mutter.h \
src/vdagent/x11-priv.h \
src/vdagent/x11-randr.c \
src/vdagent/x11.c \
diff --git a/src/vdagent/mutter.c b/src/vdagent/mutter.c
new file mode 100644
index 0000000..f6ff11b
--- /dev/null
+++ b/src/vdagent/mutter.c
@@ -0,0 +1,276 @@
+/* mutter.c - implements the DBUS interface to mutter
+
+ Copyright 2020 Red Hat, Inc.
+
+ Red Hat Authors:
+ Julien Ropé <jrope@redhat.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <syslog.h>
+
+#include "vdagentd-proto.h"
+#include "mutter.h"
+
+// MUTTER DBUS FORMAT STRINGS
+#define MODE_BASE_FORMAT "siiddad"
+#define MODE_FORMAT "(" MODE_BASE_FORMAT "a{sv})"
+#define MODES_FORMAT "a" MODE_FORMAT
+#define MONITOR_SPEC_FORMAT "(ssss)"
+#define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})"
+#define MONITORS_FORMAT "a" MONITOR_FORMAT
+
+#define LOGICAL_MONITOR_MONITORS_FORMAT "a" MONITOR_SPEC_FORMAT
+#define LOGICAL_MONITOR_FORMAT "(iidub" LOGICAL_MONITOR_MONITORS_FORMAT "a{sv})"
+#define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT
+
+#define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "a{sv})"
+
+
+struct VDAgentMutterDBus {
+ GDBusProxy *dbus_proxy;
+ GHashTable *connector_mapping;
+};
+
+/**
+ * Initialise a communication to Mutter through its DBUS interface.
+ *
+ * Errors can indicate that another compositor is used. This is not a blocker, and we should default
+ * to use a different API then.
+ *
+ * Returns:
+ * An initialise VDAgentMutterDBus structure if successful.
+ * NULL if an error occured.
+ */
+VDAgentMutterDBus *vdagent_mutter_create(GHashTable *connector_mapping)
+{
+ GError *error = NULL;
+ VDAgentMutterDBus *mutter = g_new0(VDAgentMutterDBus, 1);
+
+ mutter->connector_mapping = g_hash_table_ref(connector_mapping);
+
+ GDBusProxyFlags flags = (G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START
+ | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+ | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS);
+
+ mutter->dbus_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
+ flags,
+ NULL,
+ "org.gnome.Mutter.DisplayConfig",
+ "/org/gnome/Mutter/DisplayConfig",
+ "org.gnome.Mutter.DisplayConfig",
+ NULL,
+ &error);
+ if (!mutter->dbus_proxy) {
+ syslog(LOG_WARNING, "display: failed to create dbus proxy: %s", error->message);
+ g_clear_error(&error);
+ vdagent_mutter_destroy(mutter);
+ return NULL;
+ }
+
+ return mutter;
+}
+
+
+void vdagent_mutter_destroy(VDAgentMutterDBus *mutter)
+{
+ g_clear_object(&mutter->dbus_proxy);
+ g_hash_table_unref(mutter->connector_mapping);
+ g_free(mutter);
+}
+
+/** Look through a list of logical monitor to find the one provided.
+ * Returns the corresponding x and y position of the monitor on the desktop.
+ * This function is a helper to vdagent_mutter_get_resolution().
+ *
+ * Parameters:
+ * logical_monitor: initialized GVariant iterator. It will be copied to look through the items
+ * so that its original position is not modified.
+ * connector: name of the connector that must be found
+ * x and y: will received the found position
+ *
+ */
+static void vdagent_mutter_get_monitor_position(GVariantIter *logical_monitors,
+ const gchar *connector, int *x, int *y)
+{
+ GVariant *logical_monitor = NULL;
+ GVariantIter *logical_monitor_iterator = g_variant_iter_copy(logical_monitors);
+ while (g_variant_iter_next(logical_monitor_iterator, "@"LOGICAL_MONITOR_FORMAT,
+ &logical_monitor)) {
+ GVariantIter *tmp_monitors = NULL;
+
+ g_variant_get_child(logical_monitor, 0, "i", x);
+ g_variant_get_child(logical_monitor, 1, "i", y);
+ g_variant_get_child(logical_monitor, 5, LOGICAL_MONITOR_MONITORS_FORMAT, &tmp_monitors);
+
+ g_variant_unref(logical_monitor);
+
+ GVariant *tmp_monitor = NULL;
+ gboolean found = FALSE;
+ while (!found && g_variant_iter_next(tmp_monitors, "@"MONITOR_SPEC_FORMAT, &tmp_monitor)) {
+ const gchar *tmp_connector;
+
+ g_variant_get_child(tmp_monitor, 0, "&s", &tmp_connector);
+
+ if (g_strcmp0(connector, tmp_connector) == 0) {
+ found = TRUE;
+ }
+ g_variant_unref(tmp_monitor);
+ }
+
+ g_variant_iter_free(tmp_monitors);
+
+ if (found) {
+ break;
+ }
+ *x = *y = 0;
+ }
+ g_variant_iter_free(logical_monitor_iterator);
+}
+
+GArray *vdagent_mutter_get_resolutions(VDAgentMutterDBus *mutter,
+ int *desktop_width, int *desktop_height, int *screen_count)
+{
+ GError *error = NULL;
+ GArray *res_array = NULL;
+
+ // keep track of monitors we find and are not mapped to SPICE displays
+ // we will map them back later (assuming display ID == monitor index)
+ // this prevents the need from looping twice on all DBUS items
+ GArray *not_found_array = NULL;
+
+ if (!mutter) {
+ return res_array;
+ }
+
+ GVariant *values = g_dbus_proxy_call_sync(mutter->dbus_proxy,
+ "GetCurrentState",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, // use proxy default timeout
+ NULL,
+ &error);
+ if (!values) {
+ syslog(LOG_WARNING, "display: failed to call GetCurrentState from mutter over DBUS");
+ if (error != NULL) {
+ syslog(LOG_WARNING, " error message: %s", error->message);
+ g_clear_error(&error);
+ }
+ return res_array;
+ }
+
+ res_array = g_array_new(FALSE, FALSE, sizeof(struct vdagentd_guest_xorg_resolution));
+ not_found_array = g_array_new(FALSE, FALSE, sizeof(struct vdagentd_guest_xorg_resolution));
+
+ GVariantIter *monitors = NULL;
+ GVariantIter *logical_monitors = NULL;
+
+ g_variant_get_child(values, 1, MONITORS_FORMAT, &monitors);
+ g_variant_get_child(values, 2, LOGICAL_MONITORS_FORMAT, &logical_monitors);
+
+ // list monitors
+ GVariant *monitor = NULL;
+ *screen_count = g_variant_iter_n_children(monitors);
+
+ while (g_variant_iter_next(monitors, "@"MONITOR_FORMAT, &monitor)) {
+
+ const gchar *connector = NULL;
+ GVariantIter *modes = NULL;
+ GVariant *monitor_specs = NULL;
+
+ g_variant_get_child(monitor, 0, "@"MONITOR_SPEC_FORMAT, &monitor_specs);
+ g_variant_get_child(monitor_specs, 0, "&s", &connector);
+ g_variant_get_child(monitor, 1, MODES_FORMAT, &modes);
+
+ g_variant_unref(monitor_specs);
+ g_variant_unref(monitor);
+
+ // list modes
+ GVariant *mode = NULL;
+ while (g_variant_iter_next(modes, "@"MODE_FORMAT, &mode)) {
+ GVariant *properties = NULL;
+ gboolean is_current;
+
+ g_variant_get_child(mode, 6, "@a{sv}", &properties);
+ if (!g_variant_lookup(properties, "is-current", "b", &is_current)) {
+ is_current = FALSE;
+ }
+ g_variant_unref(properties);
+
+ if (!is_current) {
+ g_variant_unref(mode);
+ continue;
+ }
+
+ struct vdagentd_guest_xorg_resolution curr;
+ vdagent_mutter_get_monitor_position(logical_monitors, connector, &curr.x, &curr.y);
+ g_variant_get_child(mode, 1, "i", &curr.width);
+ g_variant_get_child(mode, 2, "i", &curr.height);
+ g_variant_unref(mode);
+
+ // compute the size of the desktop based on the dimension of the monitors
+ if (curr.x + curr.width > *desktop_width) {
+ *desktop_width = curr.x + curr.width;
+ }
+ if (curr.y + curr.height > *desktop_height) {
+ *desktop_height = curr.y + curr.height;
+ }
+
+ gpointer value;
+ if (g_hash_table_lookup_extended(mutter->connector_mapping, connector, NULL, &value)) {
+ curr.display_id = GPOINTER_TO_UINT(value);
+ syslog(LOG_DEBUG,
+ "Found monitor %s with geometry %dx%d+%d-%d - associating it to SPICE display #%d",
+ connector, curr.width, curr.height, curr.x, curr.y, curr.display_id);
+ g_array_append_val(res_array, curr);
+ } else {
+ syslog(LOG_DEBUG, "No SPICE display found for connector %s", connector);
+ g_array_append_val(not_found_array, curr);
+ }
+
+ break;
+ }
+ g_variant_iter_free(modes);
+ }
+
+ g_variant_iter_free(logical_monitors);
+ g_variant_iter_free(monitors);
+
+ int i;
+
+ if (res_array->len == 0) {
+ syslog(LOG_DEBUG, "%s: No Spice display ID matching - assuming display ID == Monitor index",
+ __FUNCTION__);
+ g_array_free(res_array, TRUE);
+ res_array = not_found_array;
+
+ struct vdagentd_guest_xorg_resolution *res;
+ res = (struct vdagentd_guest_xorg_resolution*)res_array->data;
+ for (i = 0; i < res_array->len; i++) {
+ res[i].display_id = i;
+ }
+ }
+ else {
+ g_array_free(not_found_array, TRUE);
+ }
+
+ g_variant_unref(values);
+ return res_array;
+}
diff --git a/src/vdagent/mutter.h b/src/vdagent/mutter.h
new file mode 100644
index 0000000..abd2bd2
--- /dev/null
+++ b/src/vdagent/mutter.h
@@ -0,0 +1,33 @@
+/* mutter.h - implements the DBUS interface to mutter
+
+ Copyright 2020 Red Hat, Inc.
+
+ Red Hat Authors:
+ Julien Ropé <jrope@redhat.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SRC_VDAGENT_MUTTER_H_
+#define SRC_VDAGENT_MUTTER_H_
+
+typedef struct VDAgentMutterDBus VDAgentMutterDBus;
+
+VDAgentMutterDBus *vdagent_mutter_create(GHashTable *connector_mapping);
+void vdagent_mutter_destroy(VDAgentMutterDBus *mutter);
+
+GArray *vdagent_mutter_get_resolutions(VDAgentMutterDBus *mutter, int *width, int *height, int *screen_count);
+
+
+#endif /* SRC_VDAGENT_MUTTER_H_ */