Files
RetroArch/input/input_pointer_frame.h
T
LibretroAdmin b1194186ac Fix Wii U, dingux and aarch64 build warnings
Wii U get_arguments() prints through the argc/argv pointers it was
handed, and argv[1] only when there is one. The in-place URL decode
passes the string's real size to string_percent_decode(), and Basic
auth decodes its own buffer with the length it already has, so LTO
no longer sees a SIZE_MAX bound wrapping around.

Helpers only some configurations call are compiled for those only:
the overlay-only input and menu helpers under HAVE_OVERLAY, the
container-only audio_transfer time and movie-header helpers under
their container, and the scalar xxh3 long path off NEON. hidraw
hands autoconfig the stored vid/pid, rcheevos copies its operand by
assignment, and the Qt companion asks QScreen from Qt 5.10 on.
2026-11-04 15:53:34 +01:00

570 lines
20 KiB
C

/* RetroArch - A frontend for libretro.
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __INPUT_POINTER_FRAME_H
#define __INPUT_POINTER_FRAME_H
/* What the frontend keeps of the mice and touches an input driver
* publishes at its poll, and how it answers for the mouse, the pointer
* and the lightgun's aim from them. input_driver.c is its one user; it
* is a header so that a test can drive the same code
* (samples/input/pointer_frame). It needs input_driver.h,
* gfx/video_driver.h's viewport calls and <string.h> before it. */
#define INPUT_TOUCHES_MAX 16
/* The pointing devices an input driver published at this poll, and
* their places in the viewport, worked out the first time one is asked
* for after the poll and kept until the next. */
static struct
{
input_pointer_frame_t frame[MAX_USERS];
uint32_t confined_pos[MAX_USERS]; /* in the viewport, held to its edges */
uint32_t viewport_pos[MAX_USERS]; /* in the viewport, -0x8000 outside it */
uint32_t screen_pos[MAX_USERS];
uint32_t read_as[MAX_USERS]; /* the device kind last read as */
uint32_t touch_pos[INPUT_TOUCHES_MAX]; /* in the window; 0: none */
uint32_t touch_confined_pos[INPUT_TOUCHES_MAX];
uint32_t touch_screen_pos[INPUT_TOUCHES_MAX];
#ifdef HAVE_VIDEO_WINDOWS
input_video_window_t window[MAX_USERS]; /* where in_window says */
input_video_window_t touch_window[INPUT_TOUCHES_MAX];
#endif
struct video_viewport vp;
#ifdef HAVE_VIDEO_WINDOWS
uint16_t in_window; /* a bit a device: in a screen's own */
uint16_t touch_in_window;
#endif
uint16_t have_confined; /* a bit a device: done this poll */
uint16_t have_viewport;
uint32_t touch_viewport_pos[INPUT_TOUCHES_MAX]; /* -0x8000 outside */
uint16_t have_touch; /* a bit a touch: done this poll */
uint16_t have_touch_viewport;
uint16_t touch_present; /* a bit a touch: a contact is there */
uint16_t touch_down; /* ... and is pressed */
uint8_t count;
uint8_t touch_count;
uint8_t flags; /* enum input_pointers_flags */
bool vp_asked;
bool vp_ok;
} input_pointers;
#ifdef HAVE_VIDEO_WINDOWS
/* @count windows kept, and a bit for each in a screen's own; NULL is
* the main window's. */
static uint16_t input_pointer_windows_set(input_video_window_t *kept,
const input_video_window_t *windows, unsigned count)
{
unsigned i;
uint16_t in_window = 0;
if (!windows)
return 0;
memcpy(kept, windows, count * sizeof(*windows));
for (i = 0; i < count; i++)
if (windows[i].window)
in_window |= (uint16_t)(1 << i);
return in_window;
}
#endif
static void input_pointer_frames_set(const input_pointer_frame_t *frames,
const input_video_window_t *windows, unsigned count, unsigned flags)
{
if (count > MAX_USERS)
count = MAX_USERS;
memcpy(input_pointers.frame, frames, count * sizeof(*frames));
#ifdef HAVE_VIDEO_WINDOWS
input_pointers.in_window = input_pointer_windows_set(
input_pointers.window, windows, count);
#else
(void)windows;
#endif
input_pointers.count = (uint8_t)count;
input_pointers.flags = (uint8_t)flags;
input_pointers.have_confined = 0;
input_pointers.have_viewport = 0;
input_pointers.vp_asked = false;
}
/* Where a touch is, whether there is one, and whether it is down are
* three things. A place can hold a contact that has lifted - the last
* place of a finger, a pen in the air - and a contact can be at 0,0,
* the window's corner: neither is told by the position. */
static void input_pointer_touches_set(const uint32_t *pos,
const input_video_window_t *windows, unsigned count,
unsigned present, unsigned down)
{
unsigned all;
if (count > INPUT_TOUCHES_MAX)
count = INPUT_TOUCHES_MAX;
all = (1u << count) - 1;
memcpy(input_pointers.touch_pos, pos, count * sizeof(*pos));
#ifdef HAVE_VIDEO_WINDOWS
input_pointers.touch_in_window = input_pointer_windows_set(
input_pointers.touch_window, windows, count);
#else
(void)windows;
#endif
input_pointers.touch_count = (uint8_t)count;
input_pointers.touch_present = (uint16_t)(present & all);
input_pointers.touch_down = (uint16_t)(down & present & all);
input_pointers.have_touch = 0;
input_pointers.have_touch_viewport = 0;
}
static bool input_pointer_viewport(void)
{
if (!input_pointers.vp_asked)
{
memset(&input_pointers.vp, 0, sizeof(input_pointers.vp));
input_pointers.vp_ok = video_driver_get_viewport_info(&input_pointers.vp);
input_pointers.vp_asked = true;
}
return input_pointers.vp_ok;
}
/* The published device a port reads as @device. */
static unsigned input_pointer_of_port(unsigned port, unsigned device)
{
if (input_pointers.flags & INPUT_POINTERS_BY_MOUSE_INDEX)
{
if ( (input_pointers.flags & INPUT_POINTERS_AIM_EVERY_PORT)
&& device != RETRO_DEVICE_MOUSE
&& device != RARCH_DEVICE_MOUSE_SCREEN)
return 0;
return input_mouse_port_index(port);
}
return (input_pointers.count == 1) ? 0 : port;
}
#ifdef HAVE_VIDEO_WINDOWS
/* @pos in a screen's own window, through the layout it last published:
* off-screen once the window is gone, and nowhere on the main window's
* screen. */
static void input_pointer_window_place(const input_video_window_t *w,
uint32_t pos, bool confined, uint32_t *place, uint32_t *screen_pos)
{
*screen_pos = VIDEO_POS_PACK(-0x8000, -0x8000);
if (!video_driver_translate_window_point(w->window, w->generation,
VIDEO_POS_X(pos), VIDEO_POS_Y(pos), !confined, place))
*place = VIDEO_POS_PACK(-0x8000, -0x8000);
}
#endif
/* Device @i's place in the viewport, or its own window's, and on the
* screen; false when there is no viewport to place it in. */
static bool input_pointer_place(unsigned i, bool confined,
uint32_t *pos, uint32_t *screen_pos)
{
uint16_t bit = (uint16_t)(1 << i);
uint16_t *have = confined
? &input_pointers.have_confined : &input_pointers.have_viewport;
#ifdef HAVE_VIDEO_WINDOWS
bool in_window = (input_pointers.in_window & bit) != 0;
#else
bool in_window = false;
#endif
if (!in_window && !input_pointer_viewport())
return false;
if (!(*have & bit))
{
uint32_t place = 0;
#ifdef HAVE_VIDEO_WINDOWS
if (in_window)
input_pointer_window_place(&input_pointers.window[i],
input_pointers.frame[i].pos, confined,
&place, &input_pointers.screen_pos[i]);
else
#endif
if (!video_driver_translate_coord_viewport(&input_pointers.vp,
VIDEO_POS_X(input_pointers.frame[i].pos),
VIDEO_POS_Y(input_pointers.frame[i].pos),
&place, &input_pointers.screen_pos[i], !confined))
{
input_pointers.vp_ok = false;
return false;
}
if (confined)
input_pointers.confined_pos[i] = place;
else
input_pointers.viewport_pos[i] = place;
*have |= bit;
}
*pos = confined
? input_pointers.confined_pos[i] : input_pointers.viewport_pos[i];
*screen_pos = input_pointers.screen_pos[i];
return true;
}
/* Touch @t's place in the viewport, or its own window's - held to its
* edges, or -0x8000 outside it - and on the screen; false when there is
* no viewport. */
static bool input_touch_place(unsigned t, bool confined,
uint32_t *pos, uint32_t *screen_pos)
{
uint16_t bit = (uint16_t)(1 << t);
uint16_t *have = confined
? &input_pointers.have_touch : &input_pointers.have_touch_viewport;
uint32_t *kept = confined
? &input_pointers.touch_confined_pos[t]
: &input_pointers.touch_viewport_pos[t];
#ifdef HAVE_VIDEO_WINDOWS
bool in_window = (input_pointers.touch_in_window & bit) != 0;
#else
bool in_window = false;
#endif
if (!in_window && !input_pointer_viewport())
return false;
if (!(*have & bit))
{
#ifdef HAVE_VIDEO_WINDOWS
if (in_window)
input_pointer_window_place(&input_pointers.touch_window[t],
input_pointers.touch_pos[t], confined,
kept, &input_pointers.touch_screen_pos[t]);
else
#endif
if (!video_driver_translate_coord_viewport(&input_pointers.vp,
VIDEO_POS_X(input_pointers.touch_pos[t]),
VIDEO_POS_Y(input_pointers.touch_pos[t]),
kept, &input_pointers.touch_screen_pos[t], !confined))
{
input_pointers.vp_ok = false;
return false;
}
*have |= bit;
}
*pos = *kept;
*screen_pos = input_pointers.touch_screen_pos[t];
return true;
}
/* Device @i's place in the main window, as published; none for one in
* a screen's own window. */
static uint32_t input_pointer_screen_place(unsigned i)
{
#ifdef HAVE_VIDEO_WINDOWS
if (input_pointers.in_window & (1 << i))
return VIDEO_POS_PACK(-0x8000, -0x8000);
#endif
return input_pointers.frame[i].pos;
}
/* Whether mouse @f, standing for touch @idx, is down. */
static bool input_pointer_mouse_down(const input_pointer_frame_t *f,
unsigned idx)
{
if (!(input_pointers.flags & INPUT_POINTERS_MOUSE_3_TOUCHES))
return (f->buttons & INPUT_POINTER_LEFT) != 0;
if (idx == 0)
return (f->buttons & (INPUT_POINTER_LEFT
| INPUT_POINTER_RIGHT | INPUT_POINTER_MIDDLE)) != 0;
if (idx == 1)
return (f->buttons & (INPUT_POINTER_RIGHT
| INPUT_POINTER_MIDDLE)) != 0;
return (f->buttons & INPUT_POINTER_MIDDLE) != 0;
}
/* Pointer @idx of mouse @i: a real touch in that place if there is one,
* else the mouse standing for it - if the port has a mouse. False if
* there is neither, or no viewport to place it in. */
static bool input_pointer_touch(unsigned i, unsigned idx,
uint32_t *pos, uint32_t *screen_pos, bool *down)
{
const input_pointer_frame_t *f = &input_pointers.frame[
i < input_pointers.count ? i : 0];
bool real = idx < input_pointers.touch_count
&& (input_pointers.touch_present & (1 << idx));
bool three = (input_pointers.flags & INPUT_POINTERS_MOUSE_3_TOUCHES) != 0;
bool mouse = i < input_pointers.count && idx < (three ? 3u : 1u);
bool confined = !(input_pointers.flags & INPUT_POINTERS_POINTER_OFFSCREEN);
if (!real && !mouse)
return false;
if (real
? !input_touch_place(idx, confined, pos, screen_pos)
: !input_pointer_place(i, confined, pos, screen_pos))
return false;
*down = real && (input_pointers.touch_down & (1 << idx));
if ( !*down && mouse
&& !(real && (input_pointers.flags & INPUT_POINTERS_TOUCH_ALONE)))
*down = input_pointer_mouse_down(f, idx);
return true;
}
/* How many pointers are down: the touches that are, or the mouse
* standing for one. */
static unsigned input_pointer_count(unsigned i)
{
unsigned n = 0;
unsigned t;
for (t = 0; t < input_pointers.touch_count; t++)
if (input_pointers.touch_down & (1 << t))
n++;
if (n)
return n;
if ( (input_pointers.flags & INPUT_POINTERS_TOUCH_ALONE)
&& (input_pointers.touch_present & 1))
return 0;
if (i < input_pointers.count)
return input_pointer_mouse_down(&input_pointers.frame[i], 0) ? 1 : 0;
return 0;
}
/* Where the lightgun of device @i aims: at the first touch, for a
* driver that has it so and while there is one, else at the mouse. */
static bool input_gun_place(unsigned i, uint32_t *pos)
{
uint32_t screen_pos;
if ( (input_pointers.flags & INPUT_POINTERS_GUN_AT_TOUCH)
&& (input_pointers.touch_present & 1))
return input_touch_place(0, false, pos, &screen_pos);
if (i >= input_pointers.count)
return false;
return input_pointer_place(i, false, pos, &screen_pos);
}
/* A mouse's, a pointer's or a lightgun aim's position, packed. False if
* the frontend does not hold it and the driver is to be asked. */
static bool input_pointer_frame_pos(unsigned port, unsigned device,
unsigned idx, uint32_t *pos)
{
unsigned i;
uint32_t screen_pos;
if (!input_pointers.count)
return false;
*pos = 0;
i = input_pointer_of_port(port, device);
/* count never exceeds MAX_USERS (input_pointer_frames_set clamps
* it); the second test says so to the compiler. */
if (i < input_pointers.count && i < MAX_USERS)
input_pointers.read_as[i] = device;
else if ( device != RETRO_DEVICE_POINTER
&& device != RARCH_DEVICE_POINTER_SCREEN)
return true; /* no mouse here; a pointer may still be a touch */
switch (device)
{
case RETRO_DEVICE_MOUSE:
*pos = input_pointers.frame[i].rel;
return true;
case RARCH_DEVICE_MOUSE_SCREEN:
*pos = input_pointer_screen_place(i);
return true;
case RETRO_DEVICE_POINTER:
case RARCH_DEVICE_POINTER_SCREEN:
{
uint32_t vp_pos = 0;
bool down;
screen_pos = 0;
input_pointer_touch(i, idx, &vp_pos, &screen_pos, &down);
*pos = (device == RETRO_DEVICE_POINTER) ? vp_pos : screen_pos;
}
return true;
case RETRO_DEVICE_LIGHTGUN:
input_gun_place(i, pos);
return true;
default:
break;
}
return false;
}
#ifdef HAVE_OVERLAY
/* Finger @idx of the pointer @port reads as @device (RETRO_DEVICE_POINTER
* or RARCH_DEVICE_POINTER_SCREEN), down or not and where, packed, at one
* look: what reading PRESSED, X and Y in turn gives. 1 down, 0 not; -1
* if the frontend does not hold it and the driver is to be asked. */
static int input_pointer_frame_finger(unsigned port, unsigned device,
unsigned idx, uint32_t *pos)
{
unsigned i;
uint32_t vp_pos = 0;
uint32_t screen_pos = 0;
bool down;
if (!input_pointers.count)
return -1;
*pos = 0;
i = input_pointer_of_port(port, device);
if (i < input_pointers.count && i < MAX_USERS)
input_pointers.read_as[i] = device;
if (!input_pointer_touch(i, idx, &vp_pos, &screen_pos, &down))
return 0;
*pos = (device == RARCH_DEVICE_POINTER_SCREEN) ? screen_pos : vp_pos;
return down ? 1 : 0;
}
#endif
/* One value of a mouse, a pointer or a lightgun's aim. False if the
* frontend does not hold it and the driver is to be asked. */
static bool input_pointer_frame_read(unsigned port, unsigned device,
unsigned idx, unsigned id, int16_t *out)
{
const input_pointer_frame_t *f;
unsigned i;
uint32_t pos;
if (!input_pointers.count)
return false;
switch (device)
{
case RETRO_DEVICE_MOUSE:
case RARCH_DEVICE_MOUSE_SCREEN:
case RETRO_DEVICE_POINTER:
case RARCH_DEVICE_POINTER_SCREEN:
break;
case RETRO_DEVICE_LIGHTGUN:
/* its aim; its buttons are binds, and the driver's */
if ( id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X
|| id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y
|| id == RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN
|| id == RETRO_DEVICE_ID_LIGHTGUN_X
|| id == RETRO_DEVICE_ID_LIGHTGUN_Y)
break;
return false;
default:
return false;
}
*out = 0;
i = input_pointer_of_port(port, device);
/* count never exceeds MAX_USERS (input_pointer_frames_set clamps
* it); the second test says so to the compiler. */
if (i < input_pointers.count && i < MAX_USERS)
input_pointers.read_as[i] = device;
else if ( device != RETRO_DEVICE_POINTER
&& device != RARCH_DEVICE_POINTER_SCREEN)
return true; /* no mouse here; a pointer may still be a touch */
f = &input_pointers.frame[i < input_pointers.count ? i : 0];
switch (device)
{
case RETRO_DEVICE_MOUSE:
case RARCH_DEVICE_MOUSE_SCREEN:
pos = (device == RETRO_DEVICE_MOUSE)
? f->rel : input_pointer_screen_place(i);
switch (id)
{
case RETRO_DEVICE_ID_MOUSE_X:
*out = VIDEO_POS_X(pos);
break;
case RETRO_DEVICE_ID_MOUSE_Y:
*out = VIDEO_POS_Y(pos);
break;
case RETRO_DEVICE_ID_MOUSE_LEFT:
*out = (f->buttons & INPUT_POINTER_LEFT) != 0;
break;
case RETRO_DEVICE_ID_MOUSE_RIGHT:
*out = (f->buttons & INPUT_POINTER_RIGHT) != 0;
break;
case RETRO_DEVICE_ID_MOUSE_MIDDLE:
*out = (f->buttons & INPUT_POINTER_MIDDLE) != 0;
break;
case RETRO_DEVICE_ID_MOUSE_BUTTON_4:
*out = (f->buttons & INPUT_POINTER_BUTTON_4) != 0;
break;
case RETRO_DEVICE_ID_MOUSE_BUTTON_5:
*out = (f->buttons & INPUT_POINTER_BUTTON_5) != 0;
break;
case RETRO_DEVICE_ID_MOUSE_WHEELUP:
*out = (f->buttons & INPUT_POINTER_WHEEL_UP) != 0;
break;
case RETRO_DEVICE_ID_MOUSE_WHEELDOWN:
*out = (f->buttons & INPUT_POINTER_WHEEL_DOWN) != 0;
break;
case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
*out = (f->buttons & INPUT_POINTER_HWHEEL_UP) != 0;
break;
case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
*out = (f->buttons & INPUT_POINTER_HWHEEL_DOWN) != 0;
break;
default:
break;
}
break;
case RETRO_DEVICE_POINTER:
case RARCH_DEVICE_POINTER_SCREEN:
{
uint32_t screen_pos;
bool down;
/* how many are down needs no place for them */
if (id == RETRO_DEVICE_ID_POINTER_COUNT)
{
*out = (int16_t)input_pointer_count(i);
break;
}
if (!input_pointer_touch(i, idx, &pos, &screen_pos, &down))
break;
if (device == RARCH_DEVICE_POINTER_SCREEN)
pos = screen_pos;
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X:
*out = VIDEO_POS_X(pos);
break;
case RETRO_DEVICE_ID_POINTER_Y:
*out = VIDEO_POS_Y(pos);
break;
case RETRO_DEVICE_ID_POINTER_PRESSED:
*out = down;
break;
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
*out = input_driver_pointer_is_offscreen(
VIDEO_POS_X(pos), VIDEO_POS_Y(pos));
break;
default:
break;
}
}
break;
case RETRO_DEVICE_LIGHTGUN:
if ( id == RETRO_DEVICE_ID_LIGHTGUN_X
|| id == RETRO_DEVICE_ID_LIGHTGUN_Y)
{
/* deprecated: the mouse's motion */
*out = (id == RETRO_DEVICE_ID_LIGHTGUN_X)
? VIDEO_POS_X(f->rel) : VIDEO_POS_Y(f->rel);
}
else
{
if (!input_gun_place(i, &pos))
break;
if (id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X)
*out = VIDEO_POS_X(pos);
else if (id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y)
*out = VIDEO_POS_Y(pos);
else
*out = input_driver_pointer_is_offscreen(
VIDEO_POS_X(pos), VIDEO_POS_Y(pos));
}
break;
default:
break;
}
return true;
}
/* Before a driver's poll: one that publishes nothing is asked itself. */
static void input_pointer_frames_clear(void)
{
input_pointers.count = 0;
input_pointers.touch_count = 0;
/* and the viewport is asked for afresh, by whoever wants it first */
input_pointers.vp_asked = false;
}
#endif