Files
RetroArch/samples/input/pointer_frame/pointer_frame_test.c
T
Davey Hughes 56952c9a25 Android: follow-up fixes for screens on other displays (gl and vulkan) (#20044)
* 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.
2026-11-09 18:59:16 +01:00

1233 lines
60 KiB
C

/* Oracle for input/input_pointer_frame.h: the frontend's answers for the
* mouse, the pointer and the lightgun's aim, from what an input driver
* published at its poll.
*
* The header is included as input_driver.c includes it, with a viewport
* and a translation of this test's own that count how often they are
* asked - which is the point of the store: a device is placed in the
* viewport once a poll, however many values are read.
*
* 1. the mouse: motion, place in the window, buttons, the second port's
* device, a port with none;
* 2. a wheel notch reads the same every time in its frame;
* 3. the pointer from a mouse standing for three touches, and for one;
* 4. real touches: a place with a touch reads the touch, one that is
* down is pressed, a lifted one keeps its place and is pressed only
* as the mouse says; a place with none reads the mouse;
* 5. the lightgun's aim reports off-screen where the pointer holds to
* the edge;
* 6. one viewport fetch a poll, one translation a device and kind;
* 7. a poll in which nothing is published: the driver is to be asked;
* 8. ports reading by Mouse Index, a port with no mouse still reading a
* real touch, and the kind of device each mouse was last read as;
* 9. only the mouse by index with the pointer and the lightgun every
* port's, and the lightgun aiming at the first touch;
* 10. how many pointers are down;
* 11. the pointer placed off-screen as the lightgun is;
* 12. a touch read alone: a pen in the air;
* 13. the poll's viewport, asked for once a poll;
* 14. a touch's place, its presence and whether it is down are three
* things: a touch at 0,0, one lifted there, a place with none,
* the viewport's edges, sparse places, the lightgun;
* 15. devices and touches in a screen's own window: placed through
* that window, nowhere on the main window's screen, off-screen
* once the window is gone; motion, buttons and ports as on the
* main window;
* 16. the flags that keep a driver's own answers (Android's), each off
* and on: pressed only inside, the second list, the mouse and
* the ids left to the driver, touch slots; and a touch read over
* its own window's client area;
* 17. Android's publication against the driver's own answers before
* it published, finger by finger;
* 18. the driver's own lightgun buttons: a tap on a main window that
* shows no screen counts no finger, one in the letterbox does. */
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <boolean.h>
#include <libretro.h>
#include "input/input_driver.h"
#include "gfx/video_driver.h"
static unsigned failures, vp_asked, translations;
#define CHECK(cond, msg) do { if (!(cond)) { failures++; \
printf("FAIL %s\n", msg); } } while (0)
/* A 200x100 viewport at 0,0 of a 200x100 screen. In it a position is
* itself; outside, held to the edge or -0x8000 as asked. The screen
* position is the position plus 1000, to tell the two apart, and
* -0x8000 left of or above -100, off the screen. */
bool video_driver_get_viewport_info(struct video_viewport *vp)
{
vp_asked++;
vp->pos = VIDEO_POS_PACK(0, 0);
vp->dims = VIDEO_SCALE_PACK(200, 100);
vp->full_dims = VIDEO_SCALE_PACK(200, 100);
return true;
}
bool video_driver_translate_coord_viewport(struct video_viewport *vp,
int x, int y, uint32_t *res_pos, uint32_t *res_screen_pos,
bool report_oob)
{
int vx = x, vy = y;
translations++;
(void)vp;
if (x < 0 || x >= 200)
vx = report_oob ? -0x8000 : (x < 0 ? 0 : 199);
if (y < 0 || y >= 100)
vy = report_oob ? -0x8000 : (y < 0 ? 0 : 99);
*res_pos = VIDEO_POS_PACK(vx, vy);
*res_screen_pos = VIDEO_POS_PACK(x < -100 ? -0x8000 : x + 1000,
y < -100 ? -0x8000 : y + 1000);
return true;
}
/* Window 1, generation 5, has a 100x100 layout: a place in it is
* itself plus 2000, held to its edges or -0x8000 outside as asked.
* Any other window or generation has none. */
static unsigned window_translations;
bool video_driver_translate_window_point(unsigned window,
unsigned generation, int x, int y, bool report_oob,
uint32_t *result)
{
window_translations++;
if (window != 1 || generation != 5)
return false;
if (report_oob && (x < 0 || x >= 100 || y < 0 || y >= 100))
{
*result = VIDEO_POS_PACK(-0x8000, -0x8000);
return true;
}
x = x < 0 ? 0 : (x > 99 ? 99 : x);
y = y < 0 ? 0 : (y > 99 ? 99 : y);
*result = VIDEO_POS_PACK(x + 2000, y + 2000);
return true;
}
/* Window shown_window has drawn, shown_dims big, on display 4 */
static unsigned shown_window, shown_dims;
unsigned video_driver_window_shown(unsigned window, unsigned *dims)
{
if (!window || window != shown_window)
return 0;
if (dims)
*dims = shown_dims;
return 4;
}
bool input_driver_pointer_is_offscreen(int16_t x, int16_t y)
{ return x == -0x8000 || y == -0x8000; }
/* Port 0's Mouse Index is 1, port 1's is 0, port 2's is a mouse that is
* not there. */
unsigned input_mouse_port_index(unsigned port)
{ return port == 0 ? 1 : (port == 1 ? 0 : 7); }
#include "input/input_pointer_frame.h"
/* @count places, each with a contact; @down says which are pressed. */
static void touches_set(const uint32_t *pos, unsigned count, unsigned down)
{
input_pointer_touches_set(pos, NULL, count, (1u << count) - 1, down);
}
static int16_t rd(unsigned port, unsigned device, unsigned idx, unsigned id)
{
int16_t v = -1;
if (!input_pointer_frame_read(port, device, idx, id, &v))
return -2; /* the driver is to be asked */
return v;
}
/* 16 */
static void test_flags(void)
{
input_pointer_frame_t f;
input_video_window_t tw[3];
uint32_t touch[3];
uint32_t pos = 0;
bool down = false;
input_pointer_frames_clear();
memset(&f, 0, sizeof(f));
memset(tw, 0, sizeof(tw));
f.pos = VIDEO_POS_PACK(10, 10);
f.buttons = INPUT_POINTER_LEFT;
/* PRESSED_INSIDE: right of the viewport, left of the screen, in */
touch[0] = VIDEO_POS_PACK(250, 40);
touch[1] = VIDEO_POS_PACK(-150, 40);
touch[2] = VIDEO_POS_PACK(30, 20);
input_pointer_frames_set(&f, NULL, 1, 0);
touches_set(touch, 3, 7);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 1,
"without PRESSED_INSIDE a touch that is down is pressed anywhere");
input_pointer_frames_set(&f, NULL, 1, INPUT_POINTERS_PRESSED_INSIDE);
touches_set(touch, 3, 7);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 199
&& rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 3,
"with it, pressed only inside the viewport, or the screen read as SCREEN, and still counted");
/* POINTER_IDS_BY_DRIVER */
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RARCH_DEVICE_ID_POINTER_BACK) == 0
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RARCH_DEVICE_ID_POINTER_BACK) == 0,
"without POINTER_IDS_BY_DRIVER an id the frontend does not hold reads 0");
input_pointer_frames_set(&f, NULL, 1, INPUT_POINTERS_POINTER_IDS_BY_DRIVER);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RARCH_DEVICE_ID_POINTER_BACK) == -2
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RARCH_DEVICE_ID_POINTER_BACK) == -2
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 199,
"with it the driver answers that id, and the frontend the rest");
/* WINDOWS_SECOND: touches 0 and 2 in window 1, 1 in the main one */
tw[0].window = 1;
tw[0].generation = 5;
tw[2] = tw[0];
touch[0] = VIDEO_POS_PACK(10, 20);
touch[1] = VIDEO_POS_PACK(50, 60);
touch[2] = VIDEO_POS_PACK(30, 40);
input_pointer_frames_set(&f, NULL, 1, 0);
input_pointer_touches_set(touch, tw, 3, 7, 7);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 2010
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 3
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 0, RETRO_DEVICE_ID_POINTER_X) == -2,
"without WINDOWS_SECOND every touch is the pointer's, and there is no second list");
input_pointer_frames_set(&f, NULL, 1,
INPUT_POINTERS_WINDOWS_SECOND | INPUT_POINTERS_GUN_AT_TOUCH);
input_pointer_touches_set(touch, tw, 3, 7, 7);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 50
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_X) == 1050
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 1
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 50
&& input_pointer_frame_pos(0, RETRO_DEVICE_POINTER, 0, &pos)
&& pos == VIDEO_POS_PACK(50, 60),
"with it the pointer and the lightgun have the main window's touches");
CHECK( rd(0, RARCH_DEVICE_POINTER_SECOND, 0, RETRO_DEVICE_ID_POINTER_X) == 2010
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 1, RETRO_DEVICE_ID_POINTER_X) == 2030
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 1, RETRO_DEVICE_ID_POINTER_Y) == 2040
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 2
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 2, RETRO_DEVICE_ID_POINTER_X) == 0
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 2, RETRO_DEVICE_ID_POINTER_PRESSED) == 0,
"and the others are a second list, in the order published");
input_pointer_touches_set(touch, tw, 0, 0, 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RARCH_DEVICE_POINTER_SECOND, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 0,
"no mouse stands for a touch of the second list");
/* MOUSE_BY_DRIVER */
f.rel = VIDEO_POS_PACK(3, 4);
f.buttons = INPUT_POINTER_RIGHT | INPUT_POINTER_WHEEL_DOWN;
input_pointer_frames_set(&f, NULL, 1, 0);
CHECK( rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X) == 3
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_X) == 3,
"without MOUSE_BY_DRIVER the frontend answers the mouse");
input_pointer_frames_set(&f, NULL, 1, INPUT_POINTERS_MOUSE_BY_DRIVER);
CHECK( rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X) == -2
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT) == -2
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN) == -2
&& rd(0, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == -2
&& rd(0, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_WHEELUP) == -2
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_X) == -2
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_Y) == -2
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == -2
&& !input_pointer_frame_pos(0, RETRO_DEVICE_MOUSE, 0, &pos)
&& !input_pointer_frame_pos(0, RARCH_DEVICE_MOUSE_SCREEN, 0, &pos),
"with it the mouse, the lightgun's motion and its mouse aim are the driver's");
touch[0] = VIDEO_POS_PACK(60, 70);
input_pointer_frames_set(&f, NULL, 1,
INPUT_POINTERS_MOUSE_BY_DRIVER | INPUT_POINTERS_GUN_AT_TOUCH);
touches_set(touch, 1, 1);
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 60
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_X) == -2
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 60,
"a lightgun aimed at a touch is the frontend's, its motion still the driver's");
/* TOUCH_SLOTS: slot 0 never placed, 1 lifted off the viewport, 2 down */
touch[0] = VIDEO_POS_PACK(60, 70);
touch[1] = VIDEO_POS_PACK(250, 40);
touch[2] = VIDEO_POS_PACK(30, 40);
f.buttons = INPUT_POINTER_LEFT;
input_pointer_frames_set(&f, NULL, 1, INPUT_POINTERS_GUN_AT_TOUCH);
input_pointer_touches_set(touch, NULL, 3, 6, 4);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 10
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 1, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 2, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 10,
"without TOUCH_SLOTS the mouse stands for an index with no touch");
input_pointer_frames_set(&f, NULL, 1,
INPUT_POINTERS_GUN_AT_TOUCH | INPUT_POINTERS_TOUCH_SLOTS);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 1
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_X) == 199
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 1, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 1
&& rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_X) == 30
&& rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_PRESSED) == 1,
"with it an index is a slot: none reads 0, a lifted one keeps its place");
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 1, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == -0x8000
&& rd(0, RETRO_DEVICE_LIGHTGUN, 1, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 1
&& rd(0, RETRO_DEVICE_LIGHTGUN, 2, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) == 40
&& rd(0, RETRO_DEVICE_LIGHTGUN, 3, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 0,
"and the lightgun aims at the touch at its index, never the mouse");
input_pointer_frames_set(&f, NULL, 1, INPUT_POINTERS_TOUCH_SLOTS);
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 1, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 1
&& rd(0, RETRO_DEVICE_LIGHTGUN, 2, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 0,
"its IS_OFFSCREEN is that touch's while the mouse aims it");
/* A touch over its own window's whole client area */
memset(tw, 0, sizeof(tw));
tw[0].window = 1;
tw[0].generation = 5;
tw[2] = tw[0];
touch[0] = VIDEO_POS_PACK(0, 50);
touch[1] = VIDEO_POS_PACK(5, 5);
touch[2] = VIDEO_POS_PACK(100, 25);
shown_window = 1;
shown_dims = VIDEO_SCALE_PACK(101, 51);
input_pointer_touches_set(touch, tw, 3, 7, 5);
CHECK( input_pointer_window_touch(1, 0, &pos, &down)
&& pos == VIDEO_POS_PACK(-0x7fff, 0x7fff) && down
&& input_pointer_window_touch(1, 1, &pos, &down)
&& pos == VIDEO_POS_PACK(0x7fff, -1) && down
&& !input_pointer_window_touch(1, 2, &pos, &down)
&& !input_pointer_window_touch(2, 0, &pos, &down),
"a window's touches over its whole client area, in order; none past them or elsewhere");
touch[2] = VIDEO_POS_PACK(150, -3);
input_pointer_touches_set(touch, tw, 3, 7, 1);
CHECK( input_pointer_window_touch(1, 1, &pos, &down)
&& pos == VIDEO_POS_PACK(-0x8000, -0x8000) && !down,
"outside its window: -0x8000; a lifted touch is not down");
input_pointer_touches_set(touch, NULL, 3, 7, 1);
CHECK(!input_pointer_window_touch(1, 0, &pos, &down),
"a publication without windows has no window touches");
shown_window = 0;
CHECK(!input_pointer_window_touch(1, 0, &pos, &down),
"a window that has not shown has no touches");
input_pointer_frames_clear();
}
/* 17. Android's publication against the driver as it was. The publish
* calls are input_driver.c's. */
#include "input/drivers/android_pointers.h"
input_pointer_frame_t *input_driver_pointers_lend(void)
{
return input_pointers.frame;
}
uint32_t *input_driver_touches_lend(void)
{
return input_pointers.touch_pos;
}
void input_driver_publish_window_pointers(
const input_pointer_frame_t *frames,
const input_video_window_t *windows,
unsigned count, unsigned flags)
{
input_pointer_frames_set(frames, windows, count, flags);
}
void input_driver_publish_window_touches(const uint32_t *pos,
const input_video_window_t *windows, unsigned count,
unsigned present, unsigned down)
{
input_pointer_touches_set(pos, windows, count, present, down);
}
/* Before the publication the driver kept each touch placed
* three ways, as its event came. The same fingers go to both. */
struct was_pointer
{
uint32_t pos;
uint32_t confined_pos;
uint32_t full_pos;
};
static struct was_pointer was_main[16], was_second[16];
static unsigned was_count, was_second_count;
static struct input_pointer now_main[16], now_second[16];
static unsigned now_count, now_second_count;
static input_video_window_t now_window;
static bool mouse_used;
/* android_pointer_set(), before the publication and with it */
static void finger_place(unsigned i, int x, int y)
{
struct video_viewport vp;
memset(&vp, 0, sizeof(vp));
if (video_driver_get_viewport_info(&vp))
{
video_driver_translate_coord_viewport(&vp, x, y,
&was_main[i].confined_pos, &was_main[i].full_pos, false);
video_driver_translate_coord_viewport(&vp, x, y,
&was_main[i].pos, &was_main[i].full_pos, true);
}
now_main[i].pos = VIDEO_POS_PACK(x, y);
now_main[i].placed = true;
}
/* A touch event with @n fingers at @xy: each placed, as the driver's
* motion handler does */
static void fingers_move(unsigned n, const int *xy)
{
unsigned i;
for (i = 0; i < n; i++)
{
finger_place(i, xy[2 * i], xy[2 * i + 1]);
if (was_count < i + 1)
was_count = i + 1;
if (now_count < i + 1)
now_count = i + 1;
}
}
/* Finger @i up: the slots above shift down */
static void finger_up(unsigned i)
{
memmove(&was_main[i], &was_main[i + 1],
(16 - i - 1) * sizeof(was_main[0]));
memmove(&now_main[i], &now_main[i + 1],
(16 - i - 1) * sizeof(now_main[0]));
if (was_count)
was_count--;
if (now_count)
now_count--;
}
/* The second display's finger @i, in down order, at @x, @y of window
* 1, generation 5; a new one when @i is the count.
* android_input_poll_second_touches(), then and now. */
static void second_place(unsigned i, int x, int y)
{
if (!video_driver_translate_window_point(1, 5, x, y, false,
&was_second[i].confined_pos))
was_second[i].confined_pos = VIDEO_POS_PACK(-0x8000, -0x8000);
if (!video_driver_translate_window_point(1, 5, x, y, true,
&was_second[i].pos))
was_second[i].pos = VIDEO_POS_PACK(-0x8000, -0x8000);
was_second[i].full_pos = VIDEO_POS_PACK(-0x8000, -0x8000);
now_second[i].pos = VIDEO_POS_PACK(x, y);
now_second[i].placed = true;
now_window.window = 1;
now_window.generation = 5;
if (i == was_second_count)
{
was_second_count++;
now_second_count++;
}
}
static void second_up(unsigned i)
{
memmove(&was_second[i], &was_second[i + 1],
(was_second_count - i - 1) * sizeof(was_second[0]));
memmove(&now_second[i], &now_second[i + 1],
(now_second_count - i - 1) * sizeof(now_second[0]));
was_second_count--;
now_second_count--;
}
/* android_input_state() before the publication, for what
* the publication answers: the pointer, the screen pointer, the second
* display's and a touch's lightgun aim */
static int16_t was_state(unsigned device, unsigned idx, unsigned id)
{
const struct was_pointer *p = (idx < 16) ? &was_main[idx] : NULL;
switch (device)
{
case RETRO_DEVICE_LIGHTGUN:
if (!p)
return 0;
if (id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X)
return VIDEO_POS_X(p->pos);
if (id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y)
return VIDEO_POS_Y(p->pos);
return input_driver_pointer_is_offscreen(
VIDEO_POS_X(p->pos), VIDEO_POS_Y(p->pos));
case RETRO_DEVICE_POINTER:
case RARCH_DEVICE_POINTER_SCREEN:
if (id == RETRO_DEVICE_ID_POINTER_COUNT)
return (int16_t)was_count;
if (!p)
return 0;
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X:
return VIDEO_POS_X((device == RARCH_DEVICE_POINTER_SCREEN)
? p->full_pos : p->confined_pos);
case RETRO_DEVICE_ID_POINTER_Y:
return VIDEO_POS_Y((device == RARCH_DEVICE_POINTER_SCREEN)
? p->full_pos : p->confined_pos);
case RETRO_DEVICE_ID_POINTER_PRESSED:
{
uint32_t at = (device == RARCH_DEVICE_POINTER_SCREEN)
? p->full_pos : p->pos;
return idx < was_count
&& VIDEO_POS_X(at) != -0x8000
&& VIDEO_POS_Y(at) != -0x8000;
}
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(
VIDEO_POS_X(p->pos), VIDEO_POS_Y(p->pos));
default:
break;
}
return 0;
case RARCH_DEVICE_POINTER_SECOND:
if (id == RETRO_DEVICE_ID_POINTER_COUNT)
return (int16_t)was_second_count;
if (idx >= was_second_count)
return 0;
p = &was_second[idx];
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X:
return VIDEO_POS_X(p->confined_pos);
case RETRO_DEVICE_ID_POINTER_Y:
return VIDEO_POS_Y(p->confined_pos);
case RETRO_DEVICE_ID_POINTER_PRESSED:
return VIDEO_POS_X(p->pos) != -0x8000
&& VIDEO_POS_Y(p->pos) != -0x8000;
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
return input_driver_pointer_is_offscreen(
VIDEO_POS_X(p->pos), VIDEO_POS_Y(p->pos));
default:
break;
}
return 0;
default:
break;
}
return 0;
}
/* Publishes the fingers as android_input_publish() does, then reads,
* at two ports, every answer the publication gives Android and
* compares it with the driver's; the number that differ. */
static unsigned android_compare(const char *what)
{
static const unsigned devices[3] = { RETRO_DEVICE_POINTER,
RARCH_DEVICE_POINTER_SCREEN, RARCH_DEVICE_POINTER_SECOND };
static const unsigned ids[5] = { RETRO_DEVICE_ID_POINTER_X,
RETRO_DEVICE_ID_POINTER_Y, RETRO_DEVICE_ID_POINTER_PRESSED,
RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN, RETRO_DEVICE_ID_POINTER_COUNT };
static const unsigned gun[3] = { RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X,
RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y,
RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN };
android_pointers_t p;
unsigned port, d, idx, i;
unsigned bad = 0;
memset(&p, 0, sizeof(p));
p.main_slots = now_main;
p.slots = 16;
p.main_count = now_count;
p.second = now_second;
p.second_count = now_second_count;
p.second_window = now_window;
p.mouse_pos = VIDEO_POS_PACK(5, 5);
p.mouse_buttons = INPUT_POINTER_LEFT;
p.mouse_used = mouse_used;
input_pointer_frames_clear();
android_pointers_publish(&p);
for (port = 0; port < 2; port++)
{
for (d = 0; d < 3; d++)
for (idx = 0; idx < 18; idx++)
for (i = 0; i < 5; i++)
{
int16_t got = rd(port, devices[d], idx, ids[i]);
int16_t want = was_state(devices[d], idx, ids[i]);
if (got != want && !bad++)
printf(" %s: device %x idx %u id %u reads %d, was %d\n",
what, devices[d], idx, ids[i], got, want);
}
for (idx = 0; idx < 18; idx++)
for (i = 0; i < 3; i++)
{
int16_t got = rd(port, RETRO_DEVICE_LIGHTGUN, idx, gun[i]);
int16_t want = (mouse_used
&& gun[i] != RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN)
? -2 : was_state(RETRO_DEVICE_LIGHTGUN, idx, gun[i]);
if (got != want && !bad++)
printf(" %s: lightgun idx %u id %u reads %d, was %d\n",
what, idx, gun[i], got, want);
}
/* the menu's pointer, read as a place */
for (idx = 0; idx < 18; idx++)
{
uint32_t pos = 1;
uint32_t want = VIDEO_POS_PACK(
was_state(RETRO_DEVICE_POINTER, idx,
RETRO_DEVICE_ID_POINTER_X),
was_state(RETRO_DEVICE_POINTER, idx,
RETRO_DEVICE_ID_POINTER_Y));
if ( ( !input_pointer_frame_pos(port, RETRO_DEVICE_POINTER,
idx, &pos)
|| pos != want)
&& !bad++)
printf(" %s: pointer %u placed at %08x, was %08x\n",
what, idx, (unsigned)pos, (unsigned)want);
}
/* the overlay's fingers, each read at one look */
for (d = 0; d < 2; d++)
for (idx = 0; idx < 18; idx++)
{
uint32_t pos = 1;
int down = input_pointer_frame_finger(port, devices[d],
idx, &pos);
int16_t want = was_state(devices[d], idx,
RETRO_DEVICE_ID_POINTER_PRESSED);
uint32_t at = VIDEO_POS_PACK(
was_state(devices[d], idx, RETRO_DEVICE_ID_POINTER_X),
was_state(devices[d], idx, RETRO_DEVICE_ID_POINTER_Y));
if ( (down != want || (want && pos != at))
&& !bad++)
printf(" %s: device %x finger %u reads %d at %08x, was %d"
" at %08x\n", what, devices[d], idx, down,
(unsigned)pos, want, (unsigned)at);
}
}
return bad;
}
/* 17 */
static void test_android_answers(void)
{
static const int one[2] = { 40, 30 };
static const int three[6] = { 10, 10, 250, 40, 60, 90 };
int many[32];
unsigned i;
uint32_t pos = 0;
memset(was_main, 0, sizeof(was_main));
memset(was_second, 0, sizeof(was_second));
memset(now_main, 0, sizeof(now_main));
memset(now_second, 0, sizeof(now_second));
memset(&now_window, 0, sizeof(now_window));
was_count = was_second_count = 0;
now_count = now_second_count = 0;
mouse_used = false;
CHECK(!android_compare("no touch yet"),
"Android, no touch yet: every answer as before");
fingers_move(1, one);
CHECK(!android_compare("one finger"),
"Android, one finger: every answer as before");
finger_up(0);
CHECK(!android_compare("lifted"),
"Android, the finger up: its index reads what its slot holds, as before");
finger_place(0, 30, 40);
CHECK(!android_compare("pen hovering"),
"Android, a pen hovering: placed, not down, as before");
fingers_move(3, three);
CHECK(!android_compare("three fingers"),
"Android, three fingers, one off the viewport: every answer as before");
finger_up(1);
CHECK(!android_compare("the middle one up"),
"Android, the middle finger up: the slots above shift down, as before");
finger_place(0, -150, 120);
CHECK(!android_compare("the pen off the screen"),
"Android, the pen off the screen: every answer as before");
second_place(0, 20, 30);
second_place(1, 150, 40);
CHECK(!android_compare("two on the second display"),
"Android, two fingers on the second display, one off its screen: every answer as before");
second_up(0);
CHECK(!android_compare("one up on the second display"),
"Android, one up on the second display: every answer as before");
for (i = 0; i < 16; i++)
{
many[2 * i] = 12 * i;
many[2 * i + 1] = 6 * i;
}
fingers_move(16, many);
for (i = 0; i < 16; i++)
second_place(i, 6 * i, 6 * i);
CHECK(!android_compare("sixteen on each display"),
"Android, sixteen fingers on each display: none dropped, every answer as before");
finger_up(3);
CHECK(!android_compare("one of sixteen up"),
"Android, one of sixteen up: the top slot keeps its place, as before");
mouse_used = true;
CHECK(!android_compare("a mouse in use"),
"Android, a mouse in use: its lightgun aim is the driver's, the rest as before");
/* What the driver keeps, with today's code: every read reaches it */
{
bool driver = true;
for (i = RETRO_DEVICE_ID_MOUSE_X; i <= RETRO_DEVICE_ID_MOUSE_BUTTON_5; i++)
if ( rd(0, RETRO_DEVICE_MOUSE, 0, i) != -2
|| rd(1, RARCH_DEVICE_MOUSE_SCREEN, 0, i) != -2)
driver = false;
CHECK( driver
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_X) == -2
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_Y) == -2
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER) == -2
&& rd(0, RETRO_DEVICE_POINTER, 0, RARCH_DEVICE_ID_POINTER_BACK) == -2
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RARCH_DEVICE_ID_POINTER_BACK) == -2
&& !input_pointer_frame_pos(0, RARCH_DEVICE_MOUSE_SCREEN, 0, &pos),
"Android: the mouse, the lightgun's motion and buttons, and BACK are the driver's");
}
CHECK( input_pointers.count == 1
&& input_pointers.frame[0].buttons == INPUT_POINTER_LEFT
&& input_pointers.frame[0].rel == 0,
"Android's mouse is published, for binds to its buttons");
input_pointer_frames_clear();
}
/* 18 */
static void test_android_gun_fingers(void)
{
video_views_layout_t layout;
memset(&layout, 0, sizeof(layout));
CHECK( android_pointers_gun_fingers(1, NULL) == 1
&& android_pointers_gun_fingers(3, NULL) == 3,
"Android, no views: every finger counts for the lightgun's buttons");
layout.num_placements = 1;
CHECK( android_pointers_gun_fingers(1, &layout) == 1
&& android_pointers_gun_fingers(2, &layout) == 2,
"Android, a main window that shows a screen: a tap counts, in its letterbox too");
layout.num_placements = 0;
CHECK( android_pointers_gun_fingers(1, &layout) == 0
&& android_pointers_gun_fingers(3, &layout) == 0,
"Android, a main window that shows no screen: a tap there is on nothing");
}
/* Frames a driver wrote in place are published as they are. */
static void test_lent_frames(void)
{
input_pointer_frame_t *lent = input_pointers.frame;
memset(lent, 0, 2 * sizeof(*lent));
lent[1].buttons = INPUT_POINTER_RIGHT;
input_pointer_frames_set(lent, NULL, 2, 0);
CHECK(input_pointers.count == 2
&& rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT) == 1
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT) == 0,
"frames written in place are read as published");
input_pointers.touch_pos[0] = VIDEO_POS_PACK(12, 34);
input_pointer_touches_set(input_pointers.touch_pos, NULL, 1, 1, 1);
CHECK(input_pointers.touch_count == 1
&& input_pointers.touch_pos[0] == VIDEO_POS_PACK(12, 34)
&& input_pointers.touch_down == 1,
"places written in place are read as published");
}
int main(void)
{
input_pointer_frame_t f[2];
uint32_t touch[3];
uint32_t pos = 0;
memset(f, 0, sizeof(f));
f[0].pos = VIDEO_POS_PACK(40, 30);
f[0].rel = VIDEO_POS_PACK(-3, 7);
f[0].buttons = INPUT_POINTER_RIGHT | INPUT_POINTER_WHEEL_DOWN;
f[1].pos = VIDEO_POS_PACK(5, 6);
f[1].buttons = INPUT_POINTER_LEFT;
/* 7 (before anything is published) */
input_pointer_frames_clear();
CHECK(rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X) == -2
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == -2
&& !input_pointer_frame_pos(0, RETRO_DEVICE_MOUSE, 0, &pos),
"nothing published: the driver is to be asked");
/* 1 */
input_pointer_frames_set(f, NULL, 2, INPUT_POINTERS_MOUSE_3_TOUCHES);
CHECK(rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X) == -3
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y) == 7,
"the mouse's motion");
CHECK(rd(0, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == 40
&& rd(0, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_Y) == 30,
"the mouse's place in the window");
CHECK(input_pointer_frame_pos(0, RARCH_DEVICE_MOUSE_SCREEN, 0, &pos)
&& pos == VIDEO_POS_PACK(40, 30)
&& input_pointer_frame_pos(0, RETRO_DEVICE_MOUSE, 0, &pos)
&& pos == VIDEO_POS_PACK(-3, 7),
"the packed position is the published word");
CHECK( rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT) == 1
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT) == 0
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE) == 0
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_BUTTON_4) == 0,
"the mouse's buttons");
CHECK( rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT) == 1
&& rd(1, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == 5,
"the second port reads the second device");
CHECK( rd(2, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT) == 0
&& rd(2, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT) == 0,
"a port with no device reads nothing, and not the driver");
CHECK( rd(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B) == -2
&& rd(0, RETRO_DEVICE_KEYBOARD, 0, RETROK_a) == -2
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER) == -2,
"pads, keys and the lightgun's buttons are still the driver's");
/* 2 */
CHECK( rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN) == 1
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN) == 1
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN) == 1
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELUP) == 0,
"a wheel notch reads the same every time in its frame");
/* 3: a mouse for three touches - any button; right or middle; middle */
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) == 30
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_X) == 1040,
"the pointer is where the translation puts the mouse");
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 3, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(1, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 0,
"a mouse standing for three touches");
/* ...and for one: the left button */
input_pointer_frames_set(f, NULL, 2, 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(1, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(1, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_X) == 0,
"a mouse standing for one touch");
/* 6 */
input_pointer_frames_set(f, NULL, 2, INPUT_POINTERS_MOUSE_3_TOUCHES);
vp_asked = translations = 0;
rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y);
rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED);
rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED);
rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_PRESSED);
rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN);
rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_X);
input_pointer_frame_pos(0, RETRO_DEVICE_POINTER, 0, &pos);
CHECK(vp_asked == 1 && translations == 1,
"eight pointer reads: one viewport fetch, one translation");
rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X);
rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y);
rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN);
CHECK(vp_asked == 1 && translations == 2,
"the lightgun's aim after them: no fetch, one translation more");
rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
CHECK(vp_asked == 1 && translations == 3,
"a second device: no fetch, one translation more");
rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X);
rd(0, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X);
CHECK(vp_asked == 1 && translations == 3, "the mouse needs neither");
input_pointer_frames_set(f, NULL, 2, INPUT_POINTERS_MOUSE_3_TOUCHES);
rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
CHECK(vp_asked == 2 && translations == 4, "the next poll places it afresh");
/* 5: out of the viewport the pointer holds to the edge, the gun says so */
f[0].pos = VIDEO_POS_PACK(250, 30);
input_pointer_frames_set(f, NULL, 2, INPUT_POINTERS_MOUSE_3_TOUCHES);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 199
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == -0x8000
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) == 30
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 1
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_X) == -3,
"out of the viewport: the pointer holds to the edge, the lightgun is off-screen");
f[0].pos = VIDEO_POS_PACK(40, 30);
/* 4: touch 0 down at 60,70; touch 1 lifted, last at 80,90; no touch 2 */
touch[0] = VIDEO_POS_PACK(60, 70);
touch[1] = VIDEO_POS_PACK(80, 90);
touch[2] = 0;
f[0].buttons = INPUT_POINTER_MIDDLE;
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_MOUSE_3_TOUCHES);
/* places 0 and 1 hold a contact; place 2 holds none */
input_pointer_touches_set(touch, NULL, 3, 3, 1 << 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 60
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) == 70
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_Y) == 1070
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1,
"a touch that is down: its place, pressed");
CHECK( rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_X) == 80
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 1,
"a lifted touch keeps its place, and is pressed as the mouse says");
CHECK( rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_PRESSED) == 1,
"a place with no touch reads the mouse");
CHECK(input_pointer_frame_pos(3, RETRO_DEVICE_POINTER, 0, &pos)
&& pos == VIDEO_POS_PACK(60, 70)
&& input_pointer_frame_pos(0, RARCH_DEVICE_POINTER_SCREEN, 1, &pos)
&& pos == VIDEO_POS_PACK(1080, 1090),
"the packed pointer position, and one mouse is every port's");
f[0].buttons = 0;
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_MOUSE_3_TOUCHES);
/* places 0 and 1 hold a contact; place 2 holds none */
input_pointer_touches_set(touch, NULL, 3, 3, 1 << 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1,
"with no button held only the touch that is down is pressed");
/* 8: by Mouse Index - port 0 reads device 1, port 1 device 0 */
f[0].buttons = INPUT_POINTER_RIGHT;
f[1].buttons = INPUT_POINTER_LEFT;
input_pointer_frames_set(f, NULL, 2, INPUT_POINTERS_BY_MOUSE_INDEX);
touches_set(touch, 1, 1 << 0);
CHECK( rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT) == 1
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT) == 0
&& rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT) == 1
&& rd(1, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == 40,
"by Mouse Index: a port reads the device at its index");
CHECK( rd(2, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT) == 0
&& rd(2, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == 0
&& rd(2, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 0,
"by Mouse Index: a port whose mouse is not there reads nothing");
CHECK( rd(2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 60
&& rd(2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(2, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& input_pointer_frame_pos(2, RETRO_DEVICE_POINTER, 0, &pos)
&& pos == VIDEO_POS_PACK(60, 70),
"a port with no mouse still reads a real touch, and only that");
touches_set(touch, 0, 0);
CHECK( rd(2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 0,
"with no mouse and no touch a pointer reads nothing");
/* the kind each device was last read as: port 0 is device 1 */
rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X);
rd(1, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_X);
CHECK(input_pointers.read_as[1] == RETRO_DEVICE_LIGHTGUN
&& input_pointers.read_as[0] == RARCH_DEVICE_POINTER_SCREEN,
"each device records the kind it was last read as");
input_pointer_frame_pos(0, RETRO_DEVICE_MOUSE, 0, &pos);
input_pointer_frames_set(f, NULL, 2, INPUT_POINTERS_BY_MOUSE_INDEX);
CHECK(input_pointers.read_as[1] == RETRO_DEVICE_MOUSE
&& input_pointers.read_as[0] == RARCH_DEVICE_POINTER_SCREEN,
"and keeps it over the next poll");
/* 9: only the mouse by index; the pointer and the gun every port's.
* Port 2's mouse is not there, port 1's index is 0. */
f[0].pos = VIDEO_POS_PACK(40, 30);
f[0].buttons = INPUT_POINTER_LEFT;
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_BY_MOUSE_INDEX
| INPUT_POINTERS_AIM_EVERY_PORT | INPUT_POINTERS_GUN_AT_TOUCH);
touches_set(touch, 0, 0);
CHECK( rd(2, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT) == 0
&& rd(2, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == 0
&& rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT) == 1,
"aim every port's: the mouse still goes by the port's index");
CHECK( rd(2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(2, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 40
&& rd(2, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) == 30,
"aim every port's: the pointer and the lightgun read the mouse on any port");
/* the gun at the first touch while there is one; out of the viewport
* it is off-screen */
touch[0] = VIDEO_POS_PACK(60, 70);
touches_set(touch, 1, 1 << 0);
vp_asked = translations = 0;
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 60
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) == 70
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 0
&& input_pointer_frame_pos(0, RETRO_DEVICE_LIGHTGUN, 0, &pos)
&& pos == VIDEO_POS_PACK(60, 70)
&& vp_asked == 0 && translations == 1,
"the lightgun aims at the first touch: one translation, the poll's viewport");
touch[0] = VIDEO_POS_PACK(260, 70);
touches_set(touch, 1, 1 << 0);
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 1
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 199,
"a touch out of the viewport: the gun is off-screen, the pointer at the edge");
touches_set(touch, 0, 0);
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 40,
"with no touch the lightgun aims at the mouse");
/* without the flag a touch does not move the gun */
touch[0] = VIDEO_POS_PACK(60, 70);
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_MOUSE_3_TOUCHES);
touches_set(touch, 1, 1 << 0);
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 40,
"a driver that does not say so: the lightgun stays on the mouse");
/* 10: how many pointers are down - the touches that are, else the
* mouse standing for one */
f[0].pos = VIDEO_POS_PACK(40, 30);
f[0].buttons = INPUT_POINTER_LEFT;
touch[0] = VIDEO_POS_PACK(60, 70);
touch[1] = VIDEO_POS_PACK(80, 90);
input_pointer_frames_set(f, NULL, 1, 0);
touches_set(touch, 2, 3);
CHECK(rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 2,
"the count is the touches that are down");
touches_set(touch, 0, 0);
CHECK(rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 1,
"with no touch the count is the mouse's, its button held");
f[0].buttons = 0;
input_pointer_frames_set(f, NULL, 1, 0);
vp_asked = translations = 0;
CHECK(rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 0
&& vp_asked == 0 && translations == 0,
"nothing down counts none, and the count places nothing");
/* 11: the pointer placed as the lightgun is - off-screen outside */
f[0].pos = VIDEO_POS_PACK(250, 30);
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_POINTER_OFFSCREEN);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == -0x8000
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) == 30
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 1,
"an off-screen pointer says so where the driver has it so");
touch[0] = VIDEO_POS_PACK(60, 170);
touches_set(touch, 1, 1);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 60
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) == -0x8000
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 1
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_Y) == 1170,
"and so does a touch");
f[0].pos = VIDEO_POS_PACK(40, 30);
/* 12: a touch read alone - a pen in the air is not pressed by the
* mouse's button, and counts none */
f[0].buttons = INPUT_POINTER_LEFT;
touch[0] = VIDEO_POS_PACK(60, 70);
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_TOUCH_ALONE);
touches_set(touch, 1, 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 60
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 0,
"a touch read alone: lifted, the mouse's button does not press it");
touches_set(touch, 1, 1);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 1,
"a touch read alone: down, it is pressed");
touches_set(touch, 0, 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 0,
"a touch read alone: with none, the mouse stands for the first");
/* without the flag a lifted touch is pressed as the mouse says */
input_pointer_frames_set(f, NULL, 1, 0);
touches_set(touch, 1, 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1,
"a driver that does not say so: a lifted touch is pressed as the mouse says");
/* 7 */
input_pointer_frames_clear();
CHECK(rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT) == -2
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == -2,
"a poll in which nothing is published: the driver is to be asked");
/* 14: where a touch is, whether it is there, and whether it is down
* are three things. The mouse is at 40,30 with no button held. */
f[0].pos = VIDEO_POS_PACK(40, 30);
f[0].buttons = 0;
/* a touch down at the window's origin is a touch */
touch[0] = VIDEO_POS_PACK(0, 0);
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_GUN_AT_TOUCH);
input_pointer_touches_set(touch, NULL, 1, 1, 1);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 1
&& input_pointer_frame_pos(0, RETRO_DEVICE_POINTER, 0, &pos)
&& pos == VIDEO_POS_PACK(0, 0),
"a touch down at 0,0 is pressed and at 0,0, not the mouse's");
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) == 0,
"the lightgun aims at a touch at 0,0, not at the mouse");
/* lifted at the origin, its place kept: there, not pressed */
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_GUN_AT_TOUCH);
input_pointer_touches_set(touch, NULL, 1, 1, 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 0,
"a touch lifted at 0,0 keeps its place and is not pressed");
/* not there at all: the mouse answers, whatever the place holds */
touch[0] = VIDEO_POS_PACK(60, 70);
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_GUN_AT_TOUCH);
input_pointer_touches_set(touch, NULL, 1, 0, 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 40,
"a place with no contact reads the mouse, whatever position it holds");
/* down said without there: not a touch */
input_pointer_frames_set(f, NULL, 1, 0);
input_pointer_touches_set(touch, NULL, 1, 0, 1);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 0,
"a place that is down but not there is not a touch");
/* at the viewport's edges: its first and last pixel, both down */
touch[0] = VIDEO_POS_PACK(0, 99);
touch[1] = VIDEO_POS_PACK(199, 0);
input_pointer_frames_set(f, NULL, 1, 0);
input_pointer_touches_set(touch, NULL, 2, 3, 3);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) == 99
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_X) == 199
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_Y) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 2,
"touches on the viewport's edges, an axis at 0 each");
/* sparse places: 0 empty, 1 down at the origin, 2 empty. The mouse
* stands for three touches and its middle button is held. */
touch[0] = VIDEO_POS_PACK(0, 0);
touch[1] = VIDEO_POS_PACK(0, 0);
touch[2] = VIDEO_POS_PACK(0, 0);
f[0].buttons = INPUT_POINTER_MIDDLE;
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_MOUSE_3_TOUCHES);
input_pointer_touches_set(touch, NULL, 3, 1 << 1, 1 << 1);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_X) == 0
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(0, RETRO_DEVICE_POINTER, 2, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 1,
"sparse places: the one with a contact reads it, the others the mouse");
/* the lightgun goes by the first place only */
f[0].buttons = 0;
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_GUN_AT_TOUCH);
input_pointer_touches_set(touch, NULL, 3, 1 << 1, 1 << 1);
CHECK( rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 40,
"the lightgun aims at the first place's touch, and with none there at the mouse");
/* a pen in the air at the origin, read alone */
f[0].buttons = INPUT_POINTER_LEFT;
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_TOUCH_ALONE);
input_pointer_touches_set(touch, NULL, 1, 1, 0);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 0
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT) == 0,
"a pen in the air at 0,0 is there, at 0,0, and not pressed by the mouse");
f[0].buttons = 0;
/* 15: device 1 and touch 0 in window 1; device 0 and touch 1 in
* the main window */
{
input_video_window_t w[2];
memset(w, 0, sizeof(w));
w[1].window = 1;
w[1].generation = 5;
f[0].pos = VIDEO_POS_PACK(40, 30);
f[0].rel = VIDEO_POS_PACK(-3, 7);
f[0].buttons = 0;
f[1].pos = VIDEO_POS_PACK(10, 20);
f[1].rel = VIDEO_POS_PACK(4, -2);
f[1].buttons = INPUT_POINTER_LEFT;
input_pointer_frames_set(f, w, 2, INPUT_POINTERS_MOUSE_3_TOUCHES);
touches_set(touch, 0, 0);
vp_asked = translations = window_translations = 0;
CHECK( rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 2010
&& rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y) == 2020
&& rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 0
&& rd(1, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 2010
&& rd(1, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 0,
"a device in a screen's window is placed through that window");
CHECK(vp_asked == 0 && translations == 0 && window_translations == 2,
"placed once a kind, with no main viewport asked for");
CHECK( rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X) == 4
&& rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y) == -2
&& rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT) == 1
&& rd(1, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_X) == 4,
"its motion and buttons are its own, untranslated");
CHECK( rd(1, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == -0x8000
&& rd(1, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_X) == -0x8000
&& input_pointer_frame_pos(1, RARCH_DEVICE_MOUSE_SCREEN, 0, &pos)
&& pos == VIDEO_POS_PACK(-0x8000, -0x8000),
"and no place on the main window's screen");
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_X) == 1040
&& rd(0, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == 40
&& vp_asked == 1 && translations == 1,
"the main window's device as before");
/* held to the window's edges, or off-screen as the lightgun is */
f[1].pos = VIDEO_POS_PACK(150, 20);
input_pointer_frames_set(f, w, 2, 0);
CHECK( rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 2099
&& rd(1, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == -0x8000
&& rd(1, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 1,
"out of the window: the pointer at its edge, the lightgun off-screen");
input_pointer_frames_set(f, w, 2, INPUT_POINTERS_POINTER_OFFSCREEN);
CHECK( rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == -0x8000,
"and the pointer off-screen where the driver has it so");
f[1].pos = VIDEO_POS_PACK(10, 20);
/* by Mouse Index: port 0 reads device 1, in the window */
input_pointer_frames_set(f, w, 2, INPUT_POINTERS_BY_MOUSE_INDEX);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 2010
&& rd(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X) == 4
&& rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 40
&& rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X) == -3,
"by Mouse Index: a port reads its device, in that device's window");
/* a window that is gone: off-screen, not the main window's place */
w[1].generation = 4;
input_pointer_frames_set(f, w, 2, INPUT_POINTERS_MOUSE_3_TOUCHES);
CHECK( rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == -0x8000
&& rd(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 1
&& rd(1, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 1
&& rd(1, RARCH_DEVICE_MOUSE_SCREEN, 0, RETRO_DEVICE_ID_MOUSE_X) == -0x8000
&& rd(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X) == 4,
"a gone window's device is off-screen, its motion still its own");
w[1].generation = 5;
/* touches: 0 in window 1, 1 in the main window */
touch[0] = VIDEO_POS_PACK(60, 70);
touch[1] = VIDEO_POS_PACK(80, 90);
input_pointer_frames_set(f, NULL, 1, INPUT_POINTERS_GUN_AT_TOUCH);
{
input_video_window_t tw[2];
tw[0] = w[1];
tw[1] = w[0];
input_pointer_touches_set(touch, tw, 2, 3, 3);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 2060
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED) == 1
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 0, RETRO_DEVICE_ID_POINTER_X) == -0x8000
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_X) == 80
&& rd(0, RARCH_DEVICE_POINTER_SCREEN, 1, RETRO_DEVICE_ID_POINTER_X) == 1080
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) == 2060,
"a touch in a screen's window is placed through it, one in the main window as before");
tw[0].generation = 9;
input_pointer_touches_set(touch, tw, 2, 3, 3);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == -0x8000
&& rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN) == 1
&& rd(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) == 1
&& rd(0, RETRO_DEVICE_POINTER, 1, RETRO_DEVICE_ID_POINTER_X) == 80,
"a touch in a gone window is off-screen");
input_pointer_touches_set(touch, NULL, 2, 3, 3);
CHECK( rd(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X) == 60,
"touches published with no windows are the main window's");
}
touches_set(touch, 0, 0);
f[1].buttons = 0;
}
/* 13: the poll's viewport, for a driver that places its own touches:
* asked for once however often it is wanted, and afresh each poll */
input_pointer_frames_clear();
vp_asked = 0;
CHECK(input_pointer_viewport() && input_pointer_viewport()
&& input_pointer_viewport() && vp_asked == 1,
"the poll's viewport is asked for once");
input_pointer_frames_clear();
CHECK(input_pointer_viewport() && vp_asked == 2,
"and afresh after the next poll");
test_flags();
test_android_answers();
test_android_gun_fingers();
test_lent_frames();
if (failures)
{
printf("FAIL pointer_frame_test: %u failure(s)\n", failures);
return 1;
}
printf("PASS pointer_frame_test\n");
return 0;
}