tablet-v2 tool: Implement implicit grab

Implement the tablet-v2 tablet tool's implicit grab semantics for
buttons and tip.

This avoids losing focus (to other [sub]surfaces) when a button is held,
or the tip is down.
This should help when the device is used close to a surface's border and
would otherwise have to be very precise.
This commit is contained in:
Markus Ongyerth 2018-07-23 10:08:43 +02:00
parent d5950255de
commit f64962ace8
3 changed files with 140 additions and 0 deletions

View file

@ -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_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_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( uint32_t wlr_send_tablet_v2_tablet_pad_enter(
struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_v2_tablet_pad *pad,
struct wlr_tablet_v2_tablet *tablet, struct wlr_tablet_v2_tablet *tablet,

View file

@ -209,6 +209,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
if (event->state == WLR_TABLET_TOOL_TIP_DOWN) { if (event->state == WLR_TABLET_TOOL_TIP_DOWN) {
wlr_tablet_v2_tablet_tool_notify_down(roots_tool->tablet_v2_tool); 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 { } else {
wlr_tablet_v2_tablet_tool_notify_up(roots_tool->tablet_v2_tool); wlr_tablet_v2_tablet_tool_notify_up(roots_tool->tablet_v2_tool);
} }

View file

@ -693,3 +693,139 @@ static const struct wlr_tablet_tool_v2_grab_interface default_tool_interface = {
.button = default_tool_button, .button = default_tool_button,
.cancel = default_tool_cancel, .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);
}