diff --git a/src/backends/meta-idle-monitor-private.h b/src/backends/meta-idle-monitor-private.h index d498f13f9..497d0486d 100644 --- a/src/backends/meta-idle-monitor-private.h +++ b/src/backends/meta-idle-monitor-private.h @@ -36,6 +36,7 @@ typedef struct guint64 timeout_msec; int idle_source_id; GSource *timeout_source; + gboolean ignore_inhibitors; } MetaIdleMonitorWatch; struct _MetaIdleMonitor @@ -56,4 +57,11 @@ struct _MetaIdleMonitorClass void meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor); +guint meta_idle_monitor_add_idle_watch_full (MetaIdleMonitor *monitor, + guint64 interval_msec, + gboolean ignore_inhibitors, + MetaIdleMonitorWatchFunc callback, + gpointer user_data, + GDestroyNotify notify); + #endif /* META_IDLE_MONITOR_PRIVATE_H */ diff --git a/src/backends/meta-idle-monitor.c b/src/backends/meta-idle-monitor.c index f2c6c75db..82e9d113f 100644 --- a/src/backends/meta-idle-monitor.c +++ b/src/backends/meta-idle-monitor.c @@ -181,7 +181,7 @@ update_inhibited_watch (gpointer key, if (!watch->timeout_source) return; - if (monitor->inhibited) + if (monitor->inhibited && !watch->ignore_inhibitors) { g_source_set_ready_time (watch->timeout_source, -1); } @@ -337,6 +337,7 @@ make_watch (MetaIdleMonitor *monitor, watch->user_data = user_data; watch->notify = notify; watch->timeout_msec = timeout_msec; + watch->ignore_inhibitors = FALSE; if (timeout_msec != 0) { @@ -498,7 +499,7 @@ meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor) } else { - if (monitor->inhibited) + if (monitor->inhibited && !watch->ignore_inhibitors) { g_source_set_ready_time (watch->timeout_source, -1); } @@ -513,3 +514,33 @@ meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor) g_list_free (watch_ids); } + +guint +meta_idle_monitor_add_idle_watch_full (MetaIdleMonitor *monitor, + guint64 interval_msec, + gboolean ignore_inhibitors, + MetaIdleMonitorWatchFunc callback, + gpointer user_data, + GDestroyNotify notify) +{ + MetaIdleMonitorWatch *watch; + + g_return_val_if_fail (META_IS_IDLE_MONITOR (monitor), 0); + g_return_val_if_fail (interval_msec > 0, 0); + + watch = make_watch (monitor, + interval_msec, + callback, + user_data, + notify); + + watch->ignore_inhibitors = ignore_inhibitors; + + if (monitor->inhibited && ignore_inhibitors && watch->timeout_source) + { + g_source_set_ready_time (watch->timeout_source, + monitor->last_event_time + + watch->timeout_msec * 1000); + } + return watch->id; +} diff --git a/src/meson.build b/src/meson.build index 796282e86..fa15dc4ba 100644 --- a/src/meson.build +++ b/src/meson.build @@ -524,6 +524,8 @@ if have_wayland 'wayland/meta-wayland-layer-shell.c', 'wayland/meta-wayland-layer-shell.h', 'wayland/meta-wayland.h', + 'wayland/meta-wayland-idle-notify.h', + 'wayland/meta-wayland-idle-notify.c', 'wayland/meta-wayland-idle-inhibit.c', 'wayland/meta-wayland-idle-inhibit.h', 'wayland/meta-wayland-inhibit-shortcuts.c', @@ -858,6 +860,7 @@ if have_wayland ['xapp-shell', 'private', ], ['xwayland-keyboard-grab', 'unstable', 'v1', ], ['wayland-drm', 'private', ], + ['ext-idle-notify', 'staging', 'v1', ], ] if have_wayland_eglstream wayland_eglstream_protocols_dir = wayland_eglstream_protocols_dep.get_variable(pkgconfig: 'pkgdatadir') diff --git a/src/wayland/meta-wayland-idle-notify.c b/src/wayland/meta-wayland-idle-notify.c new file mode 100644 index 000000000..bd047ff2a --- /dev/null +++ b/src/wayland/meta-wayland-idle-notify.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2026 Linux Mint + * + * 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 2 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 . + */ + +#include "config.h" + +#include "wayland/meta-wayland-idle-notify.h" + +#include "backends/meta-idle-monitor-private.h" +#include "wayland/meta-wayland-private.h" +#include "wayland/meta-wayland-versions.h" + +#include "ext-idle-notify-v1-server-protocol.h" + +typedef struct _MetaWaylandIdleNotification +{ + struct wl_resource *resource; + + MetaIdleMonitor *monitor; + + guint idle_watch_id; + guint active_watch_id; + + guint timeout; + + gboolean ignore_inhibitors; +} MetaWaylandIdleNotification; + +static void idle_trigger_cb (MetaIdleMonitor *monitor, + guint id, + gpointer user_data); + +static void +active_trigger_cb (MetaIdleMonitor *monitor, + guint id, + gpointer user_data) +{ + MetaWaylandIdleNotification *notification = user_data; + + notification->active_watch_id = 0; + + ext_idle_notification_v1_send_resumed (notification->resource); + + notification->idle_watch_id = + meta_idle_monitor_add_idle_watch_full (notification->monitor, + notification->timeout, + notification->ignore_inhibitors, + idle_trigger_cb, + notification, + NULL); +} + +static void +idle_trigger_cb (MetaIdleMonitor *monitor, + guint id, + gpointer user_data) +{ + MetaWaylandIdleNotification *notification = user_data; + + meta_idle_monitor_remove_watch (notification->monitor, id); + notification->idle_watch_id = 0; + + ext_idle_notification_v1_send_idled (notification->resource); + + notification->active_watch_id = + meta_idle_monitor_add_user_active_watch (notification->monitor, + active_trigger_cb, + notification, + NULL); +} + +static void +ext_idle_notification_destructor (struct wl_resource *resource) +{ + MetaWaylandIdleNotification *notification = wl_resource_get_user_data (resource); + + if (notification->idle_watch_id) + meta_idle_monitor_remove_watch (notification->monitor, + notification->idle_watch_id); + + if (notification->active_watch_id) + meta_idle_monitor_remove_watch (notification->monitor, + notification->active_watch_id); + + g_free (notification); +} + +static void +ext_idle_notification_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static const struct ext_idle_notification_v1_interface ext_idle_notification_v1_impl = { + ext_idle_notification_destroy, +}; + +static void +ext_idle_notification_create (struct wl_client *client, + struct wl_resource *manager_resource, + uint32_t id, + uint32_t timeout, + struct wl_resource *seat_resource, + gboolean ignore_inhibitors) +{ + MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); + MetaWaylandIdleNotification *notification; + uint32_t version; + ClutterInputDevice *pointer_device = NULL; + + notification = g_new0 (MetaWaylandIdleNotification, 1); + notification->timeout = timeout; + notification->ignore_inhibitors = ignore_inhibitors; + + if (seat && seat->pointer) + pointer_device = seat->pointer->device; + + if (pointer_device) + { + MetaBackend *backend = meta_get_backend (); + notification->monitor = meta_backend_get_idle_monitor (backend, pointer_device); + } + else + { + notification->monitor = meta_idle_monitor_get_core (); + } + + version = wl_resource_get_version (manager_resource); + notification->resource = wl_resource_create (client, + &ext_idle_notification_v1_interface, + version, + id); + + wl_resource_set_implementation (notification->resource, + &ext_idle_notification_v1_impl, + notification, + ext_idle_notification_destructor); + + notification->idle_watch_id = + meta_idle_monitor_add_idle_watch_full (notification->monitor, + notification->timeout, + notification->ignore_inhibitors, + idle_trigger_cb, + notification, + NULL); +} + +static void +ext_idle_notifier_get_idle_notification (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + uint32_t timeout, + struct wl_resource *seat) +{ + ext_idle_notification_create (client, resource, + id, timeout, + seat, FALSE); +} + +static void +ext_idle_notifier_get_input_idle_notification (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + uint32_t timeout, + struct wl_resource *seat) +{ + ext_idle_notification_create (client, resource, + id, timeout, + seat, TRUE); +} + +static void +ext_idle_notifier_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static const struct ext_idle_notifier_v1_interface ext_idle_notifier_v1_impl = { + ext_idle_notifier_destroy, + ext_idle_notifier_get_idle_notification, + ext_idle_notifier_get_input_idle_notification, +}; + +static void +bind_ext_idle_notifier (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, + &ext_idle_notifier_v1_interface, + version, + id); + wl_resource_set_implementation (resource, + &ext_idle_notifier_v1_impl, + NULL, + NULL); +} + +void +meta_wayland_idle_notify_init (MetaWaylandCompositor *compositor) +{ + if (wl_global_create (compositor->wayland_display, + &ext_idle_notifier_v1_interface, + META_EXT_IDLE_NOTIFY_V1_VERSION, + NULL, + bind_ext_idle_notifier) == NULL) + g_error ("Failed to register a global ext-idle-notify object"); +} diff --git a/src/wayland/meta-wayland-idle-notify.h b/src/wayland/meta-wayland-idle-notify.h new file mode 100644 index 000000000..4b9f3d04c --- /dev/null +++ b/src/wayland/meta-wayland-idle-notify.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2026 Linux Mint + * + * 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 2 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 . + */ + +#ifndef META_WAYLAND_EXT_IDLE_NOTIFY_H +#define META_WAYLAND_EXT_IDLE_NOTIFY_H + +#include "wayland/meta-wayland-types.h" + +void meta_wayland_idle_notify_init (MetaWaylandCompositor *compositor); + +#endif diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index b4b3ef2f7..6283bbac0 100644 --- a/src/wayland/meta-wayland-versions.h +++ b/src/wayland/meta-wayland-versions.h @@ -44,6 +44,7 @@ #define META_GTK_SHELL1_VERSION 6 #define META_WL_SUBCOMPOSITOR_VERSION 1 #define META_ZWP_POINTER_GESTURES_V1_VERSION 3 +#define META_EXT_IDLE_NOTIFY_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 diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index f0eba38ed..7bd756c74 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -39,6 +39,7 @@ #include "wayland/meta-wayland-drm.h" #include "wayland/meta-wayland-egl-stream.h" #include "wayland/meta-wayland-idle-inhibit.h" +#include "wayland/meta-wayland-idle-notify.h" #include "wayland/meta-wayland-inhibit-shortcuts-dialog.h" #include "wayland/meta-wayland-inhibit-shortcuts.h" #include "wayland/meta-wayland-legacy-xdg-foreign.h" @@ -478,6 +479,7 @@ meta_wayland_compositor_setup (MetaWaylandCompositor *wayland_compositor) meta_wayland_input_method_manager_init (compositor); meta_wayland_virtual_keyboard_manager_init (compositor); meta_wayland_activation_init (compositor); + meta_wayland_idle_notify_init (compositor); meta_wayland_idle_inhibit_init (compositor); meta_wayland_init_xdg_wm_dialog (compositor); meta_wayland_xdg_toplevel_tag_init (compositor);