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);