From d4482400f18a3d6abd5a16c45175db580c049a98 Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Sat, 25 Jan 2020 23:35:01 +0100 Subject: [PATCH] swaybar: Implement pointer gesture swipe support After previous commits, swaybar bindsym statements would already accept the new pseudo button names for swipe gestures. Also, sway itself would not handle swipes when executed while the pointer was above a bar. Add the necessary handling for three- and four-finger swipe pointer gestures in swaybar. Cancel ongoing swipes when the pointer leaves the bar as to not confuse successive swipes with focus changes inbetween them. Add the pointer gestures protocol in client and server variants to the meson build file to make the necessary definitions available. Bind to the global interface on startup, instantiate a swipe gesture and add a listener on seat setup as well as destroy it on seat shutdown. Extend the sway-bar manual page as necessary. Test-plan: - add workspace switching to config like so: bar bar-0 { swaybar_command swaybar bindsym release SWIPE_3_LEFT workspace prev_on_output bindsym release SWIPE_4_RIGHT workspace next_on_output bindsym --release SWIPE_3_LEFT exec yad --text foo bindsym --release SWIPE_4_UP exec yad --text bar } - start sway and open two workspaces - position pointer above a bar surface - switch back and forth using horizontal three- and four-finger swipes, observing that different finger counts are necessary per direction - observe that the --release binding for left swipes is ignored because there is an on-press binding already - observe that the --release binding for upward swipes is honoured because there is no on-press binding - move pointer away from bar surface - observe that switching by swipe no longer works Signed-off-by: Michael Weiser --- include/swaybar/bar.h | 2 + include/swaybar/input.h | 17 ++++++ protocols/meson.build | 2 + sway/sway-bar.5.scd | 11 ++++ swaybar/bar.c | 4 ++ swaybar/input.c | 124 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 545a66a8b..97c0779b7 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/input.h b/include/swaybar/input.h index e8735d883..960189b6c 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -13,6 +13,15 @@ #define SWAY_CONTINUOUS_SCROLL_TIMEOUT 1000 #define SWAY_CONTINUOUS_SCROLL_THRESHOLD 10000 +#define SWAY_SWIPE_DIR_UP 0 +#define SWAY_SWIPE_DIR_DOWN 1 +#define SWAY_SWIPE_DIR_LEFT 2 +#define SWAY_SWIPE_DIR_RIGHT 3 +#define SWAY_SWIPE_DIR_COUNT 4 + +#define SWAY_SWIPE_3 SWAY_SCROLL_RIGHT + 1 +#define SWAY_SWIPE_4 SWAY_SWIPE_3 + SWAY_SWIPE_DIR_COUNT + struct swaybar; struct swaybar_output; @@ -39,6 +48,13 @@ struct swaybar_touch { struct touch_slot slots[16]; }; +struct swaybar_swipe { + struct zwp_pointer_gesture_swipe_v1 *swipe; + bool swiping; + uint8_t fingers; + double travel_x, travel_y; +}; + enum hotspot_event_handling { HOTSPOT_IGNORE, HOTSPOT_PROCESS, @@ -66,6 +82,7 @@ struct swaybar_seat { struct wl_seat *wl_seat; struct swaybar_pointer pointer; struct swaybar_touch touch; + struct swaybar_swipe swipe; struct wl_list link; // swaybar_seat:link struct swaybar_scroll_axis axis[2]; }; diff --git a/protocols/meson.build b/protocols/meson.build index 8c4ae0af5..344f8a28b 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'], ['wlr-layer-shell-unstable-v1.xml'], ['idle.xml'], @@ -24,6 +25,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/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 42e59d57d..9cbc1a5a9 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -34,6 +34,17 @@ runtime. debug-events*. To disable the default behavior for a button, use the command _nop_. + Special events _SWIPE\_{3,4}\_{UP,DOWN,LEFT,RIGHT}_ respond to three- and + four-finger swipe gestures respectively. Gestures have to happen while + the pointer is above a bar to be considered. Gesture bindings for other + contexts can be configured similarly in sway itself. The binding to + execute is selected based on the number of fingers used and ultimate + overall direction of the gesture. Because the latter cannot be + determined before the gesture has actually finished, bindings are only + ever executed on release. Therefore bindings with flag _--release_ are + ignored if there is an on-press binding as well because they would be + executed at the same time anyway. + *binding_mode_indicator* yes|no Enable or disable binding mode indicator. Default is _yes_. diff --git a/swaybar/bar.c b/swaybar/bar.c index 15eab7829..e60f76d54 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; @@ -367,6 +368,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, 1); } } diff --git a/swaybar/input.c b/swaybar/input.c index 6e13f1773..eeadc8a13 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,12 @@ 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; + + struct swaybar_swipe *swipe = &seat->swipe; + if (swipe->swiping) { + sway_log(SWAY_DEBUG, "Cancelling ongoing swipe due to pointer leaving bar"); + swipe->swiping = false; + } } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, @@ -463,6 +470,115 @@ static const struct wl_touch_listener touch_listener = { .orientation = wl_touch_orientation, }; +static uint32_t swipe_button_base(uint8_t fingers) { + if (fingers == 3) { + return SWAY_SWIPE_3; + } else if (fingers == 4) { + return SWAY_SWIPE_4; + } + + return 0; +} + +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; + struct swaybar_output *output; + + wl_list_for_each(output, &seat->bar->outputs, link) { + if (output->surface == surface) { + surface = NULL; + break; + } + } + + // we only want to respond to swipes on a bar surface + if (surface) { + return; + } + + // Do not execute any button press bindings because due to the + // nature of swipes we cannot know yet what kind of swipe it is going + // to be. + + struct swaybar_swipe *swipe = &seat->swipe; + swipe->swiping = true; + swipe->fingers = fingers; + swipe->travel_x = 0; + swipe->travel_y = 0; +} + +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; + struct swaybar_swipe *swipe = &seat->swipe; + + if (!swipe->swiping) { + sway_log(SWAY_DEBUG, "Ignoring swipe update without begin"); + return; + } + + swipe->travel_x += dx; + swipe->travel_y += dy; +} + +static uint32_t swipe_to_button(struct swaybar_swipe *swipe) { + uint8_t direction; + if (fabs(swipe->travel_x) > fabs(swipe->travel_y)) { + if (swipe->travel_x > 0) { + direction = SWAY_SWIPE_DIR_RIGHT; + } else { + direction = SWAY_SWIPE_DIR_LEFT; + } + } else { + if (swipe->travel_y > 0) { + direction = SWAY_SWIPE_DIR_DOWN; + } else { + direction = SWAY_SWIPE_DIR_UP; + } + } + + uint32_t button_base = swipe_button_base(swipe->fingers); + return button_base + direction; +} + +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; + struct swaybar_swipe *swipe = &seat->swipe; + + if (!swipe->swiping) { + sway_log(SWAY_DEBUG, "Ignoring swipe end without begin"); + return; + } + + swipe->swiping = false; + + // can happen when changing finger count mid-swipe or swiping off the + // tracking surface (touchpad) + if (cancelled) { + return; + } + + uint32_t button = swipe_to_button(swipe); + + // try to execute button press binding because now we know what kind of + // swipe this became. Ignore any on-release binding in case of success. + if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { + return; + } + + check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED); +} + +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; @@ -481,6 +597,11 @@ 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->swipe.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, + &swipe_listener, seat); } if (!have_touch && seat->touch.touch != NULL) { wl_touch_release(seat->touch.touch); @@ -517,6 +638,9 @@ void swaybar_seat_free(struct swaybar_seat *seat) { if (seat->touch.touch != NULL) { wl_touch_release(seat->touch.touch); } + if (seat->swipe.swipe != NULL) { + zwp_pointer_gesture_swipe_v1_destroy(seat->swipe.swipe); + } wl_seat_destroy(seat->wl_seat); wl_list_remove(&seat->link); free(seat);