From 072d9493461a1c021621bc443dd3bb257bbe491e Mon Sep 17 00:00:00 2001 From: zoltanvb <101990835+zoltanvb@users.noreply.github.com> Date: Sat, 21 Dec 2024 22:28:27 +0100 Subject: [PATCH] Pointer handling sanitization - wayland and libretro.h changes (#17277) Adapt the sanitized pointer handling, discussed at #17196: Wayland driver specific changes: - make sure pointer position is always within [-0x7fff,0x7fff] by using the confined wrapper - enable lightgun to report -0x8000 if pointer is really offscreen - remove extra "inside" checks - report same pointer/lightgun coordinates for all ports - simplify pointer and lightgun handling Other changes: - unify "offscreen" condition through input_driver.c - slight tuning of pointer conversion in video_driver.c - update libretro.h with explanation and pointer offscreen value - small fixes on remote retropad test screen --- .../libretro-net-retropad/net_retropad_core.c | 16 ++-- gfx/video_driver.c | 16 ++-- input/drivers/wayland_input.c | 86 +++++-------------- input/input_driver.c | 11 +++ input/input_driver.h | 2 + libretro-common/include/libretro.h | 31 ++++--- 6 files changed, 69 insertions(+), 93 deletions(-) diff --git a/cores/libretro-net-retropad/net_retropad_core.c b/cores/libretro-net-retropad/net_retropad_core.c index aa38e18c7e..4584cf0a28 100644 --- a/cores/libretro-net-retropad/net_retropad_core.c +++ b/cores/libretro-net-retropad/net_retropad_core.c @@ -162,13 +162,13 @@ static struct descriptor mouse = { }; static struct descriptor pointer = { - .device = RETRO_DEVICE_POINTER/* | 0x10000*/, + .device = RETRO_DEVICE_POINTER, .port_min = 0, .port_max = 0, .index_min = 0, .index_max = 2, .id_min = RETRO_DEVICE_ID_POINTER_X, - .id_max = RETRO_DEVICE_ID_POINTER_COUNT + .id_max = RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN }; static struct descriptor lightgun = { @@ -710,7 +710,7 @@ static void retropad_update_input(void) } else if (id == RETRO_DEVICE_ID_MOUSE_Y || id == RETRO_DEVICE_ID_LIGHTGUN_Y) { - int32_t large = pointer_y + (int16_t)state*273; + int32_t large = pointer_y + (int16_t)state*327; if (large < -32768) pointer_y = -32768; else if (large > 32767) @@ -721,6 +721,7 @@ static void retropad_update_input(void) } else if (mouse_type == NETRETROPAD_POINTER || mouse_type == NETRETROPAD_LIGHTGUN) { + /* NETRETROPAD_CORE_PREFIX(log_cb)(RETRO_LOG_DEBUG, "Pointer state: %d %d %d (%d %d)\n",i,id, (int16_t)state,pointer_x,pointer_y); */ if (id == RETRO_DEVICE_ID_POINTER_X || id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) pointer_x = (int16_t)state; else if (id == RETRO_DEVICE_ID_POINTER_Y || id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) @@ -1114,15 +1115,18 @@ void NETRETROPAD_CORE_PREFIX(retro_run)(void) { int offset; - offset = DESC_OFFSET(&lightgun, 0, 0, RETRO_DEVICE_ID_POINTER_PRESSED); + offset = DESC_OFFSET(&pointer, 0, 0, RETRO_DEVICE_ID_POINTER_PRESSED); sensor_item_colors[104] = pointer.value[offset] ? 0xA000 : 0x0000; - offset = DESC_OFFSET(&lightgun, 0, 1, RETRO_DEVICE_ID_POINTER_PRESSED); + offset = DESC_OFFSET(&pointer, 0, 1, RETRO_DEVICE_ID_POINTER_PRESSED); sensor_item_colors[105] = pointer.value[offset] ? 0xA000 : 0x0000; - offset = DESC_OFFSET(&lightgun, 0, 2, RETRO_DEVICE_ID_POINTER_PRESSED); + offset = DESC_OFFSET(&pointer, 0, 2, RETRO_DEVICE_ID_POINTER_PRESSED); sensor_item_colors[106] = pointer.value[offset] ? 0xA000 : 0x0000; + offset = DESC_OFFSET(&pointer, 0, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN); + sensor_item_colors[77] = pointer.value[offset] ? 0xA000 : 0x0000; + } /* Edge / offscreen indicators */ if (mouse_type == NETRETROPAD_POINTER || mouse_type == NETRETROPAD_LIGHTGUN) diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 7dd01762b6..54816b92c1 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -712,19 +712,19 @@ 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 = ((2 * mouse_x * 0x7fff) + if (mouse_x >= 0 && mouse_x < norm_full_vp_width) + scaled_screen_x = ((mouse_x * 0x10000) / norm_full_vp_width) - 0x7fff; - if (mouse_y >= 0 && mouse_y <= norm_full_vp_height) - scaled_screen_y = ((2 * mouse_y * 0x7fff) + if (mouse_y >= 0 && mouse_y < norm_full_vp_height) + scaled_screen_y = ((mouse_y * 0x10000) / norm_full_vp_height) - 0x7fff; mouse_x -= vp->x; mouse_y -= vp->y; - if (mouse_x >= 0 && mouse_x <= norm_vp_width) - scaled_x = ((2 * mouse_x * 0x7fff) + if (mouse_x >= 0 && mouse_x < norm_vp_width) + scaled_x = ((mouse_x * 0x10000) / norm_vp_width) - 0x7fff; else if (!report_oob) { @@ -734,8 +734,8 @@ bool video_driver_translate_coord_viewport( scaled_x = 0x7fff; } - if (mouse_y >= 0 && mouse_y <= norm_vp_height) - scaled_y = ((2 * mouse_y * 0x7fff) + if (mouse_y >= 0 && mouse_y < norm_vp_height) + scaled_y = ((mouse_y * 0x10000) / norm_vp_height) - 0x7fff; else if (!report_oob) { diff --git a/input/drivers/wayland_input.c b/input/drivers/wayland_input.c index b4a1501d11..56c8e4096c 100644 --- a/input/drivers/wayland_input.c +++ b/input/drivers/wayland_input.c @@ -122,7 +122,7 @@ static int16_t input_wl_touch_state(input_ctx_wayland_data_t *wl, vp.full_width = 0; vp.full_height = 0; - if (video_driver_translate_coord_viewport_wrap(&vp, + if (video_driver_translate_coord_viewport_confined_wrap(&vp, wl->touches[idx].x, wl->touches[idx].y, &res_x, &res_y, &res_screen_x, &res_screen_y)) { @@ -256,7 +256,8 @@ static int16_t input_wl_state( return (id && id < RETROK_LAST) && BIT_GET(wl->key_state, rarch_keysym_lut[(enum retro_key)id]); case RETRO_DEVICE_MOUSE: case RARCH_DEVICE_MOUSE_SCREEN: - if (port == 0) /* TODO/FIXME: support mouse on additional ports */ + /* The system-wide mouse is reported for all ports. * + * Multi-mouse may be implemented using different wayland seats, see issue #16886 */ { bool state = false; bool screen = (device == RARCH_DEVICE_MOUSE_SCREEN); @@ -300,26 +301,18 @@ static int16_t input_wl_state( } break; case RETRO_DEVICE_POINTER: - /* TODO/FIXME: support pointers on additional ports */ + /* All ports report the same pointer state. See notes at mouse case. */ if (idx == 0) { - struct video_viewport vp; + struct video_viewport vp = {0}; bool screen = (device == RARCH_DEVICE_POINTER_SCREEN); - bool inside = false; 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, + if (video_driver_translate_coord_viewport_confined_wrap(&vp, wl->mouse.x, wl->mouse.y, &res_x, &res_y, &res_screen_x, &res_screen_y)) { @@ -329,10 +322,6 @@ static int16_t input_wl_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: @@ -341,13 +330,8 @@ static int16_t input_wl_state( return res_y; case RETRO_DEVICE_ID_POINTER_PRESSED: return wl->mouse.left; - case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN: - if (screen) - { - res_x = res_screen_x; - res_y = res_screen_y; - } - return (!((res_x >= -0x7fff) && (res_y >= -0x7fff))); + case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN: + return input_driver_pointer_is_offscreen(res_x, res_y); default: break; } @@ -363,68 +347,38 @@ static int16_t input_wl_state( } break; case RETRO_DEVICE_LIGHTGUN: - if (port == 0) /* TODO/FIXME: support lightguns on additional ports */ + /* All ports report the same lightgun state. See notes at mouse case. */ { - const int edge_detect = 32700; - struct video_viewport vp; - bool screen = - (device == RARCH_DEVICE_POINTER_SCREEN); - bool inside = false; - 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; + 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; if (video_driver_translate_coord_viewport_wrap(&vp, wl->mouse.x, wl->mouse.y, &res_x, &res_y, &res_screen_x, &res_screen_y)) { - if (screen) - { - res_x = res_screen_x; - res_y = res_screen_y; - } - - inside = (res_x >= -0x7fff) && (res_y >= -0x7fff); - if (!inside) - return 0; - switch (id) { - case RETRO_DEVICE_ID_LIGHTGUN_X: /* TODO: migrate to RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X */ - return wl->mouse.delta_x; /* deprecated relative coordinates */ - case RETRO_DEVICE_ID_LIGHTGUN_Y: /* TODO: migrate to RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y */ - return wl->mouse.delta_y; /* deprecated relative coordinates */ + case RETRO_DEVICE_ID_LIGHTGUN_X: + return wl->mouse.delta_x; + case RETRO_DEVICE_ID_LIGHTGUN_Y: + return wl->mouse.delta_y; case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X: return res_x; case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y: return res_y; case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER: return wl->mouse.left; - case RETRO_DEVICE_ID_LIGHTGUN_RELOAD: /* forced/faked off-screen shot */ + case RETRO_DEVICE_ID_LIGHTGUN_RELOAD: return wl->mouse.middle; case RETRO_DEVICE_ID_LIGHTGUN_START: return wl->mouse.right; case RETRO_DEVICE_ID_LIGHTGUN_SELECT: return wl->mouse.left && wl->mouse.right; case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN: - if (screen) - { - res_x = res_screen_x; - res_y = res_screen_y; - } - 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); case RETRO_DEVICE_ID_LIGHTGUN_AUX_A: /* TODO */ case RETRO_DEVICE_ID_LIGHTGUN_AUX_B: /* TODO */ case RETRO_DEVICE_ID_LIGHTGUN_AUX_C: /* TODO */ diff --git a/input/input_driver.c b/input/input_driver.c index 3c1b701122..b8534e633d 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -644,6 +644,17 @@ static bool input_driver_button_combo_hold( return false; } +bool input_driver_pointer_is_offscreen(int16_t x, int16_t y) +{ + const int edge_detect = 32700; + if ((x >= -edge_detect) && + (y >= -edge_detect) && + (x <= edge_detect) && + (y <= edge_detect)) + return false; + return true; +} + bool input_driver_button_combo( unsigned mode, retro_time_t current_time, diff --git a/input/input_driver.h b/input/input_driver.h index 4457ada399..420615bfa9 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -922,6 +922,8 @@ char *input_config_get_device_name_ptr(unsigned port); */ size_t input_config_get_device_name_size(unsigned port); +bool input_driver_pointer_is_offscreen(int16_t x, int16_t y); + bool input_driver_button_combo( unsigned mode, retro_time_t current_time, diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 288512b91f..1d7e765a55 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -4,12 +4,12 @@ * @file libretro.h * @version 1 * @author libretro - * @copyright Copyright (C) 2010-2023 The RetroArch team + * @copyright Copyright (C) 2010-2024 The RetroArch team * * @paragraph LICENSE * The following license statement only applies to this libretro API header (libretro.h). * - * Copyright (C) 2010-2023 The RetroArch team + * Copyright (C) 2010-2024 The RetroArch team * * Permission is hereby granted, free of charge, * to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -272,7 +272,10 @@ extern "C" { * [-0x7fff, 0x7fff]: -0x7fff corresponds to the far left/top of the screen, * and 0x7fff corresponds to the far right/bottom of the screen. * The "screen" is here defined as area that is passed to the frontend and - * later displayed on the monitor. + * later displayed on the monitor. If the pointer is outside this screen, + * such as in the black surrounding areas when actual display is larger, + * edge position is reported. An explicit edge detection is also provided, + * that will return 1 if the pointer is near the screen edge or actually outside it. * * The frontend is free to scale/resize this screen as it sees fit, however, * (X, Y) = (-0x7fff, -0x7fff) will correspond to the top-left pixel of the @@ -406,7 +409,8 @@ extern "C" { /* Id values for LIGHTGUN. */ #define RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X 13 /*Absolute Position*/ -#define RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y 14 /*Absolute*/ +#define RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y 14 /*Absolute Position*/ +/** Indicates if lightgun points off the screen or near the edge */ #define RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN 15 /*Status Check*/ #define RETRO_DEVICE_ID_LIGHTGUN_TRIGGER 2 #define RETRO_DEVICE_ID_LIGHTGUN_RELOAD 16 /*Forced off-screen shot*/ @@ -421,17 +425,18 @@ extern "C" { #define RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT 12 /* deprecated */ #define RETRO_DEVICE_ID_LIGHTGUN_X 0 /*Relative Position*/ -#define RETRO_DEVICE_ID_LIGHTGUN_Y 1 /*Relative*/ -#define RETRO_DEVICE_ID_LIGHTGUN_CURSOR 3 /*Use Aux:A*/ -#define RETRO_DEVICE_ID_LIGHTGUN_TURBO 4 /*Use Aux:B*/ -#define RETRO_DEVICE_ID_LIGHTGUN_PAUSE 5 /*Use Start*/ +#define RETRO_DEVICE_ID_LIGHTGUN_Y 1 /*Relative Position*/ +#define RETRO_DEVICE_ID_LIGHTGUN_CURSOR 3 /*Use Aux:A instead*/ +#define RETRO_DEVICE_ID_LIGHTGUN_TURBO 4 /*Use Aux:B instead*/ +#define RETRO_DEVICE_ID_LIGHTGUN_PAUSE 5 /*Use Start instead*/ /* Id values for POINTER. */ -#define RETRO_DEVICE_ID_POINTER_X 0 -#define RETRO_DEVICE_ID_POINTER_Y 1 -#define RETRO_DEVICE_ID_POINTER_PRESSED 2 -#define RETRO_DEVICE_ID_POINTER_COUNT 3 - +#define RETRO_DEVICE_ID_POINTER_X 0 +#define RETRO_DEVICE_ID_POINTER_Y 1 +#define RETRO_DEVICE_ID_POINTER_PRESSED 2 +#define RETRO_DEVICE_ID_POINTER_COUNT 3 +/** Indicates if pointer is off the screen or near the edge */ +#define RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN 15 /** @} */ /* Returned from retro_get_region(). */