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
This commit is contained in:
Daniel Barlow 2022-07-13 21:52:52 +01:00
parent 17814972ab
commit e7b0bae792
6 changed files with 233 additions and 0 deletions

View file

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

View file

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

View file

@ -15,6 +15,8 @@
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
@ -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;

View file

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

View file

@ -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},
};

View file

@ -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.