Skip to content

Commit

Permalink
Input sanitization, wayland enhancement and overlay
Browse files Browse the repository at this point in the history
Adapt the sanitized pointer handling, discussed at libretro#17196 :

Overlay "driver" specific changes:

- make sure pointer position is always within [-0x7fff,0x7fff] by using the confined wrapper
- enable pointer offscreen query
- report -0x8000 for lightgun if pointer is at the edge
- align lightgun offscreen reporting and button ID conversion with other drivers

Android driver specific changes:

- make sure pointer position is always within [-0x7fff,0x7fff] by using the confined wrapper
- remove extra "inside" checks, general simplification
- enable pointer offscreen reporting
- report same value for all ports when querying mouse and lightgun
- fill missing lightgun support, with fixed button map

Udev and X11 driver specific changes:

- simulate max. 3 touches instead of 1 using different mouse buttons

Wayland driver specific changes:

- integrate touch input better to the overall handling (enabling overlay usage with mouse)
- simulate max. 3 touches instead of 1 using different mouse buttons
  • Loading branch information
zoltanvb committed Dec 29, 2024
1 parent b8c9665 commit 8c7662b
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 96 deletions.
57 changes: 35 additions & 22 deletions input/drivers/android_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ typedef struct
struct input_pointer
{
int16_t x, y;
int16_t confined_x, confined_y;
int16_t full_x, full_y;
};

