aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/wlr/types/wlr_idle_notify_v1.h44
-rw-r--r--protocol/meson.build1
-rw-r--r--types/meson.build1
-rw-r--r--types/wlr_idle_notify_v1.c255
4 files changed, 301 insertions, 0 deletions
diff --git a/include/wlr/types/wlr_idle_notify_v1.h b/include/wlr/types/wlr_idle_notify_v1.h
new file mode 100644
index 00000000..7bc11cb7
--- /dev/null
+++ b/include/wlr/types/wlr_idle_notify_v1.h
@@ -0,0 +1,44 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_IDLE_NOTIFY_H
+#define WLR_TYPES_WLR_IDLE_NOTIFY_H
+
+#include <wayland-server-core.h>
+
+struct wlr_seat;
+
+/**
+ * An idle notifier, implementing the ext-idle-notify-v1 protocol.
+ */
+struct wlr_idle_notifier_v1;
+
+/**
+ * Create the ext_idle_notifier_v1 global.
+ */
+struct wlr_idle_notifier_v1 *wlr_idle_notifier_v1_create(struct wl_display *display);
+
+/**
+ * Inhibit idle.
+ *
+ * Compositors should call this function when the idle state is disabled, e.g.
+ * because a visible client is using the idle-inhibit protocol.
+ */
+void wlr_idle_notifier_v1_set_inhibited(struct wlr_idle_notifier_v1 *notifier,
+ bool inhibited);
+
+/**
+ * Notify for user activity on a seat.
+ *
+ * Compositors should call this function whenever an input event is triggered
+ * on a seat.
+ */
+void wlr_idle_notifier_v1_notify_activity(struct wlr_idle_notifier_v1 *notifier,
+ struct wlr_seat *seat);
+
+#endif
diff --git a/protocol/meson.build b/protocol/meson.build
index 35aee359..4d56bbf4 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -19,6 +19,7 @@ protocols = {
# Staging upstream protocols
'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
+ 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml',
'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml',
'single-pixel-buffer-v1': wl_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml',
diff --git a/types/meson.build b/types/meson.build
index f7c24e12..853d31c7 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -44,6 +44,7 @@ wlr_files += files(
'wlr_gamma_control_v1.c',
'wlr_idle_inhibit_v1.c',
'wlr_idle.c',
+ 'wlr_idle_notify_v1.c',
'wlr_input_device.c',
'wlr_input_inhibitor.c',
'wlr_input_method_v2.c',
diff --git a/types/wlr_idle_notify_v1.c b/types/wlr_idle_notify_v1.c
new file mode 100644
index 00000000..6283dc6a
--- /dev/null
+++ b/types/wlr_idle_notify_v1.c
@@ -0,0 +1,255 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/types/wlr_idle_notify_v1.h>
+#include <wlr/types/wlr_seat.h>
+#include "ext-idle-notify-v1-protocol.h"
+
+#define IDLE_NOTIFIER_VERSION 1
+
+struct wlr_idle_notifier_v1 {
+ struct wl_global *global;
+
+ bool inhibited;
+ struct wl_list notifications; // wlr_idle_notification_v1.link
+
+ struct wl_listener display_destroy;
+};
+
+struct wlr_idle_notification_v1 {
+ struct wl_resource *resource;
+ struct wl_list link; // wlr_idle_notifier_v1.notifications
+ struct wlr_idle_notifier_v1 *notifier;
+ struct wlr_seat *seat;
+
+ uint32_t timeout_ms;
+ struct wl_event_source *timer;
+
+ bool idle;
+
+ struct wl_listener seat_destroy;
+};
+
+static void resource_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static const struct ext_idle_notifier_v1_interface notifier_impl;
+
+static const struct ext_idle_notification_v1_interface notification_impl = {
+ .destroy = resource_handle_destroy,
+};
+
+// Returns NULL if the resource is inert
+static struct wlr_idle_notification_v1 *notification_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &ext_idle_notification_v1_interface, &notification_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static struct wlr_idle_notifier_v1 *notifier_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &ext_idle_notifier_v1_interface, &notifier_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void notification_set_idle(struct wlr_idle_notification_v1 *notification,
+ bool idle) {
+ if (notification->idle == idle) {
+ return;
+ }
+
+ if (idle) {
+ ext_idle_notification_v1_send_idled(notification->resource);
+ } else {
+ ext_idle_notification_v1_send_resumed(notification->resource);
+ }
+
+ notification->idle = idle;
+}
+
+static int notification_handle_timer(void *data) {
+ struct wlr_idle_notification_v1 *notification = data;
+ notification_set_idle(notification, true);
+ return 0;
+}
+
+static void notification_destroy(struct wlr_idle_notification_v1 *notification) {
+ if (notification == NULL) {
+ return;
+ }
+ wl_list_remove(&notification->link);
+ wl_list_remove(&notification->seat_destroy.link);
+ if (notification->timer != NULL) {
+ wl_event_source_remove(notification->timer);
+ }
+ wl_resource_set_user_data(notification->resource, NULL); // make inert
+ free(notification);
+}
+
+static void notification_reset_timer(struct wlr_idle_notification_v1 *notification) {
+ if (notification->notifier->inhibited) {
+ notification_set_idle(notification, false);
+ if (notification->timer != NULL) {
+ wl_event_source_timer_update(notification->timer, 0);
+ }
+ return;
+ }
+
+ if (notification->timer != NULL) {
+ wl_event_source_timer_update(notification->timer,
+ notification->timeout_ms);
+ } else {
+ notification_set_idle(notification, true);
+ }
+}
+
+static void notification_handle_activity(struct wlr_idle_notification_v1 *notification) {
+ notification_set_idle(notification, false);
+ notification_reset_timer(notification);
+}
+
+static void notification_handle_seat_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_idle_notification_v1 *notification =
+ wl_container_of(listener, notification, seat_destroy);
+ notification_destroy(notification);
+}
+
+static void notification_handle_resource_destroy(struct wl_resource *resource) {
+ struct wlr_idle_notification_v1 *notification =
+ notification_from_resource(resource);
+ notification_destroy(notification);
+}
+
+static void notifier_handle_get_idle_notification(struct wl_client *client,
+ struct wl_resource *notifier_resource, uint32_t id, uint32_t timeout,
+ struct wl_resource *seat_resource) {
+ struct wlr_idle_notifier_v1 *notifier =
+ notifier_from_resource(notifier_resource);
+ struct wlr_seat_client *seat_client =
+ wlr_seat_client_from_resource(seat_resource);
+
+ uint32_t version = wl_resource_get_version(notifier_resource);
+ struct wl_resource *resource = wl_resource_create(client,
+ &ext_idle_notification_v1_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &notification_impl, NULL,
+ notification_handle_resource_destroy);
+
+ if (seat_client == NULL) {
+ return; // leave the resource inert
+ }
+
+ struct wlr_idle_notification_v1 *notification =
+ calloc(1, sizeof(*notification));
+ if (notification == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ notification->notifier = notifier;
+ notification->resource = resource;
+ notification->timeout_ms = timeout;
+ notification->seat = seat_client->seat;
+
+ if (timeout > 0) {
+ struct wl_display *display = wl_client_get_display(client);
+ struct wl_event_loop *loop = wl_display_get_event_loop(display);
+ notification->timer = wl_event_loop_add_timer(loop,
+ notification_handle_timer, notification);
+ if (notification->timer == NULL) {
+ free(notification);
+ wl_client_post_no_memory(client);
+ return;
+ }
+ }
+
+ notification->seat_destroy.notify = notification_handle_seat_destroy;
+ wl_signal_add(&seat_client->seat->events.destroy, &notification->seat_destroy);
+
+ wl_resource_set_user_data(resource, notification);
+ wl_list_insert(&notifier->notifications, &notification->link);
+
+ notification_reset_timer(notification);
+}
+
+static const struct ext_idle_notifier_v1_interface notifier_impl = {
+ .destroy = resource_handle_destroy,
+ .get_idle_notification = notifier_handle_get_idle_notification,
+};
+
+static void notifier_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id) {
+ struct wlr_idle_notifier_v1 *notifier = data;
+
+ struct wl_resource *resource = wl_resource_create(client,
+ &ext_idle_notifier_v1_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &notifier_impl, notifier, NULL);
+}
+
+static void handle_display_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_idle_notifier_v1 *notifier =
+ wl_container_of(listener, notifier, display_destroy);
+ wl_global_destroy(notifier->global);
+ free(notifier);
+}
+
+struct wlr_idle_notifier_v1 *wlr_idle_notifier_v1_create(struct wl_display *display) {
+ struct wlr_idle_notifier_v1 *notifier = calloc(1, sizeof(*notifier));
+ if (notifier == NULL) {
+ return NULL;
+ }
+
+ notifier->global = wl_global_create(display,
+ &ext_idle_notifier_v1_interface, IDLE_NOTIFIER_VERSION, notifier,
+ notifier_bind);
+ if (notifier->global == NULL) {
+ free(notifier);
+ return NULL;
+ }
+
+ wl_list_init(&notifier->notifications);
+
+ notifier->display_destroy.notify = handle_display_destroy;
+ wl_display_add_destroy_listener(display, &notifier->display_destroy);
+
+ return notifier;
+}
+
+void wlr_idle_notifier_v1_set_inhibited(struct wlr_idle_notifier_v1 *notifier,
+ bool inhibited) {
+ if (notifier->inhibited == inhibited) {
+ return;
+ }
+
+ notifier->inhibited = inhibited;
+
+ struct wlr_idle_notification_v1 *notification;
+ wl_list_for_each(notification, &notifier->notifications, link) {
+ notification_reset_timer(notification);
+ }
+}
+
+void wlr_idle_notifier_v1_notify_activity(struct wlr_idle_notifier_v1 *notifier,
+ struct wlr_seat *seat) {
+ if (notifier->inhibited) {
+ return;
+ }
+
+ struct wlr_idle_notification_v1 *notification;
+ wl_list_for_each(notification, &notifier->notifications, link) {
+ if (notification->seat == seat) {
+ notification_handle_activity(notification);
+ }
+ }
+}