mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-12 16:19:29 -10:00
* rgui: upload the menu again to the video driver a reinit makes Closing content with RGUI up left the screen black until a key press. The staged content load presents frames between its stages, so RGUI can upload its framebuffer to the old video driver and clear FB_DIRTY just before the driver stage replaces that driver. The new one has no menu texture, nothing in the menu changes, and RGUI never uploads again. command_event_reinit sets FB_DIRTY after its reinit, but the staged load and video-only resets reach the menu only through context_reset, so RGUI marks its framebuffer dirty there. The menu_icon_ownership harness gets an rgui lane that reinits the video driver and checks the next upload reaches the new one. * video: draw a frame queued before a view count shrink with its views Under threaded video the wrapper ran SET_VIEW_COUNT ahead of the frame it had already claimed, so a closing core's last queued frame drew after its view chains were gone: whole, both screens stacked, until the menu's first frame replaced it. The wrapper still answers at once, but a smaller count reaches the driver only once the frames queued before it have drawn. A grow applies at once as before, with the main thread waiting for the preset parse. Vulkan and glcore therefore build view chains only when the count grows, so a deferred shrink never parses a preset. They also keep the headset's whole-frame chain across a count change: it does not depend on the count, and with chains built only on a grow nothing would rebuild it after a shrink to 0. * android: don't make a second screen's EGL surface once Java took it back Java can take the second screen's surface back between the poll that hands it over and the frame that makes its EGL surface. Skip making one then; the screens go back to the main window as they do when a make fails. Java can still take the surface back during the make itself; the node's own ANativeWindow reference keeps the window alive through that. * android: ask a display move by display id and number it A move named its display by its place in the list, and Java looked that up later on the UI thread against whatever list was newest, so a display coming or going in between could send RetroArch elsewhere. Java now gets the display id and fails the move when that display is gone. Native code now counts the ids when it compares display lists, so a display unplugged and plugged back is a new list and its failures clear. A failure was a bare flag, so one move's failure could end the next move and mark its display unable. Each move now carries a number and a failure counts only for the move it names. The move code also loses two stores that were always overwritten. * android: end the refocus hold after a move only when it is done A move onto a display that is no longer valid now fails at once instead of waiting out its timeout. The hold that keeps RetroArch's window unfocusable after a move ends only on the focus loss Android answers it with, not on a gain that was already queued; an earlier hold ends only once the new move has started; and onStop() ends a running hold rather than leaving it to its timer. * video_windows: tell an external monitor by its display type Android lists the AYN Thor's two panels and a DP monitor with the same unknown connection type, so Auto took the monitor for a second panel: on a one-screen phone Game Screen 2 went to it. Java now reads the hidden Display.getType() by reflection where it can and passes internal or external with each display. A display typed internal is never a monitor; where the type is unknown, a non-default display with an unknown connection and a product name now counts as one. * video: give a Presentation on another display its own UI wait The menu waits up to a second for the Presentation it is laid out for to show, but the wait was only counted from the first one. Once a Presentation had shown, one made on another display had no wait left and the menu fell back to the main window at once. * input: keep the menu's mouse where it was while the menu is in a window With Mouse Support on, the menu moving into the Presentation read as a mouse move to (0,0) for a frame, which marks the pointer active there. The view keeps the main window's last mouse position instead. * input: match a window's touches only from a windowed publication A publication without windows after one with them left the old window ids in place, so a window's touch lookup could still match them. Only touches published in a window match now. No Android path publishes that way today. * vulkan: copy a waiting window's views on a new frame A screen window that was up but had no free image on a new frame did not get its views copied, so a later repeat that drew it, as a pause with the menu there does, left black areas where those views go. They are copied on every new frame now. * video: load the idle logo from the assets directory without widgets The idle logo's path came from the widgets, which only start with On-Screen Notifications on, so with them off the idle window stayed black. Without the widgets' path it is built from the assets directory the way the widgets build it. * android: hold the focus when Android moves RetroArch between displays When the display RetroArch is on, or moving to, goes away, Android moves the Activity to the default display and can send that display's focus gain before the removed display's loss. A task moved to another display and straight back gets no configuration change, and can read the old display's loss last too. With the loss last, RetroArch pauses while Android keeps it focused, and no later focus event wakes it. Count these as moves, as RetroArch's own moves are: a removal, and any change of RetroArch's display it did not ask for. From Android 10, with more than one display, so is losing and regaining the top-resumed place while focused, or regaining it unfocused within a second of losing it after a loss no hold asked for. The refocus hold that covers a move's late loss then covers these too: the window is made unfocusable until Android has taken the focus, then gets the gain again. A gain already on its way skips the hold, and a move marked while RetroArch is focused ends after a second with no focus event. * samples: close test gaps in video_windows and android_focus The is_default check set the flag and read it back, so it could not fail; Auto's fallback to the default display now shows the flag at work. Cover a display listed without an ordinal, and display keys at sizes 0 to 3, two unnamed displays and a 63-character name. Drop a stale note from android_focus_test's header. * video: fix wording left over from the display rows The Screen Display sublabel still named the Separate mode, a setting the display rows removed; it now says "when it has a window of its own". The warnings for a screen window that can't be drawn, or whose surface is gone, said its screens go back to the main window, but a window that shows only the menu reaches them too. They now read like the one for a window that can't be made: what it shows stays in the main window. Comments that no longer matched the code are reworded. pointer_frame's model of the driver before the publication is named was_* rather than today_*, and a wrong commit hash and a plan tag go from the tests. video_driver_free_internal() uses VIDEO_ANDROID_RELEASE_KEPT_WINDOWS() rather than open-coding it, with no change in behaviour.
757 lines
27 KiB
C
757 lines
27 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. */
|
|
|
|
/* Android's 16 slots on the main window and 16 fingers on the second
|
|
* display */
|
|
#define INPUT_TOUCHES_MAX 32
|
|
|
|
/* 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 */
|
|
uint32_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 */
|
|
uint32_t have_touch; /* a bit a touch: done this poll */
|
|
uint32_t have_touch_viewport;
|
|
uint32_t touch_present; /* a bit a touch: a contact is there */
|
|
uint32_t touch_down; /* ... and is pressed */
|
|
uint8_t count;
|
|
uint8_t touch_count;
|
|
uint16_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 uint32_t input_pointer_windows_set(input_video_window_t *kept,
|
|
const input_video_window_t *windows, unsigned count)
|
|
{
|
|
unsigned i;
|
|
uint32_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 |= (uint32_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;
|
|
/* written in place by a driver the frames were lent to */
|
|
if (frames != input_pointers.frame)
|
|
memcpy(input_pointers.frame, frames, count * sizeof(*frames));
|
|
#ifdef HAVE_VIDEO_WINDOWS
|
|
input_pointers.in_window = (uint16_t)input_pointer_windows_set(
|
|
input_pointers.window, windows, count);
|
|
#else
|
|
(void)windows;
|
|
#endif
|
|
input_pointers.count = (uint8_t)count;
|
|
input_pointers.flags = (uint16_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)
|
|
{
|
|
uint32_t all;
|
|
if (count > INPUT_TOUCHES_MAX)
|
|
count = INPUT_TOUCHES_MAX;
|
|
all = (count < 32) ? (((uint32_t)1 << count) - 1) : 0xffffffffu;
|
|
/* written in place by a driver the places were lent to */
|
|
if (pos != input_pointers.touch_pos)
|
|
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 = (uint32_t)present & all;
|
|
input_pointers.touch_down = (uint32_t)down & (uint32_t)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)
|
|
{
|
|
uint32_t bit = (uint32_t)1 << t;
|
|
uint32_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;
|
|
}
|
|
|
|
/* Whether touch @t is the second list's (@second) or the main one's:
|
|
* with WINDOWS_SECOND by its window, else every touch is the main
|
|
* list's. */
|
|
static bool input_touch_in_list(unsigned t, bool second)
|
|
{
|
|
#ifdef HAVE_VIDEO_WINDOWS
|
|
if (input_pointers.flags & INPUT_POINTERS_WINDOWS_SECOND)
|
|
return ((input_pointers.touch_in_window >> t) & 1) == (second ? 1u : 0u);
|
|
#else
|
|
(void)t;
|
|
#endif
|
|
return !second;
|
|
}
|
|
|
|
/* The slot of index @idx of a list, present or not; -1 for none. */
|
|
static int input_touch_slot(unsigned idx, bool second)
|
|
{
|
|
unsigned t;
|
|
for (t = 0; t < input_pointers.touch_count; t++)
|
|
{
|
|
if (!input_touch_in_list(t, second))
|
|
continue;
|
|
if (!idx)
|
|
return (int)t;
|
|
idx--;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Whether touch @t is inside what it is read against: the viewport or
|
|
* its window's screens, or (@screen) the main window. */
|
|
static bool input_touch_inside(unsigned t, bool screen)
|
|
{
|
|
uint32_t pos;
|
|
uint32_t screen_pos;
|
|
if (!input_touch_place(t, false, &pos, &screen_pos))
|
|
return false;
|
|
if (screen)
|
|
pos = screen_pos;
|
|
return VIDEO_POS_X(pos) != -0x8000 && VIDEO_POS_Y(pos) != -0x8000;
|
|
}
|
|
|
|
/* 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: the touch in slot @slot (-1: none) if
|
|
* there is one, else the mouse standing for it - if the port has a
|
|
* mouse, the list is the main one and the driver lets it stand in.
|
|
* False if there is neither, or no viewport to place it in. */
|
|
static bool input_pointer_touch(unsigned i, unsigned idx, bool second,
|
|
int slot, 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 = slot >= 0
|
|
&& (input_pointers.touch_present & ((uint32_t)1 << slot));
|
|
bool three = (input_pointers.flags & INPUT_POINTERS_MOUSE_3_TOUCHES) != 0;
|
|
bool mouse = !second
|
|
&& !(input_pointers.flags & INPUT_POINTERS_TOUCH_SLOTS)
|
|
&& 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((unsigned)slot, confined, pos, screen_pos)
|
|
: !input_pointer_place(i, confined, pos, screen_pos))
|
|
return false;
|
|
*down = real && (input_pointers.touch_down & ((uint32_t)1 << slot));
|
|
if ( !*down && mouse
|
|
&& !(real && (input_pointers.flags & INPUT_POINTERS_TOUCH_ALONE)))
|
|
*down = input_pointer_mouse_down(f, idx);
|
|
return true;
|
|
}
|
|
|
|
/* How many pointers of a list are down: its touches that are, or the
|
|
* mouse standing for one. */
|
|
static unsigned input_pointer_count(unsigned i, bool second)
|
|
{
|
|
unsigned n = 0;
|
|
unsigned t;
|
|
int first;
|
|
for (t = 0; t < input_pointers.touch_count; t++)
|
|
if ( input_touch_in_list(t, second)
|
|
&& (input_pointers.touch_down & ((uint32_t)1 << t)))
|
|
n++;
|
|
if (n || second || (input_pointers.flags & INPUT_POINTERS_TOUCH_SLOTS))
|
|
return n;
|
|
first = input_touch_slot(0, false);
|
|
if ( (input_pointers.flags & INPUT_POINTERS_TOUCH_ALONE)
|
|
&& first >= 0
|
|
&& (input_pointers.touch_present & ((uint32_t)1 << first)))
|
|
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, read at @idx, aims: at the main
|
|
* list's first touch, for a driver that has it so and while there is
|
|
* one - with TOUCH_SLOTS at touch @idx, and only there - else at the
|
|
* mouse. */
|
|
static bool input_gun_place(unsigned i, unsigned idx, uint32_t *pos)
|
|
{
|
|
uint32_t screen_pos;
|
|
if (input_pointers.flags & INPUT_POINTERS_GUN_AT_TOUCH)
|
|
{
|
|
bool slots = (input_pointers.flags & INPUT_POINTERS_TOUCH_SLOTS) != 0;
|
|
int slot = input_touch_slot(slots ? idx : 0, false);
|
|
if ( slot >= 0
|
|
&& (input_pointers.touch_present & ((uint32_t)1 << slot)))
|
|
return input_touch_place((unsigned)slot, false, pos, &screen_pos);
|
|
if (slots)
|
|
return false;
|
|
}
|
|
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;
|
|
unsigned flags = input_pointers.flags;
|
|
|
|
if (!input_pointers.count)
|
|
return false;
|
|
if ( (flags & INPUT_POINTERS_MOUSE_BY_DRIVER)
|
|
&& ( device == RETRO_DEVICE_MOUSE
|
|
|| device == RARCH_DEVICE_MOUSE_SCREEN
|
|
|| ( device == RETRO_DEVICE_LIGHTGUN
|
|
&& !(flags & INPUT_POINTERS_GUN_AT_TOUCH))))
|
|
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, false, input_touch_slot(idx, false),
|
|
&vp_pos, &screen_pos, &down);
|
|
*pos = (device == RETRO_DEVICE_POINTER) ? vp_pos : screen_pos;
|
|
}
|
|
return true;
|
|
case RETRO_DEVICE_LIGHTGUN:
|
|
input_gun_place(i, idx, 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;
|
|
int slot;
|
|
bool real;
|
|
uint32_t vp_pos = 0;
|
|
uint32_t screen_pos = 0;
|
|
bool screen = (device == RARCH_DEVICE_POINTER_SCREEN);
|
|
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;
|
|
slot = input_touch_slot(idx, false);
|
|
real = slot >= 0
|
|
&& (input_pointers.touch_present & ((uint32_t)1 << slot));
|
|
if (!input_pointer_touch(i, idx, false, slot, &vp_pos, &screen_pos,
|
|
&down))
|
|
return 0;
|
|
*pos = screen ? screen_pos : vp_pos;
|
|
return (down
|
|
&& ( !real
|
|
|| !(input_pointers.flags & INPUT_POINTERS_PRESSED_INSIDE)
|
|
|| input_touch_inside((unsigned)slot, screen))) ? 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;
|
|
unsigned flags = input_pointers.flags;
|
|
|
|
if (!input_pointers.count)
|
|
return false;
|
|
switch (device)
|
|
{
|
|
case RETRO_DEVICE_MOUSE:
|
|
case RARCH_DEVICE_MOUSE_SCREEN:
|
|
if (flags & INPUT_POINTERS_MOUSE_BY_DRIVER)
|
|
return false;
|
|
break;
|
|
case RARCH_DEVICE_POINTER_SECOND:
|
|
if (!(flags & INPUT_POINTERS_WINDOWS_SECOND))
|
|
return false;
|
|
/* fall through */
|
|
case RETRO_DEVICE_POINTER:
|
|
case RARCH_DEVICE_POINTER_SCREEN:
|
|
if ( (flags & INPUT_POINTERS_POINTER_IDS_BY_DRIVER)
|
|
&& id != RETRO_DEVICE_ID_POINTER_X
|
|
&& id != RETRO_DEVICE_ID_POINTER_Y
|
|
&& id != RETRO_DEVICE_ID_POINTER_PRESSED
|
|
&& id != RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN
|
|
&& id != RETRO_DEVICE_ID_POINTER_COUNT)
|
|
return false;
|
|
break;
|
|
case RETRO_DEVICE_LIGHTGUN:
|
|
/* its aim; its buttons are binds, and the driver's */
|
|
if ( id == RETRO_DEVICE_ID_LIGHTGUN_X
|
|
|| id == RETRO_DEVICE_ID_LIGHTGUN_Y)
|
|
{
|
|
if (flags & INPUT_POINTERS_MOUSE_BY_DRIVER)
|
|
return false;
|
|
break;
|
|
}
|
|
if ( id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X
|
|
|| id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y)
|
|
{
|
|
if ( (flags & INPUT_POINTERS_MOUSE_BY_DRIVER)
|
|
&& !(flags & INPUT_POINTERS_GUN_AT_TOUCH))
|
|
return false;
|
|
break;
|
|
}
|
|
if (id == RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN)
|
|
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
|
|
&& device != RARCH_DEVICE_POINTER_SECOND)
|
|
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:
|
|
case RARCH_DEVICE_POINTER_SECOND:
|
|
{
|
|
uint32_t screen_pos;
|
|
bool down;
|
|
bool second = (device == RARCH_DEVICE_POINTER_SECOND);
|
|
bool screen = (device == RARCH_DEVICE_POINTER_SCREEN);
|
|
int slot;
|
|
bool real;
|
|
/* how many are down needs no place for them */
|
|
if (id == RETRO_DEVICE_ID_POINTER_COUNT)
|
|
{
|
|
*out = (int16_t)input_pointer_count(i, second);
|
|
break;
|
|
}
|
|
slot = input_touch_slot(idx, second);
|
|
real = slot >= 0
|
|
&& (input_pointers.touch_present & ((uint32_t)1 << slot));
|
|
if (!input_pointer_touch(i, idx, second, slot,
|
|
&pos, &screen_pos, &down))
|
|
break;
|
|
if (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
|
|
&& ( !real
|
|
|| !(flags & INPUT_POINTERS_PRESSED_INSIDE)
|
|
|| input_touch_inside((unsigned)slot, screen));
|
|
break;
|
|
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
|
|
/* the touch's own place in the viewport */
|
|
if (real && (flags & INPUT_POINTERS_TOUCH_SLOTS))
|
|
input_touch_place((unsigned)slot, false,
|
|
&pos, &screen_pos);
|
|
*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 ( id == RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN
|
|
&& (flags & INPUT_POINTERS_TOUCH_SLOTS))
|
|
{
|
|
/* the touch at its index, whatever aims the gun */
|
|
uint32_t screen_pos;
|
|
int slot = input_touch_slot(idx, false);
|
|
if ( slot >= 0
|
|
&& (input_pointers.touch_present & ((uint32_t)1 << slot))
|
|
&& input_touch_place((unsigned)slot, false,
|
|
&pos, &screen_pos))
|
|
*out = input_driver_pointer_is_offscreen(
|
|
VIDEO_POS_X(pos), VIDEO_POS_Y(pos));
|
|
}
|
|
else
|
|
{
|
|
if (!input_gun_place(i, idx, &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;
|
|
}
|
|
|
|
#ifdef HAVE_VIDEO_WINDOWS
|
|
/* @v of @size pixels as a screen place: pixel 0 at -0x7fff, the last
|
|
* at 0x7fff, outside -0x8000, as video_driver_translate_coord_viewport()
|
|
* scales over the screen. */
|
|
static int input_window_scale(int v, int size)
|
|
{
|
|
if (v > 0 && v < size)
|
|
return ((v * 0xffff) / (size - 1)) - 0x8000;
|
|
if (v == 0)
|
|
return -0x7fff;
|
|
return -0x8000;
|
|
}
|
|
|
|
/* Touch @idx of those present in window @window, over that window's
|
|
* client area as it last drew; false if there is none or the window
|
|
* has not shown. */
|
|
static bool input_pointer_window_touch(unsigned window, unsigned idx,
|
|
uint32_t *screen_pos, bool *down)
|
|
{
|
|
unsigned t;
|
|
unsigned dims = 0;
|
|
int w, h;
|
|
if (!window || !video_driver_window_shown(window, &dims))
|
|
return false;
|
|
w = (int)VIDEO_SCALE_W(dims);
|
|
h = (int)VIDEO_SCALE_H(dims);
|
|
if (w <= 0 || h <= 0)
|
|
return false;
|
|
for (t = 0; t < input_pointers.touch_count; t++)
|
|
{
|
|
if ( !(input_pointers.touch_present & ((uint32_t)1 << t))
|
|
|| !(input_pointers.touch_in_window & ((uint32_t)1 << t))
|
|
|| input_pointers.touch_window[t].window != window)
|
|
continue;
|
|
if (idx)
|
|
{
|
|
idx--;
|
|
continue;
|
|
}
|
|
*screen_pos = VIDEO_POS_PACK(
|
|
input_window_scale(VIDEO_POS_X(input_pointers.touch_pos[t]), w),
|
|
input_window_scale(VIDEO_POS_Y(input_pointers.touch_pos[t]), h));
|
|
*down = (input_pointers.touch_down & ((uint32_t)1 << t)) != 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/* 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
|