Expand Down Expand Up @@ -818,16 +819,20 @@ static INLINE void android_input_poll_event_type_motion(

for (motion_ptr = 0; motion_ptr < pointer_max; motion_ptr++)
{
struct video_viewport vp;
struct video_viewport vp = {0};
float x = AMotionEvent_getX(event, motion_ptr);
float y = AMotionEvent_getY(event, motion_ptr);

vp.x = 0;
vp.y = 0;
vp.width = 0;
vp.height = 0;
vp.full_width = 0;
vp.full_height = 0;
/* On other platforms, pointer query uses the confined wrap function, *
* but some extra functionality is added to Android which needs the *
* true offscreen value -0x8000, so both variants are called. */
video_driver_translate_coord_viewport_confined_wrap(
&vp,
x, y,
&android->pointer[motion_ptr].confined_x,
&android->pointer[motion_ptr].confined_y,
&android->pointer[motion_ptr].full_x,
&android->pointer[motion_ptr].full_y);

video_driver_translate_coord_viewport_wrap(
&vp,
Expand Down Expand Up @@ -1733,10 +1738,8 @@ static int16_t android_input_state(
case RETRO_DEVICE_MOUSE:
case RARCH_DEVICE_MOUSE_SCREEN:
{
/* Same mouse state is reported for all ports. */
int val = 0;
if (port > 0)
break; /* TODO: implement mouse for additional ports/players */

switch (id)
{
case RETRO_DEVICE_ID_MOUSE_LEFT:
Expand Down Expand Up @@ -1774,11 +1777,15 @@ static int16_t android_input_state(
break;
case RETRO_DEVICE_LIGHTGUN:
{
/* Same lightgun state is reported for all ports. */
int val = 0;
if (port > 0)
break; /* TODO: implement lightgun for additional ports/players */
switch (id)
{
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X:
return android->pointer[idx].x;
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y:
return android->pointer[idx].y;
/* Deprecated relative lightgun. */
case RETRO_DEVICE_ID_LIGHTGUN_X:
val = android->mouse_x_delta;
android->mouse_x_delta = 0;
Expand All @@ -1789,39 +1796,45 @@ static int16_t android_input_state(
android->mouse_y_delta = 0;
/* flush delta after it has been read */
return val;
case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER:
return android->mouse_l || android_check_quick_tap(android);
case RETRO_DEVICE_ID_LIGHTGUN_CURSOR:
return android->mouse_m;
case RETRO_DEVICE_ID_LIGHTGUN_TURBO:
return android->mouse_r;
case RETRO_DEVICE_ID_LIGHTGUN_RELOAD:
return android->mouse_m || android->pointer_count == 3;
case RETRO_DEVICE_ID_LIGHTGUN_SELECT:
return android->mouse_r && android->mouse_l;
case RETRO_DEVICE_ID_LIGHTGUN_START:
return android->mouse_m && android->mouse_r;
case RETRO_DEVICE_ID_LIGHTGUN_PAUSE:
return android->mouse_m && android->mouse_l;
case RETRO_DEVICE_ID_LIGHTGUN_TURBO:
return android->mouse_r || android->pointer_count == 2;
case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER:
return android->mouse_l || android_check_quick_tap(android) || android->pointer_count == 1;
case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(android->pointer[idx].x, android->pointer[idx].y);
}
}
break;
case RETRO_DEVICE_POINTER:
case RARCH_DEVICE_POINTER_SCREEN:
/* Same pointer state is reported for all ports. */
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X:
if (device == RARCH_DEVICE_POINTER_SCREEN)
return android->pointer[idx].full_x;
return android->pointer[idx].x;
return android->pointer[idx].confined_x;
case RETRO_DEVICE_ID_POINTER_Y:
if (device == RARCH_DEVICE_POINTER_SCREEN)
return android->pointer[idx].full_y;
return android->pointer[idx].y;
return android->pointer[idx].confined_y;
case RETRO_DEVICE_ID_POINTER_PRESSED:
/* On mobile platforms, touches outside screen / core viewport are not reported. */
if (device == RARCH_DEVICE_POINTER_SCREEN)
return (idx < android->pointer_count) &&
(android->pointer[idx].full_x != -0x8000) &&
(android->pointer[idx].full_y != -0x8000);
return (idx < android->pointer_count) &&
(android->pointer[idx].x != -0x8000) &&
(android->pointer[idx].y != -0x8000);
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(android->pointer[idx].x, android->pointer[idx].y);
case RETRO_DEVICE_ID_POINTER_COUNT:
return android->pointer_count;
case RARCH_DEVICE_ID_POINTER_BACK:
Expand Down
21 changes: 16 additions & 5 deletions input/drivers/udev_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -3685,7 +3685,7 @@ static bool udev_mouse_button_pressed(
}

static int16_t udev_pointer_state(udev_input_t *udev,
unsigned port, unsigned id, bool screen)
unsigned port, unsigned idx, unsigned id, bool screen)
{
udev_input_mouse_t *mouse = udev_get_mouse(udev, port);
int16_t res_x;
Expand All @@ -3701,8 +3701,19 @@ static int16_t udev_pointer_state(udev_input_t *udev,
return res_y;
case RETRO_DEVICE_ID_POINTER_PRESSED:
if (mouse->abs == 1)
return mouse->pp;
return mouse->l;
{
if (idx == 0)
return mouse->pp;
else
return 0;
}
/* Simulate max. 3 touches with mouse buttons*/
else if (idx == 0)
return (mouse->l | mouse->r | mouse->m);
else if (idx == 1)
return (mouse->r | mouse->m);
else if (idx == 2)
return mouse->m;
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(res_x, res_y);
}
Expand Down Expand Up @@ -3828,8 +3839,8 @@ static int16_t udev_input_state(
return udev_input_touch_state(udev, pointer_dev, binds,
keyboard_mapping_blocked, port, device, idx, id);
#endif
if (idx == 0) /* multi-touch unsupported (for now) */
return udev_pointer_state(udev, port, id,
if (idx < 3)
return udev_pointer_state(udev, port, idx, id,
device == RARCH_DEVICE_POINTER_SCREEN);
break;

Expand Down
56 changes: 29 additions & 27 deletions input/drivers/wayland_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,16 @@ static int16_t input_wl_touch_state(input_ctx_wayland_data_t *wl,
{
if (idx <= MAX_TOUCHES)
{
struct video_viewport vp;
struct video_viewport vp = {0};
int16_t res_x = 0;
int16_t res_y = 0;
int16_t res_screen_x = 0;
int16_t res_screen_y = 0;

vp.x = 0;
vp.y = 0;
vp.width = 0;
vp.height = 0;
vp.full_width = 0;
vp.full_height = 0;
/* Shortcut: mouse button events will be reported on desktop with 0/0 coordinates. *
* Skip these, mouse handling will catch it elsewhere. */
if (wl->touches[idx].x == 0 && wl->touches[idx].y == 0)
return 0;

if (video_driver_translate_coord_viewport_confined_wrap(&vp,
wl->touches[idx].x, wl->touches[idx].y,
Expand All @@ -132,17 +130,14 @@ static int16_t input_wl_touch_state(input_ctx_wayland_data_t *wl,
res_y = res_screen_y;
}

if ((res_x >= -0x7fff) && (res_y >= -0x7fff)) /* Inside? */
switch (id)
{
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X:
return res_x;
case RETRO_DEVICE_ID_POINTER_Y:
return res_y;
case RETRO_DEVICE_ID_POINTER_PRESSED:
return wl->touches[idx].active;
}
case RETRO_DEVICE_ID_POINTER_X:
return res_x;
case RETRO_DEVICE_ID_POINTER_Y:
return res_y;
case RETRO_DEVICE_ID_POINTER_PRESSED:
return wl->touches[idx].active;
}
}
}
Expand Down Expand Up @@ -301,8 +296,18 @@ static int16_t input_wl_state(
}
break;
case RETRO_DEVICE_POINTER:
case RARCH_DEVICE_POINTER_SCREEN:
/* All ports report the same pointer state. See notes at mouse case. */
if (idx == 0)
if (idx < MAX_TOUCHES)
{
int16_t touch_state = input_wl_touch_state(wl, idx, id,
device == RARCH_DEVICE_POINTER_SCREEN);
/* Touch state is only reported if it is meaningful. */
if (touch_state)
return touch_state;
}
/* Fall through to system pointer emulating max. 3 touches. */
if (idx < 3)
{
struct video_viewport vp = {0};
bool screen =
Expand All @@ -329,7 +334,12 @@ static int16_t input_wl_state(
case RETRO_DEVICE_ID_POINTER_Y:
return res_y;
case RETRO_DEVICE_ID_POINTER_PRESSED:
return wl->mouse.left;
if (idx == 0)
return (wl->mouse.left | wl->mouse.right | wl->mouse.middle);
else if (idx == 1)
return (wl->mouse.right | wl->mouse.middle);
else if (idx == 2)
return wl->mouse.middle;
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(res_x, res_y);
default:
Expand All @@ -338,14 +348,6 @@ static int16_t input_wl_state(
}
}
break;
case RARCH_DEVICE_POINTER_SCREEN:
if (port == 0) /* TODO/FIXME: support pointers on additional ports */
{
if (idx < MAX_TOUCHES)
return input_wl_touch_state(wl, idx, id,
device == RARCH_DEVICE_POINTER_SCREEN);
}
break;
case RETRO_DEVICE_LIGHTGUN:
/* All ports report the same lightgun state. See notes at mouse case. */
{
Expand Down
10 changes: 8 additions & 2 deletions input/drivers/x11_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ static int16_t x_input_state(
break;
case RETRO_DEVICE_POINTER:
case RARCH_DEVICE_POINTER_SCREEN:
if (idx == 0)
/* Map up to 3 touches to mouse buttons. */
if (idx < 3)
{
struct video_viewport vp = {0};
bool screen =
Expand All @@ -268,7 +269,12 @@ static int16_t x_input_state(
case RETRO_DEVICE_ID_POINTER_Y:
return res_y;
case RETRO_DEVICE_ID_POINTER_PRESSED:
return x11->mouse_l;
if (idx == 0)
return (x11->mouse_l | x11->mouse_r | x11->mouse_m);
else if (idx == 1)
return (x11->mouse_r | x11->mouse_m);
else if (idx == 2)
return x11->mouse_m;
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(res_x, res_y);
}
Expand Down
54 changes: 14 additions & 40 deletions input/input_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1207,64 +1207,36 @@ static int16_t input_overlay_lightgun_state(settings_t *settings,

switch(id)
{
/* Pointer positions have been clamped earlier in input drivers, *
* so if we want to pass true offscreen value, it must be detected */
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X:
ptr_st->device_mask |= (1 << RETRO_DEVICE_LIGHTGUN);

if ( ptr_st->ptr[0].x != -0x8000
|| settings->bools.input_overlay_lightgun_allow_offscreen)
if ( ( ptr_st->ptr[0].x > -0x7fff && ptr_st->ptr[0].x != 0x7fff)
|| !settings->bools.input_overlay_lightgun_allow_offscreen)
return ptr_st->ptr[0].x;
else if (video_driver_get_viewport_info(&vp))
{
edge = ((2 * vp.x * 0x7fff) / (int)vp.full_width) - 0x7fff;
return ((ptr_st->screen_x > edge) ? 0x7fff : -0x7fff);
}
return -0x8000;
else
return -0x8000;
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y:
if ( ptr_st->ptr[0].y != -0x8000
|| settings->bools.input_overlay_lightgun_allow_offscreen)
if ( ( ptr_st->ptr[0].y > -0x7fff && ptr_st->ptr[0].y != 0x7fff)
|| !settings->bools.input_overlay_lightgun_allow_offscreen)
return ptr_st->ptr[0].y;
else if (video_driver_get_viewport_info(&vp))
{
edge = ((2 * vp.y * 0x7fff) / (int)vp.full_height) - 0x7fff;
return ((ptr_st->screen_y > edge) ? 0x7fff : -0x7fff);
}
return -0x8000;
else
return -0x8000;
case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN:
return ( settings->bools.input_overlay_lightgun_allow_offscreen
&& (ptr_st->ptr[0].x == -0x8000 || ptr_st->ptr[0].y == -0x8000));
return input_driver_pointer_is_offscreen(ptr_st->ptr[0].x, ptr_st->ptr[0].y);
case RETRO_DEVICE_ID_LIGHTGUN_AUX_A:
rarch_id = RARCH_LIGHTGUN_AUX_A;
break;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_B:
rarch_id = RARCH_LIGHTGUN_AUX_B;
break;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_C:
rarch_id = RARCH_LIGHTGUN_AUX_C;
break;
case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER:
rarch_id = RARCH_LIGHTGUN_TRIGGER;
break;
case RETRO_DEVICE_ID_LIGHTGUN_START:
case RETRO_DEVICE_ID_LIGHTGUN_PAUSE:
rarch_id = RARCH_LIGHTGUN_START;
break;
case RETRO_DEVICE_ID_LIGHTGUN_SELECT:
rarch_id = RARCH_LIGHTGUN_SELECT;
break;
case RETRO_DEVICE_ID_LIGHTGUN_RELOAD:
rarch_id = RARCH_LIGHTGUN_RELOAD;
break;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP:
rarch_id = RARCH_LIGHTGUN_DPAD_UP;
break;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN:
rarch_id = RARCH_LIGHTGUN_DPAD_DOWN;
break;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT:
rarch_id = RARCH_LIGHTGUN_DPAD_LEFT;
break;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT:
rarch_id = RARCH_LIGHTGUN_DPAD_RIGHT;
rarch_id = input_driver_lightgun_id_convert(id);
break;
default:
rarch_id = RARCH_BIND_LIST_END;
Expand Down Expand Up @@ -1294,6 +1266,8 @@ static int16_t input_overlay_pointer_state(input_overlay_t *ol,
&& ptr_st->ptr[idx].y != -0x8000;
case RETRO_DEVICE_ID_POINTER_COUNT:
return ptr_st->count;
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(ptr_st->ptr[idx].x, ptr_st->ptr[idx].y);
}

return 0;
Expand Down

0 comments on commit 8c7662b

Please sign in to comment.