diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 2b89953f4b..48ef0dea79 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -46,6 +46,8 @@ #include "primary-selection-unstable-v1-client-protocol.h" #include "input-timestamps-unstable-v1-client-protocol.h" #include "pointer-gestures-unstable-v1-client-protocol.h" +#include "cursor-shape-v1-client-protocol.h" +#include "viewporter-client-protocol.h" #ifdef HAVE_LIBDECOR_H #include @@ -67,8 +69,6 @@ #include #include #include -#include "cursor-shape-v1-client-protocol.h" -#include "viewporter-client-protocol.h" // Weston uses a ratio of 10 units per scroll tick #define WAYLAND_WHEEL_AXIS_UNIT 10 @@ -289,6 +289,71 @@ void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display) } } +static void handle_pinch_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) +{ + if (!surface) { + return; + } + + SDL_WindowData *wind = Wayland_GetWindowDataForOwnedSurface(surface); + if (wind) { + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + seat->pointer.gesture_focus = wind; + + const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); + SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, timestamp, wind->sdlwindow, 0.0f); + } +} + +static void handle_pinch_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, + wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) +{ + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + + if (seat->pointer.gesture_focus) { + const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); + const float s = (float)wl_fixed_to_double(scale); + SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, timestamp, seat->pointer.gesture_focus->sdlwindow, s); + } +} + +static void handle_pinch_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) +{ + SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; + + if (seat->pointer.gesture_focus) { + const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time); + SDL_SendPinch(SDL_EVENT_PINCH_END, timestamp, seat->pointer.gesture_focus->sdlwindow, 0.0f); + + seat->pointer.gesture_focus = NULL; + } +} + +static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_listener = { + handle_pinch_begin, + handle_pinch_update, + handle_pinch_end +}; + +static void Wayland_SeatCreatePointerGestures(SDL_WaylandSeat *seat) +{ + if (seat->display->zwp_pointer_gestures) { + if (seat->pointer.wl_pointer && !seat->pointer.gesture_pinch) { + seat->pointer.gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(seat->display->zwp_pointer_gestures, seat->pointer.wl_pointer); + zwp_pointer_gesture_pinch_v1_set_user_data(seat->pointer.gesture_pinch, seat); + zwp_pointer_gesture_pinch_v1_add_listener(seat->pointer.gesture_pinch, &gesture_pinch_listener, seat); + } + } +} + +void Wayland_DisplayInitPointerGestureManager(SDL_VideoData *display) +{ + SDL_WaylandSeat *seat; + wl_list_for_each (seat, &display->seat_list, link) { + Wayland_SeatCreatePointerGestures(seat); + } +} + static void Wayland_SeatCreateCursorShape(SDL_WaylandSeat *seat) { if (seat->display->cursor_shape_manager) { @@ -1417,43 +1482,6 @@ static const struct wl_touch_listener touch_listener = { touch_handler_orientation // Version 6 }; -void pinch_begin(void *data, - struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, - uint32_t serial, - uint32_t time, - struct wl_surface *surface, - uint32_t fingers) -{ - SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, 0, NULL, 0); -} -void pinch_update(void *data, - struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, - uint32_t time, - wl_fixed_t dx, - wl_fixed_t dy, - wl_fixed_t scale, - wl_fixed_t rotation) -{ - - float s = (float)(wl_fixed_to_double(scale)); - SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, 0, NULL, s); -} - -void pinch_end(void *data, - struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, - uint32_t serial, - uint32_t time, - int32_t cancelled) -{ - SDL_SendPinch(SDL_EVENT_PINCH_END, 0, NULL, 0); -} - -static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_listener = { - pinch_begin, - pinch_update, - pinch_end -}; - // Fallback for xkb_keymap_key_get_mods_for_level(), which is only available from 1.0.0, while the SDL minimum is 0.5.0. #if !SDL_XKBCOMMON_CHECK_VERSION(1, 0, 0) static size_t xkb_legacy_get_mods_for_level(SDL_WaylandSeat *seat, xkb_keycode_t key, xkb_layout_index_t layout, xkb_level_index_t level, xkb_mod_mask_t *masks_out, size_t masks_size) @@ -2296,6 +2324,11 @@ static const struct wl_keyboard_listener keyboard_listener = { static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event) { + // End any active gestures. + if (seat->pointer.gesture_focus) { + SDL_SendPinch(SDL_EVENT_PINCH_END, 0, seat->pointer.gesture_focus->sdlwindow, 0.0f); + } + // Make sure focus is removed from a surface before the pointer is destroyed. if (seat->pointer.focus) { pointer_handle_leave(seat, seat->pointer.wl_pointer, 0, seat->pointer.focus->surface); @@ -2319,6 +2352,10 @@ static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event) zwp_input_timestamps_v1_destroy(seat->pointer.timestamps); } + if (seat->pointer.gesture_pinch) { + zwp_pointer_gesture_pinch_v1_destroy(seat->pointer.gesture_pinch); + } + if (seat->pointer.cursor_state.frame_callback) { wl_callback_destroy(seat->pointer.cursor_state.frame_callback); } @@ -2425,10 +2462,6 @@ static void Wayland_SeatDestroyTouch(SDL_WaylandSeat *seat) } } - if (seat->touch.gesture_pinch) { - zwp_pointer_gesture_pinch_v1_destroy(seat->touch.gesture_pinch); - } - SDL_zero(seat->touch); WAYLAND_wl_list_init(&seat->touch.points); } @@ -2447,6 +2480,9 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum w wl_pointer_set_user_data(seat->pointer.wl_pointer, seat); wl_pointer_add_listener(seat->pointer.wl_pointer, &pointer_listener, seat); + // Pointer gestures + Wayland_SeatCreatePointerGestures(seat); + seat->pointer.sdl_id = SDL_GetNextObjectID(); if (seat->name) { @@ -2472,12 +2508,6 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum w } SDL_AddTouch((SDL_TouchID)(uintptr_t)seat->touch.wl_touch, SDL_TOUCH_DEVICE_DIRECT, name_fmt); - - /* Pinch gesture */ - seat->touch.gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(seat->display->zwp_pointer_gestures, seat->pointer.wl_pointer); - zwp_pointer_gesture_pinch_v1_set_user_data(seat->touch.gesture_pinch, seat); - zwp_pointer_gesture_pinch_v1_add_listener(seat->touch.gesture_pinch, &gesture_pinch_listener, seat); - } else if (!(capabilities & WL_SEAT_CAPABILITY_TOUCH) && seat->touch.wl_touch) { Wayland_SeatDestroyTouch(seat); } diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index 70da05763a..03c44375f6 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -123,10 +123,14 @@ typedef struct SDL_WaylandSeat struct wp_cursor_shape_device_v1 *cursor_shape; struct zwp_locked_pointer_v1 *locked_pointer; struct zwp_confined_pointer_v1 *confined_pointer; + struct zwp_pointer_gesture_pinch_v1 *gesture_pinch; SDL_WindowData *focus; SDL_CursorData *current_cursor; + // According to the spec, a seat can only have one active gesture of any type at a time. + SDL_WindowData *gesture_focus; + Uint64 highres_timestamp_ns; Uint32 enter_serial; SDL_MouseButtonFlags buttons_pressed; @@ -192,7 +196,6 @@ typedef struct SDL_WaylandSeat struct zwp_input_timestamps_v1 *timestamps; Uint64 highres_timestamp_ns; struct wl_list points; - struct zwp_pointer_gesture_pinch_v1 *gesture_pinch; } touch; struct @@ -220,6 +223,7 @@ extern int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS); extern void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display); extern void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display); +extern void Wayland_DisplayInitPointerGestureManager(SDL_VideoData *display); extern void Wayland_DisplayInitTabletManager(SDL_VideoData *display); extern void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display); extern void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display); diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index cdfe4227b9..d4f97f6547 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -1324,7 +1324,8 @@ static void handle_registry_global(void *data, struct wl_registry *registry, uin } else if (SDL_strcmp(interface, "wp_pointer_warp_v1") == 0) { d->wp_pointer_warp_v1 = wl_registry_bind(d->registry, id, &wp_pointer_warp_v1_interface, 1); } else if (SDL_strcmp(interface, "zwp_pointer_gestures_v1") == 0) { - d->zwp_pointer_gestures = wl_registry_bind(d->registry, id, &zwp_pointer_gestures_v1_interface, 1); + d->zwp_pointer_gestures = wl_registry_bind(d->registry, id, &zwp_pointer_gestures_v1_interface, SDL_min(version, 3)); + Wayland_DisplayInitPointerGestureManager(d); } #ifdef SDL_WL_FIXES_VERSION else if (SDL_strcmp(interface, "wl_fixes") == 0) { @@ -1649,7 +1650,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this) } if (data->zwp_pointer_gestures) { - zwp_pointer_gestures_v1_destroy(data->zwp_pointer_gestures); + if (zwp_pointer_gestures_v1_get_version(data->zwp_pointer_gestures) >= ZWP_POINTER_GESTURES_V1_RELEASE_SINCE_VERSION) { + zwp_pointer_gestures_v1_release(data->zwp_pointer_gestures); + } else { + zwp_pointer_gestures_v1_destroy(data->zwp_pointer_gestures); + } data->zwp_pointer_gestures = NULL; }