diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index b21e53bc..189ab59b 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -71,6 +71,7 @@ struct wlr_drag { bool cancelling; int32_t grab_touch_id; + struct wl_listener point_destroy; struct wl_listener icon_destroy; struct wl_listener source_destroy; struct wl_listener seat_client_unbound; diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index e05bea47..ee13f99d 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -28,9 +28,13 @@ struct wlr_touch_point { int32_t touch_id; struct wlr_surface *surface; struct wlr_seat_client *client; + + struct wlr_surface *focus_surface; + struct wlr_seat_client *focus_client; double sx, sy; struct wl_listener surface_destroy; + struct wl_listener focus_surface_destroy; struct wl_listener resource_destroy; struct { @@ -74,6 +78,8 @@ struct wlr_touch_grab_interface { struct wlr_touch_point *point); void (*motion)(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point); + void (*enter)(struct wlr_seat_touch_grab *grab, uint32_t time, + struct wlr_touch_point *point); // XXX this will conflict with the actual touch cancel which is different so // we need to rename this void (*cancel)(struct wlr_seat_touch_grab *grab); @@ -423,10 +429,24 @@ void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, * even if the surface is not the owner of the touch point for processing by * grabs. */ -void wlr_seat_touch_notify_motion(struct wlr_seat *seat, +void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time, + int32_t touch_id, double sx, double sy); + +/** + * Notify the seat that the touch point given by `touch_id` has entered a new + * surface. The surface is required. To clear focus, use + * `wlr_seat_touch_point_clear_focus()`. + */ +void wlr_seat_touch_point_focus(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, double sy); +/** + * Clear the focused surface for the touch point given by `touch_id`. + */ +void wlr_seat_touch_point_clear_focus(struct wlr_seat *seat, uint32_t time, + int32_t touch_id); + /** * Send a touch down event to the client of the given surface. All future touch * events for this point will go to this surface. If the touch down is valid, diff --git a/rootston/cursor.c b/rootston/cursor.c index eff81104..16441f4b 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -262,8 +262,13 @@ void roots_cursor_handle_touch_motion(struct roots_cursor *cursor, view_at(desktop, lx, ly, &surface, &sx, &sy); if (surface) { - wlr_seat_touch_notify_motion(cursor->seat->seat, surface, event->time_msec, + wlr_seat_touch_point_focus(cursor->seat->seat, surface, + event->time_msec, event->slot, sx, sy); + wlr_seat_touch_notify_motion(cursor->seat->seat, event->time_msec, event->slot, sx, sy); + } else { + wlr_seat_touch_point_clear_focus(cursor->seat->seat, event->time_msec, + event->slot); } } diff --git a/types/wlr_data_device.c b/types/wlr_data_device.c index 462320e0..57122818 100644 --- a/types/wlr_data_device.c +++ b/types/wlr_data_device.c @@ -554,6 +554,12 @@ static void touch_drag_motion(struct wlr_seat_touch_grab *grab, uint32_t time, } } +static void touch_drag_enter(struct wlr_seat_touch_grab *grab, uint32_t time, + struct wlr_touch_point *point) { + struct wlr_drag *drag = grab->data; + wlr_drag_set_focus(drag, point->focus_surface, point->sx, point->sy); +} + static void touch_drag_cancel(struct wlr_seat_touch_grab *grab) { struct wlr_drag *drag = grab->data; wlr_drag_end(drag); @@ -563,6 +569,7 @@ const struct wlr_touch_grab_interface wlr_data_device_touch_drag_interface = { .down = touch_drag_down, .up = touch_drag_up, .motion = touch_drag_motion, + .enter = touch_drag_enter, .cancel = touch_drag_cancel, }; diff --git a/types/wlr_seat.c b/types/wlr_seat.c index 80b9a7c3..eb73b174 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -313,8 +313,15 @@ static void default_touch_up(struct wlr_seat_touch_grab *grab, uint32_t time, static void default_touch_motion(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { - wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, - point->sy); + if (!point->focus_surface || point->focus_surface == point->surface) { + wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, + point->sy); + } +} + +static void default_touch_enter(struct wlr_seat_touch_grab *grab, + uint32_t time, struct wlr_touch_point *point) { + // not handled by default } static void default_touch_cancel(struct wlr_seat_touch_grab *grab) { @@ -325,6 +332,7 @@ static const struct wlr_touch_grab_interface default_touch_grab_impl = { .down = default_touch_down, .up = default_touch_up, .motion = default_touch_motion, + .enter = default_touch_enter, .cancel = default_touch_cancel, }; @@ -998,9 +1006,8 @@ void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, touch_point_destroy(point); } -void wlr_seat_touch_notify_motion(struct wlr_seat *seat, - struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, - double sy) { +void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time, + int32_t touch_id, double sx, double sy) { clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_touch_grab *grab = seat->touch_state.grab; struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); @@ -1015,6 +1022,74 @@ void wlr_seat_touch_notify_motion(struct wlr_seat *seat, grab->interface->motion(grab, time, point); } +static void touch_point_clear_focus(struct wlr_touch_point *point) { + if (point->focus_surface) { + wl_list_remove(&point->focus_surface_destroy.link); + point->focus_client = NULL; + point->focus_surface = NULL; + } +} + +static void handle_point_focus_destroy(struct wl_listener *listener, + void *data) { + struct wlr_touch_point *point = + wl_container_of(listener, point, focus_surface_destroy); + touch_point_clear_focus(point); +} + +static void touch_point_set_focus(struct wlr_touch_point *point, + struct wlr_surface *surface, double sx, double sy) { + if (point->focus_surface == surface) { + return; + } + + touch_point_clear_focus(point); + + if (surface && surface->resource) { + struct wlr_seat_client *client = + wlr_seat_client_for_wl_client(point->client->seat, + wl_resource_get_client(surface->resource)); + + if (client && client->touch) { + wl_signal_add(&surface->events.destroy, &point->focus_surface_destroy); + point->focus_surface_destroy.notify = handle_point_focus_destroy; + point->focus_surface = surface; + point->focus_client = client; + point->sx = sx; + point->sy = sy; + } + } +} + +void wlr_seat_touch_point_focus(struct wlr_seat *seat, + struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, + double sy) { + assert(surface); + struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); + if (!point) { + wlr_log(L_ERROR, "got touch point focus for unknown touch point"); + return; + } + struct wlr_surface *focus = point->focus_surface; + touch_point_set_focus(point, surface, sx, sy); + + if (focus != point->focus_surface) { + struct wlr_seat_touch_grab *grab = seat->touch_state.grab; + grab->interface->enter(grab, time, point); + } +} + +void wlr_seat_touch_point_clear_focus(struct wlr_seat *seat, uint32_t time, + int32_t touch_id) { + struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); + if (!point) { + wlr_log(L_ERROR, "got touch point focus for unknown touch point"); + return; + } + + touch_point_set_focus(point, NULL, 0, 0); +} + void wlr_seat_touch_send_down(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, double sy) {