From 74a4432688bd33a203849c696a37d40bf0fd9aaf Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 14 Jul 2020 21:18:03 +0200 Subject: [PATCH 1/2] gdk/wayland: Add an API to inhibit and uninhibit idle This uses the idle-inhibit protocol from wayland-protocols, to attach an inhibitor to the GdkSurface. The inhibit function can be called as many times as the user wants, but the uninhibit function MUST be called as many times to unset the idle inhibition. This has been tested on Sway. --- gdk/wayland/gdkdisplay-wayland.c | 6 ++++++ gdk/wayland/gdkdisplay-wayland.h | 2 ++ gdk/wayland/gdksurface-wayland.c | 36 ++++++++++++++++++++++++++++++++ gdk/wayland/gdkwaylandsurface.h | 3 +++ gdk/wayland/meson.build | 1 + 5 files changed, 48 insertions(+) diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index af30f9cfc2..45f937dfab 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -544,6 +544,12 @@ gdk_registry_handle_global (void *data, gdk_wayland_display_init_xdg_output (display_wayland); _gdk_wayland_display_async_roundtrip (display_wayland); } + else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) + { + display_wayland->idle_inhibit_manager = + wl_registry_bind (display_wayland->wl_registry, id, + &zwp_idle_inhibit_manager_v1_interface, 1); + } g_hash_table_insert (display_wayland->known_globals, GUINT_TO_POINTER (id), g_strdup (interface)); diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h index 5ace0f2744..db5e6a4fb3 100644 --- a/gdk/wayland/gdkdisplay-wayland.h +++ b/gdk/wayland/gdkdisplay-wayland.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,7 @@ struct _GdkWaylandDisplay struct zwp_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; struct org_kde_kwin_server_decoration_manager *server_decoration_manager; struct zxdg_output_manager_v1 *xdg_output_manager; + struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; GList *async_roundtrips; diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index ef3f277664..46fec90c8e 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -191,6 +191,9 @@ struct _GdkWaylandSurface struct zxdg_imported_v1 *imported_transient_for; GHashTable *shortcuts_inhibitors; + + struct zwp_idle_inhibitor_v1 *idle_inhibitor; + size_t idle_inhibitor_refcount; }; struct _GdkWaylandSurfaceClass @@ -1984,6 +1987,39 @@ gdk_wayland_surface_announce_csd (GdkSurface *surface) ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT); } +gboolean +gdk_wayland_surface_inhibit_idle (GdkSurface *surface) +{ + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)); + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + + if (!display_wayland->idle_inhibit_manager) + return false; + if (!impl->idle_inhibitor) + { + g_assert (impl->idle_inhibitor_refcount == 0); + impl->idle_inhibitor = + zwp_idle_inhibit_manager_v1_create_inhibitor (display_wayland->idle_inhibit_manager, + impl->display_server.wl_surface); + } + ++impl->idle_inhibitor_refcount; + return true; +} + +void +gdk_wayland_surface_uninhibit_idle (GdkSurface *surface) +{ + GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + + g_assert (impl->idle_inhibitor && impl->idle_inhibitor_refcount > 0); + + if (--impl->idle_inhibitor_refcount == 0) + { + zwp_idle_inhibitor_v1_destroy (impl->idle_inhibitor); + impl->idle_inhibitor = NULL; + } +} + static void calculate_popup_rect (GdkSurface *surface, GdkPopupLayout *layout, diff --git a/gdk/wayland/gdkwaylandsurface.h b/gdk/wayland/gdkwaylandsurface.h index 4af358c33e..54e0c6ff49 100644 --- a/gdk/wayland/gdkwaylandsurface.h +++ b/gdk/wayland/gdkwaylandsurface.h @@ -80,6 +80,9 @@ void gdk_wayland_surface_set_application_id (GdkSurface *sur void gdk_wayland_surface_announce_csd (GdkSurface *surface); +gboolean gdk_wayland_surface_inhibit_idle (GdkSurface *surface); +void gdk_wayland_surface_uninhibit_idle (GdkSurface *surface); + G_END_DECLS #endif /* __GDK_WAYLAND_SURFACE_H__ */ diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build index 14267f4ccc..67ebb63da5 100644 --- a/gdk/wayland/meson.build +++ b/gdk/wayland/meson.build @@ -54,6 +54,7 @@ proto_sources = [ ['keyboard-shortcuts-inhibit', 'unstable', 'v1', ], ['server-decoration', 'private' ], ['xdg-output', 'unstable', 'v1', ], + ['idle-inhibit', 'unstable', 'v1', ], ] gdk_wayland_gen_headers = [] -- GitLab From 966ab1152dc9f803024d7f9b9d95f595a8c2b211 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 10 Jul 2020 15:53:05 +0200 Subject: [PATCH 2/2] gtk/wayland: Add support for idle inhibition in GtkApplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This feature was previously only supported on DBus compositors, such as Mutter, this adds support for other compositors such as all of those based on wlroots. This implementation prefers the idle-inhibit Wayland protocol to the DBus version if it is available, since the inhibitor is per-surface instead of global it allows a finer control over which displays get dimmed for instance. For every case not supported by this protocol, a fallback to the DBus version is used. It can’t do anything if the GtkWindow isn’t passed, which might warrant some documentation change to encourage users to not use NULL for this argument. This has been tested on Sway. Fixes #2202. --- gtk/gtkapplication-wayland.c | 104 ++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/gtk/gtkapplication-wayland.c b/gtk/gtkapplication-wayland.c index 34d85e31bb..face15505c 100644 --- a/gtk/gtkapplication-wayland.c +++ b/gtk/gtkapplication-wayland.c @@ -1,6 +1,7 @@ /* * Copyright © 2010 Codethink Limited * Copyright © 2013 Canonical Limited + * Copyright © 2020 Emmanuel Gil Peyrot * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,12 +25,42 @@ #include "gtknative.h" #include +#include +#include -typedef GtkApplicationImplDBusClass GtkApplicationImplWaylandClass; +typedef struct +{ + GtkApplicationImplDBusClass parent_class; + + /* stores the dbus version of the overriden methods */ + guint (*dbus_inhibit) (GtkApplicationImpl *impl, + GtkWindow *window, + GtkApplicationInhibitFlags flags, + const gchar *reason); + void (*dbus_uninhibit) (GtkApplicationImpl *impl, + guint cookie); +} GtkApplicationImplWaylandClass; + +typedef struct +{ + guint cookie; + guint dbus_cookie; + GtkApplicationInhibitFlags flags; + GdkSurface *surface; + +} GtkApplicationWaylandInhibitor; + +static void +gtk_application_wayland_inhibitor_free (GtkApplicationWaylandInhibitor *inhibitor) +{ + g_slice_free (GtkApplicationWaylandInhibitor, inhibitor); +} typedef struct { GtkApplicationImplDBus dbus; + GSList *inhibitors; + guint next_cookie; } GtkApplicationImplWayland; @@ -72,6 +103,70 @@ gtk_application_impl_wayland_before_emit (GtkApplicationImpl *impl, gdk_wayland_display_set_startup_notification_id (gdk_display_get_default (), startup_notification_id); } +static guint +gtk_application_impl_wayland_inhibit (GtkApplicationImpl *impl, + GtkWindow *window, + GtkApplicationInhibitFlags flags, + const gchar *reason) +{ + GtkApplicationImplWayland *wayland = (GtkApplicationImplWayland *) impl; + GdkSurface *surface; + GtkApplicationWaylandInhibitor *inhibitor; + gboolean success; + + if (!flags) + return 0; + + inhibitor = g_slice_new (GtkApplicationWaylandInhibitor); + inhibitor->cookie = ++wayland->next_cookie; + inhibitor->flags = flags; + wayland->inhibitors = g_slist_prepend (wayland->inhibitors, inhibitor); + + if (flags & GTK_APPLICATION_INHIBIT_IDLE) + { + surface = gtk_native_get_surface (GTK_NATIVE (window)); + if (GDK_IS_WAYLAND_SURFACE (surface)) + { + success = gdk_wayland_surface_inhibit_idle (surface); + if (success) + { + flags &= ~GTK_APPLICATION_INHIBIT_IDLE; + inhibitor->surface = surface; + } + } + } + + inhibitor->dbus_cookie = ((GtkApplicationImplWaylandClass *) G_OBJECT_GET_CLASS (wayland))->dbus_inhibit (impl, window, flags, reason); + + return inhibitor->cookie; +} + +static void +gtk_application_impl_wayland_uninhibit (GtkApplicationImpl *impl, + guint cookie) +{ + GtkApplicationImplWayland *wayland = (GtkApplicationImplWayland *) impl; + GSList *iter; + + for (iter = wayland->inhibitors; iter; iter = iter->next) + { + GtkApplicationWaylandInhibitor *inhibitor = iter->data; + + if (inhibitor->cookie == cookie) + { + if (inhibitor->dbus_cookie) + ((GtkApplicationImplWaylandClass *) G_OBJECT_GET_CLASS (wayland))->dbus_uninhibit (impl, inhibitor->dbus_cookie); + if (inhibitor->surface) + gdk_wayland_surface_uninhibit_idle (inhibitor->surface); + gtk_application_wayland_inhibitor_free (inhibitor); + wayland->inhibitors = g_slist_delete_link (wayland->inhibitors, iter); + return; + } + } + + g_warning ("Invalid inhibitor cookie"); +} + static void gtk_application_impl_wayland_init (GtkApplicationImplWayland *wayland) { @@ -82,8 +177,15 @@ gtk_application_impl_wayland_class_init (GtkApplicationImplWaylandClass *class) { GtkApplicationImplClass *impl_class = GTK_APPLICATION_IMPL_CLASS (class); + class->dbus_inhibit = impl_class->inhibit; + class->dbus_uninhibit = impl_class->uninhibit; + impl_class->handle_window_realize = gtk_application_impl_wayland_handle_window_realize; impl_class->before_emit = gtk_application_impl_wayland_before_emit; + impl_class->inhibit = + gtk_application_impl_wayland_inhibit; + impl_class->uninhibit = + gtk_application_impl_wayland_uninhibit; } -- GitLab