diff --git a/include/wlr/types/wlr_tablet_v2.h b/include/wlr/types/wlr_tablet_v2.h index 3d06bf56..e2a39392 100644 --- a/include/wlr/types/wlr_tablet_v2.h +++ b/include/wlr/types/wlr_tablet_v2.h @@ -240,6 +240,9 @@ struct wlr_tablet_tool_v2_grab_interface { void wlr_tablet_tool_v2_start_grab(struct wlr_tablet_v2_tablet_tool *tool, struct wlr_tablet_tool_v2_grab *grab); void wlr_tablet_tool_v2_end_grab(struct wlr_tablet_v2_tablet_tool *tool); +void wlr_tablet_tool_v2_start_implicit_grab(struct wlr_tablet_v2_tablet_tool *tool); + + uint32_t wlr_send_tablet_v2_tablet_pad_enter( struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_v2_tablet *tablet, diff --git a/rootston/seat.c b/rootston/seat.c index 343720ae..f308da52 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -209,6 +209,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { if (event->state == WLR_TABLET_TOOL_TIP_DOWN) { wlr_tablet_v2_tablet_tool_notify_down(roots_tool->tablet_v2_tool); + wlr_tablet_tool_v2_start_implicit_grab(roots_tool->tablet_v2_tool); } else { wlr_tablet_v2_tablet_tool_notify_up(roots_tool->tablet_v2_tool); } diff --git a/types/tablet_v2/wlr_tablet_v2_tool.c b/types/tablet_v2/wlr_tablet_v2_tool.c index 9165fb23..81c518ab 100644 --- a/types/tablet_v2/wlr_tablet_v2_tool.c +++ b/types/tablet_v2/wlr_tablet_v2_tool.c @@ -693,3 +693,139 @@ static const struct wlr_tablet_tool_v2_grab_interface default_tool_interface = { .button = default_tool_button, .cancel = default_tool_cancel, }; + +struct implicit_grab_state { + struct wlr_surface *original; + bool released; + + struct wlr_surface *focused; + struct wlr_tablet_v2_tablet *tablet; +}; + +static void check_and_release_implicit_grab(struct wlr_tablet_tool_v2_grab *grab) { + struct implicit_grab_state *state = grab->data; + /* Still button or tip pressed. We should hold the grab */ + if (grab->tool->is_down || grab->tool->num_buttons > 0 || state->released) { + return; + } + + state->released = true; + + /* We should still focus the same surface. Do nothing */ + if (state->original == state->focused) { + wlr_tablet_tool_v2_end_grab(grab->tool); + return; + } + + wlr_send_tablet_v2_tablet_tool_proximity_out(grab->tool); + if (state->focused) { + wlr_send_tablet_v2_tablet_tool_proximity_in(grab->tool, + state->tablet, state->focused); + } + + wlr_tablet_tool_v2_end_grab(grab->tool); +} + +static void implicit_tool_proximity_in( + struct wlr_tablet_tool_v2_grab *grab, + struct wlr_tablet_v2_tablet *tablet, + struct wlr_surface *surface) { + + /* As long as we got an implicit grab, proximity won't change + * But should track the currently focused surface to change to it when + * the grab is released. + */ + struct implicit_grab_state *state = grab->data; + state->focused = surface; + state->tablet = tablet; +} + +static void implicit_tool_proximity_out(struct wlr_tablet_tool_v2_grab *grab) { + struct implicit_grab_state *state = grab->data; + state->focused = NULL; +} + +static void implicit_tool_down(struct wlr_tablet_tool_v2_grab *grab) { + wlr_send_tablet_v2_tablet_tool_down(grab->tool); +} + +static void implicit_tool_up(struct wlr_tablet_tool_v2_grab *grab) { + wlr_send_tablet_v2_tablet_tool_up(grab->tool); + check_and_release_implicit_grab(grab); +} + +/* Only send the motion event, when we are over the surface for now */ +static void implicit_tool_motion( + struct wlr_tablet_tool_v2_grab *grab, double x, double y) { + struct implicit_grab_state *state = grab->data; + if (state->focused != state->original) { + return; + } + + wlr_send_tablet_v2_tablet_tool_motion(grab->tool, x, y); +} + + +static void implicit_tool_button( + struct wlr_tablet_tool_v2_grab *grab, uint32_t button, + enum zwp_tablet_pad_v2_button_state state) { + wlr_send_tablet_v2_tablet_tool_button(grab->tool, button, state); + check_and_release_implicit_grab(grab); +} + +static void implicit_tool_cancel(struct wlr_tablet_tool_v2_grab *grab) { + check_and_release_implicit_grab(grab); + free(grab->data); + free(grab); +} + +const struct wlr_tablet_tool_v2_grab_interface implicit_tool_interface = { + .proximity_in = implicit_tool_proximity_in, + .down = implicit_tool_down, + .up = implicit_tool_up, + .motion = implicit_tool_motion, + .pressure = default_tool_pressure, + .distance = default_tool_distance, + .tilt = default_tool_tilt, + .rotation = default_tool_rotation, + .slider = default_tool_slider, + .wheel = default_tool_wheel, + .proximity_out = implicit_tool_proximity_out, + .button = implicit_tool_button, + .cancel = implicit_tool_cancel, +}; + +static bool tool_has_implicit_grab(struct wlr_tablet_v2_tablet_tool *tool) { + return tool->grab->interface == &implicit_tool_interface; +} + +void wlr_tablet_tool_v2_start_implicit_grab(struct wlr_tablet_v2_tablet_tool *tool) { + /* Durr */ + if (tool_has_implicit_grab(tool) || !tool->focused_surface) { + return; + } + + /* No current implicit grab */ + if (!(tool->is_down || tool->num_buttons > 0)) { + return; + } + + struct wlr_tablet_tool_v2_grab *grab = + calloc(1, sizeof(struct wlr_tablet_tool_v2_grab)); + if (!grab) { + return; + } + + grab->interface = &implicit_tool_interface; + grab->tool = tool; + struct implicit_grab_state *state = calloc(1, sizeof(struct implicit_grab_state)); + if (!state) { + free(grab); + return; + } + + state->original = tool->focused_surface; + grab->data = state; + + wlr_tablet_tool_v2_start_grab(tool, grab); +}