diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index f178d9baf..1da1065e7 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -110,23 +110,48 @@ void init_seat_keyboard(struct wlr_wl_seat *seat) { &seat->wlr_keyboard.base); } -static void touch_coordinates_to_absolute(struct wlr_wl_seat *seat, - wl_fixed_t x, wl_fixed_t y, double *sx, double *sy) { - /** - * TODO: multi-output touch support - * Although the wayland backend supports multi-output pointers, the support - * for multi-output touch has been left on the side for simplicity reasons. - * If this is a feature you want/need, please open an issue on the wlroots - * tracker here https://gitlab.freedesktop.org/wlroots/wlroots/-/issues - */ - struct wlr_wl_output *output, *tmp; - wl_list_for_each_safe(output, tmp, &seat->backend->outputs, link) { - *sx = wl_fixed_to_double(x) / output->wlr_output.width; - *sy = wl_fixed_to_double(y) / output->wlr_output.height; - return; // Choose the first output in the list +static struct wlr_wl_output *first_wl_output(struct wlr_wl_seat *seat) { + struct wlr_wl_output *output; + wl_list_for_each(output, &seat->backend->outputs, link) { + return output; } + return NULL; +} - *sx = *sy = 0; +static struct wlr_wl_output *touch_output_for_id(struct wlr_wl_seat *seat, + int32_t id) { + struct wlr_wl_touch_points *points = &seat->touch_points; + for (size_t i = 0; i < points->len; i++) { + if (points->ids[i] == id) { + return points->outputs[i]; + } + } + return NULL; +} + +static void touch_set_output(struct wlr_wl_seat *seat, + struct wlr_wl_output *output) { + if (output == NULL) { + return; + } + const char *name = output->wlr_output.name; + if (seat->wlr_touch.output_name != NULL && + strcmp(seat->wlr_touch.output_name, name) == 0) { + return; + } + free(seat->wlr_touch.output_name); + seat->wlr_touch.output_name = strdup(name); +} + +static void touch_coordinates_to_absolute(struct wlr_wl_output *output, + wl_fixed_t x, wl_fixed_t y, double *sx, double *sy) { + if (output == NULL || output->wlr_output.width <= 0 || + output->wlr_output.height <= 0) { + *sx = *sy = 0; + return; + } + *sx = wl_fixed_to_double(x) / output->wlr_output.width; + *sy = wl_fixed_to_double(y) / output->wlr_output.height; } static void touch_handle_down(void *data, struct wl_touch *wl_touch, @@ -135,16 +160,25 @@ static void touch_handle_down(void *data, struct wl_touch *wl_touch, struct wlr_wl_seat *seat = data; struct wlr_touch *touch = &seat->wlr_touch; + struct wlr_wl_output *output = surface != NULL ? + get_wl_output_from_surface(seat->backend, surface) : NULL; + if (output == NULL) { + output = first_wl_output(seat); + } + struct wlr_wl_touch_points *points = &seat->touch_points; assert(points->len != sizeof(points->ids) / sizeof(points->ids[0])); + points->outputs[points->len] = output; points->ids[points->len++] = id; + touch_set_output(seat, output); + struct wlr_touch_down_event event = { .touch = touch, .time_msec = time, .touch_id = id, }; - touch_coordinates_to_absolute(seat, x, y, &event.x, &event.y); + touch_coordinates_to_absolute(output, x, y, &event.x, &event.y); wl_signal_emit_mutable(&touch->events.down, &event); } @@ -154,6 +188,8 @@ static bool remove_touch_point(struct wlr_wl_touch_points *points, int32_t id) { if (points->ids[i] == id) { size_t remaining = points->len - i - 1; memmove(&points->ids[i], &points->ids[i + 1], remaining * sizeof(id)); + memmove(&points->outputs[i], &points->outputs[i + 1], + remaining * sizeof(points->outputs[0])); points->len--; return true; } @@ -181,13 +217,16 @@ static void touch_handle_motion(void *data, struct wl_touch *wl_touch, struct wlr_wl_seat *seat = data; struct wlr_touch *touch = &seat->wlr_touch; + struct wlr_wl_output *output = touch_output_for_id(seat, id); + touch_set_output(seat, output); + struct wlr_touch_motion_event event = { .touch = touch, .time_msec = time, .touch_id = id, }; - touch_coordinates_to_absolute(seat, x, y, &event.x, &event.y); + touch_coordinates_to_absolute(output, x, y, &event.x, &event.y); wl_signal_emit_mutable(&touch->events.motion, &event); } @@ -244,11 +283,10 @@ void init_seat_touch(struct wlr_wl_seat *seat) { wlr_touch_init(&seat->wlr_touch, &touch_impl, name); - struct wlr_wl_output *output; - wl_list_for_each(output, &seat->backend->outputs, link) { - /* Multi-output touch not supported */ + struct wlr_wl_output *output = first_wl_output(seat); + if (output != NULL) { + /* Default until the first touch point lands on a known output */ seat->wlr_touch.output_name = strdup(output->wlr_output.name); - break; } wl_touch_add_listener(seat->wl_touch, &touch_listener, seat); diff --git a/include/backend/wayland.h b/include/backend/wayland.h index e24eb9fdf..1788bcc99 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -151,6 +151,7 @@ struct wlr_wl_pointer { struct wlr_wl_touch_points { int32_t ids[64]; + struct wlr_wl_output *outputs[64]; size_t len; };