diff --git a/include/rootston/cursor.h b/include/rootston/cursor.h index f49b6439..b5af7de3 100644 --- a/include/rootston/cursor.h +++ b/include/rootston/cursor.h @@ -39,7 +39,6 @@ struct roots_cursor { uint32_t resize_edges; // Ring buffer of input events that could trigger move/resize/rotate int input_events_idx; - struct wl_list touch_points; struct roots_input_event input_events[16]; struct wl_listener motion; diff --git a/include/rootston/seat.h b/include/rootston/seat.h index bef515a4..5fb2f196 100644 --- a/include/rootston/seat.h +++ b/include/rootston/seat.h @@ -43,13 +43,6 @@ struct roots_touch { struct wl_list link; }; -struct roots_touch_point { - struct roots_touch *device; - int32_t slot; - double x, y; - struct wl_list link; -}; - struct roots_tablet_tool { struct roots_seat *seat; struct wlr_input_device *device; diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index 0db32eb2..c73a4c8d 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -126,4 +126,11 @@ void wlr_cursor_map_to_region(struct wlr_cursor *cur, struct wlr_box *box); void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, struct wlr_input_device *dev, struct wlr_box *box); +/** + * Convert absolute coordinates to layout coordinates for the device. + */ +bool wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, + struct wlr_input_device *device, double x_mm, double y_mm, + double width_mm, double height_mm, double *lx, double *ly); + #endif diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index a5f00402..e8477b18 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -103,6 +103,23 @@ struct wlr_seat_keyboard_state { struct wlr_seat_keyboard_grab *default_grab; }; +struct wlr_touch_point { + int32_t touch_id; + struct wlr_surface *surface; + struct wlr_seat_client *client; + double sx, sy; + + struct wl_listener surface_destroy; + struct wl_listener resource_destroy; + + struct wl_list link; +}; + +struct wlr_seat_touch_state { + struct wlr_seat *seat; + struct wl_list touch_points; // wlr_touch_point::link +}; + struct wlr_seat { struct wl_global *wl_global; struct wl_display *display; @@ -117,6 +134,7 @@ struct wlr_seat { struct wlr_seat_pointer_state pointer_state; struct wlr_seat_keyboard_state keyboard_state; + struct wlr_seat_touch_state touch_state; struct wl_listener selection_data_source_destroy; @@ -328,4 +346,17 @@ void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat); // TODO: May be useful to be able to simulate keyboard input events +struct wlr_touch_point *wlr_seat_touch_get_point(struct wlr_seat *seat, + int32_t touch_id); + +void wlr_seat_touch_notify_down(struct wlr_seat *seat, + struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, + double sy); + +void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, + int32_t touch_id); + +void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time, + int32_t touch_id, double sx, double sy); + #endif diff --git a/rootston/cursor.c b/rootston/cursor.c index ecd5e9a0..417bdaf5 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -214,50 +214,57 @@ void roots_cursor_handle_axis(struct roots_cursor *cursor, void roots_cursor_handle_touch_down(struct roots_cursor *cursor, struct wlr_event_touch_down *event) { - struct roots_touch_point *point = - calloc(1, sizeof(struct roots_touch_point)); - if (!point) { - wlr_log(L_ERROR, "could not allocate memory for touch point"); + struct roots_desktop *desktop = cursor->seat->input->server->desktop; + struct wlr_surface *surface = NULL; + double lx, ly; + bool result = + wlr_cursor_absolute_to_layout_coords(cursor->cursor, + event->device, event->x_mm, event->y_mm, event->width_mm, + event->height_mm, &lx, &ly); + if (!result) { return; } + double sx, sy; + view_at(desktop, lx, ly, &surface, &sx, &sy); - point->device = event->device->data; - point->slot = event->slot; - point->x = event->x_mm / event->width_mm; - point->y = event->y_mm / event->height_mm; - wlr_cursor_warp_absolute(cursor->cursor, event->device, point->x, point->y); - roots_cursor_update_position(cursor, event->time_msec); - wl_list_insert(&cursor->touch_points, &point->link); - roots_cursor_press_button(cursor, event->device, - event->time_msec, BTN_LEFT, 1); + if (surface) { + wlr_seat_touch_notify_down(cursor->seat->seat, surface, + event->time_msec, event->slot, sx, sy); + } } void roots_cursor_handle_touch_up(struct roots_cursor *cursor, struct wlr_event_touch_up *event) { - struct roots_touch_point *point; - wl_list_for_each(point, &cursor->touch_points, link) { - if (point->slot == event->slot) { - wl_list_remove(&point->link); - free(point); - break; - } - } - roots_cursor_press_button(cursor, event->device, - event->time_msec, BTN_LEFT, 0); + // TODO + wlr_seat_touch_notify_up(cursor->seat->seat, event->time_msec, event->slot); + //roots_cursor_press_button(cursor, event->device, event->time_msec, BTN_LEFT, 0); } void roots_cursor_handle_touch_motion(struct roots_cursor *cursor, struct wlr_event_touch_motion *event) { - struct roots_touch_point *point; - wl_list_for_each(point, &cursor->touch_points, link) { - if (point->slot == event->slot) { - point->x = event->x_mm / event->width_mm; - point->y = event->y_mm / event->height_mm; - wlr_cursor_warp_absolute(cursor->cursor, event->device, - point->x, point->y); - roots_cursor_update_position(cursor, event->time_msec); - break; - } + struct roots_desktop *desktop = cursor->seat->input->server->desktop; + struct wlr_touch_point *point = + wlr_seat_touch_get_point(cursor->seat->seat, event->slot); + if (!point) { + return; + } + + struct wlr_surface *surface = NULL; + double lx, ly; + bool result = + wlr_cursor_absolute_to_layout_coords(cursor->cursor, + event->device, event->x_mm, event->y_mm, event->width_mm, + event->height_mm, &lx, &ly); + if (!result) { + return; + } + + double sx, sy; + view_at(desktop, lx, ly, &surface, &sx, &sy); + + if (surface == point->surface) { + wlr_seat_touch_notify_motion(cursor->seat->seat, event->time_msec, + event->slot, sx, sy); } } diff --git a/rootston/seat.c b/rootston/seat.c index 6d8dc749..a99e2310 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -188,8 +188,6 @@ static void roots_seat_init_cursor(struct roots_seat *seat) { // TODO: be able to configure per-seat cursor themes seat->cursor->xcursor_manager = desktop->xcursor_manager; - wl_list_init(&seat->cursor->touch_points); - roots_seat_configure_cursor(seat); roots_seat_configure_xcursor(seat); diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index e8965b68..ec66f7c7 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -637,3 +637,21 @@ void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, c_device->mapped_box = box; } + +bool wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, + struct wlr_input_device *device, double x_mm, double y_mm, + double width_mm, double height_mm, double *lx, double *ly) { + if (width_mm <= 0 || height_mm <= 0) { + return false; + } + + struct wlr_box *mapping = get_mapping(cur, device); + if (!mapping) { + mapping = wlr_output_layout_get_box(cur->state->layout, NULL); + } + + *lx = x_mm > 0 ? mapping->width * (x_mm / width_mm) + mapping->x : cur->x; + *ly = y_mm > 0 ? mapping->height * (y_mm / height_mm) + mapping->y : cur->y; + + return true; +} diff --git a/types/wlr_seat.c b/types/wlr_seat.c index dad88354..d52a4bdc 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -338,6 +338,10 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_list_init( &wlr_seat->keyboard_state.surface_destroy.link); + // touch state + wlr_seat->touch_state.seat = wlr_seat; + wl_list_init(&wlr_seat->touch_state.touch_points); + struct wl_global *wl_global = wl_global_create(display, &wl_seat_interface, 6, wlr_seat, wl_seat_bind); if (!wl_global) { @@ -819,3 +823,120 @@ void wlr_seat_keyboard_notify_key(struct wlr_seat *seat, uint32_t time, struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; grab->interface->key(grab, time, key, state); } + +static void touch_point_destroy(struct wlr_touch_point *point) { + wl_list_remove(&point->surface_destroy.link); + wl_list_remove(&point->resource_destroy.link); + wl_list_remove(&point->link); + free(point); +} + +static void handle_touch_point_resource_destroy(struct wl_listener *listener, + void *data) { + struct wlr_touch_point *point = + wl_container_of(listener, point, resource_destroy); + touch_point_destroy(point); +} + +static void handle_touch_point_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_touch_point *point = + wl_container_of(listener, point, surface_destroy); + touch_point_destroy(point); +} + +static struct wlr_touch_point *touch_point_create( + struct wlr_seat *seat, int32_t touch_id, + struct wlr_surface *surface, double sx, double sy) { + struct wl_client *wl_client = wl_resource_get_client(surface->resource); + struct wlr_seat_client *client = wlr_seat_client_for_wl_client(seat, wl_client); + + if (!client || !client->touch) { + // touch points are not valid without a connected client with touch + return NULL; + } + + struct wlr_touch_point *point = calloc(1, sizeof(struct wlr_touch_point)); + if (!point) { + return NULL; + } + + point->touch_id = touch_id; + point->surface = surface; + point->client = client; + + point->sx = sx; + point->sy = sy; + + wl_signal_add(&surface->events.destroy, &point->surface_destroy); + point->surface_destroy.notify = handle_touch_point_surface_destroy; + wl_resource_add_destroy_listener(surface->resource, + &point->resource_destroy); + point->resource_destroy.notify = handle_touch_point_resource_destroy; + + wl_list_insert(&seat->touch_state.touch_points, &point->link); + + return point; +} + +struct wlr_touch_point *wlr_seat_touch_get_point( + struct wlr_seat *seat, int32_t touch_id) { + struct wlr_touch_point *point = NULL; + wl_list_for_each(point, &seat->touch_state.touch_points, link) { + if (point->touch_id == touch_id) { + return point; + } + } + + return NULL; +} + +void wlr_seat_touch_notify_down(struct wlr_seat *seat, + struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, + double sy) { + if (wlr_seat_touch_get_point(seat, touch_id)) { + wlr_log(L_ERROR, "got touch down for a touch point that's already down"); + return; + } + + struct wlr_touch_point *point = + touch_point_create(seat, touch_id, surface, sx, sy); + if (!point) { + wlr_log(L_ERROR, "could not create touch point"); + return; + } + + uint32_t serial = wl_display_next_serial(seat->display); + wl_touch_send_down(point->client->touch, serial, time, surface->resource, + touch_id, wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + wl_touch_send_frame(point->client->touch); +} + +void wlr_seat_touch_notify_up(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 notify up for unknown touch point"); + return; + } + + uint32_t serial = wl_display_next_serial(seat->display); + wl_touch_send_up(point->client->touch, serial, time, touch_id); + wl_touch_send_frame(point->client->touch); + touch_point_destroy(point); +} + +void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time, int32_t touch_id, + double sx, double sy) { + struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); + if (!point) { + wlr_log(L_ERROR, "got touch motion notify for unknown touch point"); + return; + } + + point->sx = sx; + point->sy = sy; + + wl_touch_send_motion(point->client->touch, time, touch_id, + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + wl_touch_send_frame(point->client->touch); +}