From e7b0bae792ffde873518fca7f5e3aaf70ad5ce1d Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Wed, 13 Jul 2022 21:52:52 +0100 Subject: [PATCH] add support for touch events (WIP) This is tested with Gtk clients. There are a number of TODO items: - hardcoded scaling for mapping the touch co-ordinate system into layout co-ordinates (works on my Pinephone :-). - we should send events to the client no matter what the Lua handler returns (I think the code to not do this is broken anyway...). Instead, if the handler returns true we should send the client a touch cancel event. - maybe there's a better way to deal with seat capabilities than adding a "touchpads" field to kiwmi_input --- include/input/cursor.h | 13 ++++ include/input/input.h | 1 + kiwmi/input/cursor.c | 133 ++++++++++++++++++++++++++++++++++++++ kiwmi/input/input.c | 13 ++++ kiwmi/luak/kiwmi_cursor.c | 56 ++++++++++++++++ lua_docs.md | 17 +++++ 6 files changed, 233 insertions(+) diff --git a/include/input/cursor.h b/include/input/cursor.h index 9f6c570..1fd1014 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -39,6 +39,10 @@ struct kiwmi_cursor { struct wl_listener cursor_button; struct wl_listener cursor_axis; struct wl_listener cursor_frame; + struct wl_listener cursor_touch_up; + struct wl_listener cursor_touch_down; + struct wl_listener cursor_touch_motion; + struct wl_listener cursor_touch_frame; struct { struct wl_signal button_down; @@ -46,6 +50,7 @@ struct kiwmi_cursor { struct wl_signal destroy; struct wl_signal motion; struct wl_signal scroll; + struct wl_signal touch; } events; }; @@ -61,6 +66,14 @@ struct kiwmi_cursor_motion_event { double newy; }; +struct kiwmi_cursor_touch_event { + char *event; + int id; + double x; + double y; + bool handled; +}; + struct kiwmi_cursor_scroll_event { const char *device_name; bool is_vertical; diff --git a/include/input/input.h b/include/input/input.h index 0889d34..eb65a82 100644 --- a/include/input/input.h +++ b/include/input/input.h @@ -16,6 +16,7 @@ struct kiwmi_input { struct wl_listener new_input; struct kiwmi_cursor *cursor; struct kiwmi_seat *seat; + int touchpads; struct { struct wl_signal keyboard_new; diff --git a/kiwmi/input/cursor.c b/kiwmi/input/cursor.c index beda260..15862be 100644 --- a/kiwmi/input/cursor.c +++ b/kiwmi/input/cursor.c @@ -15,6 +15,8 @@ #include #include #include +#include + #include #include #include @@ -120,6 +122,124 @@ cursor_motion_notify(struct wl_listener *listener, void *data) process_cursor_motion(server, event->time_msec); } +static void +cursor_touch_down_notify(struct wl_listener *listener, void *data) +{ + struct kiwmi_cursor *cursor = + wl_container_of(listener, cursor, cursor_touch_down); + struct kiwmi_server *server = cursor->server; + struct kiwmi_desktop *desktop = &server->desktop; + struct wlr_event_touch_down *event = data; + + struct kiwmi_input *input = &server->input; + struct wlr_seat *seat = input->seat->seat; + struct wlr_surface *surface = NULL; + + /* FIXME figure out what scaling should be done here. + * Does the touch co-ordinate system map onto a single output + * (e.g. phone screen) or onto the entire layout (tablet, + * maybe?) + */ + double lx = 720 * event->x; + double ly = 1440 * event->y; + double sx, sy; + + struct kiwmi_view *view = view_at( + desktop, + lx, ly, + &surface, + &sx, &sy); + + /* we send the event to lua with 0..1 co-ordinates, because + * it may not be over any surface + */ + struct kiwmi_cursor_touch_event new_event = { + .event = "down", + .id = event->touch_id, + .x = event->x, + .y = event->y, + }; + + wl_signal_emit(&cursor->events.touch, &new_event); + + if(!new_event.handled && + surface && + wlr_surface_accepts_touch(seat, surface)) { + wlr_seat_touch_notify_down(seat, surface, event->time_msec, + event->touch_id, sx, sy); + } +} + +static void +cursor_touch_up_notify(struct wl_listener *listener, void *data) +{ + struct kiwmi_cursor *cursor = + wl_container_of(listener, cursor, cursor_touch_up); + struct kiwmi_server *server = cursor->server; + struct wlr_event_touch_up *event = data; + struct kiwmi_input *input = &server->input; + struct wlr_seat *seat = input->seat->seat; + + struct kiwmi_cursor_touch_event new_event = { + .event = "up", + .id = event->touch_id, + }; + + wl_signal_emit(&cursor->events.touch, &new_event); + if(!new_event.handled) { + wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id); + } +} + +static void +cursor_touch_motion_notify(struct wl_listener *listener, void *data) +{ + struct kiwmi_cursor *cursor = + wl_container_of(listener, cursor, cursor_touch_motion); + struct kiwmi_server *server = cursor->server; + struct wlr_event_touch_motion *event = data; + struct kiwmi_input *input = &server->input; + struct wlr_seat *seat = input->seat->seat; + + struct kiwmi_cursor_touch_event new_event = { + .event = "motion", + .id = event->touch_id, + .x = event->x, + .y = event->y, + }; + + wl_signal_emit(&cursor->events.touch, &new_event); + + if(!new_event.handled) { + /* UNSURE: should we still send this even if the touch_down + * didn't get sent because the surface doesn't accept + * touch? */ + wlr_seat_touch_notify_motion(seat, event->time_msec, + event->touch_id, event->x, event->y); + } +} + +static void +cursor_touch_frame_notify(struct wl_listener *listener, void *data) +{ + struct kiwmi_cursor *cursor = + wl_container_of(listener, cursor, cursor_touch_frame); + struct kiwmi_server *server = cursor->server; + struct kiwmi_input *input = &server->input; + + struct wlr_seat *seat = input->seat->seat; + + struct kiwmi_cursor_touch_event new_event = { + .event = "frame", + }; + + wl_signal_emit(&cursor->events.touch, &new_event); + + if(!new_event.handled) { + wlr_seat_touch_notify_frame(seat); + } +} + static void cursor_motion_absolute_notify(struct wl_listener *listener, void *data) { @@ -253,10 +373,23 @@ cursor_create( cursor->cursor_frame.notify = cursor_frame_notify; wl_signal_add(&cursor->cursor->events.frame, &cursor->cursor_frame); + cursor->cursor_touch_down.notify = cursor_touch_down_notify; + wl_signal_add(&cursor->cursor->events.touch_down, &cursor->cursor_touch_down); + + cursor->cursor_touch_up.notify = cursor_touch_up_notify; + wl_signal_add(&cursor->cursor->events.touch_up, &cursor->cursor_touch_up); + + cursor->cursor_touch_motion.notify = cursor_touch_motion_notify; + wl_signal_add(&cursor->cursor->events.touch_motion, &cursor->cursor_touch_motion); + + cursor->cursor_touch_frame.notify = cursor_touch_frame_notify; + wl_signal_add(&cursor->cursor->events.touch_frame, &cursor->cursor_touch_frame); + wl_signal_init(&cursor->events.button_down); wl_signal_init(&cursor->events.button_up); wl_signal_init(&cursor->events.destroy); wl_signal_init(&cursor->events.motion); + wl_signal_init(&cursor->events.touch); wl_signal_init(&cursor->events.scroll); return cursor; diff --git a/kiwmi/input/input.c b/kiwmi/input/input.c index 1e3f27b..b5b0ad8 100644 --- a/kiwmi/input/input.c +++ b/kiwmi/input/input.c @@ -36,6 +36,13 @@ new_pointer(struct kiwmi_input *input, struct wlr_input_device *device) wl_list_insert(&input->pointers, &pointer->link); } +static void +new_touch(struct kiwmi_input *input, struct wlr_input_device *device) +{ + wlr_cursor_attach_input_device(input->cursor->cursor, device); + input->touchpads++; +} + static void new_keyboard(struct kiwmi_input *input, struct wlr_input_device *device) { @@ -61,6 +68,9 @@ new_input_notify(struct wl_listener *listener, void *data) case WLR_INPUT_DEVICE_POINTER: new_pointer(input, device); break; + case WLR_INPUT_DEVICE_TOUCH: + new_touch(input, device); + break; case WLR_INPUT_DEVICE_KEYBOARD: new_keyboard(input, device); break; @@ -73,6 +83,9 @@ new_input_notify(struct wl_listener *listener, void *data) if (!wl_list_empty(&input->keyboards)) { caps |= WL_SEAT_CAPABILITY_KEYBOARD; } + if(input->touchpads) { + caps |= WL_SEAT_CAPABILITY_TOUCH; + } wlr_seat_set_capabilities(input->seat->seat, caps); } diff --git a/kiwmi/luak/kiwmi_cursor.c b/kiwmi/luak/kiwmi_cursor.c index edd8c00..9d56841 100644 --- a/kiwmi/luak/kiwmi_cursor.c +++ b/kiwmi/luak/kiwmi_cursor.c @@ -153,6 +153,36 @@ kiwmi_cursor_on_motion_notify(struct wl_listener *listener, void *data) } } +static void +kiwmi_cursor_on_touch_notify(struct wl_listener *listener, void *data) +{ + struct kiwmi_lua_callback *lc = wl_container_of(listener, lc, listener); + struct kiwmi_server *server = lc->server; + lua_State *L = server->lua->L; + struct kiwmi_cursor_touch_event *event = data; + + lua_rawgeti(L, LUA_REGISTRYINDEX, lc->callback_ref); + + lua_newtable(L); + + lua_pushnumber(L, event->x); + lua_setfield(L, -2, "x"); + + lua_pushnumber(L, event->y); + lua_setfield(L, -2, "y"); + + lua_pushnumber(L, event->id); + lua_setfield(L, -2, "id"); + + lua_pushstring(L, event->event); + lua_setfield(L, -2, "name"); + + if (lua_pcall(L, 1, 0, 0)) { + wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1)); + lua_pop(L, 1); + } +} + static void kiwmi_cursor_on_scroll_notify(struct wl_listener *listener, void *data) { @@ -258,6 +288,31 @@ l_kiwmi_cursor_on_motion(lua_State *L) return 0; } +static int +l_kiwmi_cursor_on_touch(lua_State *L) +{ + struct kiwmi_object *obj = + *(struct kiwmi_object **)luaL_checkudata(L, 1, "kiwmi_cursor"); + luaL_checktype(L, 2, LUA_TFUNCTION); + + struct kiwmi_cursor *cursor = obj->object; + struct kiwmi_server *server = cursor->server; + + lua_pushcfunction(L, luaK_kiwmi_lua_callback_new); + lua_pushlightuserdata(L, server); + lua_pushvalue(L, 2); + lua_pushlightuserdata(L, kiwmi_cursor_on_touch_notify); + lua_pushlightuserdata(L, &cursor->events.touch); + lua_pushlightuserdata(L, obj); + + if (lua_pcall(L, 5, 0, 0)) { + wlr_log(WLR_ERROR, "%s", lua_tostring(L, -1)); + return 0; + } + + return 0; +} + static int l_kiwmi_cursor_on_scroll(lua_State *L) { @@ -287,6 +342,7 @@ static const luaL_Reg kiwmi_cursor_events[] = { {"button_down", l_kiwmi_cursor_on_button_down}, {"button_up", l_kiwmi_cursor_on_button_up}, {"motion", l_kiwmi_cursor_on_motion}, + {"touch", l_kiwmi_cursor_on_touch}, {"scroll", l_kiwmi_cursor_on_scroll}, {NULL, NULL}, }; diff --git a/lua_docs.md b/lua_docs.md index 7f63b60..00f29d1 100644 --- a/lua_docs.md +++ b/lua_docs.md @@ -157,6 +157,23 @@ The callback receives a table containing `device` with the device name, `vertica The callback is supposed to return `true` if the event was handled. The compositor will not forward it to the view under the cursor. +#### touch + +A touchpad or toushcreen was interacted with in some way. The callback +receives a table containing + +* `name`: one of `down`, `up`, `motion` or `frame` +* `id`: a number used to indicate which finger has dome something, used on devices that support "multitouch". For example, the first finger to touch the pad may be assigned id 0 and a second finger added may be assigned 1. The id may or may not be reassigned if a finger is lifted and replaced. +* `x`, `y`: for `down` or `motion` events, the location where the screen was touched. These each range between 0.0 and 1.0 + +All touch events are sent to the same callback, because the +appropriate mode of processing them is to collect messages until +there's a `frame` event and group them together. For more information see [the Wayland book](https://wayland-book.com/seat/touch.html) + +The callback is supposed to return `true` if the event was handled. +The compositor will not forward it to the view under the cursor. +_FIXME: should use gesture cancellation instead_ + ## kiwmi_keyboard A handle to a keyboard.