diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index f69e41f8..36b64836 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -270,7 +270,8 @@ void handle_tablet_tool_proximity(struct libinput_event *event, // If the tool is not unique, libinput will not find it again after the // proximity out, so we should destroy it - if (!tool->unique) { + if (!tool->unique && + libinput_event_tablet_tool_get_proximity_state(tevent) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) { // The tool isn't unique, it can't be on multiple tablets assert(tool->pad_refs == 1); struct wlr_libinput_tablet *tablet = diff --git a/include/rootston/seat.h b/include/rootston/seat.h index 31ddd6fa..1a0b889a 100644 --- a/include/rootston/seat.h +++ b/include/rootston/seat.h @@ -111,6 +111,19 @@ struct roots_tablet_pad { struct wl_listener tablet_destroy; }; +struct roots_tablet_tool_tool { + struct wl_list link; + struct wl_list tool_link; + struct wlr_tablet_v2_tablet_tool *tablet_v2_tool; + + struct roots_seat *seat; + + struct wl_listener tool_destroy; + + struct roots_tablet_tool *current_tablet; + struct wl_listener tablet_destroy; +}; + struct roots_seat *roots_seat_create(struct roots_input *input, char *name); void roots_seat_destroy(struct roots_seat *seat); diff --git a/include/wlr/types/wlr_tablet_v2.h b/include/wlr/types/wlr_tablet_v2.h index 4b2fffe4..a07a0177 100644 --- a/include/wlr/types/wlr_tablet_v2.h +++ b/include/wlr/types/wlr_tablet_v2.h @@ -7,6 +7,7 @@ #include "tablet-unstable-v2-protocol.h" +struct wlr_tablet_client_v2; struct wlr_tablet_tool_client_v2; struct wlr_tablet_pad_client_v2; @@ -27,6 +28,8 @@ struct wlr_tablet_v2_tablet { struct wl_list clients; // wlr_tablet_client_v2::tablet_link struct wl_listener tool_destroy; + + struct wlr_tablet_client_v2 *current_client; }; struct wlr_tablet_v2_tablet_tool { @@ -37,6 +40,9 @@ struct wlr_tablet_v2_tablet_tool { struct wl_listener tool_destroy; struct wlr_tablet_tool_client_v2 *current_client; + struct wlr_surface *focused_surface; + struct wl_listener surface_destroy; + struct wl_listener client_destroy; }; struct wlr_tablet_v2_tablet_pad { @@ -79,6 +85,12 @@ uint32_t wlr_send_tablet_v2_tablet_tool_proximity_in( void wlr_send_tablet_v2_tablet_tool_motion( struct wlr_tablet_v2_tablet_tool *tool, double x, double y); +void wlr_send_tablet_v2_tablet_tool_distance( + struct wlr_tablet_v2_tablet_tool *tool, uint32_t distance); + +void wlr_send_tablet_v2_tablet_tool_wheel( + struct wlr_tablet_v2_tablet_tool *tool, double delta, int32_t clicks); + void wlr_send_tablet_v2_tablet_tool_proximity_out( struct wlr_tablet_v2_tablet_tool *tool); @@ -101,4 +113,7 @@ uint32_t wlr_send_tablet_v2_tablet_pad_leave(struct wlr_tablet_v2_tablet_pad *pa uint32_t wlr_send_tablet_v2_tablet_pad_mode(struct wlr_tablet_v2_tablet_pad *pad, size_t group, uint32_t mode, uint32_t time); + +bool wlr_surface_accepts_tablet_v2(struct wlr_tablet_v2_tablet *tablet, + struct wlr_surface *surface); #endif /* WLR_TYPES_WLR_TABLET_V2_H */ diff --git a/rootston/seat.c b/rootston/seat.c index 8f84fc4b..eb909e78 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -101,13 +101,75 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { roots_cursor_handle_touch_motion(cursor, event); } +static void handle_tablet_tool_position(struct roots_cursor *cursor, + struct roots_tablet_tool *tool, + struct wlr_tablet_tool_tool *tool_tool, + bool change_x, bool change_y, double x, double y, + uint32_t time) { + if (!change_x && !change_y) { + return; + } + + wlr_cursor_warp_absolute(cursor->cursor, tool->device, + change_x ? x : -1 , change_y ? y : -1); + + double sx, sy; + struct roots_view *view = NULL; + struct roots_seat *seat = cursor->seat; + struct roots_desktop *desktop = seat->input->server->desktop; + struct wlr_surface *surface = desktop_surface_at(desktop, + cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view); + struct roots_tablet_tool_tool *roots_tool = tool_tool->data; + + if (!surface) { + wlr_send_tablet_v2_tablet_tool_proximity_out(roots_tool->tablet_v2_tool); + /* XXX: TODO: Fallback pointer semantics */ + return; + } + + if (!wlr_surface_accepts_tablet_v2(tool->tablet_v2, surface)) { + wlr_send_tablet_v2_tablet_tool_proximity_out(roots_tool->tablet_v2_tool); + /* XXX: TODO: Fallback pointer semantics */ + return; + } + + wlr_send_tablet_v2_tablet_tool_proximity_in(roots_tool->tablet_v2_tool, + tool->tablet_v2, surface); + + wlr_send_tablet_v2_tablet_tool_motion(roots_tool->tablet_v2_tool, sx, sy); +} + static void handle_tool_axis(struct wl_listener *listener, void *data) { + wlr_log(L_DEBUG, "Tool Axis"); struct roots_cursor *cursor = wl_container_of(listener, cursor, tool_axis); struct roots_desktop *desktop = cursor->seat->input->server->desktop; wlr_idle_notify_activity(desktop->idle, cursor->seat->seat); struct wlr_event_tablet_tool_axis *event = data; - roots_cursor_handle_tool_axis(cursor, event); + struct roots_tablet_tool_tool *roots_tool = event->tool->data; + + if (!roots_tool) { + wlr_log(L_DEBUG, "Tool Axis, before proximity"); + return; + } + + /** + * We need to handle them ourselves, not pass it into the cursor + * without any consideration + */ + handle_tablet_tool_position(cursor, event->device->data, event->tool, + event->updated_axes & WLR_TABLET_TOOL_AXIS_X, + event->updated_axes & WLR_TABLET_TOOL_AXIS_Y, + event->x, event->y, event->time_msec); + + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) { + wlr_send_tablet_v2_tablet_tool_distance(roots_tool->tablet_v2_tool, event->distance); + } + + if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) { + wlr_send_tablet_v2_tablet_tool_wheel(roots_tool->tablet_v2_tool, event->wheel_delta, 0); + } + //roots_cursor_handle_tool_axis(cursor, event); } static void handle_tool_tip(struct wl_listener *listener, void *data) { @@ -119,7 +181,22 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { roots_cursor_handle_tool_tip(cursor, event); } +static void handle_tablet_tool_tool_destroy(struct wl_listener *listener, void *data) { + wlr_log(L_DEBUG, "Tool destroy"); + struct roots_tablet_tool_tool *tool = + wl_container_of(listener, tool, tool_destroy); + + wl_list_remove(&tool->link); + wl_list_remove(&tool->tool_link); + + wl_list_remove(&tool->tool_destroy.link); + wl_list_remove(&tool->tablet_destroy.link); + + free(tool); +} + static void handle_tool_proximity(struct wl_listener *listener, void *data) { + wlr_log(L_DEBUG, "Tool Proximity"); struct roots_cursor *cursor = wl_container_of(listener, cursor, tool_proximity); struct roots_desktop *desktop = cursor->seat->input->server->desktop; @@ -128,8 +205,18 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_tool *tool = event->tool; if (!tool->data) { - tool->data = wlr_make_tablet_tool(desktop->tablet_v2, cursor->seat->seat, tool); + struct roots_tablet_tool_tool *roots_tool = + calloc(1, sizeof(struct roots_tablet_tool_tool)); + roots_tool->seat = cursor->seat; + tool->data = roots_tool; + roots_tool->tablet_v2_tool = + wlr_make_tablet_tool(desktop->tablet_v2, cursor->seat->seat, tool); + roots_tool->tool_destroy.notify = handle_tablet_tool_tool_destroy; + wl_signal_add(&tool->events.destroy, &roots_tool->tool_destroy); } + + handle_tablet_tool_position(cursor, event->device->data, event->tool, + true, true, event->x, event->y, event->time_msec); } static void handle_request_set_cursor(struct wl_listener *listener, diff --git a/types/wlr_tablet_v2.c b/types/wlr_tablet_v2.c index f980aa0d..042713b7 100644 --- a/types/wlr_tablet_v2.c +++ b/types/wlr_tablet_v2.c @@ -950,8 +950,11 @@ static void send_tool_frame(void *data) { } static void queue_tool_frame(struct wlr_tablet_tool_client_v2 *tool) { + struct wl_display *display = wl_client_get_display(tool->client); + struct wl_event_loop *loop = wl_display_get_event_loop(display); if (!tool->frame_source) { - tool->frame_source = wl_event_loop_add_idle(NULL, send_tool_frame, tool); + tool->frame_source = + wl_event_loop_add_idle(loop, send_tool_frame, tool); } } @@ -961,6 +964,10 @@ uint32_t wlr_send_tablet_v2_tablet_tool_proximity_in( struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); + if (tool->focused_surface == surface) { + return 0; + } + struct wlr_tablet_client_v2 *tablet_tmp; struct wlr_tablet_client_v2 *tablet_client = NULL; wl_list_for_each(tablet_tmp, &tablet->clients, tablet_link) { @@ -1004,6 +1011,7 @@ uint32_t wlr_send_tablet_v2_tablet_tool_proximity_in( tablet_client->resource, surface->resource); queue_tool_frame(tool_client); + tool->focused_surface = surface; return serial; } @@ -1028,9 +1036,31 @@ void wlr_send_tablet_v2_tablet_tool_proximity_out( wl_event_source_remove(tool->current_client->frame_source); send_tool_frame(tool->current_client); } + tool->current_client = NULL; } } +void wlr_send_tablet_v2_tablet_tool_distance( + struct wlr_tablet_v2_tablet_tool *tool, uint32_t distance) { + if (tool->current_client) { + zwp_tablet_tool_v2_send_distance(tool->current_client->resource, + distance); + + queue_tool_frame(tool->current_client); + } +} + +void wlr_send_tablet_v2_tablet_tool_wheel( + struct wlr_tablet_v2_tablet_tool *tool, double delta, int32_t clicks) { + if (tool->current_client) { + zwp_tablet_tool_v2_send_wheel(tool->current_client->resource, + clicks, delta); + + queue_tool_frame(tool->current_client); + } + +} + uint32_t wlr_send_tablet_v2_tablet_pad_enter( struct wlr_tablet_v2_tablet_pad *pad, @@ -1183,3 +1213,22 @@ uint32_t wlr_send_tablet_v2_tablet_pad_mode(struct wlr_tablet_v2_tablet_pad *pad pad->current_client->groups[group], time, serial, mode); return serial; } + +bool wlr_surface_accepts_tablet_v2(struct wlr_tablet_v2_tablet *tablet, + struct wlr_surface *surface) { + struct wl_client *client = wl_resource_get_client(surface->resource); + + if (tablet->current_client && + tablet->current_client->client == client) { + return true; + } + + struct wlr_tablet_client_v2 *tablet_tmp; + wl_list_for_each(tablet_tmp, &tablet->clients, tablet_link) { + if (tablet_tmp->client == client) { + return true; + } + } + + return false; +}