Files
RetroArch/input/drivers/test_input.c
T
LibretroAdmin 87cfa110a1 input: an input driver's state call is not told the keyboard is held
Every input driver's state call took a flag, keyboard_mapping_blocked:
whether the keys are kept from the binds. It mattered when drivers
answered the binds themselves. The frontend answers them now, from the
keys it asks a driver for once a poll, and applies the flag itself; a
driver's state call answers the keyboard, the mouse and the pointer,
and none of the twenty-three read the flag. (udev passed it to its
touch code, which did not read it either.)

The parameter goes: from the interface, the twenty-three drivers, the
frontend's own stand-in driver and its nine calls. The frontend's own
functions keep theirs - that is where it is used.

Measured against this same master built without it, the frame's input
work over 300 frames (X11): 3,690,770 instructions to 3,690,476 with
no controller, 4,354,940 to 4,354,646 with one. An argument a call.

Checked: the frontend harness passes every lane; the X11 keyboard
session logs the same as before, and the hotkey and quit-combination
sessions what master's binary logged; the raw input keyboards test
and the plain Win32 input test, which call a driver's state function
themselves, pass under Wine. Built: Linux in full; DirectInput, raw
input and the plain Win32 driver with a mingw compiler; the Android
unity build with the NDK; Wayland; the Visual C++ 6 project and the
QNX driver on their stand-ins; a build with the optional features
off. Not compiled, there being no compiler for them here: Cocoa, UWP,
the web driver, SDL 1 and the console drivers (DOS, Wii, PS3, PS4,
PSP and Vita, Switch, Wii U); theirs is the same one line, taken out
by script.

The second of three pieces before steps 3 and 4 of the mapping
compiler (plan, section 21). The joypad, its info and the binds are
handed to the same call, and two drivers use any of it: the Wii's
reads one field of the info, the Wii U's asks the joypad for its
touch screen. With those two reworked the call would take the port
and what is asked, and nothing else.
2026-10-08 03:29:08 +02:00

