From e3fed5d0edadd4de3f0227afbb37e033b2ea59d2 Mon Sep 17 00:00:00 2001 From: Sunderland93 Date: Fri, 15 Dec 2023 01:43:30 +0400 Subject: [PATCH] wayland: add support for idle-inhibit --- src/meson.build | 3 + src/wayland/meta-wayland-idle-inhibit.c | 400 ++++++++++++++++++++++++ src/wayland/meta-wayland-idle-inhibit.h | 25 ++ src/wayland/meta-wayland-versions.h | 1 + src/wayland/meta-wayland.c | 1 + 5 files changed, 430 insertions(+) create mode 100644 src/wayland/meta-wayland-idle-inhibit.c create mode 100644 src/wayland/meta-wayland-idle-inhibit.h diff --git a/src/meson.build b/src/meson.build index 2dd095e2a..b8a130e43 100644 --- a/src/meson.build +++ b/src/meson.build @@ -521,6 +521,8 @@ if have_wayland 'wayland/meta-wayland-gtk-shell.c', 'wayland/meta-wayland-gtk-shell.h', 'wayland/meta-wayland.h', + 'wayland/meta-wayland-idle-inhibit.c', + 'wayland/meta-wayland-idle-inhibit.h', 'wayland/meta-wayland-inhibit-shortcuts.c', 'wayland/meta-wayland-inhibit-shortcuts-dialog.c', 'wayland/meta-wayland-inhibit-shortcuts-dialog.h', @@ -803,6 +805,7 @@ if have_wayland wayland_protocols = [ ['gtk-primary-selection', 'private', ], ['gtk-shell', 'private', ], + ['idle-inhibit', 'unstable', 'v1', ], ['gtk-text-input', 'private', ], ['keyboard-shortcuts-inhibit', 'unstable', 'v1', ], ['linux-dmabuf', 'unstable', 'v1', ], diff --git a/src/wayland/meta-wayland-idle-inhibit.c b/src/wayland/meta-wayland-idle-inhibit.c new file mode 100644 index 000000000..03df1fd16 --- /dev/null +++ b/src/wayland/meta-wayland-idle-inhibit.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2021 SUSE Software Solutions Germany GmbH + * + * 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 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 . + * + */ + +#include "config.h" + +#include "wayland/meta-wayland-idle-inhibit.h" + +#include + +#include "backends/meta-backend-private.h" +#include "backends/meta-logical-monitor.h" +#include "backends/meta-settings-private.h" +#include "wayland/meta-wayland-private.h" +#include "wayland/meta-wayland-versions.h" + +#include "idle-inhibit-unstable-v1-server-protocol.h" + +typedef enum _IdleState +{ + IDLE_STATE_INITIALIZING, + IDLE_STATE_UNINHIBITED, + IDLE_STATE_INHIBITING, + IDLE_STATE_INHIBITED, + IDLE_STATE_UNINHIBITING, +} IdleState; + +struct _MetaWaylandIdleInhibitor +{ + GDBusProxy *session_proxy; + struct wl_resource *resource; + + MetaSurfaceActor *actor; + gulong is_obscured_changed_handler; + gulong actor_destroyed_handler_id; + + MetaWaylandSurface *surface; + gulong surface_destroy_handler_id; + gulong actor_changed_handler_id; + + uint32_t cookie; + IdleState state; +}; + +typedef struct _MetaWaylandIdleInhibitor MetaWaylandIdleInhibitor; + +static void update_inhibition (MetaWaylandIdleInhibitor *inhibitor); + +static void +meta_wayland_inhibitor_free (MetaWaylandIdleInhibitor *inhibitor) +{ + g_clear_signal_handler (&inhibitor->is_obscured_changed_handler, + inhibitor->actor); + g_clear_signal_handler (&inhibitor->actor_destroyed_handler_id, + inhibitor->actor); + g_clear_signal_handler (&inhibitor->actor_changed_handler_id, + inhibitor->surface); + g_clear_signal_handler (&inhibitor->surface_destroy_handler_id, + inhibitor->surface); + + g_free (inhibitor); +} + +static void +inhibit_completed (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + MetaWaylandIdleInhibitor *inhibitor = user_data; + g_autoptr (GVariant) ret = NULL; + g_autoptr (GError) error = NULL; + + ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!ret) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to inhibit: %s", error->message); + return; + } + + g_warn_if_fail (inhibitor->state == IDLE_STATE_INHIBITING); + + g_variant_get (ret, "(u)", &inhibitor->cookie); + inhibitor->state = IDLE_STATE_INHIBITED; + + update_inhibition (inhibitor); +} + +static void +uninhibit_completed (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + MetaWaylandIdleInhibitor *inhibitor = user_data; + g_autoptr (GVariant) ret = NULL; + g_autoptr (GError) error = NULL; + + ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!ret) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to uninhibit: %s", error->message); + return; + } + + if (!inhibitor) + return; + + g_warn_if_fail (inhibitor->state == IDLE_STATE_UNINHIBITING); + inhibitor->state = IDLE_STATE_UNINHIBITED; + + update_inhibition (inhibitor); +} + +static void +update_inhibition (MetaWaylandIdleInhibitor *inhibitor) +{ + gboolean should_inhibit; + + if (!inhibitor->session_proxy) + return; + + if (!inhibitor->surface || + !inhibitor->resource || + !inhibitor->actor) + { + should_inhibit = FALSE; + } + else + { + if (meta_surface_actor_is_effectively_obscured (inhibitor->actor)) + should_inhibit = FALSE; + else + should_inhibit = TRUE; + } + + switch (inhibitor->state) + { + case IDLE_STATE_INITIALIZING: + case IDLE_STATE_UNINHIBITED: + if (!inhibitor->resource) + { + meta_wayland_inhibitor_free (inhibitor); + return; + } + + if (!should_inhibit) + return; + + break; + case IDLE_STATE_INHIBITED: + if (should_inhibit) + return; + break; + case IDLE_STATE_INHIBITING: + case IDLE_STATE_UNINHIBITING: + /* Update inhibition after current asynchronous call completes. */ + return; + } + + if (should_inhibit) + { + g_dbus_proxy_call (G_DBUS_PROXY (inhibitor->session_proxy), + "Inhibit", + g_variant_new ("(ss)", "mutter", "idle-inhibit"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + inhibit_completed, + inhibitor); + inhibitor->state = IDLE_STATE_INHIBITING; + } + else + { + g_dbus_proxy_call (G_DBUS_PROXY (inhibitor->session_proxy), + "UnInhibit", + g_variant_new ("(u)", inhibitor->cookie), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + uninhibit_completed, + inhibitor); + inhibitor->state = IDLE_STATE_UNINHIBITING; + } +} + +static void +is_obscured_changed (MetaSurfaceActor *actor, + GParamSpec *pspec, + MetaWaylandIdleInhibitor *inhibitor) +{ + update_inhibition (inhibitor); +} + +static void +inhibitor_proxy_completed (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + MetaWaylandIdleInhibitor *inhibitor = user_data; + GDBusProxy *proxy; + g_autoptr (GError) error = NULL; + + proxy = g_dbus_proxy_new_finish (res, &error); + if (!proxy) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + g_warning ("Failed to obtain org.freedesktop.ScreenSaver proxy: %s", + error->message); + } + return; + } + + inhibitor->session_proxy = proxy; + inhibitor->state = IDLE_STATE_UNINHIBITED; + + update_inhibition (inhibitor); +} + +static void +idle_inhibit_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +idle_inhibitor_destructor (struct wl_resource *resource) +{ + MetaWaylandIdleInhibitor *inhibitor = wl_resource_get_user_data (resource); + + switch (inhibitor->state) + { + case IDLE_STATE_UNINHIBITED: + meta_wayland_inhibitor_free (inhibitor); + return; + case IDLE_STATE_INITIALIZING: + case IDLE_STATE_INHIBITED: + case IDLE_STATE_INHIBITING: + case IDLE_STATE_UNINHIBITING: + inhibitor->resource = NULL; + break; + } + + update_inhibition (inhibitor); +} + +static void +on_surface_destroyed (MetaWaylandSurface *surface, + MetaWaylandIdleInhibitor *inhibitor) +{ + g_clear_signal_handler (&inhibitor->is_obscured_changed_handler, + inhibitor->actor); + g_clear_signal_handler (&inhibitor->actor_destroyed_handler_id, + inhibitor->actor); + inhibitor->actor = NULL; + g_clear_signal_handler (&inhibitor->actor_changed_handler_id, + inhibitor->surface); + g_clear_signal_handler (&inhibitor->surface_destroy_handler_id, + inhibitor->surface); + inhibitor->surface = NULL; +} + +static void +on_actor_destroyed (MetaSurfaceActor *actor, + MetaWaylandIdleInhibitor *inhibitor) +{ + g_warn_if_fail (actor == inhibitor->actor); + + g_clear_signal_handler (&inhibitor->is_obscured_changed_handler, actor); + g_clear_signal_handler (&inhibitor->actor_destroyed_handler_id, actor); + inhibitor->actor = NULL; +} + +static void +attach_actor (MetaWaylandIdleInhibitor *inhibitor) +{ + inhibitor->actor = meta_wayland_surface_get_actor (inhibitor->surface); + + if (inhibitor->actor) + { + inhibitor->is_obscured_changed_handler = + g_signal_connect (inhibitor->actor, "notify::is-obscured", + G_CALLBACK (is_obscured_changed), inhibitor); + inhibitor->actor_destroyed_handler_id = + g_signal_connect (inhibitor->actor, "destroy", + G_CALLBACK (on_actor_destroyed), inhibitor); + } +} + +static void +on_actor_changed (MetaWaylandSurface *surface, + MetaWaylandIdleInhibitor *inhibitor) +{ + g_clear_signal_handler (&inhibitor->is_obscured_changed_handler, + inhibitor->actor); + g_clear_signal_handler (&inhibitor->actor_destroyed_handler_id, + inhibitor->actor); + attach_actor (inhibitor); +} + +static const struct zwp_idle_inhibitor_v1_interface meta_wayland_idle_inhibitor_interface = +{ + idle_inhibit_destroy, +}; + +static void +idle_inhibit_manager_create_inhibitor (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource) +{ + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + MetaWaylandIdleInhibitor *inhibitor; + struct wl_resource *inhibitor_resource; + + inhibitor_resource = wl_resource_create (client, + &zwp_idle_inhibitor_v1_interface, + wl_resource_get_version (resource), + id); + + inhibitor = g_new0 (MetaWaylandIdleInhibitor, 1); + inhibitor->surface = surface; + inhibitor->resource = inhibitor_resource; + + attach_actor (inhibitor); + + inhibitor->actor_changed_handler_id = + g_signal_connect (surface, "actor-changed", + G_CALLBACK (on_actor_changed), inhibitor); + inhibitor->surface_destroy_handler_id = + g_signal_connect (surface, "destroy", + G_CALLBACK (on_surface_destroyed), inhibitor); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.ScreenSaver", + "/org/freedesktop/ScreenSaver", + "org.freedesktop.ScreenSaver", + NULL, + inhibitor_proxy_completed, + inhibitor); + + wl_resource_set_implementation (inhibitor_resource, + &meta_wayland_idle_inhibitor_interface, + inhibitor, + idle_inhibitor_destructor); +} + + +static const struct zwp_idle_inhibit_manager_v1_interface meta_wayland_idle_inhibit_manager_interface = +{ + idle_inhibit_destroy, + idle_inhibit_manager_create_inhibitor, +}; + +static void +bind_idle_inhibit (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, + &zwp_idle_inhibit_manager_v1_interface, + version, id); + + wl_resource_set_implementation (resource, + &meta_wayland_idle_inhibit_manager_interface, + NULL, NULL); +} + +gboolean +meta_wayland_idle_inhibit_init (MetaWaylandCompositor *compositor) +{ + if (wl_global_create (compositor->wayland_display, + &zwp_idle_inhibit_manager_v1_interface, + META_ZWP_IDLE_INHIBIT_V1_VERSION, + NULL, + bind_idle_inhibit) == NULL) + return FALSE; + return TRUE; +} diff --git a/src/wayland/meta-wayland-idle-inhibit.h b/src/wayland/meta-wayland-idle-inhibit.h new file mode 100644 index 000000000..21773c9fb --- /dev/null +++ b/src/wayland/meta-wayland-idle-inhibit.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 SUSE Software Solutions Germany GmbH + * + * 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 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 . + * + */ + +#pragma once + +#include + +#include "wayland/meta-wayland-types.h" + +gboolean meta_wayland_idle_inhibit_init (MetaWaylandCompositor *compositor); diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index 2d6ce5ace..cc2e924dc 100644 --- a/src/wayland/meta-wayland-versions.h +++ b/src/wayland/meta-wayland-versions.h @@ -46,6 +46,7 @@ #define META_GTK_SHELL1_VERSION 3 #define META_WL_SUBCOMPOSITOR_VERSION 1 #define META_ZWP_POINTER_GESTURES_V1_VERSION 1 +#define META_ZWP_IDLE_INHIBIT_V1_VERSION 1 #define META_ZXDG_EXPORTER_V1_VERSION 1 #define META_ZXDG_IMPORTER_V1_VERSION 1 #define META_ZWP_LINUX_DMABUF_V1_VERSION 3 diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index e4b09cbfc..ce9b10051 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -34,6 +34,7 @@ #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-wayland-dma-buf.h" #include "wayland/meta-wayland-egl-stream.h" +#include "wayland/meta-wayland-idle-inhibit.h" #include "wayland/meta-wayland-inhibit-shortcuts-dialog.h" #include "wayland/meta-wayland-inhibit-shortcuts.h" #include "wayland/meta-wayland-outputs.h"