diff options
-rw-r--r-- | common/loop.c | 179 | ||||
-rw-r--r-- | common/meson.build | 1 | ||||
-rw-r--r-- | include/loop.h | 54 | ||||
-rw-r--r-- | include/swaybar/bar.h | 3 | ||||
-rw-r--r-- | include/swaybar/event_loop.h | 26 | ||||
-rw-r--r-- | include/swaybar/status_line.h | 2 | ||||
-rw-r--r-- | include/swaylock/swaylock.h | 4 | ||||
-rw-r--r-- | swaybar/bar.c | 17 | ||||
-rw-r--r-- | swaybar/event_loop.c | 156 | ||||
-rw-r--r-- | swaybar/meson.build | 1 | ||||
-rw-r--r-- | swaybar/status_line.c | 4 | ||||
-rw-r--r-- | swaylock/main.c | 14 | ||||
-rw-r--r-- | swaylock/password.c | 62 |
13 files changed, 328 insertions, 195 deletions
diff --git a/common/loop.c b/common/loop.c new file mode 100644 index 00000000..1b174967 --- /dev/null +++ b/common/loop.c @@ -0,0 +1,179 @@ +#include <limits.h> +#include <string.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <poll.h> +#include <time.h> +#include <unistd.h> +#include "list.h" +#include "log.h" +#include "loop.h" + +struct loop_fd_event { + void (*callback)(int fd, short mask, void *data); + void *data; +}; + +struct loop_timer { + void (*callback)(void *data); + void *data; + struct timespec expiry; +}; + +struct loop { + struct pollfd *fds; + int fd_length; + int fd_capacity; + + list_t *fd_events; // struct loop_fd_event + list_t *timers; // struct loop_timer +}; + +struct loop *loop_create(void) { + struct loop *loop = calloc(1, sizeof(struct loop)); + if (!loop) { + wlr_log(WLR_ERROR, "Unable to allocate memory for loop"); + return NULL; + } + loop->fd_capacity = 10; + loop->fds = malloc(sizeof(struct pollfd) * loop->fd_capacity); + loop->fd_events = create_list(); + loop->timers = create_list(); + return loop; +} + +void loop_destroy(struct loop *loop) { + list_foreach(loop->fd_events, free); + list_foreach(loop->timers, free); + list_free(loop->fd_events); + list_free(loop->timers); + free(loop->fds); + free(loop); +} + +void loop_poll(struct loop *loop) { + // Calculate next timer in ms + int ms = INT_MAX; + if (loop->timers->length) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + for (int i = 0; i < loop->timers->length; ++i) { + struct loop_timer *timer = loop->timers->items[i]; + int timer_ms = (timer->expiry.tv_sec - now.tv_sec) * 1000; + timer_ms += (timer->expiry.tv_nsec - now.tv_nsec) / 1000000; + if (timer_ms < ms) { + ms = timer_ms; + } + } + } + if (ms < 0) { + ms = 0; + } + + poll(loop->fds, loop->fd_length, ms); + + // Dispatch fds + for (int i = 0; i < loop->fd_length; ++i) { + struct pollfd pfd = loop->fds[i]; + struct loop_fd_event *event = loop->fd_events->items[i]; + + // Always send these events + unsigned events = pfd.events | POLLHUP | POLLERR; + + if (pfd.revents & events) { + event->callback(pfd.fd, pfd.revents, event->data); + } + } + + // Dispatch timers + if (loop->timers->length) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + for (int i = 0; i < loop->timers->length; ++i) { + struct loop_timer *timer = loop->timers->items[i]; + bool expired = timer->expiry.tv_sec < now.tv_sec || + (timer->expiry.tv_sec == now.tv_sec && + timer->expiry.tv_nsec < now.tv_nsec); + if (expired) { + timer->callback(timer->data); + loop_remove_timer(loop, timer); + --i; + } + } + } +} + +void loop_add_fd(struct loop *loop, int fd, short mask, + void (*callback)(int fd, short mask, void *data), void *data) { + struct loop_fd_event *event = calloc(1, sizeof(struct loop_fd_event)); + if (!event) { + wlr_log(WLR_ERROR, "Unable to allocate memory for event"); + return; + } + event->callback = callback; + event->data = data; + list_add(loop->fd_events, event); + + struct pollfd pfd = {fd, mask, 0}; + + if (loop->fd_length == loop->fd_capacity) { + loop->fd_capacity += 10; + loop->fds = realloc(loop->fds, + sizeof(struct pollfd) * loop->fd_capacity); + } + + loop->fds[loop->fd_length++] = pfd; +} + +struct loop_timer *loop_add_timer(struct loop *loop, int ms, + void (*callback)(void *data), void *data) { + struct loop_timer *timer = calloc(1, sizeof(struct loop_timer)); + if (!timer) { + wlr_log(WLR_ERROR, "Unable to allocate memory for timer"); + return NULL; + } + timer->callback = callback; + timer->data = data; + + clock_gettime(CLOCK_MONOTONIC, &timer->expiry); + timer->expiry.tv_sec += ms / 1000; + + long int nsec = (ms % 1000) * 1000000; + if (timer->expiry.tv_nsec + nsec >= 1000000000) { + timer->expiry.tv_sec++; + nsec -= 1000000000; + } + timer->expiry.tv_nsec += nsec; + + list_add(loop->timers, timer); + + return timer; +} + +bool loop_remove_fd(struct loop *loop, int fd) { + for (int i = 0; i < loop->fd_length; ++i) { + if (loop->fds[i].fd == fd) { + free(loop->fd_events->items[i]); + list_del(loop->fd_events, i); + + loop->fd_length--; + memmove(&loop->fds[i], &loop->fds[i + 1], + sizeof(struct pollfd) * (loop->fd_length - i)); + + return true; + } + } + return false; +} + +bool loop_remove_timer(struct loop *loop, struct loop_timer *timer) { + for (int i = 0; i < loop->timers->length; ++i) { + if (loop->timers->items[i] == timer) { + list_del(loop->timers, i); + free(timer); + return true; + } + } + return false; +} diff --git a/common/meson.build b/common/meson.build index 44a29508..224a9c3f 100644 --- a/common/meson.build +++ b/common/meson.build @@ -5,6 +5,7 @@ lib_sway_common = static_library( 'cairo.c', 'ipc-client.c', 'log.c', + 'loop.c', 'list.c', 'pango.c', 'readline.c', diff --git a/include/loop.h b/include/loop.h new file mode 100644 index 00000000..2f608eda --- /dev/null +++ b/include/loop.h @@ -0,0 +1,54 @@ +#ifndef _SWAY_LOOP_H +#define _SWAY_LOOP_H +#include <stdbool.h> + +/** + * This is an event loop system designed for sway clients, not sway itself. + * + * The loop consists of file descriptors and timers. Typically the Wayland + * display's file descriptor will be one of the fds in the loop. + */ + +struct loop; +struct loop_timer; + +/** + * Create an event loop. + */ +struct loop *loop_create(void); + +/** + * Destroy the event loop (eg. on program termination). + */ +void loop_destroy(struct loop *loop); + +/** + * Poll the event loop. This will block until one of the fds has data. + */ +void loop_poll(struct loop *loop); + +/** + * Add a file descriptor to the loop. + */ +void loop_add_fd(struct loop *loop, int fd, short mask, + void (*func)(int fd, short mask, void *data), void *data); + +/** + * Add a timer to the loop. + * + * When the timer expires, the timer will be removed from the loop and freed. + */ +struct loop_timer *loop_add_timer(struct loop *loop, int ms, + void (*callback)(void *data), void *data); + +/** + * Remove a file descriptor from the loop. + */ +bool loop_remove_fd(struct loop *loop, int fd); + +/** + * Remove a timer from the loop. + */ +bool loop_remove_timer(struct loop *loop, struct loop_timer *timer); + +#endif diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 9ff3fe7b..58e2dee6 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -8,6 +8,7 @@ struct swaybar_config; struct swaybar_output; struct swaybar_workspace; +struct loop; struct swaybar_pointer { struct wl_pointer *pointer; @@ -66,6 +67,8 @@ struct swaybar { struct swaybar_pointer pointer; struct status_line *status; + struct loop *eventloop; + int ipc_event_socketfd; int ipc_socketfd; diff --git a/include/swaybar/event_loop.h b/include/swaybar/event_loop.h deleted file mode 100644 index 47be5b79..00000000 --- a/include/swaybar/event_loop.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef _SWAYBAR_EVENT_LOOP_H -#define _SWAYBAR_EVENT_LOOP_H -#include <stdbool.h> -#include <time.h> - -void add_event(int fd, short mask, - void(*cb)(int fd, short mask, void *data), - void *data); - -// Not guaranteed to notify cb immediately -void add_timer(timer_t timer, - void(*cb)(timer_t timer, void *data), - void *data); - -// Returns false if nothing exists, true otherwise -bool remove_event(int fd); - -// Returns false if nothing exists, true otherwise -bool remove_timer(timer_t timer); - -// Blocks and returns after sending callbacks -void event_loop_poll(void); - -void init_event_loop(void); - -#endif diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index 5e7e8771..957a808e 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -14,6 +14,8 @@ enum status_protocol { }; struct status_line { + struct swaybar *bar; + pid_t pid; int read_fd, write_fd; FILE *read, *write; diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index fbdd42a8..25b41a71 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -54,6 +54,10 @@ struct swaylock_password { }; struct swaylock_state { + struct loop *eventloop; + struct loop_timer *clear_indicator_timer; // clears the indicator + struct loop_timer *clear_password_timer; // clears the password buffer + struct loop_timer *verify_password_timer; struct wl_display *display; struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; diff --git a/swaybar/bar.c b/swaybar/bar.c index 9f72c94c..be290c18 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -18,7 +18,6 @@ #endif #include "swaybar/bar.h" #include "swaybar/config.h" -#include "swaybar/event_loop.h" #include "swaybar/i3bar.h" #include "swaybar/ipc.h" #include "swaybar/status_line.h" @@ -26,6 +25,7 @@ #include "ipc-client.h" #include "list.h" #include "log.h" +#include "loop.h" #include "pool-buffer.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" @@ -573,7 +573,7 @@ static void set_bar_dirty(struct swaybar *bar) { bool bar_setup(struct swaybar *bar, const char *socket_path) { bar_init(bar); - init_event_loop(); + bar->eventloop = loop_create(); bar->ipc_socketfd = ipc_open_socket(socket_path); bar->ipc_event_socketfd = ipc_open_socket(socket_path); @@ -582,6 +582,7 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { } if (bar->config->status_command) { bar->status = status_line_init(bar->config->status_command); + bar->status->bar = bar; } bar->display = wl_display_connect(NULL); @@ -646,21 +647,23 @@ static void status_in(int fd, short mask, void *data) { if (mask & (POLLHUP | POLLERR)) { status_error(bar->status, "[error reading from status command]"); set_bar_dirty(bar); - remove_event(fd); + loop_remove_fd(bar->eventloop, fd); } else if (status_handle_readable(bar->status)) { set_bar_dirty(bar); } } void bar_run(struct swaybar *bar) { - add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); - add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar); + loop_add_fd(bar->eventloop, wl_display_get_fd(bar->display), POLLIN, + display_in, bar); + loop_add_fd(bar->eventloop, bar->ipc_event_socketfd, POLLIN, ipc_in, bar); if (bar->status) { - add_event(bar->status->read_fd, POLLIN, status_in, bar); + loop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN, + status_in, bar); } while (1) { wl_display_flush(bar->display); - event_loop_poll(); + loop_poll(bar->eventloop); } } diff --git a/swaybar/event_loop.c b/swaybar/event_loop.c deleted file mode 100644 index 686b9962..00000000 --- a/swaybar/event_loop.c +++ /dev/null @@ -1,156 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <strings.h> -#include <poll.h> -#include <time.h> -#include "swaybar/event_loop.h" -#include "list.h" - -struct event_item { - void (*cb)(int fd, short mask, void *data); - void *data; -}; - -struct timer_item { - timer_t timer; - void (*cb)(timer_t timer, void *data); - void *data; -}; - -static struct { - // The order of each must be kept consistent - struct { /* pollfd array */ - struct pollfd *items; - int capacity; - int length; - } fds; - list_t *items; /* event_item list */ - - // Timer list - list_t *timers; -} event_loop; - -void add_timer(timer_t timer, - void(*cb)(timer_t timer, void *data), - void *data) { - - struct timer_item *item = malloc(sizeof(struct timer_item)); - item->timer = timer; - item->cb = cb; - item->data = data; - - list_add(event_loop.timers, item); -} - -void add_event(int fd, short mask, - void(*cb)(int fd, short mask, void *data), void *data) { - - struct pollfd pollfd = { - fd, - mask, - 0, - }; - - // Resize - if (event_loop.fds.length == event_loop.fds.capacity) { - event_loop.fds.capacity += 10; - event_loop.fds.items = realloc(event_loop.fds.items, - sizeof(struct pollfd) * event_loop.fds.capacity); - } - - event_loop.fds.items[event_loop.fds.length++] = pollfd; - - struct event_item *item = malloc(sizeof(struct event_item)); - item->cb = cb; - item->data = data; - - list_add(event_loop.items, item); - - return; -} - -bool remove_event(int fd) { - /* - * Instead of removing events immediately, we mark them for deletion - * and clean them up later. This is so we can call remove_event inside - * an event callback safely. - */ - for (int i = 0; i < event_loop.fds.length; ++i) { - if (event_loop.fds.items[i].fd == fd) { - event_loop.fds.items[i].fd = -1; - return true; - } - } - return false; -} - -static int timer_item_timer_cmp(const void *_timer_item, const void *_timer) { - const struct timer_item *timer_item = _timer_item; - const timer_t *timer = _timer; - if (timer_item->timer == *timer) { - return 0; - } else { - return -1; - } -} -bool remove_timer(timer_t timer) { - int index = list_seq_find(event_loop.timers, timer_item_timer_cmp, &timer); - if (index != -1) { - free(event_loop.timers->items[index]); - list_del(event_loop.timers, index); - return true; - } - return false; -} - -void event_loop_poll(void) { - poll(event_loop.fds.items, event_loop.fds.length, -1); - - for (int i = 0; i < event_loop.fds.length; ++i) { - struct pollfd pfd = event_loop.fds.items[i]; - struct event_item *item = (struct event_item *)event_loop.items->items[i]; - - // Always send these events - unsigned events = pfd.events | POLLHUP | POLLERR; - - if (pfd.revents & events) { - item->cb(pfd.fd, pfd.revents, item->data); - } - } - - // Cleanup removed events - int end = 0; - int length = event_loop.fds.length; - for (int i = 0; i < length; ++i) { - if (event_loop.fds.items[i].fd == -1) { - free(event_loop.items->items[i]); - list_del(event_loop.items, i); - --event_loop.fds.length; - } else if (end != i) { - event_loop.fds.items[end++] = event_loop.fds.items[i]; - } else { - end = i + 1; - } - } - - // check timers - // not tested, but seems to work - for (int i = 0; i < event_loop.timers->length; ++i) { - struct timer_item *item = event_loop.timers->items[i]; - int overrun = timer_getoverrun(item->timer); - if (overrun && overrun != -1) { - item->cb(item->timer, item->data); - } - } -} - -void init_event_loop(void) { - event_loop.fds.length = 0; - event_loop.fds.capacity = 10; - event_loop.fds.items = malloc( - event_loop.fds.capacity * sizeof(struct pollfd)); - event_loop.items = create_list(); - event_loop.timers = create_list(); -} diff --git a/swaybar/meson.build b/swaybar/meson.build index 7a02a33f..0c116172 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -2,7 +2,6 @@ executable( 'swaybar', [ 'bar.c', 'config.c', - 'event_loop.c', 'i3bar.c', 'ipc.c', 'main.c', diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 000609ce..65d6c052 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -7,16 +7,16 @@ #include <stdio.h> #include <unistd.h> #include <wlr/util/log.h> +#include "loop.h" #include "swaybar/bar.h" #include "swaybar/config.h" #include "swaybar/i3bar.h" -#include "swaybar/event_loop.h" #include "swaybar/status_line.h" #include "readline.h" static void status_line_close_fds(struct status_line *status) { if (status->read_fd != -1) { - remove_event(status->read_fd); + loop_remove_fd(status->bar->eventloop, status->read_fd); close(status->read_fd); status->read_fd = -1; } diff --git a/swaylock/main.c b/swaylock/main.c index d1384c6f..27bcfe32 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -21,6 +21,7 @@ #include "pool-buffer.h" #include "cairo.h" #include "log.h" +#include "loop.h" #include "readline.h" #include "stringop.h" #include "util.h" @@ -844,6 +845,10 @@ static int load_config(char *path, struct swaylock_state *state, static struct swaylock_state state; +static void display_in(int fd, short mask, void *data) { + wl_display_dispatch(state.display); +} + int main(int argc, char **argv) { wlr_log_init(WLR_DEBUG, NULL); initialize_pw_backend(); @@ -946,9 +951,14 @@ int main(int argc, char **argv) { daemonize(); } + state.eventloop = loop_create(); + loop_add_fd(state.eventloop, wl_display_get_fd(state.display), POLL_IN, + display_in, NULL); + state.run_display = true; - while (wl_display_dispatch(state.display) != -1 && state.run_display) { - // This space intentionally left blank + while (state.run_display) { + wl_display_flush(state.display); + loop_poll(state.eventloop); } free(state.args.font); diff --git a/swaylock/password.c b/swaylock/password.c index 50b81f6b..fecaecbf 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -8,6 +8,7 @@ #include <xkbcommon/xkbcommon.h> #include "swaylock/swaylock.h" #include "swaylock/seat.h" +#include "loop.h" #include "unicode.h" void clear_password_buffer(struct swaylock_password *pw) { @@ -39,6 +40,43 @@ static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { pw->len += utf8_size; } +static void clear_indicator(void *data) { + struct swaylock_state *state = data; + state->clear_indicator_timer = NULL; + state->auth_state = AUTH_STATE_IDLE; + damage_state(state); +} + +static void schedule_indicator_clear(struct swaylock_state *state) { + if (state->clear_indicator_timer) { + loop_remove_timer(state->eventloop, state->clear_indicator_timer); + } + state->clear_indicator_timer = loop_add_timer( + state->eventloop, 3000, clear_indicator, state); +} + +static void clear_password(void *data) { + struct swaylock_state *state = data; + state->clear_password_timer = NULL; + state->auth_state = AUTH_STATE_CLEAR; + clear_password_buffer(&state->password); + damage_state(state); + schedule_indicator_clear(state); +} + +static void schedule_password_clear(struct swaylock_state *state) { + if (state->clear_password_timer) { + loop_remove_timer(state->eventloop, state->clear_password_timer); + } + state->clear_password_timer = loop_add_timer( + state->eventloop, 10000, clear_password, state); +} + +static void handle_preverify_timeout(void *data) { + struct swaylock_state *state = data; + state->verify_password_timer = NULL; +} + void swaylock_handle_key(struct swaylock_state *state, xkb_keysym_t keysym, uint32_t codepoint) { switch (keysym) { @@ -50,7 +88,18 @@ void swaylock_handle_key(struct swaylock_state *state, state->auth_state = AUTH_STATE_VALIDATING; damage_state(state); - while (wl_display_dispatch(state->display) != -1 && state->run_display) { + + // We generally want to wait until all surfaces are showing the + // "verifying" state before we go and verify the password, because + // verifying it is a blocking operation. However, if the surface is on + // an output with DPMS off then it won't update, so we set a timer. + state->verify_password_timer = loop_add_timer( + state->eventloop, 50, handle_preverify_timeout, state); + + while (state->run_display && state->verify_password_timer) { + wl_display_flush(state->display); + loop_poll(state->eventloop); + bool ok = 1; struct swaylock_surface *surface; wl_list_for_each(surface, &state->surfaces, link) { @@ -70,6 +119,7 @@ void swaylock_handle_key(struct swaylock_state *state, } state->auth_state = AUTH_STATE_INVALID; damage_state(state); + schedule_indicator_clear(state); break; case XKB_KEY_Delete: case XKB_KEY_BackSpace: @@ -79,11 +129,14 @@ void swaylock_handle_key(struct swaylock_state *state, state->auth_state = AUTH_STATE_CLEAR; } damage_state(state); + schedule_indicator_clear(state); + schedule_password_clear(state); break; case XKB_KEY_Escape: clear_password_buffer(&state->password); state->auth_state = AUTH_STATE_CLEAR; damage_state(state); + schedule_indicator_clear(state); break; case XKB_KEY_Caps_Lock: /* The state is getting active after this @@ -91,6 +144,8 @@ void swaylock_handle_key(struct swaylock_state *state, state->xkb.caps_lock = !state->xkb.caps_lock; state->auth_state = AUTH_STATE_INPUT_NOP; damage_state(state); + schedule_indicator_clear(state); + schedule_password_clear(state); break; case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: @@ -104,12 +159,15 @@ void swaylock_handle_key(struct swaylock_state *state, case XKB_KEY_Super_R: state->auth_state = AUTH_STATE_INPUT_NOP; damage_state(state); + schedule_indicator_clear(state); + schedule_password_clear(state); break; case XKB_KEY_u: if (state->xkb.control) { clear_password_buffer(&state->password); state->auth_state = AUTH_STATE_CLEAR; damage_state(state); + schedule_indicator_clear(state); break; } // fallthrough @@ -118,6 +176,8 @@ void swaylock_handle_key(struct swaylock_state *state, append_ch(&state->password, codepoint); state->auth_state = AUTH_STATE_INPUT; damage_state(state); + schedule_indicator_clear(state); + schedule_password_clear(state); } break; } |