557 lines
18 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2016-2019 - Brad Parker
*
* 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/>.
*/
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <retro_miscellaneous.h>
#include <file/file_path.h>
#include <string/stdstring.h>
#include <streams/file_stream.h>
#include <formats/rjson.h>
#include <formats/rjson_stream.h>
#include <compat/strl.h>
#include "../input_driver.h"
#include "../input_keymaps.h"
#include "../../verbosity.h"
#include "../../gfx/video_driver.h"
#define MAX_TEST_STEPS 200
#define INPUT_TEST_COMMAND_PRESS_KEY 1
#define INPUT_TEST_COMMAND_RELEASE_KEY 2
#define INPUT_TEST_COMMAND_SET_SENSOR_ACC_X 10
#define INPUT_TEST_COMMAND_SET_SENSOR_ACC_Y 11
#define INPUT_TEST_COMMAND_SET_SENSOR_ACC_Z 12
#define INPUT_TEST_COMMAND_SET_SENSOR_GYR_X 13
#define INPUT_TEST_COMMAND_SET_SENSOR_GYR_Y 14
#define INPUT_TEST_COMMAND_SET_SENSOR_GYR_Z 15
#define INPUT_TEST_COMMAND_SET_SENSOR_LUX 16
/* TODO/FIXME - static globals */
/* Allocated with the step array when the test driver starts; see
* the note there. Indexed as [DEFAULT_MAX_PADS+1][RETROK_LAST]. */
static uint16_t (*test_key_state)[RETROK_LAST];
typedef struct
{
float x;
float y;
float z;
} sensor_t;
typedef struct test_input
{
sensor_t accelerometer_state; /* float alignment */
sensor_t gyroscope_state; /* float alignment */
float lux_sensor_state;
} test_input_t;
static test_input_t test_input_values;
typedef struct
{
unsigned frame;
unsigned action;
unsigned param_num;
char param_str[NAME_MAX_LENGTH];
bool handled;
} input_test_step_t;
/* Allocated when the test driver or core actually starts; a static
* array here is load-resident forever on platforms without demand
* paging, for a feature almost no session activates. */
static input_test_step_t *input_test_steps;
static unsigned current_test_step = 0;
static unsigned last_test_step = MAX_TEST_STEPS + 1;
/************************************/
/* JSON Helpers for test input file */
/************************************/
typedef struct
{
unsigned *current_entry_uint_val;
char **current_entry_str_val;
unsigned frame;
unsigned action;
unsigned param_num;
char *param_str;
} KTifJSONContext;
static bool KTifJSONObjectEndHandler(void* context)
{
KTifJSONContext *pCtx = (KTifJSONContext*)context;
/* Too long input is handled elsewhere, it should not lead to parse error */
if (current_test_step >= MAX_TEST_STEPS)
return true;
/* Copy values read from JSON file + fill defaults */
if (pCtx->frame == 0xffff)
input_test_steps[current_test_step].frame = input_test_steps[current_test_step-1].frame + 60;
else
input_test_steps[current_test_step].frame = pCtx->frame;
input_test_steps[current_test_step].action = pCtx->action;
input_test_steps[current_test_step].param_num = pCtx->param_num;
input_test_steps[current_test_step].handled = false;
if (pCtx->param_str && *pCtx->param_str)
strlcpy(
input_test_steps[current_test_step].param_str, pCtx->param_str,
sizeof(input_test_steps[current_test_step].param_str));
else
input_test_steps[current_test_step].param_str[0] = '\0';
current_test_step++;
last_test_step = current_test_step;
pCtx->frame = 0xffff;
return true;
}
static bool KTifJSONObjectMemberHandler(void* context, const char *pValue, size_t len)
{
KTifJSONContext *pCtx = (KTifJSONContext*)context;
/* something went wrong */
if (pCtx->current_entry_str_val)
return false;
if (len)
{
if (string_is_equal(pValue, "frame"))
pCtx->current_entry_uint_val = &pCtx->frame;
else if (string_is_equal(pValue, "action"))
pCtx->current_entry_uint_val = &pCtx->action;
else if (string_is_equal(pValue, "param_num"))
pCtx->current_entry_uint_val = &pCtx->param_num;
else if (string_is_equal(pValue, "param_str"))
pCtx->current_entry_str_val = &pCtx->param_str;
/* ignore unknown members */
}
return true;
}
static bool KTifJSONNumberHandler(void* context, const char *pValue, size_t len)
{
KTifJSONContext *pCtx = (KTifJSONContext*)context;
if (pCtx->current_entry_uint_val && len && pValue && *pValue)
*pCtx->current_entry_uint_val = string_to_unsigned(pValue);
/* ignore unknown members */
pCtx->current_entry_uint_val = NULL;
return true;
}
static bool KTifJSONStringHandler(void* context, const char *pValue, size_t len)
{
KTifJSONContext *pCtx = (KTifJSONContext*)context;
if (pCtx->current_entry_str_val && len && pValue && *pValue)
{
if (*pCtx->current_entry_str_val)
free(*pCtx->current_entry_str_val);
*pCtx->current_entry_str_val = strdup(pValue);
}
/* ignore unknown members */
pCtx->current_entry_str_val = NULL;
return true;
}
/* Parses test input file referenced by file_path.
* Does nothing if test input file does not exist. */
static bool input_test_file_read(const char* file_path)
{
bool success = false;
KTifJSONContext context = {0};
uint8_t *file_buf = NULL;
int64_t file_len = 0;
rjson_t* parser;
/* Sanity check */
if (!file_path || !*file_path)
{
RARCH_DBG("[Test input] No test input file supplied.\n");
return false;
}
/* Read the whole file in one operation: it is tiny and always
* parsed in full, so a single open/size/read/close beats a
* pre-open stat plus the chunked callback path (which itself
* sizes the stream with an extra fstat). The stat below runs
* only to classify a failure. */
if (!filestream_read_file(file_path,
(void**)&file_buf, &file_len))
{
if (!path_is_valid(file_path))
RARCH_DBG("[Test input] No test input file supplied.\n");
else
RARCH_ERR("[Test input] Failed to open test input file: \"%s\".\n",
file_path);
return false;
}
/* Initialise JSON parser */
if (!(parser = rjson_open_buffer(file_buf, (size_t)file_len)))
{
RARCH_ERR("[Test input] Failed to create JSON parser.\n");
goto end;
}
/* Configure parser */
rjson_set_options(parser, RJSON_OPTION_ALLOW_UTF8BOM);
/* Read file */
if (rjson_parse(parser, &context,
KTifJSONObjectMemberHandler,
KTifJSONStringHandler,
KTifJSONNumberHandler,
NULL, KTifJSONObjectEndHandler, NULL, NULL, /* object/array handlers */
NULL, NULL) /* unused boolean/null handlers */
!= RJSON_DONE)
{
if (rjson_get_source_context_len(parser))
{
RARCH_ERR(
"[Test input] Error parsing chunk of test input file: %s\n---snip---\n%.*s\n---snip---\n",
file_path,
rjson_get_source_context_len(parser),
rjson_get_source_context_buf(parser));
}
RARCH_WARN(
"[Test input] Error parsing test input file: \"%s\".\n",
file_path);
RARCH_ERR(
"[Test input] Error: Invalid JSON at line %d, column %d - %s.\n",
(int)rjson_get_source_line(parser),
(int)rjson_get_source_column(parser),
(*rjson_get_error(parser) ? rjson_get_error(parser) : "format error"));
}
/* Free parser */
rjson_free(parser);
success = true;
end:
/* Clean up leftover strings */
if (context.param_str)
free(context.param_str);
/* Release file contents */
free(file_buf);
if (last_test_step >= MAX_TEST_STEPS)
{
RARCH_WARN("[Test input] Too long test input json, maximum size: %d.\n",MAX_TEST_STEPS);
}
for (current_test_step = 0; current_test_step < last_test_step; current_test_step++)
{
RARCH_DBG(
"[Test input] test step %02d read from file: frame %d, action %x, num %x, str %s\n",
current_test_step,
input_test_steps[current_test_step].frame,
input_test_steps[current_test_step].action,
input_test_steps[current_test_step].param_num,
input_test_steps[current_test_step].param_str);
}
current_test_step = 0;
return success;
}
/********************************/
/* Test input file handling end */
/********************************/
/*uint16_t *test_keyboard_state_get(unsigned port)
{
return test_key_state[port];
}*/
static void test_keyboard_free(void)
{
unsigned i, j;
if (!test_key_state)
return;
for (i = 0; i < DEFAULT_MAX_PADS; i++)
for (j = 0; j < RETROK_LAST; j++)
test_key_state[i][j] = 0;
}
/* Which of @keys are down: bit n of @down for keys[n]. The first
* port's only, as this driver has always answered. */
static void test_keys_down(void *data, unsigned port,
const uint16_t *keys, const uint8_t *bind, unsigned count,
uint32_t *down)
{
unsigned i;
(void)data;
(void)bind;
if (port || !test_key_state)
return;
for (i = 0; i < count; i++)
if ( keys[i] && keys[i] < RETROK_LAST
&& test_key_state[DEFAULT_MAX_PADS][keys[i]])
down[i >> 5] |= (1u << (i & 31));
}
static int16_t test_input_state(
void *data,
const input_device_driver_t *joypad,
rarch_joypad_info_t *joypad_info,
const retro_keybind_set *binds,
unsigned port,
unsigned device,
unsigned idx,
unsigned id)
{
/* The RetroPad's buttons and the hotkeys, where they are bound to
* keys, are the frontend's to answer: it asks test_keys_down() for
* the keys once a poll. This worked them out from the binds it was
* handed - and for the whole pad read the set at the number that
* asks for the whole pad, far past its end. */
if ( port <= 0
&& device == RETRO_DEVICE_KEYBOARD
&& test_key_state
&& id && id < RETROK_LAST)
return (test_key_state[DEFAULT_MAX_PADS][id]);
return 0;
}
static void test_input_free_input(void *data)
{
test_keyboard_free();
if (input_test_steps)
free(input_test_steps);
input_test_steps = NULL;
if (test_key_state)
free(test_key_state);
test_key_state = NULL;
}
static void* test_input_init(const char *joypad_driver)
{
if (!input_test_steps)
input_test_steps = (input_test_step_t*)
calloc(MAX_TEST_STEPS, sizeof(*input_test_steps));
if (!test_key_state)
test_key_state = (uint16_t(*)[RETROK_LAST])
calloc(DEFAULT_MAX_PADS + 1, sizeof(*test_key_state));
if (!input_test_steps || !test_key_state)
return NULL;
RARCH_DBG("[Test input] Start.\n");
input_test_file_read(input_config_get_test_input_file(false));
if (last_test_step > MAX_TEST_STEPS)
last_test_step = 0;
/* No need for keyboard mapping look-up table */
/* input_keymaps_init_keyboard_lut(rarch_key_map_test);*/
return (void*)-1;
}
static float test_input_unsigned_to_float_acc(unsigned i)
{
if (i>200)
return 0.0f;
return ((signed int)(100-i))/100.0f*9.81f;
}
static float test_input_unsigned_to_float_gyro(unsigned i)
{
if (i>200)
return 0.0f;
return ((signed int)(100-i))/100.0f;
}
static float test_input_unsigned_to_float_lux(unsigned i)
{
if (i>200 || i<100)
return 0.0f;
return (float)pow(10,(((i-100)/100.0f)*4));
}
static void test_input_poll(void *data)
{
uint64_t curr_frame = video_driver_get_frame_count();
unsigned i;
for (i=0; i<last_test_step; i++)
{
if (!input_test_steps[i].handled && curr_frame > input_test_steps[i].frame)
{
if (input_test_steps[i].action == INPUT_TEST_COMMAND_PRESS_KEY)
{
if (input_test_steps[i].param_num < RETROK_LAST)
{
test_key_state[DEFAULT_MAX_PADS][input_test_steps[i].param_num] = 1;
input_keyboard_event(true, input_test_steps[i].param_num, 0, 0, RETRO_DEVICE_KEYBOARD);
}
input_test_steps[i].handled = true;
RARCH_DBG(
"[Test input] Pressing keyboard button %d at frame %d.\n",
input_test_steps[i].param_num, curr_frame);
}
else if (input_test_steps[i].action == INPUT_TEST_COMMAND_RELEASE_KEY)
{
if (input_test_steps[i].param_num < RETROK_LAST)
{
test_key_state[DEFAULT_MAX_PADS][input_test_steps[i].param_num] = 0;
input_keyboard_event(false, input_test_steps[i].param_num, 0, 0, RETRO_DEVICE_KEYBOARD);
}
input_test_steps[i].handled = true;
RARCH_DBG(
"[Test input] Releasing keyboard button %d at frame %d.\n",
input_test_steps[i].param_num, curr_frame);
}
else if ( input_test_steps[i].action == INPUT_TEST_COMMAND_SET_SENSOR_ACC_X
|| input_test_steps[i].action == INPUT_TEST_COMMAND_SET_SENSOR_ACC_Y
|| input_test_steps[i].action == INPUT_TEST_COMMAND_SET_SENSOR_ACC_Z)
{
float setval = test_input_unsigned_to_float_acc(input_test_steps[i].param_num);
switch (input_test_steps[i].action)
{
case INPUT_TEST_COMMAND_SET_SENSOR_ACC_X:
test_input_values.accelerometer_state.x = setval;
break;
case INPUT_TEST_COMMAND_SET_SENSOR_ACC_Y:
test_input_values.accelerometer_state.y = setval;
break;
case INPUT_TEST_COMMAND_SET_SENSOR_ACC_Z:
test_input_values.accelerometer_state.z = setval;
break;
}
input_test_steps[i].handled = true;
RARCH_DBG(
"[Test input] Setting accelerometer axis %d to %f at frame %d.\n",
input_test_steps[i].action - INPUT_TEST_COMMAND_SET_SENSOR_ACC_X, setval, curr_frame);
}
else if ( input_test_steps[i].action == INPUT_TEST_COMMAND_SET_SENSOR_GYR_X
|| input_test_steps[i].action == INPUT_TEST_COMMAND_SET_SENSOR_GYR_Y
|| input_test_steps[i].action == INPUT_TEST_COMMAND_SET_SENSOR_GYR_Z)
{
float setval = test_input_unsigned_to_float_gyro(input_test_steps[i].param_num);
switch (input_test_steps[i].action)
{
case INPUT_TEST_COMMAND_SET_SENSOR_GYR_X:
test_input_values.gyroscope_state.x = setval;
break;
case INPUT_TEST_COMMAND_SET_SENSOR_GYR_Y:
test_input_values.gyroscope_state.y = setval;
break;
case INPUT_TEST_COMMAND_SET_SENSOR_GYR_Z:
test_input_values.gyroscope_state.z = setval;
break;
}
input_test_steps[i].handled = true;
RARCH_DBG(
"[Test input] Setting gyroscope axis %d to %f at frame %d.\n",
input_test_steps[i].action - INPUT_TEST_COMMAND_SET_SENSOR_GYR_X, setval, curr_frame);
}
else if (input_test_steps[i].action == INPUT_TEST_COMMAND_SET_SENSOR_LUX)
{
float setval = test_input_unsigned_to_float_lux(input_test_steps[i].param_num);
test_input_values.lux_sensor_state = setval;
input_test_steps[i].handled = true;
RARCH_DBG(
"[Test input] Setting lux sensor to %f at frame %d.\n",
setval, curr_frame);
}
else
{
input_test_steps[i].handled = true;
RARCH_WARN(
"[Test input] Unrecognized action %d in step %d, skipping...\n",
input_test_steps[i].action,i);
}
}
}
}
static bool test_input_set_sensor_state(void *data, unsigned port,
enum retro_sensor_action action, unsigned event_rate)
{
RARCH_DBG("[Test input] Setting sensor action %d rate %d.\n", action, event_rate);
return true;
}
static float test_input_get_sensor_input(void *data,
unsigned port, unsigned id)
{
switch (id)
{
case RETRO_SENSOR_ACCELEROMETER_X:
return test_input_values.accelerometer_state.x;
case RETRO_SENSOR_ACCELEROMETER_Y:
return test_input_values.accelerometer_state.y;
case RETRO_SENSOR_ACCELEROMETER_Z:
return test_input_values.accelerometer_state.z;
case RETRO_SENSOR_GYROSCOPE_X:
return test_input_values.gyroscope_state.x;
case RETRO_SENSOR_GYROSCOPE_Y:
return test_input_values.gyroscope_state.y;
case RETRO_SENSOR_GYROSCOPE_Z:
return test_input_values.gyroscope_state.z;
case RETRO_SENSOR_ILLUMINANCE:
return test_input_values.lux_sensor_state;
}
return 0.0f;
}
static uint64_t test_input_get_capabilities(void *data)
{
return
(1 << RETRO_DEVICE_JOYPAD)
| (1 << RETRO_DEVICE_ANALOG)
| (1 << RETRO_DEVICE_KEYBOARD)
/* | (1 << RETRO_DEVICE_MOUSE)
| (1 << RETRO_DEVICE_POINTER)
| (1 << RETRO_DEVICE_LIGHTGUN)*/;
}
input_driver_t input_test = {
test_input_init,
test_input_poll,
test_input_state,
test_input_free_input,
test_input_set_sensor_state,
test_input_get_sensor_input,
test_input_get_capabilities,
"test",
NULL, /* grab_mouse */
NULL,
NULL,
NULL, /* survives_video */
test_keys_down
};