wayland: move tablet tool handling to mpv input system

Upgrade tablet input to first class citizen similar like
touch input, but more simple since use case for now is
mouse emulation only.

Keep state for coordinates and in-proximity.
Introduce property for disabling tablet input.
Introduce Client-API property `tablet-pos`.
This commit is contained in:
Jens Peters
2025-04-26 11:45:12 +02:00
committed by Dudemanguy
parent 43a030452c
commit db60145c2d
6 changed files with 213 additions and 18 deletions

View File

@@ -158,6 +158,14 @@ struct input_ctx {
struct touch_point *touch_points;
int num_touch_points;
int tablet_x, tablet_y;
// Indicates tablet tools in proximity
bool tablet_tool_in_proximity;
bool tablet_tool_down;
bool tablet_tool_stylus_btn1_pressed;
bool tablet_tool_stylus_btn2_pressed;
bool tablet_tool_stylus_btn3_pressed;
unsigned int mouse_event_counter;
struct mp_input_src *sources[MP_MAX_SOURCES];
@@ -197,6 +205,7 @@ struct input_opts {
bool allow_win_drag;
bool preprocess_wheel;
bool touch_emulate_mouse;
bool tablet_emulate_mouse;
};
const struct m_sub_options input_config = {
@@ -219,6 +228,7 @@ const struct m_sub_options input_config = {
{"input-media-keys", OPT_BOOL(use_media_keys)},
{"input-preprocess-wheel", OPT_BOOL(preprocess_wheel)},
{"input-touch-emulate-mouse", OPT_BOOL(touch_emulate_mouse)},
{"input-tablet-emulate-mouse", OPT_BOOL(tablet_emulate_mouse)},
{"input-dragging-deadzone", OPT_INT(dragging_deadzone)},
#if HAVE_SDL2_GAMEPAD
{"input-gamepad", OPT_BOOL(use_gamepad)},
@@ -243,6 +253,7 @@ const struct m_sub_options input_config = {
.allow_win_drag = true,
.preprocess_wheel = true,
.touch_emulate_mouse = true,
.tablet_emulate_mouse = true,
},
.change_flags = UPDATE_INPUT,
};
@@ -1066,6 +1077,122 @@ int mp_input_get_touch_pos(struct input_ctx *ictx, int count, int *x, int *y, in
return num_touch_points;
}
static void notify_tablet_update(struct input_ctx *ictx)
{
// queue dummy cmd so that tablet-pos can notify observers
mp_cmd_t *cmd = mp_input_parse_cmd(ictx, bstr0("ignore"), "<internal>");
if (cmd)
cmd->notify_event = true;
queue_cmd(ictx, cmd);
}
void mp_input_set_tablet_tool_in_proximity(struct input_ctx *ictx, bool in_proximity)
{
MP_TRACE(ictx, "tablet tool proximity %s\n", in_proximity ? "in" : "out");
input_lock(ictx);
ictx->tablet_tool_in_proximity = in_proximity;
if (!in_proximity) {
ictx->tablet_tool_down = false;
ictx->tablet_tool_stylus_btn1_pressed = false;
ictx->tablet_tool_stylus_btn2_pressed = false;
ictx->tablet_tool_stylus_btn3_pressed = false;
}
notify_tablet_update(ictx);
input_unlock(ictx);
}
void mp_input_tablet_tool_down(struct input_ctx *ictx)
{
MP_TRACE(ictx, "tablet tool down\n");
input_lock(ictx);
ictx->tablet_tool_down = true;
if (ictx->opts->tablet_emulate_mouse && ictx->tablet_tool_in_proximity)
feed_key(ictx, MP_MBTN_LEFT | MP_KEY_STATE_DOWN, 1, false);
notify_tablet_update(ictx);
input_unlock(ictx);
}
void mp_input_tablet_tool_up(struct input_ctx *ictx)
{
MP_TRACE(ictx, "tablet tool up\n");
input_lock(ictx);
ictx->tablet_tool_down = false;
if (ictx->opts->tablet_emulate_mouse && ictx->tablet_tool_in_proximity)
feed_key(ictx, MP_MBTN_LEFT | MP_KEY_STATE_UP, 1, false);
notify_tablet_update(ictx);
input_unlock(ictx);
}
void mp_input_tablet_tool_button(struct input_ctx *ictx, int button, int state)
{
char *key = mp_input_get_key_name(button);
MP_TRACE(ictx, "tablet tool button %s %s%s \n",
key,
(state & MP_KEY_STATE_DOWN) ? "pressed" : "",
(state & MP_KEY_STATE_UP) ? "released" : "");
input_lock(ictx);
switch (button) {
case MP_KEY_TABLET_TOOL_STYLUS_BTN1:
ictx->tablet_tool_stylus_btn1_pressed = state == MP_KEY_STATE_DOWN;
button = MP_MBTN_MID;
break;
case MP_KEY_TABLET_TOOL_STYLUS_BTN2:
ictx->tablet_tool_stylus_btn2_pressed = state == MP_KEY_STATE_DOWN;
button = MP_MBTN_RIGHT;
break;
case MP_KEY_TABLET_TOOL_STYLUS_BTN3:
ictx->tablet_tool_stylus_btn3_pressed = state == MP_KEY_STATE_DOWN;
button = MP_MBTN_BACK;
break;
default:
break;
}
if (ictx->opts->tablet_emulate_mouse && ictx->tablet_tool_in_proximity && button)
feed_key(ictx, button | state, 1, false);
notify_tablet_update(ictx);
input_unlock(ictx);
}
void mp_input_set_tablet_pos(struct input_ctx *ictx, int x, int y, bool quiet)
{
MP_TRACE(ictx, "tablet tool position %d/%d \n", x, y);
input_lock(ictx);
ictx->tablet_x = x;
ictx->tablet_y = y;
if (ictx->opts->tablet_emulate_mouse && ictx->tablet_tool_in_proximity)
set_mouse_pos(ictx, x, y, quiet);
notify_tablet_update(ictx);
input_unlock(ictx);
}
void mp_input_get_tablet_pos(struct input_ctx *ictx, int *x, int *y,
bool *tool_in_proximity, bool *tool_down,
bool *tool_stylus_btn1_pressed,
bool *tool_stylus_btn2_pressed,
bool *tool_stylus_btn3_pressed)
{
input_lock(ictx);
*x = ictx->tablet_x;
*y = ictx->tablet_y;
*tool_in_proximity = ictx->tablet_tool_in_proximity;
*tool_down = ictx->tablet_tool_down;
*tool_stylus_btn1_pressed = ictx->tablet_tool_stylus_btn1_pressed;
*tool_stylus_btn2_pressed = ictx->tablet_tool_stylus_btn2_pressed;
*tool_stylus_btn3_pressed = ictx->tablet_tool_stylus_btn3_pressed;
input_unlock(ictx);
}
static bool test_mouse(struct input_ctx *ictx, int x, int y, int rej_flags)
{
bool res = false;

View File

@@ -112,6 +112,22 @@ void mp_input_remove_touch_point(struct input_ctx *ictx, int id);
// identify touch points. Return the current number of touch points.
int mp_input_get_touch_pos(struct input_ctx *ictx, int count, int *xs, int *ys, int *ids);
// Set tablet tool proximity and process tool tip down/up and buttons
void mp_input_set_tablet_tool_in_proximity(struct input_ctx *ictx, bool in_proximity);
void mp_input_tablet_tool_down(struct input_ctx *ictx);
void mp_input_tablet_tool_up(struct input_ctx *ictx);
void mp_input_tablet_tool_button(struct input_ctx *ictx, int button, int state);
// Update tablet position (in window coordinates).
void mp_input_set_tablet_pos(struct input_ctx *ictx, int x, int y, bool quiet);
void mp_input_get_tablet_pos(struct input_ctx *ictx, int *x, int *y,
bool *tool_in_proximity,
bool *tool_down,
bool *tool_stylus_btn1_pressed,
bool *tool_stylus_btn2_pressed,
bool *tool_stylus_btn3_pressed);
// Return whether we want/accept mouse input.
bool mp_input_mouse_enabled(struct input_ctx *ictx);

View File

@@ -127,6 +127,11 @@ static const struct key_name key_names[] = {
{ MP_MBTN_MID_DBL, "MBTN_MID_DBL" },
{ MP_MBTN_RIGHT_DBL, "MBTN_RIGHT_DBL" },
{ MP_KEY_TABLET_TOOL_TIP, "TABLET_TOOL_TIP" },
{ MP_KEY_TABLET_TOOL_STYLUS_BTN1, "TABLET_TOOL_STYLUS_BTN1" },
{ MP_KEY_TABLET_TOOL_STYLUS_BTN2, "TABLET_TOOL_STYLUS_BTN2" },
{ MP_KEY_TABLET_TOOL_STYLUS_BTN3, "TABLET_TOOL_STYLUS_BTN3" },
{ MP_KEY_GAMEPAD_ACTION_DOWN, "GAMEPAD_ACTION_DOWN" },
{ MP_KEY_GAMEPAD_ACTION_RIGHT, "GAMEPAD_ACTION_RIGHT" },
{ MP_KEY_GAMEPAD_ACTION_LEFT, "GAMEPAD_ACTION_LEFT" },

View File

@@ -161,6 +161,13 @@
#define MP_KEY_MOUSE_BTN_COUNT (MP_MBTN_END - MP_MBTN_BASE)
/* tablet buttons */
#define MP_KEY_TABLET (MP_KEY_BASE+0xD0)
#define MP_KEY_TABLET_TOOL_TIP (MP_KEY_TABLET+1)
#define MP_KEY_TABLET_TOOL_STYLUS_BTN1 (MP_KEY_TABLET+2)
#define MP_KEY_TABLET_TOOL_STYLUS_BTN2 (MP_KEY_TABLET+3)
#define MP_KEY_TABLET_TOOL_STYLUS_BTN3 (MP_KEY_TABLET+4)
/* game controller keys */
#define MP_KEY_GAMEPAD (MP_KEY_BASE+0xF0)
#define MP_KEY_GAMEPAD_ACTION_DOWN (MP_KEY_GAMEPAD+0)

View File

@@ -3124,6 +3124,43 @@ static int mp_property_touch_pos(void *ctx, struct m_property *prop,
get_touch_pos, (void *)pos);
}
static int mp_property_tablet_pos(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
switch (action) {
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE};
return M_PROPERTY_OK;
case M_PROPERTY_GET: {
int xs, ys;
bool tool_in_proximity;
bool tool_down;
bool tool_stylus_btn1_pressed;
bool tool_stylus_btn2_pressed;
bool tool_stylus_btn3_pressed;
mp_input_get_tablet_pos(mpctx->input, &xs, &ys, &tool_in_proximity, &tool_down,
&tool_stylus_btn1_pressed, &tool_stylus_btn2_pressed, &tool_stylus_btn3_pressed);
struct mpv_node node;
node_init(&node, MPV_FORMAT_NODE_MAP, NULL);
node_map_add_int64(&node, "x", xs);
node_map_add_int64(&node, "y", ys);
node_map_add_flag(&node, "tool-in-proximity", tool_in_proximity);
node_map_add_string(&node, "tool-tip", tool_down ? "down" : "up");
node_map_add_string(&node, "tool-stylus-btn1", tool_stylus_btn1_pressed ? "pressed" : "released");
node_map_add_string(&node, "tool-stylus-btn2", tool_stylus_btn2_pressed ? "pressed" : "released");
node_map_add_string(&node, "tool-stylus-btn3", tool_stylus_btn3_pressed ? "pressed" : "released");
*(struct mpv_node *)arg = node;
return M_PROPERTY_OK;
}
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
/// Video fps (RO)
static int mp_property_fps(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -4416,6 +4453,7 @@ static const struct m_property mp_properties_base[] = {
{"mouse-pos", mp_property_mouse_pos},
{"touch-pos", mp_property_touch_pos},
{"tablet-pos", mp_property_tablet_pos},
// Subs
{"sid", mp_property_switch_track, .priv = (void *)(const int[]){0, STREAM_SUB}},
@@ -4553,7 +4591,7 @@ static const char *const *const mp_event_property_change[] = {
E(MP_EVENT_CHANGE_PLAYLIST, "playlist", "playlist-pos", "playlist-pos-1",
"playlist-count", "playlist/count", "playlist-current-pos",
"playlist-playing-pos"),
E(MP_EVENT_INPUT_PROCESSED, "mouse-pos", "touch-pos"),
E(MP_EVENT_INPUT_PROCESSED, "mouse-pos", "touch-pos", "tablet-pos"),
E(MP_EVENT_CORE_IDLE, "core-idle", "eof-reached"),
};
#undef E

View File

@@ -698,11 +698,14 @@ static void tablet_tool_handle_proximity_in(void *data,
struct vo_wayland_tablet_tool *tablet_tool = data;
tablet_tool->proximity_serial = serial;
set_cursor_visibility(tablet_tool->seat, true);
mp_input_set_tablet_tool_in_proximity(tablet_tool->wl->vo->input_ctx, true);
}
static void tablet_tool_handle_proximity_out(void *data,
struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2)
{
struct vo_wayland_tablet_tool *tablet_tool = data;
mp_input_set_tablet_tool_in_proximity(tablet_tool->wl->vo->input_ctx, false);
}
static void tablet_tool_handle_down(void *data,
@@ -724,7 +727,7 @@ static void tablet_tool_handle_down(void *data,
tablet_tool->seat->pointer_button_serial = serial;
wl->last_button_seat = tablet_tool->seat;
mp_input_put_key(tablet_tool->wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_DOWN | tablet_tool->seat->mpmod);
mp_input_tablet_tool_down(tablet_tool->wl->vo->input_ctx);
}
static void tablet_tool_handle_up(void *data,
@@ -732,7 +735,7 @@ static void tablet_tool_handle_up(void *data,
{
struct vo_wayland_tablet_tool *tablet_tool = data;
tablet_tool->seat->wl->last_button_seat = NULL;
mp_input_put_key(tablet_tool->wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP | tablet_tool->seat->mpmod);
mp_input_tablet_tool_up(tablet_tool->wl->vo->input_ctx);
}
static void tablet_tool_handle_motion(void *data,
@@ -746,7 +749,7 @@ static void tablet_tool_handle_motion(void *data,
wl->mouse_x = handle_round(wl->scaling, wl_fixed_to_int(x));
wl->mouse_y = handle_round(wl->scaling, wl_fixed_to_int(y));
mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y,
mp_input_set_tablet_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y,
wl->toplevel_configured);
wl->toplevel_configured = false;
}
@@ -799,26 +802,25 @@ static void tablet_tool_handle_button(void *data,
tablet_tool->seat->last_serial = serial;
switch (button) {
case BTN_STYLUS:
button = MP_MBTN_MID;
break;
case BTN_STYLUS2:
button = MP_MBTN_RIGHT;
break;
case BTN_STYLUS3:
button = MP_MBTN_BACK;
break;
default:
button = 0;
break;
case BTN_STYLUS:
button = MP_KEY_TABLET_TOOL_STYLUS_BTN1;
break;
case BTN_STYLUS2:
button = MP_KEY_TABLET_TOOL_STYLUS_BTN2;
break;
case BTN_STYLUS3:
button = MP_KEY_TABLET_TOOL_STYLUS_BTN3;
break;
default:
button = 0;
break;
}
state = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_PRESSED
? MP_KEY_STATE_DOWN
: MP_KEY_STATE_UP;
if (button)
mp_input_put_key(tablet_tool->wl->vo->input_ctx, button | state | tablet_tool->seat->mpmod);
mp_input_tablet_tool_button(tablet_tool->wl->vo->input_ctx, button, state);
}
static void tablet_tool_handle_frame(void *data,