From 2e64eeda423a2ed33e017fa52d315d2f81a89b48 Mon Sep 17 00:00:00 2001 From: Florian Franzen Date: Sat, 23 Apr 2022 10:34:42 +0200 Subject: [PATCH] swaybar: add bindgesture support Co-authored-by: Michael Weiser --- include/swaybar/bar.h | 2 + include/swaybar/config.h | 8 ++ include/swaybar/input.h | 5 + include/swaybar/ipc.h | 1 + protocols/meson.build | 2 + swaybar/bar.c | 4 + swaybar/config.c | 14 +++ swaybar/input.c | 198 +++++++++++++++++++++++++++++++++++++++ swaybar/ipc.c | 34 +++++++ 9 files changed, 268 insertions(+) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 3ad0bdf3c..8505b0968 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -6,6 +6,7 @@ #include "pool-buffer.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" +#include "pointer-gestures-unstable-v1-client-protocol.h" struct swaybar_config; struct swaybar_output; @@ -31,6 +32,7 @@ struct swaybar { struct zwlr_layer_shell_v1 *layer_shell; struct zxdg_output_manager_v1 *xdg_output_manager; struct wl_shm *shm; + struct zwp_pointer_gestures_v1 *pointer_gestures; struct swaybar_config *config; struct status_line *status; diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 4cacd21a4..d87fefe6b 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -4,6 +4,7 @@ #include #include #include "../include/config.h" +#include "gesture.h" #include "list.h" #include "util.h" @@ -24,6 +25,11 @@ struct swaybar_binding { bool release; }; +struct swaybar_gesture { + struct gesture gesture; + char *command; +}; + struct swaybar_config { char *status_command; bool pango_markup; @@ -40,6 +46,7 @@ struct swaybar_config { bool workspace_buttons; uint32_t workspace_min_width; list_t *bindings; + list_t *gestures; struct wl_list outputs; // config_output::link int height; int status_padding; @@ -90,5 +97,6 @@ struct swaybar_config *init_config(void); void free_config(struct swaybar_config *config); uint32_t parse_position(const char *position); void free_binding(struct swaybar_binding *binding); +void free_gesture(struct swaybar_gesture *gesture); #endif diff --git a/include/swaybar/input.h b/include/swaybar/input.h index e8735d883..bcb4de384 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -3,6 +3,7 @@ #include #include +#include "gesture.h" #include "list.h" #define SWAY_SCROLL_UP KEY_MAX + 1 @@ -66,6 +67,10 @@ struct swaybar_seat { struct wl_seat *wl_seat; struct swaybar_pointer pointer; struct swaybar_touch touch; + struct zwp_pointer_gesture_hold_v1 *hold; + struct zwp_pointer_gesture_pinch_v1 *pinch; + struct zwp_pointer_gesture_swipe_v1 *swipe; + struct gesture_tracker gestures; struct wl_list link; // swaybar_seat:link struct swaybar_scroll_axis axis[2]; }; diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h index d8cd0c761..2d2330695 100644 --- a/include/swaybar/ipc.h +++ b/include/swaybar/ipc.h @@ -8,5 +8,6 @@ bool handle_ipc_readable(struct swaybar *bar); bool ipc_get_workspaces(struct swaybar *bar); void ipc_send_workspace_command(struct swaybar *bar, const char *ws); void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind); +void ipc_execute_gesture(struct swaybar *bar, struct swaybar_gesture *gest); #endif diff --git a/protocols/meson.build b/protocols/meson.build index df24a4e59..4ddf3b483 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -14,6 +14,7 @@ protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'], [wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], @@ -25,6 +26,7 @@ protocols = [ client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['wlr-input-inhibitor-unstable-v1.xml'], ] diff --git a/swaybar/bar.c b/swaybar/bar.c index 5e4ebd97c..926a4a515 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -29,6 +29,7 @@ #include "pool-buffer.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" +#include "pointer-gestures-unstable-v1-client-protocol.h" void free_workspaces(struct wl_list *list) { struct swaybar_workspace *ws, *tmp; @@ -362,6 +363,9 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { bar->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 2); + } else if (strcmp(interface, zwp_pointer_gestures_v1_interface.name) == 0) { + bar->pointer_gestures = wl_registry_bind(registry, name, + &zwp_pointer_gestures_v1_interface, 3); } } diff --git a/swaybar/config.c b/swaybar/config.c index abedaec0e..59144f060 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -37,6 +37,7 @@ struct swaybar_config *init_config(void) { config->workspace_buttons = true; config->workspace_min_width = 0; config->bindings = create_list(); + config->gestures = create_list(); wl_list_init(&config->outputs); config->status_padding = 1; config->status_edge_padding = 3; @@ -93,6 +94,14 @@ void free_binding(struct swaybar_binding *binding) { free(binding); } +void free_gesture(struct swaybar_gesture *gesture) { + if (!gesture) { + return; + } + free(gesture->command); + free(gesture); +} + #if HAVE_TRAY void free_tray_binding(struct tray_binding *binding) { if (!binding) { @@ -114,6 +123,11 @@ void free_config(struct swaybar_config *config) { free_binding(binding); } list_free(config->bindings); + for (int i = 0; i < config->gestures->length; i++) { + struct swaybar_gesture *gesture = config->gestures->items[i]; + free_gesture(gesture); + } + list_free(config->gestures); struct config_output *coutput, *tmp; wl_list_for_each_safe(coutput, tmp, &config->outputs, link) { wl_list_remove(&coutput->link); diff --git a/swaybar/input.c b/swaybar/input.c index c8c8f0d4f..1189ff495 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -9,6 +9,7 @@ #include "swaybar/config.h" #include "swaybar/input.h" #include "swaybar/ipc.h" +#include "pointer-gestures-unstable-v1-client-protocol.h" void free_hotspots(struct wl_list *list) { struct swaybar_hotspot *hotspot, *tmp; @@ -118,6 +119,11 @@ static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { struct swaybar_seat *seat = data; seat->pointer.current = NULL; + + // Cancel any ongoing gesture if pointer leaves bar + if(!gesture_tracker_check(&seat->gestures, GESTURE_TYPE_NONE)) { + gesture_tracker_cancel(&seat->gestures); + } } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, @@ -461,6 +467,174 @@ static const struct wl_touch_listener touch_listener = { .orientation = wl_touch_orientation, }; + +static bool gesture_on_bar(struct swaybar_seat *seat, + struct wl_surface *surface) { + struct swaybar_output *output; + + wl_list_for_each(output, &seat->bar->outputs, link) { + if (output->surface == surface) { + return true; + } + } + + return false; +} + +// Check if bar has any binding for gesture +static bool bar_gestures_check(struct swaybar *bar, + enum gesture_type type, uint8_t fingers) { + for (int i = 0; i < bar->config->gestures->length; i++) { + struct swaybar_gesture *binding = bar->config->gestures->items[i]; + if (gesture_check(&binding->gesture, type, fingers)) { + return true; + } + } + + return false; +} + +// Check bar gesture binding for match +static struct swaybar_gesture *bar_gestures_match(struct swaybar *bar, + struct gesture *gesture) { + struct swaybar_gesture *current = NULL; + + for (int i = 0; i < bar->config->gestures->length; i++) { + struct swaybar_gesture *binding = bar->config->gestures->items[i]; + if (gesture_match(&binding->gesture, gesture, false)) { + if (current && + gesture_compare(¤t->gesture, &binding->gesture) < 0) { + continue; + } + current = binding; + } + } + + return current; +} + +static void gesture_begin(struct swaybar_seat* seat, struct wl_surface *surface, + enum gesture_type type, uint8_t fingers) { + // We only want to respond to holds on a bar surface + if (!gesture_on_bar(seat, surface)) { + return; + } + + // Ensure there is a binding for this gesture + if (!bar_gestures_check(seat->bar, type, fingers)) { + sway_log(SWAY_DEBUG, "Ignore tracking gesture without binding: %s:%u:any", + gesture_type_string(type), fingers); + return; + } + + gesture_tracker_begin(&seat->gestures, type, fingers); +} + +static void gesture_end(struct swaybar_seat* seat, enum gesture_type type, + bool cancelled) { + + if (!gesture_tracker_check(&seat->gestures, type)) { + return; + } + + if (cancelled) { + gesture_tracker_cancel(&seat->gestures); + return; + } + + struct gesture *gesture = gesture_tracker_end(&seat->gestures); + struct swaybar_gesture* binding = bar_gestures_match(seat->bar, gesture); + + if (binding) { + ipc_execute_gesture(seat->bar, binding); + } +} + +static void hold_begin(void *data, struct zwp_pointer_gesture_hold_v1 *wl_hold, + uint32_t serial, uint32_t time,struct wl_surface *surface, + uint32_t fingers) { + struct swaybar_seat *seat = data; + gesture_begin(seat, surface, GESTURE_TYPE_HOLD, fingers); +} + +static void hold_end(void *data, + struct zwp_pointer_gesture_hold_v1 *wl_hold, + uint32_t serial, uint32_t time, int32_t cancelled) { + struct swaybar_seat *seat = data; + gesture_end(seat, GESTURE_TYPE_HOLD, cancelled); +} + +static const struct zwp_pointer_gesture_hold_v1_listener hold_listener = { + .begin = hold_begin, + .end = hold_end, +}; + +static void pinch_begin(void *data, + struct zwp_pointer_gesture_pinch_v1 *wl_pinch, uint32_t serial, + uint32_t time, struct wl_surface *surface, uint32_t fingers) { + struct swaybar_seat *seat = data; + gesture_begin(seat, surface, GESTURE_TYPE_PINCH, fingers); +} + +static void pinch_update(void *data, + struct zwp_pointer_gesture_pinch_v1 *wl_pinch, + uint32_t time, wl_fixed_t dx, wl_fixed_t dy, + wl_fixed_t scale, wl_fixed_t rotation) { + struct swaybar_seat *seat = data; + if (gesture_tracker_check(&seat->gestures, GESTURE_TYPE_PINCH)) { + gesture_tracker_update(&seat->gestures, + wl_fixed_to_double(dx), wl_fixed_to_double(dy), + wl_fixed_to_double(scale), + wl_fixed_to_double(rotation)); + } +} + +static void pinch_end(void *data, + struct zwp_pointer_gesture_pinch_v1 *wl_pinch, + uint32_t serial, uint32_t time, int32_t cancelled) { + struct swaybar_seat *seat = data; + gesture_end(seat, GESTURE_TYPE_PINCH, cancelled); +} + +static const struct zwp_pointer_gesture_pinch_v1_listener pinch_listener = { + .begin = pinch_begin, + .update = pinch_update, + .end = pinch_end, +}; + +static void swipe_begin(void *data, + struct zwp_pointer_gesture_swipe_v1 *wl_swipe, + uint32_t serial, uint32_t time, + struct wl_surface *surface, uint32_t fingers) { + struct swaybar_seat *seat = data; + gesture_begin(seat, surface, GESTURE_TYPE_SWIPE, fingers); +} + +static void swipe_update(void *data, + struct zwp_pointer_gesture_swipe_v1 *wl_swipe, + uint32_t time, wl_fixed_t dx, wl_fixed_t dy) { + struct swaybar_seat *seat = data; + if (gesture_tracker_check(&seat->gestures, GESTURE_TYPE_SWIPE)) { + gesture_tracker_update(&seat->gestures, + wl_fixed_to_double(dx), wl_fixed_to_double(dy), + NAN, NAN); + } +} + +static void swipe_end(void *data, + struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, + uint32_t serial, uint32_t time, int32_t cancelled) { + struct swaybar_seat *seat = data; + gesture_end(seat, GESTURE_TYPE_SWIPE, cancelled); +} + +static const struct zwp_pointer_gesture_swipe_v1_listener swipe_listener = { + .begin = swipe_begin, + .update = swipe_update, + .end = swipe_end, +}; + + static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct swaybar_seat *seat = data; @@ -479,6 +653,21 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, assert(seat->pointer.cursor_surface); } wl_pointer_add_listener(seat->pointer.pointer, &pointer_listener, seat); + + seat->hold = zwp_pointer_gestures_v1_get_hold_gesture( + seat->bar->pointer_gestures, seat->pointer.pointer); + zwp_pointer_gesture_hold_v1_add_listener(seat->hold, + &hold_listener, seat); + + seat->pinch = zwp_pointer_gestures_v1_get_pinch_gesture( + seat->bar->pointer_gestures, seat->pointer.pointer); + zwp_pointer_gesture_pinch_v1_add_listener(seat->pinch, + &pinch_listener, seat); + + seat->swipe = zwp_pointer_gestures_v1_get_swipe_gesture( + seat->bar->pointer_gestures, seat->pointer.pointer); + zwp_pointer_gesture_swipe_v1_add_listener(seat->swipe, + &swipe_listener, seat); } if (!have_touch && seat->touch.touch != NULL) { wl_touch_release(seat->touch.touch); @@ -515,6 +704,15 @@ void swaybar_seat_free(struct swaybar_seat *seat) { if (seat->touch.touch != NULL) { wl_touch_release(seat->touch.touch); } + if (seat->hold != NULL) { + zwp_pointer_gesture_hold_v1_destroy(seat->hold); + } + if (seat->pinch != NULL) { + zwp_pointer_gesture_pinch_v1_destroy(seat->pinch); + } + if (seat->swipe != NULL) { + zwp_pointer_gesture_swipe_v1_destroy(seat->swipe); + } wl_seat_destroy(seat->wl_seat); wl_list_remove(&seat->link); free(seat); diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 2cb235bf4..4df0fbef4 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -140,6 +140,30 @@ static bool ipc_parse_config( } } + json_object *gestures = json_object_object_get(bar_config, "gestures"); + while (config->gestures->length) { + struct swaybar_gesture *binding = config->gestures->items[0]; + list_del(config->gestures, 0); + free_gesture(binding); + } + if (gestures) { + int length = json_object_array_length(gestures); + for (int i = 0; i < length; ++i) { + json_object *bindobj = json_object_array_get_idx(gestures, i); + struct swaybar_gesture *binding = + calloc(1, sizeof(struct swaybar_gesture)); + binding->gesture.type = json_object_get_int( + json_object_object_get(bindobj, "type")); + binding->gesture.fingers = json_object_get_int( + json_object_object_get(bindobj, "fingers")); + binding->gesture.directions = json_object_get_int( + json_object_object_get(bindobj, "directions")); + binding->command = strdup(json_object_get_string( + json_object_object_get(bindobj, "command"))); + list_add(config->gestures, binding); + } + } + json_object *colors = json_object_object_get(bar_config, "colors"); if (colors) { ipc_parse_colors(config, colors); @@ -414,6 +438,16 @@ void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) { IPC_COMMAND, bind->command, &len)); } +void ipc_execute_gesture(struct swaybar *bar, struct swaybar_gesture *bind) { + char *description = gesture_to_string(&bind->gesture); + sway_log(SWAY_DEBUG, "Executing binding for gesture %s: `%s`", + description, bind->command); + free(description); + uint32_t len = strlen(bind->command); + free(ipc_single_command(bar->ipc_socketfd, + IPC_COMMAND, bind->command, &len)); +} + bool ipc_initialize(struct swaybar *bar) { uint32_t len = strlen(bar->id); char *res = ipc_single_command(bar->ipc_socketfd,