From dc89a164693ede7125f2eec975d85cee89c9409b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 30 Aug 2018 01:06:51 -0400 Subject: GtkApplication: track screensaver state A number of applications want to track the state of the screensaver. Make this information available as a boolean property. We only listen for state changes when ::register-session is set to TRUE. This is implemented for unsandboxed D-Bus access by talking directly to org.gnome.ScreenSaver or org.freedesktop.ScreenSaver, and for sandboxed D-Bus by using a (new) portal API. A Quartz implementation is missing. --- gtk/gtkapplication-dbus.c | 126 ++++++++++++++++++++++++++++++++++++++++++++ gtk/gtkapplication.c | 37 +++++++++++++ gtk/gtkapplicationprivate.h | 6 +++ gtk/gtkprivate.h | 1 + 4 files changed, 170 insertions(+) diff --git a/gtk/gtkapplication-dbus.c b/gtk/gtkapplication-dbus.c index d0110a73a9..3c241ca7b0 100644 --- a/gtk/gtkapplication-dbus.c +++ b/gtk/gtkapplication-dbus.c @@ -39,6 +39,9 @@ G_DEFINE_TYPE (GtkApplicationImplDBus, gtk_application_impl_dbus, GTK_TYPE_APPLI #define XFCE_DBUS_OBJECT_PATH "/org/xfce/SessionManager" #define XFCE_DBUS_INTERFACE "org.xfce.Session.Manager" #define XFCE_DBUS_CLIENT_INTERFACE "org.xfce.Session.Client" +#define GNOME_SCREENSAVER_DBUS_NAME "org.gnome.ScreenSaver" +#define GNOME_SCREENSAVER_DBUS_OBJECT_PATH "/org/gnome/ScreenSaver" +#define GNOME_SCREENSAVER_DBUS_INTERFACE "org.gnome.ScreenSaver" static void unregister_client (GtkApplicationImplDBus *dbus) @@ -175,6 +178,63 @@ stash_desktop_autostart_id (void) g_unsetenv ("DESKTOP_AUTOSTART_ID"); } +static void +screensaver_signal_session (GDBusProxy *proxy, + const char *sender_name, + const char *signal_name, + GVariant *parameters, + GtkApplication *application) +{ + gboolean active; + + if (!g_str_equal (signal_name, "ActiveChanged")) + return; + + g_variant_get (parameters, "(b)", &active); + gtk_application_set_screensaver_active (application, active); +} + +static void +screensaver_signal_portal (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data) +{ + GtkApplication *application = data; + gboolean active; + GVariant *state; + + if (!g_str_equal (signal_name, "StateChanged")) + return; + + g_variant_get (parameters, "(o@a{sv})", NULL, &state); + g_variant_lookup (state, "screensaver-active", "b", &active); + gtk_application_set_screensaver_active (application, active); +} + +static void +create_monitor_cb (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (source); + GError *error = NULL; + GVariant *ret = NULL; + + ret = g_dbus_proxy_call_finish (proxy, result, &error); + if (ret == NULL) + { + g_warning ("Creating a portal monitor failed: %s", error->message); + g_error_free (error); + return; + } + + g_variant_unref (ret); +} + static void gtk_application_impl_dbus_startup (GtkApplicationImpl *impl, gboolean register_session) @@ -243,6 +303,27 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl, if (!register_session) goto out; + dbus->ss_proxy = gtk_application_get_proxy_if_service_present (dbus->session, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_NONE, + GNOME_SCREENSAVER_DBUS_NAME, + GNOME_SCREENSAVER_DBUS_OBJECT_PATH, + GNOME_SCREENSAVER_DBUS_INTERFACE, + &error); + if (error) + { + g_debug ("Failed to get the GNOME screensaver proxy: %s", error->message); + g_clear_error (&error); + g_clear_object (&dbus->ss_proxy); + } + + if (dbus->ss_proxy) + { + g_signal_connect (dbus->ss_proxy, "g-signal", + G_CALLBACK (screensaver_signal_session), impl->application); + } + g_debug ("Registering client '%s' '%s'", dbus->application_id, client_id); res = g_dbus_proxy_call_sync (dbus->sm_proxy, @@ -355,8 +436,43 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl, { g_debug ("Failed to get an inhibit portal proxy: %s", error->message); g_clear_error (&error); + goto end; + } + + if (register_session) + { + char *token; + GVariantBuilder opt_builder; + + /* Monitor screensaver state */ + + dbus->session_id = gtk_get_portal_session_path (dbus->session, &token); + dbus->state_changed_handler = + g_dbus_connection_signal_subscribe (dbus->session, + PORTAL_BUS_NAME, + PORTAL_INHIBIT_INTERFACE, + "StateChanged", + PORTAL_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + screensaver_signal_portal, + impl->application, + NULL); + g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&opt_builder, "{sv}", + "session_handle_token", g_variant_new_string (token)); + g_dbus_proxy_call (dbus->inhibit_proxy, + "CreateMonitor", + g_variant_new ("(sa{sv})", "", &opt_builder), + G_DBUS_CALL_FLAGS_NONE, + G_MAXINT, + NULL, + create_monitor_cb, dbus); + g_free (token); } } + +end:; } static void @@ -674,11 +790,21 @@ gtk_application_impl_dbus_finalize (GObject *object) { GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) object; + g_dbus_connection_call (dbus->session, + PORTAL_BUS_NAME, + dbus->session_id, + PORTAL_SESSION_INTERFACE, + "Close", + NULL, NULL, 0, -1, NULL, NULL, NULL); + + g_free (dbus->session_id); + g_dbus_connection_signal_unsubscribe (dbus->session, dbus->state_changed_handler); g_clear_object (&dbus->inhibit_proxy); g_slist_free_full (dbus->inhibit_handles, inhibit_handle_free); g_free (dbus->app_menu_path); g_free (dbus->menubar_path); g_clear_object (&dbus->sm_proxy); + g_clear_object (&dbus->ss_proxy); G_OBJECT_CLASS (gtk_application_impl_dbus_parent_class)->finalize (object); } diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c index aab751ce66..999ef1523e 100644 --- a/gtk/gtkapplication.c +++ b/gtk/gtkapplication.c @@ -137,6 +137,7 @@ static guint gtk_application_signals[LAST_SIGNAL]; enum { PROP_ZERO, PROP_REGISTER_SESSION, + PROP_SCREENSAVER_ACTIVE, PROP_APP_MENU, PROP_MENUBAR, PROP_ACTIVE_WINDOW, @@ -157,6 +158,7 @@ typedef struct guint last_window_id; gboolean register_session; + gboolean screensaver_active; GtkActionMuxer *muxer; GtkBuilder *menus_builder; gchar *help_overlay_path; @@ -534,6 +536,10 @@ gtk_application_get_property (GObject *object, g_value_set_boolean (value, priv->register_session); break; + case PROP_SCREENSAVER_ACTIVE: + g_value_set_boolean (value, priv->screensaver_active); + break; + case PROP_APP_MENU: g_value_set_object (value, gtk_application_get_app_menu (application)); break; @@ -661,6 +667,24 @@ gtk_application_class_init (GtkApplicationClass *class) FALSE, G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS); + /** + * GtkApplication:screensaver-active: + * + * This property is %TRUE if GTK+ believes that the screensaver is + * currently active. GTK+ only tracks session state (including this) + * when #GtkApplication::register-session is set to %TRUE. + * + * Tracking the screensaver state is supported on Linux. + * + * Since: 3.24 + */ + gtk_application_props[PROP_SCREENSAVER_ACTIVE] = + g_param_spec_boolean ("screensaver-active", + P_("Screensaver Active"), + P_("Whether the screensaver is active"), + FALSE, + G_PARAM_READABLE|G_PARAM_STATIC_STRINGS); + gtk_application_props[PROP_APP_MENU] = g_param_spec_object ("app-menu", P_("Application menu"), @@ -1370,3 +1394,16 @@ gtk_application_get_menu_by_id (GtkApplication *application, return G_MENU (object); } + +void +gtk_application_set_screensaver_active (GtkApplication *application, + gboolean active) +{ + GtkApplicationPrivate *priv = gtk_application_get_instance_private (application); + + if (priv->screensaver_active != active) + { + priv->screensaver_active = active; + g_object_notify (G_OBJECT (application), "screensaver-active"); + } +} diff --git a/gtk/gtkapplicationprivate.h b/gtk/gtkapplicationprivate.h index 74403de894..8f6cdb001d 100644 --- a/gtk/gtkapplicationprivate.h +++ b/gtk/gtkapplicationprivate.h @@ -45,6 +45,9 @@ void gtk_application_insert_action_group (GtkAppl GtkApplicationAccels * gtk_application_get_application_accels (GtkApplication *application); +void gtk_application_set_screensaver_active (GtkApplication *application, + gboolean active); + #define GTK_TYPE_APPLICATION_IMPL (gtk_application_impl_get_type ()) #define GTK_APPLICATION_IMPL_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ GTK_TYPE_APPLICATION_IMPL, \ @@ -129,10 +132,13 @@ typedef struct GDBusProxy *sm_proxy; GDBusProxy *client_proxy; gchar *client_path; + GDBusProxy *ss_proxy; /* Portal support */ GDBusProxy *inhibit_proxy; GSList *inhibit_handles; + guint state_changed_handler; + char * session_id; } GtkApplicationImplDBus; typedef struct diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h index 809c855959..58b2453930 100644 --- a/gtk/gtkprivate.h +++ b/gtk/gtkprivate.h @@ -118,6 +118,7 @@ char *gtk_get_portal_session_path (GDBusConnection *connection, #define PORTAL_BUS_NAME "org.freedesktop.portal.Desktop" #define PORTAL_OBJECT_PATH "/org/freedesktop/portal/desktop" #define PORTAL_REQUEST_INTERFACE "org.freedesktop.portal.Request" +#define PORTAL_SESSION_INTERFACE "org.freedesktop.portal.Session" #define PORTAL_FILECHOOSER_INTERFACE "org.freedesktop.portal.FileChooser" #define PORTAL_PRINT_INTERFACE "org.freedesktop.portal.Print" #define PORTAL_SCREENSHOT_INTERFACE "org.freedesktop.portal.Screenshot" -- cgit v1.2.1