Commit 834ab350e5cd1fed9b023587875e5be1aec2a64b

Sam Lantinga 2017-08-21T11:19:38

Fixed bug 3644 - Wayland touch event support Moritz Bitsch Attached is a small patch which enables multitouch events on Wayland.

diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 7a777e1..961325f 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -52,6 +52,7 @@ struct SDL_WaylandInput {
     SDL_VideoData *display;
     struct wl_seat *seat;
     struct wl_pointer *pointer;
+    struct wl_touch *touch;
     struct wl_keyboard *keyboard;
     SDL_WaylandDataDevice *data_device;
     struct zwp_relative_pointer_v1 *relative_pointer;
@@ -71,6 +72,105 @@ struct SDL_WaylandInput {
     } xkb;
 };
 
+struct SDL_WaylandTouchPoint {
+    SDL_TouchID id;
+    float x;
+    float y;
+    struct wl_surface* surface;
+
+    struct SDL_WaylandTouchPoint* prev;
+    struct SDL_WaylandTouchPoint* next;
+};
+
+struct SDL_WaylandTouchPointList {
+    struct SDL_WaylandTouchPoint* head;
+    struct SDL_WaylandTouchPoint* tail;
+};
+
+static struct SDL_WaylandTouchPointList touch_points = {NULL, NULL};
+
+static void
+touch_add(SDL_TouchID id, float x, float y, struct wl_surface *surface)
+{
+    struct SDL_WaylandTouchPoint* tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint));
+
+    tp->id = id;
+    tp->x = x;
+    tp->y = y;
+    tp->surface = surface;
+
+    if (touch_points.tail) {
+        touch_points.tail->next = tp;
+        tp->prev = touch_points.tail;
+    } else {
+        touch_points.head = tp;
+        tp->prev = NULL;
+    }
+
+    touch_points.tail = tp;
+    tp->next = NULL;
+}
+
+static void
+touch_update(SDL_TouchID id, float x, float y)
+{
+    struct SDL_WaylandTouchPoint* tp = touch_points.head;
+
+    while (tp) {
+        if (tp->id == id) {
+            tp->x = x;
+            tp->y = y;
+        }
+
+        tp = tp->next;
+    }
+}
+
+static void
+touch_del(SDL_TouchID id, float* x, float* y)
+{
+    struct SDL_WaylandTouchPoint* tp = touch_points.head;
+
+    while (tp) {
+        if (tp->id == id) {
+            *x = tp->x;
+            *y = tp->y;
+
+            if (tp->prev) {
+                tp->prev->next = tp->next;
+            } else {
+                touch_points.head = tp->next;
+            }
+
+            if (tp->next) {
+                tp->next->prev = tp->prev;
+            } else {
+                touch_points.tail = tp->prev;
+            }
+
+            SDL_free(tp);
+        }
+
+        tp = tp->next;
+    }
+}
+
+static struct wl_surface*
+touch_surface(SDL_TouchID id)
+{
+    struct SDL_WaylandTouchPoint* tp = touch_points.head;
+
+    while (tp) {
+        if (tp->id == id) {
+            return tp->surface;
+        }
+
+        tp = tp->next;
+    }
+
+    return NULL;
+}
+
 void
 Wayland_PumpEvents(_THIS)
 {
@@ -269,6 +369,69 @@ static const struct wl_pointer_listener pointer_listener = {
 };
 
 static void
+touch_handler_down(void *data, struct wl_touch *touch, unsigned int serial,
+                   unsigned int timestamp, struct wl_surface *surface,
+                   int id, wl_fixed_t fx, wl_fixed_t fy)
+{
+    float x, y;
+    SDL_WindowData* window;
+
+    window = (SDL_WindowData *)wl_surface_get_user_data(surface);
+
+    x = wl_fixed_to_double(fx) / window->sdlwindow->w;
+    y = wl_fixed_to_double(fy) / window->sdlwindow->h;
+
+    touch_add(id, x, y, surface);
+    SDL_SendTouch(1, (SDL_FingerID)id, SDL_TRUE, x, y, 1.0f);
+}
+
+static void
+touch_handler_up(void *data, struct wl_touch *touch, unsigned int serial,
+                 unsigned int timestamp, int id)
+{
+    float x = 0, y = 0;
+
+    touch_del(id, &x, &y);
+    SDL_SendTouch(1, (SDL_FingerID)id, SDL_FALSE, x, y, 0.0f);
+}
+
+static void
+touch_handler_motion(void *data, struct wl_touch *touch, unsigned int timestamp,
+                     int id, wl_fixed_t fx, wl_fixed_t fy)
+{
+    float x, y;
+    SDL_WindowData* window;
+
+    window = (SDL_WindowData *)wl_surface_get_user_data(touch_surface(id));
+
+    x = wl_fixed_to_double(fx) / window->sdlwindow->w;
+    y = wl_fixed_to_double(fy) / window->sdlwindow->h;
+
+    touch_update(id, x, y);
+    SDL_SendTouchMotion(1, (SDL_FingerID)id, x, y, 1.0f);
+}
+
+static void
+touch_handler_frame(void *data, struct wl_touch *touch)
+{
+
+}
+
+static void
+touch_handler_cancel(void *data, struct wl_touch *touch)
+{
+
+}
+
+static const struct wl_touch_listener touch_listener = {
+    touch_handler_down,
+    touch_handler_up,
+    touch_handler_motion,
+    touch_handler_frame,
+    touch_handler_cancel
+};
+
+static void
 keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
                        uint32_t format, int fd, uint32_t size)
 {
@@ -420,6 +583,18 @@ seat_handle_capabilities(void *data, struct wl_seat *seat,
         input->pointer = NULL;
     }
 
+    if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
+        SDL_AddTouch(1, "wayland_touch");
+        input->touch = wl_seat_get_touch(seat);
+        wl_touch_set_user_data(input->touch, input);
+        wl_touch_add_listener(input->touch, &touch_listener,
+                                 input);
+    } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
+        SDL_DelTouch(1);
+        wl_touch_destroy(input->touch);
+        input->touch = NULL;
+    }
+
     if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
         input->keyboard = wl_seat_get_keyboard(seat);
         wl_keyboard_set_user_data(input->keyboard, input);
@@ -739,6 +914,11 @@ void Wayland_display_destroy_input(SDL_VideoData *d)
     if (input->pointer)
         wl_pointer_destroy(input->pointer);
 
+    if (input->touch) {
+        SDL_DelTouch(1);
+        wl_touch_destroy(input->touch);
+    }
+
     if (input->seat)
         wl_seat_destroy(input->seat);