diff --git a/cheat_manager.c b/cheat_manager.c index 044911f64542..c453a6cb9bf2 100644 --- a/cheat_manager.c +++ b/cheat_manager.c @@ -1370,6 +1370,7 @@ static int cheat_manager_search_input_start( line.label_setting = value_buf; line.type = label; line.idx = (unsigned)idx; + line.text_type = MENU_INPUT_DIALOG_KB_TYPE_NUMBER; line.cb = cb; if (menu_input_dialog_start(&line)) diff --git a/gfx/common/sdl3_common.c b/gfx/common/sdl3_common.c index 5aaa1a629441..0f14776b7b7e 100644 --- a/gfx/common/sdl3_common.c +++ b/gfx/common/sdl3_common.c @@ -275,7 +275,8 @@ static SDL_Window *sdl3_window_create(unsigned width, unsigned height, /* SDL_EVENT_TEXT_INPUT is emitted for windows that opted in for * it. The SDL3 input driver handles those events for menu * text entry and core keyboard callbacks. */ - SDL_StartTextInput(win); + if (!SDL_HasScreenKeyboardSupport()) + SDL_StartTextInput(win); return win; } @@ -541,3 +542,29 @@ void sdl3_show_mouse(void *data, bool state) else SDL_HideCursor(); } + +SDL_Window *sdl3_get_window(void) +{ + gfx_ctx_ident_t ident_info; + + if (string_is_equal(video_driver_get_ident(), "sdl3")) + { + sdl3_video_t *video_ptr = (sdl3_video_t*)video_driver_get_ptr(); + return video_ptr != NULL ? video_ptr->window : NULL; + } + + /* gl/gl1/glcore/vulkan running on the SDL3 context drivers. They + * register their SDL_Window as the display userdata via sdl3_set_handles. */ + ident_info.ident = NULL; + video_context_driver_get_ident(&ident_info); + if (string_is_equal(ident_info.ident, "gl_sdl3") || string_is_equal(ident_info.ident, "vk_sdl3")) + return (SDL_Window*)video_driver_display_userdata_get(); + + return NULL; +} + +bool sdl3_screen_keyboard_shown(void) +{ + SDL_Window *win = sdl3_get_window(); + return win && SDL_ScreenKeyboardShown(win); +} diff --git a/gfx/common/sdl3_common.h b/gfx/common/sdl3_common.h index 509743ccbd17..0ecdcf594f19 100644 --- a/gfx/common/sdl3_common.h +++ b/gfx/common/sdl3_common.h @@ -126,4 +126,13 @@ bool sdl3_ctx_get_metrics(void *data, enum display_metric_types type, /* Shows or hides the mouse cursor. */ void sdl3_show_mouse(void *data, bool state); +/* Returns the active SDL_Window, whether it belongs to the sdl3 + * video driver or one of the gl/vk sdl3 context drivers. NULL when + * SDL3 isn't driving the window. */ +SDL_Window *sdl3_get_window(void); + +/* True if the on-screen keyboard is being displayed for the SDL3 + * window. */ +bool sdl3_screen_keyboard_shown(void); + #endif diff --git a/gfx/gfx_display.c b/gfx/gfx_display.c index 99b0fc99118d..0183d608292f 100644 --- a/gfx/gfx_display.c +++ b/gfx/gfx_display.c @@ -38,6 +38,10 @@ #include "../ui/drivers/cocoa/apple_platform.h" #endif +#ifdef HAVE_SDL3 +#include "common/sdl3_common.h" +#endif + /* Standard reference DPI value, used when determining * DPI-aware scaling factors */ #define REFERENCE_DPI 96.0f @@ -1044,6 +1048,10 @@ void gfx_display_draw_keyboard( if (ios_keyboard_active()) return; #endif +#ifdef HAVE_SDL3 + if (sdl3_screen_keyboard_shown()) + return; +#endif gfx_display_draw_quad( p_disp, diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index f10fbf7cdfcd..84ee30f5e9e6 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -31,6 +31,10 @@ #include "../../configuration.h" #include "../../retroarch.h" +#ifdef HAVE_MENU +#include "../../menu/menu_input.h" +#endif + #include "../../gfx/common/sdl3_common.h" /* OVERLAY_MAX_TOUCH */ @@ -468,27 +472,6 @@ static bool sdl3_set_sensor_state(void *data, unsigned port, return false; } -/* Gets the SDL_Window, if it exists. */ -static SDL_Window *sdl3_input_window(void) -{ - gfx_ctx_ident_t ident_info; - - if (string_is_equal(video_driver_get_ident(), "sdl3")) - { - sdl3_video_t *video_ptr = (sdl3_video_t*)video_driver_get_ptr(); - return video_ptr != NULL ? video_ptr->window : NULL; - } - - /* gl/gl1/glcore/vulkan running on the SDL3 context drivers. They - * register their SDL_Window as the display userdata via sdl3_set_handles. */ - ident_info.ident = NULL; - video_context_driver_get_ident(&ident_info); - if (string_is_equal(ident_info.ident, "gl_sdl3") || string_is_equal(ident_info.ident, "vk_sdl3")) - return (SDL_Window*)video_driver_display_userdata_get(); - - return NULL; -} - static void sdl3_poll_mouse(sdl3_input_t *sdl) { SDL_Window *win; @@ -515,7 +498,7 @@ static void sdl3_poll_mouse(sdl3_input_t *sdl) /* SDL reports mouse coordinates in window coordinates (points), * while the video driver's viewport metrics are in output pixels. */ - if (!(win = sdl3_input_window())) + if (!(win = sdl3_get_window())) win = SDL_GetMouseFocus(); if (win) @@ -621,6 +604,64 @@ static uint16_t sdl3_translate_mod(SDL_Keymod smod) return mod; } +/* On mobile devices, SDL_StartTextInput() brings up the + * on-screen keyboard. On desktop, text input stays quietly + * enabled in the background, ensuring normal keyboard + * controls work. */ +static void sdl3_manage_text_input(void) +{ + bool want = false; + SDL_Window *win; + + if (!SDL_HasScreenKeyboardSupport() || !(win = sdl3_get_window())) + return; + +#ifdef HAVE_MENU + want = menu_input_dialog_get_display_kb(); +#endif + + if (want == SDL_TextInputActive(win)) + return; + + if (want) + { + int w, h; + SDL_Rect area; + SDL_TextInputType type = SDL_TEXTINPUT_TYPE_TEXT; + SDL_PropertiesID props = SDL_CreateProperties(); + +#ifdef HAVE_MENU + switch (menu_input_dialog_get_kb_text_type()) + { + case MENU_INPUT_DIALOG_KB_TYPE_PASSWORD: + type = SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN; + break; + case MENU_INPUT_DIALOG_KB_TYPE_NUMBER: + type = SDL_TEXTINPUT_TYPE_NUMBER; + break; + default: + break; + } +#endif + + /* Menu drivers draw the dialog's entry field in the top half of + * the screen, so keep the system keyboard/IME from covering it. + * Uses window coordinates, not pixels. */ + SDL_GetWindowSize(win, &w, &h); + area.x = 0; + area.y = 0; + area.w = w; + area.h = h / 2; + SDL_SetTextInputArea(win, &area, 0); + + SDL_SetNumberProperty(props, SDL_PROP_TEXTINPUT_TYPE_NUMBER, type); + SDL_StartTextInputWithProperties(win, props); + SDL_DestroyProperties(props); + } + else + SDL_StopTextInput(win); +} + /* Translates control/modifier keys into their ASCII character counterpart. */ static uint32_t sdl3_translate_control_key(unsigned code, uint16_t mod) { @@ -673,6 +714,7 @@ static void sdl3_input_poll(void *data) * never updates. */ SDL_PumpEvents(); + sdl3_manage_text_input(); sdl3_poll_mouse(sdl); sdl3_poll_touch(sdl); @@ -746,7 +788,7 @@ static void sdl3_input_poll(void *data) static void sdl3_grab_mouse(void *data, bool state) { - SDL_Window *win = sdl3_input_window(); + SDL_Window *win = sdl3_get_window(); if (win) { diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index d046451a8f2a..87575bf9852d 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -180,7 +180,7 @@ static int (funcname)(const char *path, const char *label, unsigned type, size_t return generic_action_ok(path, label, type, idx, entry_idx, _id, _flush); \ } -#define DEFAULT_ACTION_DIALOG_START(funcname, _label, _idx, _cb) \ +#define DEFAULT_ACTION_DIALOG_START_TYPE(funcname, _label, _idx, _cb, _text_type) \ static int (funcname)(const char *path, const char *label_setting, unsigned type, size_t idx, size_t entry_idx) \ { \ menu_input_ctx_line_t line; \ @@ -188,12 +188,16 @@ static int (funcname)(const char *path, const char *label_setting, unsigned type line.label_setting = label_setting; \ line.type = type; \ line.idx = (_idx); \ + line.text_type = (_text_type); \ line.cb = _cb; \ if (!menu_input_dialog_start(&line)) \ return -1; \ return 0; \ } +#define DEFAULT_ACTION_DIALOG_START(funcname, _label, _idx, _cb) \ + DEFAULT_ACTION_DIALOG_START_TYPE(funcname, _label, _idx, _cb, MENU_INPUT_DIALOG_KB_TYPE_TEXT) + #define DEFAULT_ACTION_OK_START_BUILTIN_CORE(funcname, _id) \ static int (funcname)(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) \ @@ -3491,6 +3495,7 @@ static int action_ok_wifi(const char *path, const char *label_setting, line.label_setting = label_setting; line.type = type; line.idx = (unsigned)idx; + line.text_type = MENU_INPUT_DIALOG_KB_TYPE_PASSWORD; line.cb = menu_input_wifi_cb; if (!menu_input_dialog_start(&line)) return -1; @@ -3930,20 +3935,22 @@ static void menu_input_st_string_cb_cheat_file_save_as( } #endif -DEFAULT_ACTION_DIALOG_START(action_ok_enable_settings, +DEFAULT_ACTION_DIALOG_START_TYPE(action_ok_enable_settings, msg_hash_to_str(MSG_INPUT_ENABLE_SETTINGS_PASSWORD), (unsigned)entry_idx, - menu_input_st_string_cb_enable_settings) + menu_input_st_string_cb_enable_settings, + MENU_INPUT_DIALOG_KB_TYPE_PASSWORD) #ifdef HAVE_CHEATS DEFAULT_ACTION_DIALOG_START(action_ok_cheat_file_save_as, msg_hash_to_str(MSG_INPUT_CHEAT_FILENAME), (unsigned)idx, menu_input_st_string_cb_cheat_file_save_as) #endif -DEFAULT_ACTION_DIALOG_START(action_ok_disable_kiosk_mode, +DEFAULT_ACTION_DIALOG_START_TYPE(action_ok_disable_kiosk_mode, msg_hash_to_str(MSG_INPUT_KIOSK_MODE_PASSWORD), (unsigned)entry_idx, - menu_input_st_string_cb_disable_kiosk_mode) + menu_input_st_string_cb_disable_kiosk_mode, + MENU_INPUT_DIALOG_KB_TYPE_PASSWORD) static int action_ok_rename_entry(const char *path, const char *label_setting, unsigned type, size_t idx, size_t entry_idx) { @@ -3955,6 +3962,7 @@ static int action_ok_rename_entry(const char *path, line.label_setting = label_setting; line.type = type; line.idx = (unsigned)entry_idx; + line.text_type = MENU_INPUT_DIALOG_KB_TYPE_TEXT; line.cb = menu_input_st_string_cb_rename_entry; if (!menu_input_dialog_start(&line)) @@ -6577,6 +6585,7 @@ static int action_ok_add_entry_to_new_playlist(const char *path, line.label_setting = NULL; line.type = 0; line.idx = 0; + line.text_type = MENU_INPUT_DIALOG_KB_TYPE_TEXT; line.cb = (string_is_equal(label, (char*)MENU_ENUM_LABEL_CREATE_NEW_PLAYLIST_STR) ? action_input_add_entry_to_new_playlist : action_input_add_entry_to_new_playlist_quickmenu); diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 2760d7d3095a..903587543dba 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -88,6 +88,10 @@ #include "../ui/drivers/cocoa/apple_platform.h" #endif +#ifdef HAVE_SDL3 +#include "../gfx/common/sdl3_common.h" +#endif + typedef struct menu_input_ctx_bind { char *s; @@ -4548,6 +4552,7 @@ void menu_input_dialog_end(void) struct menu_state *menu_st = &menu_driver_state; menu_st->input_dialog_kb_type = 0; menu_st->input_dialog_kb_idx = 0; + menu_st->input_dialog_kb_text_type = MENU_INPUT_DIALOG_KB_TYPE_TEXT; menu_st->flags &= ~MENU_ST_FLAG_INP_DLG_KB_DISPLAY; menu_st->input_dialog_kb_label[0] = '\0'; menu_st->input_dialog_kb_label_setting[0] = '\0'; @@ -5127,7 +5132,7 @@ MENU_NOINLINE static bool menu_input_key_bind_iterate( * and input_event_osk_append() calls input_keyboard_line_append(), * which can realloc the buffer out from under state the native path * is holding. Steam's OSK already had this guard open-coded at the - * two call sites; the iOS native keyboard needs the same. */ + * two call sites; the iOS native keyboard and SDL3 need the same. */ static bool menu_input_native_kb_active(void) { #ifdef HAVE_MIST @@ -5137,10 +5142,19 @@ static bool menu_input_native_kb_active(void) #ifdef HAVE_COCOATOUCH if (ios_keyboard_active()) return true; +#endif +#ifdef HAVE_SDL3 + if (sdl3_screen_keyboard_shown()) + return true; #endif return false; } +enum menu_input_dialog_kb_text_type menu_input_dialog_get_kb_text_type(void) +{ + return menu_driver_state.input_dialog_kb_text_type; +} + bool menu_input_dialog_get_display_kb(void) { struct menu_state *menu_st = &menu_driver_state; @@ -8431,8 +8445,9 @@ bool menu_input_dialog_start(menu_input_ctx_line_t *line) line->label_setting, sizeof(menu_st->input_dialog_kb_label_setting)); - menu_st->input_dialog_kb_type = line->type; - menu_st->input_dialog_kb_idx = line->idx; + menu_st->input_dialog_kb_type = line->type; + menu_st->input_dialog_kb_idx = line->idx; + menu_st->input_dialog_kb_text_type = line->text_type; input_keyboard_line_free(input_st); diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 5f2659bf924b..34fb736807b5 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -527,6 +527,7 @@ struct menu_state /* unsigned alignment */ unsigned input_dialog_kb_type; unsigned input_dialog_kb_idx; + enum menu_input_dialog_kb_text_type input_dialog_kb_text_type; unsigned input_driver_flushing_input; menu_dialog_t dialog_st; enum menu_action prev_action; diff --git a/menu/menu_explore.c b/menu/menu_explore.c index a835613bd457..69beb01d2039 100644 --- a/menu/menu_explore.c +++ b/menu/menu_explore.c @@ -1014,6 +1014,7 @@ static int explore_action_ok_find(const char *path, const char *label, line.label_setting = NULL; line.type = 0; line.idx = 0; + line.text_type = MENU_INPUT_DIALOG_KB_TYPE_TEXT; line.cb = explore_action_find_complete; menu_input_dialog_start(&line); return 0; @@ -1225,6 +1226,7 @@ static int explore_action_ok_saveview(const char *path, const char *label, line.label_setting = NULL; line.type = 0; line.idx = 0; + line.text_type = MENU_INPUT_DIALOG_KB_TYPE_TEXT; line.cb = explore_action_saveview_complete; menu_input_dialog_start(&line); return 0; diff --git a/menu/menu_input.h b/menu/menu_input.h index a02f73b86586..1d95e4c8c2dd 100644 --- a/menu/menu_input.h +++ b/menu/menu_input.h @@ -239,12 +239,21 @@ typedef struct key_desc char desc[32]; } key_desc_t; +/* Determines the type of input that's expected for the keyboard dialog. */ +enum menu_input_dialog_kb_text_type +{ + MENU_INPUT_DIALOG_KB_TYPE_TEXT = 0, + MENU_INPUT_DIALOG_KB_TYPE_PASSWORD, /**< Hides typed input. */ + MENU_INPUT_DIALOG_KB_TYPE_NUMBER +}; + typedef struct menu_input_ctx_line { const char *label; const char *label_setting; unsigned type; unsigned idx; + enum menu_input_dialog_kb_text_type text_type; input_keyboard_line_complete_t cb; } menu_input_ctx_line_t; @@ -264,6 +273,10 @@ bool menu_input_dialog_start_search(void); bool menu_input_dialog_get_display_kb(void); +/* Text type of the currently open keyboard dialog; + * MENU_INPUT_DIALOG_KB_TYPE_TEXT when no dialog is open. */ +enum menu_input_dialog_kb_text_type menu_input_dialog_get_kb_text_type(void); + void menu_input_dialog_end(void); /* TODO/FIXME - public global variables */ diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 954afd45d29f..80dbe9477d2f 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -674,6 +674,7 @@ static int setting_generic_action_ok_linefeed( { menu_input_ctx_line_t line; input_keyboard_line_complete_t cb = NULL; + enum menu_input_dialog_kb_text_type text_type = MENU_INPUT_DIALOG_KB_TYPE_TEXT; if (!setting) return -1; @@ -685,9 +686,11 @@ static int setting_generic_action_ok_linefeed( case ST_SIZE: case ST_UINT: cb = menu_input_st_uint_cb; + text_type = MENU_INPUT_DIALOG_KB_TYPE_NUMBER; break; case ST_INT: cb = menu_input_st_int_cb; + text_type = MENU_INPUT_DIALOG_KB_TYPE_NUMBER; break; case ST_FLOAT: cb = menu_input_st_float_cb; @@ -695,6 +698,8 @@ static int setting_generic_action_ok_linefeed( case ST_STRING: case ST_STRING_OPTIONS: cb = menu_input_st_string_cb; + if (setting->ui_type == ST_UI_TYPE_PASSWORD_LINE_EDIT) + text_type = MENU_INPUT_DIALOG_KB_TYPE_PASSWORD; break; default: break; @@ -704,6 +709,7 @@ static int setting_generic_action_ok_linefeed( line.label_setting = setting->name; line.type = 0; line.idx = 0; + line.text_type = text_type; line.cb = cb; if (!menu_input_dialog_start(&line)) @@ -3009,6 +3015,7 @@ static int setting_action_ok_color_rgb(rarch_setting_t *setting, size_t idx, line.label_setting = setting->name; line.type = 0; line.idx = 0; + line.text_type = MENU_INPUT_DIALOG_KB_TYPE_TEXT; line.cb = setting_action_ok_color_rgb_cb; if (!menu_input_dialog_start(&line)) diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index d616aabf3335..040457773803 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -1321,6 +1321,7 @@ bool netplay_handshake_init(netplay_t *netplay, retroarch_menu_running(); line.label = msg_hash_to_str(MSG_NETPLAY_ENTER_PASSWORD); line.label_setting = "no_setting"; + line.text_type = MENU_INPUT_DIALOG_KB_TYPE_PASSWORD; line.cb = handshake_password; if (!menu_input_dialog_start(&line)) return false;