Pointer sanitization - x11, udev (#17281)

Adapt the sanitized pointer handling, discussed at #17196 :

X11 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

Udev driver specific changes:

    remove custom calculation and use common viewport translation
    unify pointer query instead of separate _x and _y
    enable pointer offscreen reporting

Other changes:

    more tuning of pointer conversion in video_driver.c for edges
    lightgun button ID conversion moved to input_driver.c
This commit is contained in:
zoltanvb 2024-12-24 16:08:58 +01:00 committed by GitHub
parent f8ffcef86c
commit 6a85844b41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 127 additions and 213 deletions

View File

@ -711,20 +711,34 @@ bool video_driver_translate_coord_viewport(
|| (norm_full_vp_height <= 0))
return false;
if (mouse_x >= 0 && mouse_x < norm_full_vp_width)
scaled_screen_x = ((mouse_x * 0x10000)
/ norm_full_vp_width) - 0x7fff;
/* The conditions may look a bit unnecessarily complex, *
* but there are following edge cases to consider: *
* - 0 should map to -0x7fff (not -0x8000) *
* - edge (vp_width -1) should map to 0x7fff *
* This should make it possible to hit the edges on all *
* screen resolutions, even when pointer cannot be *
* moved offscreen. */
if (mouse_x > 0 && mouse_x < norm_full_vp_width)
scaled_screen_x = ((mouse_x * 0xffff)
/ (norm_full_vp_width - 1)) - 0x8000;
else if (mouse_x == 0)
scaled_screen_x = -0x7fff;
if (mouse_y >= 0 && mouse_y < norm_full_vp_height)
scaled_screen_y = ((mouse_y * 0x10000)
/ norm_full_vp_height) - 0x7fff;
if (mouse_y > 0 && mouse_y < norm_full_vp_height)
scaled_screen_y = ((mouse_y * 0xffff)
/ (norm_full_vp_height - 1)) - 0x8000;
else if (mouse_y == 0)
scaled_screen_y = -0x7fff;
mouse_x -= vp->x;
mouse_y -= vp->y;
if (mouse_x >= 0 && mouse_x < norm_vp_width)
scaled_x = ((mouse_x * 0x10000)
/ norm_vp_width) - 0x7fff;
if (mouse_x > 0 && mouse_x < norm_vp_width)
scaled_x = ((mouse_x * 0xffff)
/ (norm_vp_width - 1)) - 0x8000;
else if (mouse_x == 0)
scaled_x = -0x7fff;
else if (!report_oob)
{
if (mouse_x < 0)
@ -733,9 +747,11 @@ bool video_driver_translate_coord_viewport(
scaled_x = 0x7fff;
}
if (mouse_y >= 0 && mouse_y < norm_vp_height)
scaled_y = ((mouse_y * 0x10000)
/ norm_vp_height) - 0x7fff;
if (mouse_y > 0 && mouse_y < norm_vp_height)
scaled_y = ((mouse_y * 0xffff)
/ (norm_vp_height - 1)) - 0x8000;
else if (mouse_y == 0)
scaled_y = -0x7fff;
else if (!report_oob)
{
if (mouse_y < 0)

View File

@ -781,96 +781,60 @@ static int16_t udev_mouse_get_y(const udev_input_mouse_t *mouse)
return y + (y < 0 ? -0.5 : 0.5);
}
static int16_t udev_mouse_get_pointer_x(const udev_input_mouse_t *mouse, bool screen)
static bool udev_mouse_get_pointer(const udev_input_mouse_t *mouse,
bool screen, bool confined, int16_t *ret_x, int16_t *ret_y)
{
video_viewport_t vp;
double src_min;
double src_width;
int32_t x;
struct video_viewport vp = {0};
int16_t scaled_x;
int16_t scaled_y;
int16_t res_x = 0;
int16_t res_y = 0;
int16_t res_screen_x = 0;
int16_t res_screen_y = 0;
if (!video_driver_get_viewport_info(&vp))
return 0;
return false;
/* mouse coords are absolute? */
if (mouse->abs)
{
/* mouse coordinates are relative to the screen; convert them
/* mouse coordinates are relative to the full screen; convert them
* to be relative to the viewport */
double scaled_x;
src_min = mouse->x_min;
src_width = mouse->x_max - mouse->x_min + 1;
scaled_x = vp.full_width * (mouse->x_abs - src_min) / src_width;
x = -32767.0 + 65535.0 / vp.width * (scaled_x - vp.x);
scaled_x = mouse->x_abs - mouse->x_min;
scaled_y = mouse->y_abs - mouse->y_min;
}
else /* mouse coords are viewport relative */
{
if (screen)
{
src_min = 0.0;
src_width = vp.full_width;
}
else
{
src_min = vp.x;
src_width = vp.width;
}
x = -32767.0 + 65535.0 / src_width * (mouse->x_abs - src_min);
scaled_x = mouse->x_abs;
scaled_y = mouse->y_abs;
}
x += (x < 0 ? -0.5 : 0.5);
if (x < -0x7fff)
return -0x7fff;
else if (x > 0x7fff)
return 0x7fff;
return x;
}
static int16_t udev_mouse_get_pointer_y(const udev_input_mouse_t *mouse, bool screen)
{
video_viewport_t vp;
double src_min;
double src_height;
int32_t y;
if (!video_driver_get_viewport_info(&vp))
return 0;
/* Mouse coords are absolute? */
if (mouse->abs)
if (confined && video_driver_translate_coord_viewport_confined_wrap(
&vp, scaled_x, scaled_y,
&res_x, &res_y, &res_screen_x, &res_screen_y))
{
double scaled_y;
/* mouse coordinates are relative to the screen; convert them
* to be relative to the viewport */
src_min = mouse->y_min;
src_height = mouse->y_max - mouse->y_min + 1;
scaled_y = vp.full_height * (mouse->y_abs - src_min) / src_height;
y = -32767.0 + 65535.0 / vp.height * (scaled_y - vp.y);
}
else /* mouse coords are viewport relative */
else if (!confined && video_driver_translate_coord_viewport_wrap(
&vp, scaled_x, scaled_y,
&res_x, &res_y, &res_screen_x, &res_screen_y))
{
if (screen)
{
src_min = 0.0;
src_height = vp.full_height;
}
else
{
src_min = vp.y;
src_height = vp.height;
}
y = -32767.0 + 65535.0 / src_height * (mouse->y_abs - src_min);
}
y += (y < 0 ? -0.5 : 0.5);
if (y < -0x7fff)
return -0x7fff;
else if (y > 0x7fff)
return 0x7fff;
return y;
else
{
return false;
}
if (screen)
{
*ret_x = res_screen_x;
*ret_y = res_screen_y;
}
else
{
*ret_x = res_x;
*ret_y = res_y;
}
return true;
}
static void udev_handle_mouse(void *data,
@ -3616,15 +3580,12 @@ static int16_t udev_lightgun_aiming_state(
udev_input_t *udev, unsigned port, unsigned id )
{
const int edge_detect = 32700;
udev_input_mouse_t *mouse = udev_get_mouse(udev, port);
int16_t res_x;
int16_t res_y;
if (mouse)
if (mouse && udev_mouse_get_pointer(mouse, false, false, &res_x, &res_y))
{
bool inside = false;
int16_t res_x = udev_mouse_get_pointer_x(mouse, false);
int16_t res_y = udev_mouse_get_pointer_y(mouse, false);
switch ( id )
{
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X:
@ -3632,11 +3593,7 @@ static int16_t udev_lightgun_aiming_state(
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y:
return res_y;
case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN:
inside = (res_x >= -edge_detect)
&& (res_y >= -edge_detect)
&& (res_x <= edge_detect)
&& (res_y <= edge_detect);
return !inside;
return input_driver_pointer_is_offscreen(res_x, res_y);
default:
break;
}
@ -3731,60 +3688,29 @@ static int16_t udev_pointer_state(udev_input_t *udev,
unsigned port, unsigned id, bool screen)
{
udev_input_mouse_t *mouse = udev_get_mouse(udev, port);
int16_t res_x;
int16_t res_y;
if (mouse)
if (mouse && udev_mouse_get_pointer(mouse, screen, true, &res_x, &res_y))
{
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X:
return udev_mouse_get_pointer_x(mouse, screen);
return res_x;
case RETRO_DEVICE_ID_POINTER_Y:
return udev_mouse_get_pointer_y(mouse, screen);
return res_y;
case RETRO_DEVICE_ID_POINTER_PRESSED:
if (mouse->abs == 1)
return mouse->pp;
return mouse->l;
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(res_x, res_y);
}
}
return 0;
}
static unsigned udev_retro_id_to_rarch(unsigned id)
{
switch (id)
{
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT:
return RARCH_LIGHTGUN_DPAD_RIGHT;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT:
return RARCH_LIGHTGUN_DPAD_LEFT;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP:
return RARCH_LIGHTGUN_DPAD_UP;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN:
return RARCH_LIGHTGUN_DPAD_DOWN;
case RETRO_DEVICE_ID_LIGHTGUN_SELECT:
return RARCH_LIGHTGUN_SELECT;
case RETRO_DEVICE_ID_LIGHTGUN_PAUSE:
return RARCH_LIGHTGUN_START;
case RETRO_DEVICE_ID_LIGHTGUN_RELOAD:
return RARCH_LIGHTGUN_RELOAD;
case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER:
return RARCH_LIGHTGUN_TRIGGER;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_A:
return RARCH_LIGHTGUN_AUX_A;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_B:
return RARCH_LIGHTGUN_AUX_B;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_C:
return RARCH_LIGHTGUN_AUX_C;
case RETRO_DEVICE_ID_LIGHTGUN_START:
return RARCH_LIGHTGUN_START;
default:
break;
}
return 0;
}
static int16_t udev_input_state(
void *data,
const input_device_driver_t *joypad,
@ -3935,7 +3861,7 @@ static int16_t udev_input_state(
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT:
case RETRO_DEVICE_ID_LIGHTGUN_PAUSE: /* deprecated */
{
unsigned new_id = udev_retro_id_to_rarch(id);
unsigned new_id = input_driver_lightgun_id_convert(id);
const uint64_t bind_joykey = input_config_binds[port][new_id].joykey;
const uint64_t bind_joyaxis = input_config_binds[port][new_id].joyaxis;
const uint64_t autobind_joykey = input_autoconf_binds[port][new_id].joykey;

View File

@ -103,41 +103,6 @@ static bool x_mouse_button_pressed(
return false;
}
static unsigned x_retro_id_to_rarch(unsigned id)
{
switch (id)
{
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT:
return RARCH_LIGHTGUN_DPAD_RIGHT;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT:
return RARCH_LIGHTGUN_DPAD_LEFT;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP:
return RARCH_LIGHTGUN_DPAD_UP;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN:
return RARCH_LIGHTGUN_DPAD_DOWN;
case RETRO_DEVICE_ID_LIGHTGUN_SELECT:
return RARCH_LIGHTGUN_SELECT;
case RETRO_DEVICE_ID_LIGHTGUN_PAUSE:
return RARCH_LIGHTGUN_START;
case RETRO_DEVICE_ID_LIGHTGUN_RELOAD:
return RARCH_LIGHTGUN_RELOAD;
case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER:
return RARCH_LIGHTGUN_TRIGGER;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_A:
return RARCH_LIGHTGUN_AUX_A;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_B:
return RARCH_LIGHTGUN_AUX_B;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_C:
return RARCH_LIGHTGUN_AUX_C;
case RETRO_DEVICE_ID_LIGHTGUN_START:
return RARCH_LIGHTGUN_START;
default:
break;
}
return 0;
}
static int16_t x_input_state(
void *data,
const input_device_driver_t *joypad,
@ -278,22 +243,15 @@ static int16_t x_input_state(
case RARCH_DEVICE_POINTER_SCREEN:
if (idx == 0)
{
struct video_viewport vp;
bool screen = device == RARCH_DEVICE_POINTER_SCREEN;
bool inside = false;
struct video_viewport vp = {0};
bool screen =
(device == RARCH_DEVICE_POINTER_SCREEN);
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;
if (video_driver_translate_coord_viewport_wrap(
if (video_driver_translate_coord_viewport_confined_wrap(
&vp, x11->mouse_x, x11->mouse_y,
&res_x, &res_y, &res_screen_x, &res_screen_y))
{
@ -303,11 +261,6 @@ static int16_t x_input_state(
res_y = res_screen_y;
}
inside = (res_x >= -0x7fff) && (res_y >= -0x7fff);
if (!inside)
return 0;
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X:
@ -316,6 +269,8 @@ static int16_t x_input_state(
return res_y;
case RETRO_DEVICE_ID_POINTER_PRESSED:
return x11->mouse_l;
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(res_x, res_y);
}
}
}
@ -328,42 +283,24 @@ static int16_t x_input_state(
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y:
case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN:
{
struct video_viewport vp;
const int edge_detect = 32700;
bool inside = false;
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;
if (video_driver_translate_coord_viewport_wrap(&vp,
x11->mouse_x, x11->mouse_y,
&res_x, &res_y, &res_screen_x, &res_screen_y))
{
inside = (res_x >= -edge_detect)
&& (res_y >= -edge_detect)
&& (res_x <= edge_detect)
&& (res_y <= edge_detect);
switch ( id )
{
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X:
if (inside)
return res_x;
break;
return res_x;
case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y:
if (inside)
return res_y;
break;
return res_y;
case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN:
return !inside;
return input_driver_pointer_is_offscreen(res_x, res_y);
default:
break;
}
@ -384,7 +321,7 @@ static int16_t x_input_state(
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT:
case RETRO_DEVICE_ID_LIGHTGUN_PAUSE: /* deprecated */
{
unsigned new_id = x_retro_id_to_rarch(id);
unsigned new_id = input_driver_lightgun_id_convert(id);
const uint64_t bind_joykey = input_config_binds[port][new_id].joykey;
const uint64_t bind_joyaxis = input_config_binds[port][new_id].joyaxis;
const uint64_t autobind_joykey = input_autoconf_binds[port][new_id].joykey;
@ -410,11 +347,8 @@ static int16_t x_input_state(
&& x_keyboard_pressed(x11, binds[port][new_id].key)
)
return 1;
else if (settings->uints.input_mouse_index[port] == 0)
{
if (x_mouse_button_pressed(x11, port, binds[port][new_id].mbutton))
else if (x_mouse_button_pressed(x11, port, binds[port][new_id].mbutton))
return 1;
}
}
}
break;

View File

@ -655,6 +655,42 @@ bool input_driver_pointer_is_offscreen(int16_t x, int16_t y)
return true;
}
unsigned input_driver_lightgun_id_convert(unsigned id)
{
switch (id)
{
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT:
return RARCH_LIGHTGUN_DPAD_RIGHT;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT:
return RARCH_LIGHTGUN_DPAD_LEFT;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP:
return RARCH_LIGHTGUN_DPAD_UP;
case RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN:
return RARCH_LIGHTGUN_DPAD_DOWN;
case RETRO_DEVICE_ID_LIGHTGUN_SELECT:
return RARCH_LIGHTGUN_SELECT;
case RETRO_DEVICE_ID_LIGHTGUN_PAUSE:
return RARCH_LIGHTGUN_START;
case RETRO_DEVICE_ID_LIGHTGUN_RELOAD:
return RARCH_LIGHTGUN_RELOAD;
case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER:
return RARCH_LIGHTGUN_TRIGGER;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_A:
return RARCH_LIGHTGUN_AUX_A;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_B:
return RARCH_LIGHTGUN_AUX_B;
case RETRO_DEVICE_ID_LIGHTGUN_AUX_C:
return RARCH_LIGHTGUN_AUX_C;
case RETRO_DEVICE_ID_LIGHTGUN_START:
return RARCH_LIGHTGUN_START;
default:
break;
}
return 0;
}
bool input_driver_button_combo(
unsigned mode,
retro_time_t current_time,

View File

@ -922,6 +922,8 @@ char *input_config_get_device_name_ptr(unsigned port);
*/
size_t input_config_get_device_name_size(unsigned port);
unsigned input_driver_lightgun_id_convert(unsigned id);
bool input_driver_pointer_is_offscreen(int16_t x, int16_t y);
bool input_driver_button_combo(