From b1508262b0f91a43e6b98a2c07afca9754d87d69 Mon Sep 17 00:00:00 2001 From: Alice R Date: Sun, 21 Jan 2024 02:48:24 -0700 Subject: [PATCH] SDL3 preparation (event_sdl.c). (#420) * Most references to "gamecontrollers" in code and config settings are now "gamepad", which is half the length, less annoying to type and look at, and also matches SDL3's current renaming of their API. The old "gamecontroller" config settings still work, but config.txt now only contains references to "gamepad". * The SDL event unicode fallback behavior now only triggers when text events are disabled in SDL2+. This should allow text input events if the onscreen keyboard is active in the Android port, but still allow the unicode fallback to work when it's hidden. Text input is now disabled whenever a platform supports an onscreen keyboard instead of being excluded by Android specifically. * SDL joystick GUIDs are now derived from the `SDL_Joystick *` rather than the SDL device index. The SDL device index is now only used for initializing the `SDL_Joystick` and `SDL_Gamepad` and getting the mapping string, which should make it easier to support SDL2 and SDL3 at the same time. --- arch/switch/pad.config | 12 +-- config.txt | 58 +++++++------- docs/changelog.txt | 2 + docs/joystick.html | 18 ++--- src/configure.c | 36 +++++---- src/configure.h | 2 +- src/event.h | 4 +- src/event_sdl.c | 167 ++++++++++++++++++++++------------------- src/platform_sdl.c | 23 +++--- unit/configure.cpp | 9 ++- 10 files changed, 183 insertions(+), 148 deletions(-) diff --git a/arch/switch/pad.config b/arch/switch/pad.config index 57bb3b7e5..7043dba3c 100644 --- a/arch/switch/pad.config +++ b/arch/switch/pad.config @@ -27,11 +27,11 @@ # Buttons 17-24 are directional axis "buttons" ignored by MZX because MZX does # its own internal analog-to-digital conversion. -# Because the Switch uses SDL2, MZX can generate mappings automatically with -# the SDL_GameController API. However, this API is based on XInput and has +# Because the Switch uses SDL, MZX can generate mappings automatically with +# the SDL gamepad API. However, this API is based on XInput and has # the face buttons inverted from where they'd be expected on a Nintendo system. -gamecontroller.a = act_b -gamecontroller.b = act_a -gamecontroller.x = act_y -gamecontroller.y = act_x +gamepad.a = act_b +gamepad.b = act_a +gamepad.x = act_y +gamepad.y = act_x diff --git a/config.txt b/config.txt index c4362eb2b..afe649687 100644 --- a/config.txt +++ b/config.txt @@ -650,47 +650,47 @@ # joystick number 'X' can be substituted with a range '[Z,W]', which will # assign the control for all joysticks numbered Z through W (inclusive). -# Automatic mapping for actions is handled using SDL 2's game controller API. -# Supported controllers should generally "just work" when they're plugged in. +# Automatic mapping for actions is handled using SDL's gamepad API. +# Supported gamepads should generally "just work" when they're plugged in. # This setting can be used to globally disable the automatic mapping feature # entirely. If 1, it will be enabled (default). If 0, it will be disabled. -# gamecontroller_enable = 1 +# gamepad_enable = 1 -# Controllers that aren't supported can be fixed by adding SDL mapping strings. +# Gamepads that aren't supported can be fixed by adding SDL mapping strings. # This config option adds a new mapping string and can be used multiple times; # each line adds a new mapping. See docs/joystick.html for more information. -# gamecontroller_add = +# gamepad_add = # The following settings can be used to globally customize the automated # joystick mappings generated from SDL mapping strings. # See docs/joystick.html for more information. -# gamecontroller.a = act_a -# gamecontroller.b = act_b -# gamecontroller.x = act_x -# gamecontroller.y = act_y -# gamecontroller.back = act_select -# gamecontroller.start = act_start -# gamecontroller.leftstick = act_lstick -# gamecontroller.rightstick = act_rstick -# gamecontroller.leftshoulder = act_lshoulder -# gamecontroller.rightshoulder = act_rshoulder -# gamecontroller.dpup = act_up -# gamecontroller.dpdown = act_down -# gamecontroller.dpleft = act_left -# gamecontroller.dpright = act_right -# gamecontroller.-leftx = act_l_left -# gamecontroller.+leftx = act_l_right -# gamecontroller.-lefty = act_l_up -# gamecontroller.+lefty = act_l_down -# gamecontroller.-rightx = act_r_left -# gamecontroller.+rightx = act_r_right -# gamecontroller.-righty = act_r_up -# gamecontroller.+righty = act_r_down -# gamecontroller.lefttrigger = act_ltrigger -# gamecontroller.righttrigger = act_rtrigger +# gamepad.a = act_a +# gamepad.b = act_b +# gamepad.x = act_x +# gamepad.y = act_y +# gamepad.back = act_select +# gamepad.start = act_start +# gamepad.leftstick = act_lstick +# gamepad.rightstick = act_rstick +# gamepad.leftshoulder = act_lshoulder +# gamepad.rightshoulder = act_rshoulder +# gamepad.dpup = act_up +# gamepad.dpdown = act_down +# gamepad.dpleft = act_left +# gamepad.dpright = act_right +# gamepad.-leftx = act_l_left +# gamepad.+leftx = act_l_right +# gamepad.-lefty = act_l_up +# gamepad.+lefty = act_l_down +# gamepad.-rightx = act_r_left +# gamepad.+rightx = act_r_right +# gamepad.-righty = act_r_up +# gamepad.+righty = act_r_down +# gamepad.lefttrigger = act_ltrigger +# gamepad.righttrigger = act_rtrigger # Misc Options diff --git a/docs/changelog.txt b/docs/changelog.txt index 2df65e472..f83a872ca 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -5,6 +5,8 @@ FREAD/FREADn bugfixes, as the old behavior was unusably buggy. USERS ++ All "gamecontroller[...]" config.txt options have been renamed + to "gamepad[...]". The old names are still supported. + HTML5: fixed the poor performance of FREADn and other features that rely on calculating the length of an open file. + Setting a string offset, limit, or splice to FREADn now diff --git a/docs/joystick.html b/docs/joystick.html index 28b2deb5e..791600d1e 100644 --- a/docs/joystick.html +++ b/docs/joystick.html @@ -648,7 +648,7 @@

Adding/Modifying Controller Support

- gamecontroller_add = 030000005e0400008e02000000007801,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, + gamepad_add = 030000005e0400008e02000000007801,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,

@@ -667,9 +667,9 @@

Adding/Modifying Controller Support

- gamecontroller.SDLBUTTON = act_NAME - gamecontroller.+SDLAXIS = act_NAME - gamecontroller.-SDLAXIS = act_NAME + gamepad.SDLBUTTON = act_NAME + gamepad.+SDLAXIS = act_NAME + gamepad.-SDLAXIS = act_NAME

@@ -687,7 +687,7 @@

Adding/Modifying Controller Support

- gamecontroller_enable = 0 + gamepad_enable = 0 @@ -1151,10 +1151,10 @@

Switch

- gamecontroller.a = act_b - gamecontroller.b = act_a - gamecontroller.x = act_y - gamecontroller.y = act_x + gamepad.a = act_b + gamepad.b = act_a + gamepad.x = act_y + gamepad.y = act_x

diff --git a/src/configure.c b/src/configure.c index be5af0008..f98ddeba0 100644 --- a/src/configure.c +++ b/src/configure.c @@ -290,7 +290,7 @@ static const struct config_info user_conf_default = true, // pc_speaker_on // Event options - true, // allow_gamecontroller + true, // allow_gamepad false, // pause_on_unfocus 1, // num_buffered_events @@ -957,16 +957,23 @@ static void config_set_joy_axis_threshold(struct config_info *conf, char *name, #ifdef CONFIG_SDL #if SDL_VERSION_ATLEAST(2,0,0) -#define GC_ENUM "%15[-+0-9A-Za-z_]" +#define GAMEPAD_ENUM "%15[-+0-9A-Za-z_]" -static void config_sdl_gc_set(struct config_info *conf, char *name, +static void config_gamepad_set(struct config_info *conf, char *name, char *value, char *extended_data) { - char gc_sym[16]; + char gamepad_sym[16]; char key[16]; int read = 0; - if(sscanf(name, "gamecontroller." GC_ENUM "%n", gc_sym, &read) != 1 + // Option may be either "gamepad.[enum]" or "gamecontroller.[enum]". + if(!strncmp(name, "gamepad", 7)) + name += 7; + else + if(!strncmp(name, "gamecontroller", 14)) + name += 14; + + if(sscanf(name, "." GAMEPAD_ENUM "%n", gamepad_sym, &read) != 1 || (name[read] != 0)) return; @@ -974,19 +981,19 @@ static void config_sdl_gc_set(struct config_info *conf, char *name, if(sscanf(value, JOY_ENUM "%n", key, &read) != 1 || (value[read] != 0)) return; - gamecontroller_map_sym(gc_sym, key); + gamepad_map_sym(gamepad_sym, key); } -static void config_sdl_gc_add(struct config_info *conf, char *name, +static void config_gamepad_add(struct config_info *conf, char *name, char *value, char *extended_data) { - gamecontroller_add_mapping(value); + gamepad_add_mapping(value); } -static void config_sdl_gc_enable(struct config_info *conf, char *name, +static void config_gamepad_enable(struct config_info *conf, char *name, char *value, char *extended_data) { - config_boolean(&conf->allow_gamecontroller, value); + config_boolean(&conf->allow_gamepad, value); } #endif // SDL_VERSION_ATLEAST(2,0,0) @@ -1231,9 +1238,12 @@ static const struct config_entry config_options[] = { "fullscreen_windowed", config_set_fullscreen_windowed, false }, #ifdef CONFIG_SDL #if SDL_VERSION_ATLEAST(2,0,0) - { "gamecontroller.*", config_sdl_gc_set, false }, - { "gamecontroller_add", config_sdl_gc_add, false }, - { "gamecontroller_enable", config_sdl_gc_enable, false }, + { "gamecontroller.*", config_gamepad_set, false }, + { "gamecontroller_add", config_gamepad_add, false }, + { "gamecontroller_enable", config_gamepad_enable, false }, + { "gamepad.*", config_gamepad_set, false }, + { "gamepad_add", config_gamepad_add, false }, + { "gamepad_enable", config_gamepad_enable, false }, #endif #endif { "gl_filter_method", config_set_gl_filter_method, false }, diff --git a/src/configure.h b/src/configure.h index 597edf708..f913f3652 100644 --- a/src/configure.h +++ b/src/configure.h @@ -141,7 +141,7 @@ struct config_info boolean pc_speaker_on; // Event options - boolean allow_gamecontroller; + boolean allow_gamepad; boolean pause_on_unfocus; int num_buffered_events; diff --git a/src/event.h b/src/event.h index 727697572..f86ba51af 100644 --- a/src/event.h +++ b/src/event.h @@ -207,8 +207,8 @@ void __wait_event(void); void __warp_mouse(int x, int y); // "Driver" functions currently only supported by SDL. -void gamecontroller_map_sym(const char *sym, const char *value); -void gamecontroller_add_mapping(const char *mapping); +void gamepad_map_sym(const char *sym, const char *value); +void gamepad_add_mapping(const char *mapping); #ifdef CONFIG_NDS const struct buffered_status *load_status(void); diff --git a/src/event_sdl.c b/src/event_sdl.c index 74753f4ab..8ec0485d7 100644 --- a/src/event_sdl.c +++ b/src/event_sdl.c @@ -237,13 +237,13 @@ static int get_pandora_joystick_button(SDL_Keycode key) #if SDL_VERSION_ATLEAST(2,0,0) /** - * The SDL GameController API puts this in an interesting place. The best way + * The SDL gamepad API puts this in an interesting place. The best way * to populate MZX action mappings is to parse the SDL mapping string directly. * The API offers no other way to get detailed mapping information on half axes * and axis inversions (these were added after the API was designed; no new - * functions were added to read this extended info), and using gamecontroller + * functions were added to read this extended info), and using gamepad * events to simulate presses could result in situations where the joystick - * events and the gamecontroller events create different simultaneous presses. + * events and the gamepad events create different simultaneous presses. * * However, the axis mappings can be so convoluted it's better to open the * controller with the API anyway just to populate the analog axis values. This @@ -252,7 +252,7 @@ static int get_pandora_joystick_button(SDL_Keycode key) * No equivalent of this API exists for SDL 1.x. */ -static SDL_GameController *gamecontrollers[MAX_JOYSTICKS]; +static SDL_GameController *gamepads[MAX_JOYSTICKS]; static enum joystick_special_axis sdl_axis_map[SDL_CONTROLLER_AXIS_MAX] = { @@ -295,13 +295,13 @@ static int16_t sdl_action_map[SDL_CONTROLLER_BUTTON_MAX] = enum { - GC_NONE, - GC_BUTTON, - GC_AXIS, - GC_HAT, + GAMEPAD_NONE, + GAMEPAD_BUTTON, + GAMEPAD_AXIS, + GAMEPAD_HAT, }; -struct gc_map +struct gamepad_map { char *dbg; uint8_t feature; @@ -309,10 +309,10 @@ struct gc_map uint8_t pos; }; -struct gc_axis_map +struct gamepad_axis_map { - struct gc_map neg; - struct gc_map pos; + struct gamepad_map neg; + struct gamepad_map pos; }; static int sdl_hat_to_dir(int hat_mask) @@ -336,8 +336,8 @@ static int sdl_hat_to_dir(int hat_mask) } } -static void parse_gamecontroller_read_value(char *key, char *value, - struct gc_map *single, struct gc_map *neg, struct gc_map *pos) +static void parse_gamepad_read_value(char *key, char *value, + struct gamepad_map *single, struct gamepad_map *neg, struct gamepad_map *pos) { // Joystick axes may have half-axis prefixes + or -. // Joystick axes may also have an inversion suffix ~. @@ -389,14 +389,14 @@ static void parse_gamecontroller_read_value(char *key, char *value, if(neg) { - neg->feature = GC_AXIS; + neg->feature = GAMEPAD_AXIS; neg->which = axis; neg->pos = 0; neg->dbg = key; } if(pos) { - pos->feature = GC_AXIS; + pos->feature = GAMEPAD_AXIS; pos->which = axis; pos->pos = 1; pos->dbg = key; @@ -421,7 +421,7 @@ static void parse_gamecontroller_read_value(char *key, char *value, if(single) { - single->feature = GC_BUTTON; + single->feature = GAMEPAD_BUTTON; single->which = button; single->dbg = key; } @@ -452,7 +452,7 @@ static void parse_gamecontroller_read_value(char *key, char *value, if(single) { - single->feature = GC_HAT; + single->feature = GAMEPAD_HAT; single->which = hat; single->pos = dir; single->dbg = key; @@ -464,17 +464,17 @@ static void parse_gamecontroller_read_value(char *key, char *value, return; } -static void parse_gamecontroller_read_entry(char *key, char *value, - struct gc_axis_map *axes, struct gc_map *buttons) +static void parse_gamepad_read_entry(char *key, char *value, + struct gamepad_axis_map *axes, struct gamepad_map *buttons) { SDL_GameControllerAxis a; SDL_GameControllerButton b; - struct gc_map *single = NULL; - struct gc_map *neg = NULL; - struct gc_map *pos = NULL; + struct gamepad_map *single = NULL; + struct gamepad_map *neg = NULL; + struct gamepad_map *pos = NULL; char half_axis = 0; - // Gamecontroller axes may have half-axis prefixes + or -. + // Gamepad axes may have half-axis prefixes + or -. if(*key == '+' || *key == '-') half_axis = *(key++); @@ -518,11 +518,11 @@ static void parse_gamecontroller_read_entry(char *key, char *value, return; } - parse_gamecontroller_read_value(key, value, single, neg, pos); + parse_gamepad_read_value(key, value, single, neg, pos); } -static void parse_gamecontroller_read_string(char *map, - struct gc_axis_map *axes, struct gc_map *buttons) +static void parse_gamepad_read_string(char *map, + struct gamepad_axis_map *axes, struct gamepad_map *buttons) { // Format: entry,entry,... // Entry: value or key:value @@ -550,12 +550,12 @@ static void parse_gamecontroller_read_string(char *map, *(map++) = 0; if(value) - parse_gamecontroller_read_entry(key, value, axes, buttons); + parse_gamepad_read_entry(key, value, axes, buttons); } } -static void parse_gamecontroller_apply(int joy, int16_t mapping, - struct gc_map *target, boolean *select_mapped, boolean *select_used) +static void parse_gamepad_apply(int joy, int16_t mapping, + struct gamepad_map *target, boolean *select_mapped, boolean *select_used) { uint8_t which = target->which; uint8_t pos = target->pos; @@ -565,10 +565,10 @@ static void parse_gamecontroller_apply(int joy, int16_t mapping, switch(target->feature) { - case GC_NONE: + case GAMEPAD_NONE: return; - case GC_BUTTON: + case GAMEPAD_BUTTON: { debug("--JOYSTICK-- b%u -> '%s' (%d)\n", which, target->dbg, mapping); if(!input.joystick_global_map.button_is_conf[joy][which]) @@ -579,7 +579,7 @@ static void parse_gamecontroller_apply(int joy, int16_t mapping, break; } - case GC_AXIS: + case GAMEPAD_AXIS: { debug("--JOYSTICK-- a%u%s -> '%s' (%d)\n", which, pos?"+":"-", target->dbg, mapping); @@ -592,7 +592,7 @@ static void parse_gamecontroller_apply(int joy, int16_t mapping, break; } - case GC_HAT: + case GAMEPAD_HAT: { debug("--JOYSTICK-- hd%u -> '%s' (%d)\n", pos, target->dbg, mapping); if(!input.joystick_global_map.hat_is_conf[joy]) @@ -608,10 +608,10 @@ static void parse_gamecontroller_apply(int joy, int16_t mapping, return; } -static void parse_gamecontroller_map(int joystick_index, char *map) +static void parse_gamepad_map(int joystick_index, char *map) { - struct gc_axis_map axes[SDL_CONTROLLER_AXIS_MAX]; - struct gc_map buttons[SDL_CONTROLLER_BUTTON_MAX]; + struct gamepad_axis_map axes[SDL_CONTROLLER_AXIS_MAX]; + struct gamepad_map buttons[SDL_CONTROLLER_BUTTON_MAX]; boolean select_mapped = false; boolean select_used = false; size_t i; @@ -619,22 +619,22 @@ static void parse_gamecontroller_map(int joystick_index, char *map) memset(axes, 0, sizeof(axes)); memset(buttons, 0, sizeof(buttons)); - parse_gamecontroller_read_string(map, axes, buttons); + parse_gamepad_read_string(map, axes, buttons); // Apply axes. for(i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++) { - parse_gamecontroller_apply(joystick_index, + parse_gamepad_apply(joystick_index, sdl_axis_action_map[i][0], &(axes[i].neg), &select_mapped, &select_used); - parse_gamecontroller_apply(joystick_index, + parse_gamepad_apply(joystick_index, sdl_axis_action_map[i][1], &(axes[i].pos), &select_mapped, &select_used); } // Apply buttons. for(i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) { - parse_gamecontroller_apply(joystick_index, + parse_gamepad_apply(joystick_index, sdl_action_map[i], &(buttons[i]), &select_mapped, &select_used); } @@ -642,7 +642,7 @@ static void parse_gamecontroller_map(int joystick_index, char *map) { // TODO originally this was going to try to place JOY_SELECT on another // button. That was kind of a bad idea, so just print a warning for now. - info("--JOYSTICK-- %d doesn't have any gamecontroller button that binds " + info("--JOYSTICK-- %d doesn't have any gamepad button that binds " "'select' or 'start' (by default, these are the SDL gamecontoller buttons " "'back' and 'start', respectively). Since these buttons are used to open " "the joystick menu, you may want to override this controller mapping.\n", @@ -650,31 +650,35 @@ static void parse_gamecontroller_map(int joystick_index, char *map) } } -static void init_gamecontroller(int sdl_index, int joystick_index) +/** + * `sdl_joystick_id` is a device ID for SDL1/SDL2, but an instance ID for SDL3. + */ +static void init_gamepad(SDL_Joystick *joystick, int sdl_joystick_id, + int joystick_index) { - SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(sdl_index); + SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); char guid_string[33]; SDL_JoystickGetGUIDString(guid, guid_string, 33); - gamecontrollers[joystick_index] = NULL; + gamepads[joystick_index] = NULL; - if(SDL_IsGameController(sdl_index)) + if(SDL_IsGameController(sdl_joystick_id)) { - SDL_GameController *gamecontroller = SDL_GameControllerOpen(sdl_index); + SDL_GameController *gamepad = SDL_GameControllerOpen(sdl_joystick_id); - if(gamecontroller) + if(gamepad) { struct config_info *conf = get_config(); char *mapping = NULL; - gamecontrollers[joystick_index] = gamecontroller; + gamepads[joystick_index] = gamepad; #if SDL_VERSION_ATLEAST(2,0,9) // NOTE: the other functions for this will not return the default mapping // string; this is the only one that can return everything. Right now, // this only matters for the Emscripten port. - mapping = (char *)SDL_GameControllerMappingForDeviceIndex(sdl_index); + mapping = (char *)SDL_GameControllerMappingForDeviceIndex(sdl_joystick_id); #else - mapping = (char *)SDL_GameControllerMapping(gamecontroller); + mapping = (char *)SDL_GameControllerMapping(gamepad); #endif #ifdef __EMSCRIPTEN__ @@ -703,8 +707,8 @@ static void init_gamecontroller(int sdl_index, int joystick_index) if(strncmp(mapping, guid_string, strlen(guid_string))) info("--JOYSTICK-- GUID: %s\n", guid_string); - if(conf->allow_gamecontroller) - parse_gamecontroller_map(joystick_index, mapping); + if(conf->allow_gamepad) + parse_gamepad_map(joystick_index, mapping); SDL_free(mapping); return; @@ -712,12 +716,12 @@ static void init_gamecontroller(int sdl_index, int joystick_index) } } info("--JOYSTICK-- joystick %d does not have an SDL mapping or could not be " - "opened as a gamecontroller (GUID: %s).\n", joystick_index + 1, guid_string); + "opened as a gamepad (GUID: %s).\n", joystick_index + 1, guid_string); } // Clean up auto-generated bindings so they don't cause problems for other // controllers that might end up using this position. -static void gamecontroller_clean_map(int joy) +static void gamepad_clean_map(int joy) { int i; @@ -760,7 +764,7 @@ static void gamecontroller_clean_map(int joy) /** * Load gamecontrollerdb.txt if configured and if it isn't already loaded. - * This adds more gamecontroller mappings so MZX can support more controllers. + * This adds more gamepad mappings so MZX can support more controllers. * The function this uses wasn't added until 2.0.2. */ static void load_gamecontrollerdb(void) @@ -787,7 +791,7 @@ static void load_gamecontrollerdb(void) /** * Change one of the default SDL to MZX mapping values. */ -void gamecontroller_map_sym(const char *sym, const char *value) +void gamepad_map_sym(const char *sym, const char *value) { SDL_GameControllerAxis a; SDL_GameControllerButton b; @@ -819,25 +823,23 @@ void gamecontroller_map_sym(const char *sym, const char *value) /** * Add a mapping string to SDL. */ -void gamecontroller_add_mapping(const char *mapping) +void gamepad_add_mapping(const char *mapping) { // Make sure this is loaded first so it doesn't override the user mapping. load_gamecontrollerdb(); if(SDL_GameControllerAddMapping(mapping) < 0) - warn("Failed to add gamecontroller mapping: %s\n", SDL_GetError()); + warn("Failed to add gamepad mapping: %s\n", SDL_GetError()); } #endif /* SDL_VERSION_ATLEAST(2,0,0) */ /** - * SDL 2 uses joystick instance IDs instead of the joystick index for all - * purposes aside from SDL_JoystickOpen(). We prefer the joystick index (which - * SDL 1.2 used exclusively) as the instance IDs increment every time - * SDL_JoystickOpen() is used, so keep a map between the two. Additionally, - * store the joystick pointer to make things easier when closing joysticks. + * SDL 2+ uses joystick instance IDs instead of the device ID (index) for all + * purposes (except for initialization in SDL 2, but not SDL 3). These instance + * IDs need to be mapped to MegaZeux joystick indices. For SDL 1.2, the SDL + * device ID is used as a substitute for the instance ID. */ - static int get_joystick_index(int sdl_instance_id) { int i; @@ -858,15 +860,17 @@ static int get_next_unused_joystick_index(void) return -1; } -static void init_joystick(int sdl_index) +/** + * `sdl_joystick_id` is a device ID for SDL1/SDL2, but an instance ID for SDL3. + */ +static void init_joystick(int sdl_joystick_id) { struct buffered_status *status = store_status(); int joystick_index = get_next_unused_joystick_index(); if(joystick_index >= 0) { - SDL_Joystick *joystick = SDL_JoystickOpen(sdl_index); - + SDL_Joystick *joystick = SDL_JoystickOpen(sdl_joystick_id); if(joystick) { joystick_instance_ids[joystick_index] = SDL_JoystickInstanceID(joystick); @@ -877,7 +881,7 @@ static void init_joystick(int sdl_index) joystick_index + 1, joystick_instance_ids[joystick_index]); #if SDL_VERSION_ATLEAST(2,0,0) - init_gamecontroller(sdl_index, joystick_index); + init_gamepad(joystick, sdl_joystick_id, joystick_index); #endif } } @@ -893,11 +897,11 @@ static void close_joystick(int joystick_index) joystick_index + 1, joystick_instance_ids[joystick_index]); // SDL_GameControllerClose also closes the joystick. - if(gamecontrollers[joystick_index]) + if(gamepads[joystick_index]) { - SDL_GameControllerClose(gamecontrollers[joystick_index]); - gamecontroller_clean_map(joystick_index); - gamecontrollers[joystick_index] = NULL; + SDL_GameControllerClose(gamepads[joystick_index]); + gamepad_clean_map(joystick_index); + gamepads[joystick_index] = NULL; } else SDL_JoystickClose(joysticks[joystick_index]); @@ -962,9 +966,18 @@ static inline uint32_t utf8_next_char(uint8_t **_src) static boolean process_event(SDL_Event *event) { struct buffered_status *status = store_status(); - static boolean unicode_fallback = true; enum keycode ckey; +#if SDL_VERSION_ATLEAST(2,0,0) + /* Enable converting keycodes to fake unicode presses when text input isn't + * active. Enabling text input also enables an onscreen keyboard in some + * ports, so it isn't always desired. */ + boolean unicode_fallback = !SDL_IsTextInputActive(); +#else + /* SDL 1.2 might also need this (Pandora? doesn't generate unicode presses). */ + static boolean unicode_fallback = true; +#endif + /* SDL's numlock keyboard modifier handling seems to be broken on X11, * and it will only get numlock's status right on application init. We * can trust this value once, and then toggle based on user presses of @@ -1404,8 +1417,8 @@ static boolean process_event(SDL_Event *event) case SDL_CONTROLLERAXISMOTION: { - // Since gamecontroller axis mappings can be complicated, use - // the gamecontroller events to update the named axis values. + // Since gamepad axis mappings can be complicated, use + // the gamepad events to update the named axis values. struct config_info *conf = get_config(); int value = event->caxis.value; int which = event->caxis.which; @@ -1413,7 +1426,7 @@ static boolean process_event(SDL_Event *event) enum joystick_special_axis special_axis = sdl_axis_map[axis]; int joystick_index = get_joystick_index(which); - if(joystick_index < 0 || !special_axis || !conf->allow_gamecontroller) + if(joystick_index < 0 || !special_axis || !conf->allow_gamepad) break; trace( diff --git a/src/platform_sdl.c b/src/platform_sdl.c index d2594d0c9..e2c9524c3 100644 --- a/src/platform_sdl.c +++ b/src/platform_sdl.c @@ -153,15 +153,20 @@ boolean platform_init(void) } #if SDL_VERSION_ATLEAST(2,0,0) -/** - * TODO: Don't enable SDL_TEXTINPUT events on Android for now. They seem to - * cause more issues than they solve. Enabling these on Android also seems - * to assume that the onscreen keyboard should be opened, which usually - * covers whatever part of the MZX screen is being typed into. - */ -#if !defined(ANDROID) - SDL_StartTextInput(); -#endif + /* Most platforms want text input events always on so they can generate + * convenient unicode text values, but in Android this causes some problems: + * + * - On older versions the navigation bar will ALWAYS display, regardless + * of whether or not there's an attached keyboard. + * - Holding the space key no longer works, breaking built-in shooting (as + * recent as Android 10). + * - The onscreen keyboard Android pops up can be moved but not collapsed. + * + * TODO: Instead, enable text input on demand at text prompts. + * TODO: this probably redundant with behavior already in SDL. + */ + if(!SDL_HasScreenKeyboardSupport()) + SDL_StartTextInput(); #else SDL_EnableUNICODE(1); #endif diff --git a/unit/configure.cpp b/unit/configure.cpp index a2aeb276f..adbc8ee58 100644 --- a/unit/configure.cpp +++ b/unit/configure.cpp @@ -624,9 +624,14 @@ UNITTEST(Settings) #ifdef CONFIG_SDL #if SDL_VERSION_ATLEAST(2,0,0) - SECTION(allow_gamecontroller) + SECTION(gamecontroller_enable) { - TEST_ENUM("gamecontroller_enable", conf->allow_gamecontroller, boolean_data); + TEST_ENUM("gamecontroller_enable", conf->allow_gamepad, boolean_data); + } + + SECTION(gamepad_enable) + { + TEST_ENUM("gamepad_enable", conf->allow_gamepad, boolean_data); } #endif #endif