diff --git a/configuration.c b/configuration.c index 814d036dd4..522becf719 100644 --- a/configuration.c +++ b/configuration.c @@ -1430,6 +1430,9 @@ static struct config_array_setting *populate_settings_array(settings_t *settings SETTING_ARRAY("input_driver", settings->arrays.input_driver, false, NULL, true); SETTING_ARRAY("input_joypad_driver", settings->arrays.input_joypad_driver, false, NULL, true); SETTING_ARRAY("input_keyboard_layout", settings->arrays.input_keyboard_layout, false, NULL, true); +#ifdef ANDROID + SETTING_ARRAY("input_android_physical_keyboard", settings->arrays.input_android_physical_keyboard, false, NULL, true); +#endif SETTING_ARRAY("led_driver", settings->arrays.led_driver, false, NULL, true); SETTING_ARRAY("netplay_mitm_server", settings->arrays.netplay_mitm_server, false, NULL, true); SETTING_ARRAY("midi_driver", settings->arrays.midi_driver, false, NULL, true); diff --git a/configuration.h b/configuration.h index 1d0bdfef25..0a27999d68 100644 --- a/configuration.h +++ b/configuration.h @@ -434,6 +434,10 @@ typedef struct settings char input_keyboard_layout[64]; +#ifdef ANDROID + char input_android_physical_keyboard[255]; +#endif + char audio_device[255]; char camera_device[255]; char netplay_mitm_server[255]; diff --git a/input/drivers/android_input.c b/input/drivers/android_input.c index c33d09c298..b98bcd985e 100644 --- a/input/drivers/android_input.c +++ b/input/drivers/android_input.c @@ -95,15 +95,6 @@ uint8_t *android_keyboard_state_get(unsigned port) return android_key_state[port]; } -static void android_keyboard_free(void) -{ - unsigned i, j; - - for (i = 0; i < DEFAULT_MAX_PADS; i++) - for (j = 0; j < MAX_KEYS; j++) - android_key_state[i][j] = 0; -} - /* TODO/FIXME - * fix game focus toggle */ @@ -192,6 +183,20 @@ static typeof(AMotionEvent_getButtonState) *p_AMotionEvent_getButtonState; static void *libandroid_handle; #endif +static void android_keyboard_free(void) +{ + unsigned i, j; + + for (i = 0; i < DEFAULT_MAX_PADS; i++) + for (j = 0; j < MAX_KEYS; j++) + android_key_state[i][j] = 0; + + for (i = 0; i < (unsigned) kbd_num; i++) + kbd_id[i] = -1; + + kbd_num = 0; +} + static bool android_input_lookup_name_prekitkat(char *buf, int *vendorId, int *productId, size_t size, int id) { @@ -297,6 +302,59 @@ static bool android_input_lookup_name(char *buf, return true; } +static bool android_input_can_be_keyboard_jni(int id) +{ + jmethodID getKeyboardType = NULL; + jobject device = NULL; + jint keyboard_type = -1; + jmethodID method = NULL; + jclass class = NULL; + const char *str = NULL; + JNIEnv *env = (JNIEnv*)jni_thread_getenv(); + + if (!env) + return false; + + FIND_CLASS(env, class, "android/view/InputDevice"); + if (!class) + return false; + + GET_STATIC_METHOD_ID(env, method, class, "getDevice", + "(I)Landroid/view/InputDevice;"); + if (!method) + return false; + + CALL_OBJ_STATIC_METHOD_PARAM(env, device, class, method, (jint)id); + if (!device) + return false; + + GET_METHOD_ID(env, getKeyboardType, class, "getKeyboardType", "()I"); + if (!getKeyboardType) + return false; + + CALL_INT_METHOD(env, keyboard_type, device, getKeyboardType); + if (keyboard_type < 0) + return false; + + return keyboard_type == AINPUT_KEYBOARD_TYPE_ALPHABETIC; +} + +bool android_input_can_be_keyboard(void *data, int port) +{ + android_input_t *android = (android_input_t *) data; + if (!android) + return false; + + if (port < 0 || port >= android->pads_connected) + return false; + + state_device_t *device = &android->pad_states[port]; + if (!device->id && string_is_empty(device->name)) + return false; + + return android_input_can_be_keyboard_jni(device->id); +} + static void android_input_poll_main_cmd(void) { int8_t cmd; @@ -867,6 +925,55 @@ static int android_input_recover_port(android_input_t *android, int id) } +static bool is_configured_as_physical_keyboard(int vendor_id, int product_id, const char *device_name) +{ + settings_t *settings = config_get_ptr(); + + char keyboard_name[sizeof(settings->arrays.input_android_physical_keyboard)]; + int keyboard_vendor_id; + int keyboard_product_id; + + bool is_keyboard; + bool compare_by_id; + if (sscanf(settings->arrays.input_android_physical_keyboard, "%04x:%04x ", &keyboard_vendor_id, &keyboard_product_id) != 2) + { + strlcpy(keyboard_name, settings->arrays.input_android_physical_keyboard, sizeof(keyboard_name)); + is_keyboard = string_is_equal(device_name, keyboard_name); + compare_by_id = false; + } + else + { + is_keyboard = vendor_id == keyboard_vendor_id && product_id == keyboard_product_id; + compare_by_id = true; + } + + if (is_keyboard) + { + /* + * Check that there is not already a similar physical keyboard attached + * attached to the system + */ + for (int i = 0; i < kbd_num; i++) + { + char kbd_device_name[256] = { 0 }; + int kbd_vendor_id = 0; + int kbd_product_id = 0; + + if (!engine_lookup_name(kbd_device_name, &kbd_vendor_id, + &kbd_product_id, sizeof(kbd_device_name), kbd_id[i])) + return false; + + if (compare_by_id && vendor_id == kbd_vendor_id && product_id == kbd_product_id) + return false; + + if (!compare_by_id && string_is_equal(device_name, kbd_device_name)) + return false; + } + return true; + } + return false; +} + static void handle_hotplug(android_input_t *android, struct android_app *android_app, int *port, int id, int source) @@ -1146,6 +1253,18 @@ static void handle_hotplug(android_input_t *android, return; } + /* If the device is a keyboard, didn't match any of the devices above + * and is designated as the physical keyboard, then assume it is a keyboard, + * register the id, and return unless the + * maximum number of keyboards are already registered. */ + else if ((source & AINPUT_SOURCE_KEYBOARD) && kbd_num < MAX_NUM_KEYBOARDS && + is_configured_as_physical_keyboard(vendorId, productId, device_name)) + { + kbd_id[kbd_num] = id; + kbd_num++; + return; + } + /* if device was not keyboard only, yet did not match any of the devices * then try to autoconfigure as gamepad based on device_name. */ else if (!string_is_empty(device_name)) diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index a8ced36c62..3cf6d10539 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -778,6 +778,12 @@ MSG_HASH( MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, "deferred_dropdown_box_list_input_device_type" ) +#ifdef ANDROID +MSG_HASH( + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, + "deferred_dropdown_box_list_input_select_physical_keyboard" +) +#endif MSG_HASH( MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, "deferred_dropdown_box_list_input_description" @@ -1560,6 +1566,12 @@ MSG_HASH( "input_nowinkey_enable" ) #endif +#ifdef ANDROID +MSG_HASH( + MENU_ENUM_LABEL_INPUT_SELECT_PHYSICAL_KEYBOARD, + "input_android_physical_keyboard" +) +#endif MSG_HASH( MENU_ENUM_LABEL_INPUT_SENSORS_ENABLE, "input_sensors_enable" @@ -1758,6 +1770,7 @@ MSG_HASH( MENU_ENUM_LABEL_INPUT_SMALL_KEYBOARD_ENABLE, "input_small_keyboard_enable" ) + MSG_HASH( MENU_ENUM_LABEL_INPUT_TOUCH_ENABLE, "input_touch_enable" diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index e41edb7de1..e04f2ba3f0 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -663,6 +663,18 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_HELP_GAMEMODE_ENABLE), len); break; #endif + +#ifdef ANDROID + case MENU_ENUM_LABEL_INPUT_SELECT_PHYSICAL_KEYBOARD: + snprintf(s, len, + "If RetroArch identifies a hardware keyboard as some kind of\n" + "gamepad, this setting can be used to force RetroArch to treat\n" + "the misidentified device as a keyboard.\n" + "This can be useful if you are trying to emulate a computer in some\n" + "Android TV device and also own a physical keyboard that can be\n" + "attached to the box.\n"); + break; +#endif default: if (string_is_empty(s)) strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE), len); diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 5c6d817578..2f0a9eae4b 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -2676,6 +2676,16 @@ MSG_HASH( "Keep Win-key combinations inside the application." ) #endif +#ifdef ANDROID +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_SELECT_PHYSICAL_KEYBOARD, + "Select physical keyboard" +) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_SELECT_PHYSICAL_KEYBOARD, + "Use this device as a physical keyboard and not as a gamepad." +) +#endif MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_SENSORS_ENABLE, "Auxiliary Sensor Input" diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index 54aae7f858..b10fa87816 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -644,6 +644,10 @@ GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_disk_index, PUSH_D GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_device_type, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_DEVICE_TYPE) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_description, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_description_kbd, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION_KBD) +#ifdef ANDROID +GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_select_physical_keyboard, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD) +#endif + #ifdef HAVE_NETWORKING GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_netplay_mitm_server, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_NETPLAY_MITM_SERVER) #endif @@ -681,6 +685,9 @@ static int menu_cbs_init_bind_deferred_push_compare_label( {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, deferred_push_dropdown_box_list_input_device_type}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, deferred_push_dropdown_box_list_input_description}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD, deferred_push_dropdown_box_list_input_description_kbd}, +#ifdef ANDROID + {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, deferred_push_dropdown_box_list_input_select_physical_keyboard}, +#endif #ifdef HAVE_NETWORKING {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_NETPLAY_MITM_SERVER, deferred_push_dropdown_box_list_netplay_mitm_server}, #endif diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 6db4b412e7..3cd4a0fcbd 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -301,6 +301,10 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION; case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD: return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD; +#ifdef ANDROID + case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD: + return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD; +#endif #ifdef HAVE_NETWORKING case ACTION_OK_DL_DROPDOWN_BOX_LIST_NETPLAY_MITM_SERVER: return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_NETPLAY_MITM_SERVER; @@ -796,6 +800,17 @@ int generic_action_ok_displaylist_push(const char *path, info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE; dl_type = DISPLAYLIST_GENERIC; break; +#ifdef ANDROID + case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD: + info.type = type; + info.directory_ptr = idx; + info_path = path; + info_label = msg_hash_to_str( + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD); + info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD; + dl_type = DISPLAYLIST_GENERIC; + break; +#endif case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION: info.type = type; info.directory_ptr = idx; @@ -6779,6 +6794,63 @@ static int action_ok_push_dropdown_item_input_device_type(const char *path, return action_cancel_pop_default(NULL, NULL, 0, 0); } +#ifdef ANDROID +static int action_ok_push_dropdown_item_input_select_physical_keyboard(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + settings_t *settings = config_get_ptr(); + + const char *menu_path = NULL; + enum msg_hash_enums enum_idx; + rarch_setting_t *setting; + menu_entries_get_last_stack(&menu_path, NULL, NULL, NULL, NULL); + enum_idx = (enum msg_hash_enums)atoi(menu_path); + setting = menu_setting_find_enum(enum_idx); + + if (!setting) + return -1; + + char* keyboard; + const char* keyboard_name = path; + const char* no_keyboard = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE); + if (string_is_equal(keyboard_name, no_keyboard)) + settings->arrays.input_android_physical_keyboard[0] = '\0'; + else + { + for (int i = 0; i < MAX_INPUT_DEVICES; i++) + { + const char* device_name = input_config_get_device_name(i); + if (string_is_equal(device_name, keyboard_name)) + { + uint16_t vendor_id = input_config_get_device_vid(i); + uint16_t product_id = input_config_get_device_pid(i); + snprintf(settings->arrays.input_android_physical_keyboard, + sizeof(settings->arrays.input_android_physical_keyboard), + "%04x:%04x %s", + vendor_id, product_id, keyboard_name); + break; + } + } + /* + * if we did not find the selected device, do nothing, the user has chosen to keep + * the previous configuration, which is to use a device that is either not plugged right + * now or already working as the physical keyboard. + */ + } + settings->modified = true; + + command_event(CMD_EVENT_REINIT, NULL); + + /* Refresh menu */ + bool refresh = false; + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); + menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); + + return action_cancel_pop_default(NULL, NULL, 0, 0); +} + +#endif + static int action_ok_push_dropdown_item_input_description(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { @@ -8614,6 +8686,11 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs, case MENU_SETTING_DROPDOWN_ITEM_INPUT_DEVICE_TYPE: BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_input_device_type); break; +#ifdef ANDROID + case MENU_SETTING_DROPDOWN_ITEM_INPUT_SELECT_PHYSICAL_KEYBOARD: + BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_input_select_physical_keyboard); + break; +#endif case MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION: BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_input_description); break; diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 749c831c7d..342bfdcb8a 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -711,6 +711,9 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_autodetect_enable, MENU_ #if defined(HAVE_DINPUT) || defined(HAVE_WINRAWINPUT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_nowinkey_enable, MENU_ENUM_SUBLABEL_INPUT_NOWINKEY_ENABLE) #endif +#ifdef ANDROID +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_select_physical_keyboard, MENU_ENUM_SUBLABEL_INPUT_SELECT_PHYSICAL_KEYBOARD) +#endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_sensors_enable, MENU_ENUM_SUBLABEL_INPUT_SENSORS_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_auto_mouse_grab, MENU_ENUM_SUBLABEL_INPUT_AUTO_MOUSE_GRAB) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_auto_game_focus, MENU_ENUM_SUBLABEL_INPUT_AUTO_GAME_FOCUS) @@ -3611,6 +3614,11 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_INPUT_NOWINKEY_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_nowinkey_enable); break; +#endif +#ifdef ANDROID + case MENU_ENUM_LABEL_INPUT_SELECT_PHYSICAL_KEYBOARD: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_select_physical_keyboard); + break; #endif case MENU_ENUM_LABEL_INPUT_SENSORS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_sensors_enable); diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index 2d7c7c2b53..417110f9b0 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -1813,6 +1813,9 @@ int menu_cbs_init_bind_title(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, action_get_title_dropdown_item}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, action_get_title_dropdown_input_description}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD, action_get_title_dropdown_input_description_kbd}, +#ifdef ANDROID + {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, action_get_title_dropdown_item}, +#endif #ifdef HAVE_NETWORKING {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_NETPLAY_MITM_SERVER, action_get_title_dropdown_item}, #endif diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 9d0eba1dee..76571847d1 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -53,6 +53,9 @@ enum ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD, +#ifdef ANDROID + ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, +#endif #ifdef HAVE_NETWORKING ACTION_OK_DL_DROPDOWN_BOX_LIST_NETPLAY_MITM_SERVER, #endif diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 30237b1715..4c9fed861f 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5160,6 +5160,131 @@ end: return count; } +#ifdef ANDROID +static int menu_displaylist_parse_input_select_physical_keyboard_list( + menu_displaylist_info_t *info, settings_t *settings) +{ + char device_label[128]; + const char *val_disabled = NULL; + rarch_system_info_t *system = &runloop_state_get_ptr()->system; + enum msg_hash_enums enum_idx = (enum msg_hash_enums)atoi(info->path); + rarch_setting_t *setting = menu_setting_find_enum(enum_idx); + size_t menu_index = 0; + unsigned count = 0; + + int i = 0; + char keyboard[sizeof(settings->arrays.input_android_physical_keyboard)]; + bool keyboard_added = false; + + input_driver_state_t *st = input_state_get_ptr(); + input_driver_t *current_input = st->current_driver; + bool is_android_driver = string_is_equal(current_input->ident, "android"); + + device_label[0] = '\0'; + + if (!system || !settings || !setting || !is_android_driver) + goto end; + + val_disabled = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE); + if (string_is_empty(settings->arrays.input_android_physical_keyboard)) + strlcpy(keyboard, val_disabled, sizeof(keyboard)); + else + { + unsigned int vendor_id; + unsigned int product_id; + if (sscanf(settings->arrays.input_android_physical_keyboard, "%04x:%04x ", &vendor_id, &product_id) != 2) + strlcpy(keyboard, settings->arrays.input_android_physical_keyboard, sizeof(keyboard)); + else + /* If the vendor_id:product_id is encoded in the name, ignore them. */ + strlcpy(keyboard, &settings->arrays.input_android_physical_keyboard[10], sizeof(keyboard)); + } + + for (i = MAX_INPUT_DEVICES; i >= -1; --i) + { + device_label[0] = '\0'; + + if (i < 0) + strlcpy(device_label, keyboard, sizeof(device_label)); + else if (i == MAX_INPUT_DEVICES) + strlcpy(device_label, val_disabled, sizeof(device_label)); + else if (i < MAX_INPUT_DEVICES) + { + /* + * Skip devices that do not look like keyboards + */ + if (!android_input_can_be_keyboard(st->current_data, i)) + continue; + + const char *device_name = input_config_get_device_display_name(i) ? + input_config_get_device_display_name(i) : input_config_get_device_name(i); + + if (!string_is_empty(device_name)) + { + unsigned idx = input_config_get_device_name_index(i); + size_t _len = strlcpy(device_label, device_name, + sizeof(device_label)); + + /*if idx is non-zero, it's part of a set*/ + if (idx > 0) + snprintf(device_label + _len, + sizeof(device_label) - _len, " (#%u)", idx); + } + } + + if (!string_is_empty(device_label)) + { + size_t previous_position; + if (file_list_search(info->list, device_label, &previous_position)) + continue; + + /* Add menu entry */ + if (menu_entries_append(info->list, + device_label, + device_label, + MSG_UNKNOWN, + MENU_SETTING_DROPDOWN_ITEM_INPUT_SELECT_PHYSICAL_KEYBOARD, + 0, menu_index, NULL)) + { + /* Add checkmark if input is currently + * mapped to this entry */ + if (string_is_equal(device_label, keyboard)) + { + menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)info->list->list[menu_index].actiondata; + if (cbs) + cbs->checked = true; + menu_navigation_set_selection(menu_index); + keyboard_added = true; + } + count++; + menu_index++; + } + } + } + + /* if nothing is configured, select None by default */ + if (!keyboard_added) + { + menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)info->list->list[0].actiondata; + if (cbs) + cbs->checked = true; + menu_navigation_set_selection(0); + } + + end: + /* Fallback */ + if (count == 0) + if (menu_entries_append(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY), + msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY), + MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY, + FILE_TYPE_NONE, 0, 0, NULL)) + count++; + + return count; +} + +#endif + static int menu_displaylist_parse_input_description_list( menu_displaylist_info_t *info, settings_t *settings) { @@ -7007,6 +7132,11 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_ANDROID_INPUT_DISCONNECT_WORKAROUND, PARSE_ONLY_BOOL, false) == 0) count++; + + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_INPUT_SELECT_PHYSICAL_KEYBOARD, + PARSE_ACTION, true) == 0) + count++; #endif if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_INPUT_SENSORS_ENABLE, @@ -12966,6 +13096,14 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, info->flags |= MD_FLAG_NEED_REFRESH | MD_FLAG_NEED_PUSH; break; +#ifdef ANDROID + case DISPLAYLIST_DROPDOWN_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD: + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + count = menu_displaylist_parse_input_select_physical_keyboard_list(info, settings); + info->flags |= MD_FLAG_NEED_REFRESH + | MD_FLAG_NEED_PUSH; + break; +#endif case DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); count = menu_displaylist_parse_input_description_list(info, settings); diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 778e0f55a6..508efd2575 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -71,6 +71,9 @@ enum menu_displaylist_ctl_state DISPLAYLIST_DROPDOWN_LIST_INPUT_DEVICE_TYPE, DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION, DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION_KBD, +#ifdef ANDROID + DISPLAYLIST_DROPDOWN_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, +#endif #ifdef HAVE_NETWORKING DISPLAYLIST_DROPDOWN_LIST_NETPLAY_MITM_SERVER, #endif diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 49f25dc044..e2ad966258 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -108,6 +108,9 @@ enum menu_settings_type MENU_SETTING_DROPDOWN_ITEM_DISK_INDEX, MENU_SETTING_DROPDOWN_ITEM_INPUT_DEVICE_TYPE, MENU_SETTING_DROPDOWN_ITEM_INPUT_DEVICE_INDEX, +#ifdef ANDROID + MENU_SETTING_DROPDOWN_ITEM_INPUT_SELECT_PHYSICAL_KEYBOARD, +#endif MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION, MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION_KBD, #ifdef HAVE_NETWORKING diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 9f66c6f4f9..78169a5bdb 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -2888,6 +2888,24 @@ static int setting_action_ok_libretro_device_type( return 0; } +#ifdef ANDROID +static int setting_action_ok_select_physical_keyboard( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + char enum_idx[16]; + if (!setting) + return -1; + + snprintf(enum_idx, sizeof(enum_idx), "%d", setting->enum_idx); + + generic_action_ok_displaylist_push( + enum_idx, /* we will pass the enumeration index of the string as a path */ + NULL, NULL, 0, idx, 0, + ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD); + return 0; +} +#endif + static int setting_string_action_left_string_options( rarch_setting_t* setting, size_t idx, bool wraparound) { @@ -6845,6 +6863,26 @@ static void setting_get_string_representation_input_touch_scale(rarch_setting_t snprintf(s, len, "x%d", *setting->value.target.unsigned_integer); } +#ifdef ANDROID +static void setting_get_string_representation_android_physical_keyboard( + rarch_setting_t *setting, + char *s, size_t len) +{ + if (!setting) + return; + + settings_t *settings = config_get_ptr(); + + int keyboard_vendor_id; + int keyboard_product_id; + + if (sscanf(setting->value.target.string, "%04x:%04x ", &keyboard_vendor_id, &keyboard_product_id) != 2) + strlcpy(s, setting->value.target.string, len); + else + strlcpy(s, &setting->value.target.string[10], len); +} +#endif + #ifdef HAVE_LANGEXTRA static void setting_get_string_representation_uint_user_language( rarch_setting_t *setting, @@ -7850,6 +7888,11 @@ static void general_read_handler(rarch_setting_t *setting) case MENU_ENUM_LABEL_INPUT_PLAYER5_JOYPAD_INDEX: *setting->value.target.integer = settings->uints.input_joypad_index[setting->enum_idx - MENU_ENUM_LABEL_INPUT_PLAYER1_JOYPAD_INDEX]; break; +#ifdef ANDROID + case MENU_ENUM_LABEL_INPUT_SELECT_PHYSICAL_KEYBOARD: + setting->value.target.string = settings->arrays.input_android_physical_keyboard; + break; +#endif default: break; } @@ -8170,6 +8213,15 @@ static void general_write_handler(rarch_setting_t *setting) settings->uints.input_joypad_index[setting->enum_idx - MENU_ENUM_LABEL_INPUT_PLAYER1_JOYPAD_INDEX] = *setting->value.target.integer; } break; +#ifdef ANDROID + case MENU_ENUM_LABEL_INPUT_SELECT_PHYSICAL_KEYBOARD: + { + settings_t *settings = config_get_ptr(); + settings->modified = true; + strlcpy(settings->arrays.input_android_physical_keyboard, setting->value.target.string, sizeof(settings->arrays.input_android_physical_keyboard)); + } + break; +#endif case MENU_ENUM_LABEL_LOG_TO_FILE: if (verbosity_is_enabled()) { @@ -14064,6 +14116,24 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE ); + + input_driver_state_t *st = input_state_get_ptr(); + input_driver_t *current_input = st->current_driver; + if (string_is_equal(current_input->ident, "android")) + { + CONFIG_ACTION( + list, list_info, + MENU_ENUM_LABEL_INPUT_SELECT_PHYSICAL_KEYBOARD, + MENU_ENUM_LABEL_VALUE_INPUT_SELECT_PHYSICAL_KEYBOARD, + &group_info, + &subgroup_info, + parent_group); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_select_physical_keyboard; + (*list)[list_info->index - 1].read_handler = &general_read_handler; + (*list)[list_info->index - 1].change_handler = &general_write_handler; + (*list)[list_info->index - 1].get_string_representation = &setting_get_string_representation_android_physical_keyboard; + (*list)[list_info->index - 1].default_value.string = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE); + } #endif CONFIG_UINT( diff --git a/msg_hash.h b/msg_hash.h index a78605feb9..0326032c8d 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1080,6 +1080,11 @@ enum msg_hash_enums #if defined(HAVE_DINPUT) || defined(HAVE_WINRAWINPUT) MENU_LABEL(INPUT_NOWINKEY_ENABLE), #endif + +#ifdef ANDROID + MENU_LABEL(INPUT_SELECT_PHYSICAL_KEYBOARD), +#endif + MENU_LABEL(INPUT_SENSORS_ENABLE), MENU_LABEL(INPUT_AUTO_MOUSE_GRAB), @@ -1710,6 +1715,9 @@ enum msg_hash_enums MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD, +#ifdef ANDROID + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, +#endif MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_NETPLAY_MITM_SERVER, MENU_ENUM_LABEL_DEFERRED_MIXER_STREAM_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_CONFIGURATIONS_LIST,