diff --git a/Makefile b/Makefile index e9758ee..397555f 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,14 @@ PREFIX=/usr/local BINDIR=$(PREFIX)/bin MANDIR=$(PREFIX)/share/man -CFLAGS=-Wall -Werror -Wextra -Wpedantic -Wno-unused-parameter -Wconversion -Wformat-security -Wformat -Wsign-conversion -Wfloat-conversion -Wunused-result $(shell pkg-config --cflags pixman-1) +CFLAGS=-Wall -Werror -Wextra -Wpedantic -Wno-unused-parameter -Wconversion $\ + -Wformat-security -Wformat -Wsign-conversion -Wfloat-conversion $\ + -Wunused-result $(shell pkg-config --cflags pixman-1) LIBS=-lwayland-client $(shell pkg-config --libs pixman-1) -lrt -OBJ=wayneko.o wlr-layer-shell-unstable-v1.o xdg-shell.o -GEN=wlr-layer-shell-unstable-v1.c wlr-layer-shell-unstable-v1.h xdg-shell.c xdg-shell.h +OBJ=wayneko.o wlr-layer-shell-unstable-v1.o xdg-shell.o ext-idle-notify-v1.o +GEN=wlr-layer-shell-unstable-v1.c wlr-layer-shell-unstable-v1.h $\ + xdg-shell.c xdg-shell.h $\ + ext-idle-notify-v1.c ext-idle-notify-v1.h wayneko: $(OBJ) $(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) diff --git a/ext-idle-notify-v1.xml b/ext-idle-notify-v1.xml new file mode 100644 index 0000000..6fe97d7 --- /dev/null +++ b/ext-idle-notify-v1.xml @@ -0,0 +1,102 @@ + + + + Copyright © 2015 Martin Gräßlin + Copyright © 2022 Simon Ser + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This interface allows clients to monitor user idle status. + + After binding to this global, clients can create ext_idle_notification_v1 + objects to get notified when the user is idle for a given amount of time. + + + + + Destroy the manager object. All objects created via this interface + remain valid. + + + + + + Create a new idle notification object. + + The notification object has a minimum timeout duration and is tied to a + seat. The client will be notified if the seat is inactive for at least + the provided timeout. See ext_idle_notification_v1 for more details. + + A zero timeout is valid and means the client wants to be notified as + soon as possible when the seat is inactive. + + + + + + + + + + This interface is used by the compositor to send idle notification events + to clients. + + Initially the notification object is not idle. The notification object + becomes idle when no user activity has happened for at least the timeout + duration, starting from the creation of the notification object. User + activity may include input events or a presence sensor, but is + compositor-specific. If an idle inhibitor is active (e.g. another client + has created a zwp_idle_inhibitor_v1 on a visible surface), the compositor + must not make the notification object idle. + + When the notification object becomes idle, an idled event is sent. When + user activity starts again, the notification object stops being idle, + a resumed event is sent and the timeout is restarted. + + + + + Destroy the notification object. + + + + + + This event is sent when the notification object becomes idle. + + It's a compositor protocol error to send this event twice without a + resumed event in-between. + + + + + + This event is sent when the notification object stops being idle. + + It's a compositor protocol error to send this event twice without an + idled event in-between. It's a compositor protocol error to send this + event prior to any idled event. + + + + diff --git a/wayneko.c b/wayneko.c index 428bd2b..7335475 100644 --- a/wayneko.c +++ b/wayneko.c @@ -24,6 +24,7 @@ #define MIN(A, B) (A < B ? A : B) #include "wlr-layer-shell-unstable-v1.h" +#include "ext-idle-notify-v1.h" const char usage[] = "Usage: wayneko [options...]\n" @@ -77,6 +78,9 @@ bool follow_pointer = true; bool recreate_surface_on_close = false; enum zwlr_layer_shell_v1_layer layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; +struct ext_idle_notifier_v1 *idle_notifier = NULL; +const uint32_t neko_idle_timeout_ms = 180000; /* 3 minutes. */ // TODO make configurable + struct Seat { struct wl_list link; @@ -85,6 +89,8 @@ struct Seat uint32_t global_name; bool on_surface; uint32_t surface_x; + struct ext_idle_notification_v1 *idle_notification; + bool currently_idle; }; struct Buffer @@ -571,6 +577,41 @@ static const struct wl_seat_listener seat_listener = { .name = noop, }; +static void ext_idle_notification_handle_idled (void *data, + struct ext_idle_notification_v1 *ext_idle_notification_v1) +{ + (void)ext_idle_notification_v1; + struct Seat *seat = (struct Seat *)data; + assert(!seat->currently_idle); + seat->currently_idle = true; +} + +static void ext_idle_notification_handle_resumed (void *data, + struct ext_idle_notification_v1 *ext_idle_notification_v1) +{ + (void)ext_idle_notification_v1; + struct Seat *seat = (struct Seat *)data; + assert(seat->currently_idle); + seat->currently_idle = false; +} + +static const struct ext_idle_notification_v1_listener ext_idle_notification_listener = { + .idled = ext_idle_notification_handle_idled, + .resumed = ext_idle_notification_handle_resumed, +}; + +static void seat_add_idle (struct Seat *seat) +{ + assert(seat->idle_notification == NULL); + assert(!seat->currently_idle); + seat->idle_notification = ext_idle_notifier_v1_get_idle_notification( + idle_notifier, neko_idle_timeout_ms, seat->wl_seat + ); + ext_idle_notification_v1_add_listener( + seat->idle_notification, &ext_idle_notification_listener, seat + ); +} + static void seat_new (struct wl_seat *wl_seat, uint32_t name) { struct Seat *seat = calloc(1, sizeof(struct Seat)); @@ -584,6 +625,13 @@ static void seat_new (struct wl_seat *wl_seat, uint32_t name) seat->global_name = name; seat->on_surface = false; + /* Create idle_notification if we have the global idle_notifier. Note + * that during the initial registry burst seats may be advertised before + * the idle protocol global, so this also has to be done on first sync. + */ + if ( idle_notifier != NULL ) + seat_add_idle(seat); + wl_seat_set_user_data(seat->wl_seat, seat); wl_list_insert(&seats, &seat->link); @@ -592,6 +640,8 @@ static void seat_new (struct wl_seat *wl_seat, uint32_t name) static void seat_destroy (struct Seat *seat) { + if ( seat->idle_notification != NULL ) + ext_idle_notification_v1_destroy(seat->idle_notification); seat_release_pointer(seat); wl_seat_destroy(seat->wl_seat); wl_list_remove(&seat->link); @@ -766,6 +816,25 @@ static bool animtation_neko_wants_sleep (void) return tm.tm_hour >= 23 || tm.tm_hour <= 6; } +/** Returns true if new frame is needed. */ +static bool animation_next_state_with_idle (void) +{ + fprintf(stderr, "with idle!!!\n"); + /* If no one is there (system is idle), neko gets bored and will sleep. */ + switch (current_neko) + { + case NEKO_SLEEP_1: + case NEKO_SLEEP_2: + case NEKO_YAWN: + animation_neko_do_sleep(); + return true; + + default: + animation_neko_do_yawn(); + return true; + } +} + /** Returns true if new frame is needed. */ static bool animation_next_state_with_hotspot (uint32_t x) { @@ -990,6 +1059,11 @@ static bool animation_next_state (void) struct Seat *seat; wl_list_for_each(seat, &seats, link) + { + if (seat->currently_idle) + return animation_next_state_with_idle(); + } + wl_list_for_each(seat, &seats, link) { if (seat->on_surface) return animation_next_state_with_hotspot(seat->surface_x); @@ -1142,6 +1216,8 @@ static void registry_handle_global (void *data, struct wl_registry *registry, wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); else if ( strcmp(interface, wl_shm_interface.name) == 0 ) wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + else if ( strcmp(interface, ext_idle_notifier_v1_interface.name) == 0 ) + idle_notifier = wl_registry_bind(registry, name, &ext_idle_notifier_v1_interface, 1); else if ( strcmp(interface, wl_seat_interface.name) == 0 ) { struct wl_seat *wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 7); @@ -1187,6 +1263,20 @@ static void sync_handle_done (void *data, struct wl_callback *wl_callback, uint3 return; } + /* During the initial registry burst, seats may be advertised before + * the global idle objects. So we need to go over all seats again and + * add the idle_notification. + */ + if ( idle_notifier != NULL ) + { + struct Seat *seat; + wl_list_for_each(seat, &seats, link) + { + if ( seat->idle_notification == NULL ) + seat_add_idle(seat); + } + } + surface_create(); } @@ -1459,6 +1549,8 @@ exit_main_loop: wl_shm_destroy(wl_shm); if ( layer_shell != NULL ) zwlr_layer_shell_v1_destroy(layer_shell); + if ( idle_notifier != NULL ) + ext_idle_notifier_v1_destroy(idle_notifier); if ( sync_callback != NULL ) wl_callback_destroy(sync_callback); if ( wl_registry != NULL )