We need to integrate closely with the desktop environment of the user in several cases. Some examples are disabling auto-mounting when auto-usbredir is active (rhbz#812972), and disabling the screensaver when fullscreen (fdo#34793). Unfortuntely these kinds of things require desktop environment specific handling. Therefor this patch introduces a desktop-integration helper class, which is to server as a container for all sort of desktop environment specific functions. For now it just supports disabling automounting under Gnome, but this will be extended in the future. Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> --- configure.ac | 20 ++++ gtk/Makefile.am | 4 + gtk/desktop-integration.c | 260 +++++++++++++++++++++++++++++++++++++++++++++ gtk/desktop-integration.h | 67 ++++++++++++ gtk/spice-session-priv.h | 2 + gtk/spice-session.c | 1 + 6 files changed, 354 insertions(+) create mode 100644 gtk/desktop-integration.c create mode 100644 gtk/desktop-integration.h diff --git a/configure.ac b/configure.ac index 09129b7..3841c56 100644 --- a/configure.ac +++ b/configure.ac @@ -566,6 +566,26 @@ fi AM_CONDITIONAL(WITH_PYTHON, [test "$WITH_PYTHON" = "yes"]) + +AC_ARG_ENABLE([dbus], + AS_HELP_STRING([--enable-dbus=@<:@auto/yes/no@:>@], + [Enable dbus support for desktop integration (disabling automount) @<:@default=auto@:>@]), + [], + [enable_dbus="auto"]) + +if test "x$enable_dbus" != "xno"; then + PKG_CHECK_MODULES([DBUS_GLIB], [dbus-glib-1], + [have_dbus=yes], + [have_dbus=no]) + if test "x$enable_dbus" = "xyes" && test "x$have_dbus" = "xno"; then + AC_MSG_ERROR([D-Bus support explicitly requested, but some required packages are not available]) + fi + + if test "x$have_dbus" = "xyes"; then + AC_DEFINE(USE_DBUS, [1], [Define if supporting dbus]) + fi +fi + dnl =========================================================================== dnl check compiler flags diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 0327d65..f5f6bc6 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -77,6 +77,7 @@ SPICE_COMMON_CPPFLAGS = \ $(GLIB2_CFLAGS) \ $(GIO_CFLAGS) \ $(GOBJECT2_CFLAGS) \ + $(DBUS_GLIB_CFLAGS) \ $(SSL_CFLAGS) \ $(SASL_CFLAGS) \ $(GST_CFLAGS) \ @@ -100,6 +101,7 @@ SPICE_GTK_LIBADD_COMMON = \ libspice-client-glib-2.0.la \ $(GTK_LIBS) \ $(CAIRO_LIBS) \ + $(DBUS_GLIB_LIBS) \ $(XRANDR_LIBS) \ $(LIBM) \ $(NULL) @@ -114,6 +116,8 @@ SPICE_GTK_SOURCES_COMMON = \ vncdisplaykeymap.h \ spice-grabsequence.c \ spice-grabsequence.h \ + desktop-integration.c \ + desktop-integration.h \ usb-device-widget.c \ $(NULL) diff --git a/gtk/desktop-integration.c b/gtk/desktop-integration.c new file mode 100644 index 0000000..8c5ac75 --- /dev/null +++ b/gtk/desktop-integration.c @@ -0,0 +1,260 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2012 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede <hdegoede@xxxxxxxxxx> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" +#include <glib-object.h> +#include <glib/gi18n.h> +#ifdef USE_DBUS +#include <dbus/dbus-glib.h> +#endif +#include "glib-compat.h" +#include "spice-session-priv.h" +#include "desktop-integration.h" + +#define GNOME_SESSION_INHIBIT_AUTOMOUNT 16 + +/* ------------------------------------------------------------------ */ +/* gobject glue */ + +#define SPICE_DESKTOP_INTEGRATION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), SPICE_TYPE_DESKTOP_INTEGRATION, SpiceDesktopIntegrationPrivate)) + +struct _SpiceDesktopIntegrationPrivate { +#ifdef USE_DBUS + DBusGConnection *dbus_conn; + DBusGProxy *gnome_session_proxy; + GData *gnome_automount_inhibit_cookie_list; +#endif +}; + +G_DEFINE_TYPE(SpiceDesktopIntegration, spice_desktop_integration, G_TYPE_OBJECT); + +/* ------------------------------------------------------------------ */ +/* Gnome specific code */ + +#ifdef USE_DBUS + +static void handle_dbus_call_error(const char *call, GError **_error) +{ + GError *error = *_error; + const char *message = error->message; + + if (error->domain == DBUS_GERROR && + error->code == DBUS_GERROR_REMOTE_EXCEPTION) + message = dbus_g_error_get_name(error); + g_debug("Error calling '%s': %s", call, message); + g_clear_error(_error); +} + +static void gnome_integration_init(SpiceDesktopIntegration *self) +{ + SpiceDesktopIntegrationPrivate *priv = self->priv; + GError *error = NULL; + + g_datalist_init(&priv->gnome_automount_inhibit_cookie_list); + + if (!priv->dbus_conn) + return; + + /* We use for_name_owner, to resolve the name now, as we may not be + running under gnome-session manager at all! */ + priv->gnome_session_proxy = dbus_g_proxy_new_for_name_owner( + priv->dbus_conn, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + &error); + if (error) { + g_debug("Could not create org.gnome.SessionManager dbus proxy: %s", + error->message); + g_clear_error(&error); + } +} + +static void gnome_integration_inhibit_automount(SpiceDesktopIntegration *self, + guint toplevel_window_id) +{ + SpiceDesktopIntegrationPrivate *priv = self->priv; + GError *error = NULL; + guint cookie; + + if (!priv->gnome_session_proxy) + return; + + cookie = GPOINTER_TO_UINT(g_datalist_id_get_data( + &priv->gnome_automount_inhibit_cookie_list, + toplevel_window_id)); + /* If a cookie exists for the toplevel we are already inhibitting */ + if (cookie != 0) + return; + + if (!dbus_g_proxy_call( + priv->gnome_session_proxy, "Inhibit", &error, + G_TYPE_STRING, "spice-gtk-app", + G_TYPE_UINT, toplevel_window_id, + G_TYPE_STRING, + _("Automounting has been inhibited for USB auto-redirecting"), + G_TYPE_UINT, GNOME_SESSION_INHIBIT_AUTOMOUNT, + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID)) { + handle_dbus_call_error("org.gnome.SessionManager.Inhibit", &error); + return; + } + + g_datalist_id_set_data(&priv->gnome_automount_inhibit_cookie_list, + toplevel_window_id, GUINT_TO_POINTER(cookie)); +} + +static void gnome_integration_do_uninhibit_automount(GQuark id, + gpointer data, + gpointer user_data) +{ + SpiceDesktopIntegration *self = SPICE_DESKTOP_INTEGRATION(user_data); + SpiceDesktopIntegrationPrivate *priv = self->priv; + guint cookie = GPOINTER_TO_UINT(data); + GError *error = NULL; + + if (!dbus_g_proxy_call( + priv->gnome_session_proxy, "Uninhibit", &error, + G_TYPE_UINT, cookie, + G_TYPE_INVALID, + G_TYPE_INVALID)) + handle_dbus_call_error("org.gnome.SessionManager.Uninhibit", &error); +} + +static void gnome_integration_uninhibit_automount( + SpiceDesktopIntegration *self, + guint toplevel_window_id) +{ + SpiceDesktopIntegrationPrivate *priv = self->priv; + gpointer cookie; + + if (!priv->gnome_session_proxy) + return; + + cookie = g_datalist_id_get_data(&priv->gnome_automount_inhibit_cookie_list, + toplevel_window_id); + + /* If there is no cookie we are not inhibitting automount */ + if (cookie == 0) + return; + + gnome_integration_do_uninhibit_automount(toplevel_window_id, cookie, self); + + g_datalist_id_remove_data(&priv->gnome_automount_inhibit_cookie_list, + toplevel_window_id); +} + +static void gnome_integration_dispose(SpiceDesktopIntegration *self) +{ + SpiceDesktopIntegrationPrivate *priv = self->priv; + + g_datalist_foreach(&priv->gnome_automount_inhibit_cookie_list, + gnome_integration_do_uninhibit_automount, self); + g_datalist_clear(&priv->gnome_automount_inhibit_cookie_list); + g_clear_object(&priv->gnome_session_proxy); +} + +#endif + +/* ------------------------------------------------------------------ */ +/* gobject glue */ + +static void spice_desktop_integration_init(SpiceDesktopIntegration *self) +{ + SpiceDesktopIntegrationPrivate *priv; +#ifdef USE_DBUS + GError *error = NULL; +#endif + + priv = SPICE_DESKTOP_INTEGRATION_GET_PRIVATE(self); + self->priv = priv; + +#ifdef USE_DBUS + priv->dbus_conn = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (!priv->dbus_conn) { + g_warning("Error connecting to session dbus: %s", error->message); + g_clear_error(&error); + } + gnome_integration_init(self); +#endif +} + +static void spice_desktop_integration_dispose(GObject *gobject) +{ + SpiceDesktopIntegration *self = SPICE_DESKTOP_INTEGRATION(gobject); + +#ifdef USE_DBUS + gnome_integration_dispose(self); +#endif + + /* Chain up to the parent class */ + if (G_OBJECT_CLASS(spice_desktop_integration_parent_class)->dispose) + G_OBJECT_CLASS(spice_desktop_integration_parent_class)->dispose(gobject); +} + +static void spice_desktop_integration_class_init(SpiceDesktopIntegrationClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = spice_desktop_integration_dispose; + + g_type_class_add_private(klass, sizeof(SpiceDesktopIntegrationPrivate)); +} + +/* ------------------------------------------------------------------ */ +/* public methods */ + +SpiceDesktopIntegration *spice_desktop_integration_get(SpiceSession *session) +{ + SpiceDesktopIntegration *self; + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + + g_return_val_if_fail(session != NULL, NULL); + + g_static_mutex_lock(&mutex); + self = session->priv->desktop_integration; + if (self == NULL) { + self = g_object_new(SPICE_TYPE_DESKTOP_INTEGRATION, NULL); + session->priv->desktop_integration = self; + } + g_static_mutex_unlock(&mutex); + + return self; +} + +void spice_desktop_integration_inhibit_automount(SpiceDesktopIntegration *self, + guint toplevel_window_id) +{ +#ifdef USE_DBUS + gnome_integration_inhibit_automount(self, toplevel_window_id); +#endif +} + +void spice_desktop_integration_uninhibit_automount( + SpiceDesktopIntegration *self, + guint toplevel_window_id) +{ +#ifdef USE_DBUS + gnome_integration_uninhibit_automount(self, toplevel_window_id); +#endif +} diff --git a/gtk/desktop-integration.h b/gtk/desktop-integration.h new file mode 100644 index 0000000..7191483 --- /dev/null +++ b/gtk/desktop-integration.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2012 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede <hdegoede@xxxxxxxxxx> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef __SPICE_DESKTOP_INTEGRATION_H__ +#define __SPICE_DESKTOP_INTEGRATION_H__ + +#include "spice-client.h" + +G_BEGIN_DECLS + +#define SPICE_TYPE_DESKTOP_INTEGRATION (spice_desktop_integration_get_type ()) +#define SPICE_DESKTOP_INTEGRATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_DESKTOP_INTEGRATION, SpiceDesktopIntegration)) +#define SPICE_DESKTOP_INTEGRATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_DESKTOP_INTEGRATION, SpiceDesktopIntegrationClass)) +#define SPICE_IS_DESKTOP_INTEGRATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_DESKTOP_INTEGRATION)) +#define SPICE_IS_DESKTOP_INTEGRATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_DESKTOP_INTEGRATION)) +#define SPICE_DESKTOP_INTEGRATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_DESKTOP_INTEGRATION, SpiceDesktopIntegrationClass)) + +typedef struct _SpiceDesktopIntegration SpiceDesktopIntegration; +typedef struct _SpiceDesktopIntegrationClass SpiceDesktopIntegrationClass; +typedef struct _SpiceDesktopIntegrationPrivate SpiceDesktopIntegrationPrivate; + +/* + * SpiceDesktopIntegration offers helper-functions to do desktop environment + * and/or platform specific tasks like disabling automount, disabling the + * screen-saver, etc. SpiceDesktopIntegration is for internal spice-gtk usage + * only! + */ +struct _SpiceDesktopIntegration +{ + GObject parent; + + SpiceDesktopIntegrationPrivate *priv; +}; + +struct _SpiceDesktopIntegrationClass +{ + GObjectClass parent_class; +}; + +GType spice_desktop_integration_get_type(void); +SpiceDesktopIntegration *spice_desktop_integration_get(SpiceSession *session); +void spice_desktop_integration_inhibit_automount(SpiceDesktopIntegration *self, + guint toplevel_window_id); +void spice_desktop_integration_uninhibit_automount( + SpiceDesktopIntegration *self, + guint toplevel_window_id); + +G_END_DECLS + +#endif /* __SPICE_DESKTOP_INTEGRATION_H__ */ diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h index 5cef264..c24ef8e 100644 --- a/gtk/spice-session-priv.h +++ b/gtk/spice-session-priv.h @@ -20,6 +20,7 @@ #include <glib.h> #include <gio/gio.h> +#include "desktop-integration.h" #include "spice-session.h" #include "spice-gtk-session.h" #include "spice-channel-cache.h" @@ -97,6 +98,7 @@ struct _SpiceSessionPrivate { /* associated objects */ SpiceAudio *audio_manager; + SpiceDesktopIntegration *desktop_integration; SpiceGtkSession *gtk_session; SpiceUsbDeviceManager *usb_manager; }; diff --git a/gtk/spice-session.c b/gtk/spice-session.c index fface67..995b2ed 100644 --- a/gtk/spice-session.c +++ b/gtk/spice-session.c @@ -155,6 +155,7 @@ spice_session_dispose(GObject *gobject) } g_clear_object(&s->audio_manager); + g_clear_object(&s->desktop_integration); g_clear_object(&s->gtk_session); g_clear_object(&s->usb_manager); -- 1.7.10.4 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel