From 01413018c36e90791d7b3de5dc3fcac2b1fc1aca Mon Sep 17 00:00:00 2001 From: hae-carrie-perez Date: Thu, 19 Mar 2026 23:55:14 -0400 Subject: [PATCH 1/2] Fixes: https://bugs.dolphin-emu.org/issues/14001 Fixes: https://bugs.dolphin-emu.org/issues/14001 Android: Add persistent controller labeling using device descriptors --- .../input/model/ControllerInterface.kt | 2 + .../settings/ui/SettingsFragmentPresenter.kt | 77 +- .../app/src/main/res/values/strings.xml | 2 + .../gradle/gradle-daemon-jvm.properties | 12 + .../ControllerInterface/Android/Android.cpp | 2043 +++++++++-------- 5 files changed, 1111 insertions(+), 1025 deletions(-) create mode 100644 Source/Android/gradle/gradle-daemon-jvm.properties diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt index 71b37ff513..ff9fa58844 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt @@ -99,6 +99,8 @@ object ControllerInterface { external fun getDevice(deviceString: String): CoreDevice? + external fun getDescriptorForDevice(deviceString: String): String + private fun onInputStateChanged() { // When a single SensorEvent is dispatched, this method is likely to get called many times. // For the sake of performance, let's batch input state updates so that observers only have diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt index 1f68bbbd1a..1110f7a0f4 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt @@ -32,6 +32,8 @@ import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialogPresenter import org.dolphinemu.dolphinemu.features.settings.model.* import org.dolphinemu.dolphinemu.features.settings.model.view.* import org.dolphinemu.dolphinemu.features.settings.model.AchievementModel.logout +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface +import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting import org.dolphinemu.dolphinemu.model.GpuDriverMetadata import org.dolphinemu.dolphinemu.utils.* import kotlin.collections.ArrayList @@ -502,16 +504,16 @@ class SettingsFragmentPresenter( override val isOverridden: Boolean get() = BooleanSetting.MAIN_DSP_HLE.isOverridden || - BooleanSetting.MAIN_DSP_JIT.isOverridden + BooleanSetting.MAIN_DSP_JIT.isOverridden override val isRuntimeEditable: Boolean get() = BooleanSetting.MAIN_DSP_HLE.isRuntimeEditable && - BooleanSetting.MAIN_DSP_JIT.isRuntimeEditable + BooleanSetting.MAIN_DSP_JIT.isRuntimeEditable override fun delete(settings: Settings): Boolean { // Not short circuiting return BooleanSetting.MAIN_DSP_HLE.delete(settings) and - BooleanSetting.MAIN_DSP_JIT.delete(settings) + BooleanSetting.MAIN_DSP_JIT.delete(settings) } } @@ -970,8 +972,8 @@ class SettingsFragmentPresenter( 0, false ) { - fragmentView.showDialogFragment(LoginDialog(this)) - loadSettingsList() + fragmentView.showDialogFragment(LoginDialog(this)) + loadSettingsList() }) } else { sl.add( @@ -983,8 +985,8 @@ class SettingsFragmentPresenter( 0, false ) { - logout() - loadSettingsList() + logout() + loadSettingsList() }) } sl.add( @@ -1081,16 +1083,16 @@ class SettingsFragmentPresenter( override val isOverridden: Boolean get() = BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.isOverridden || - BooleanSetting.MAIN_SYNC_GPU.isOverridden + BooleanSetting.MAIN_SYNC_GPU.isOverridden override val isRuntimeEditable: Boolean get() = BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.isRuntimeEditable && - BooleanSetting.MAIN_SYNC_GPU.isRuntimeEditable + BooleanSetting.MAIN_SYNC_GPU.isRuntimeEditable override fun delete(settings: Settings): Boolean { // Not short circuiting return BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.delete(settings) and - BooleanSetting.MAIN_SYNC_GPU.delete(settings) + BooleanSetting.MAIN_SYNC_GPU.delete(settings) } } @@ -2207,7 +2209,7 @@ class SettingsFragmentPresenter( BooleanSetting.MAIN_DEBUG_JIT_ENABLE_PROFILING, R.string.debug_jit_enable_block_profiling, 0 - ) + ) ) sl.add( RunRunnable( @@ -2545,6 +2547,49 @@ class SettingsFragmentPresenter( controller ) ) + sl.add( + InputStringSetting( + context, + object : AbstractStringSetting { + override val isOverridden: Boolean = false + override val isRuntimeEditable: Boolean = true + + override fun delete(settings: Settings): Boolean { + val descriptor = ControllerInterface.getDescriptorForDevice( + controller.getDefaultDevice() + ) + val key = if (descriptor.isNotEmpty()) descriptor else controller.getDefaultDevice() + NativeConfig.deleteKey(NativeConfig.LAYER_BASE, "Dolphin", "ControllerLabels", key) + NativeConfig.save(NativeConfig.LAYER_BASE) + return true + } + + override val string: String + get() { + val descriptor = ControllerInterface.getDescriptorForDevice( + controller.getDefaultDevice() + ) + val key = if (descriptor.isNotEmpty()) descriptor else controller.getDefaultDevice() + return NativeConfig.getString(NativeConfig.LAYER_BASE, "Dolphin", "ControllerLabels", key, "") + } + + override fun setString(settings: Settings, newValue: String) { + val descriptor = ControllerInterface.getDescriptorForDevice( + controller.getDefaultDevice() + ) + val key = if (descriptor.isNotEmpty()) descriptor else controller.getDefaultDevice() + if (newValue.isEmpty()) { + NativeConfig.deleteKey(NativeConfig.LAYER_BASE, "Dolphin", "ControllerLabels", key) + } else { + NativeConfig.setString(NativeConfig.LAYER_BASE, "Dolphin", "ControllerLabels", key, newValue) + } + NativeConfig.save(NativeConfig.LAYER_BASE) + } + }, + R.string.input_controller_label, + R.string.input_controller_label_description + ) + ) sl.add(SwitchSetting(context, object : AbstractBooleanSetting { override val isOverridden: Boolean = false @@ -2603,11 +2648,11 @@ class SettingsFragmentPresenter( * @param groupTypeFilter If this is non-null, only groups whose types match this are considered. */ private fun addControllerMappingSettings( - sl: ArrayList, - controller: EmulatedController, - groupTypeFilter: Set? + sl: ArrayList, + controller: EmulatedController, + groupTypeFilter: Set? ) { - addContainerMappingSettings(sl, controller, controller, groupTypeFilter) + addContainerMappingSettings(sl, controller, controller, groupTypeFilter) } /** @@ -2702,7 +2747,7 @@ class SettingsFragmentPresenter( val defaultDevice = controller.getDefaultDevice() hasOldControllerSettings = defaultDevice.startsWith("Android/") && - defaultDevice.endsWith("/Touchscreen") + defaultDevice.endsWith("/Touchscreen") fragmentView.setOldControllerSettingsWarningVisibility(hasOldControllerSettings) } diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 855cb56ab8..08e00a5e42 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -32,6 +32,8 @@ Extension Device + Controller Label + Give this controller a unique name Create Mappings for Other Devices Detects inputs from all devices, not just the selected device. Profile diff --git a/Source/Android/gradle/gradle-daemon-jvm.properties b/Source/Android/gradle/gradle-daemon-jvm.properties new file mode 100644 index 0000000000..6c1139ec06 --- /dev/null +++ b/Source/Android/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,12 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect +toolchainVersion=21 diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 0b0609f010..95cfc3647b 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -31,1012 +31,1023 @@ namespace { -std::string SOURCE = "Android"; + std::string SOURCE = "Android"; -jclass s_list_class; -jmethodID s_list_get; -jmethodID s_list_size; + jclass s_list_class; + jmethodID s_list_get; + jmethodID s_list_size; -jclass s_input_device_class; -jmethodID s_input_device_get_device_ids; -jmethodID s_input_device_get_device; -jmethodID s_input_device_get_controller_number; -jmethodID s_input_device_get_motion_ranges; -jmethodID s_input_device_get_name; -jmethodID s_input_device_get_sources; -jmethodID s_input_device_has_keys; + jclass s_input_device_class; + jmethodID s_input_device_get_device_ids; + jmethodID s_input_device_get_device; + jmethodID s_input_device_get_controller_number; + jmethodID s_input_device_get_motion_ranges; + jmethodID s_input_device_get_name; + jmethodID s_input_device_get_descriptor; + jmethodID s_input_device_get_sources; + jmethodID s_input_device_has_keys; -jclass s_motion_range_class; -jmethodID s_motion_range_get_axis; -jmethodID s_motion_range_get_max; -jmethodID s_motion_range_get_min; -jmethodID s_motion_range_get_source; + jclass s_motion_range_class; + jmethodID s_motion_range_get_axis; + jmethodID s_motion_range_get_max; + jmethodID s_motion_range_get_min; + jmethodID s_motion_range_get_source; -jclass s_input_event_class; -jmethodID s_input_event_get_device_id; + jclass s_input_event_class; + jmethodID s_input_event_get_device_id; -jclass s_key_event_class; -jmethodID s_key_event_get_action; -jmethodID s_key_event_get_keycode; + jclass s_key_event_class; + jmethodID s_key_event_get_action; + jmethodID s_key_event_get_keycode; -jclass s_motion_event_class; -jmethodID s_motion_event_get_axis_value; -jmethodID s_motion_event_get_source; + jclass s_motion_event_class; + jmethodID s_motion_event_get_axis_value; + jmethodID s_motion_event_get_source; -jclass s_controller_interface_class; -jmethodID s_controller_interface_register_input_device_listener; -jmethodID s_controller_interface_unregister_input_device_listener; -jmethodID s_controller_interface_get_vibrator_manager; -jmethodID s_controller_interface_get_system_vibrator_manager; -jmethodID s_controller_interface_vibrate; + jclass s_controller_interface_class; + jmethodID s_controller_interface_register_input_device_listener; + jmethodID s_controller_interface_unregister_input_device_listener; + jmethodID s_controller_interface_get_vibrator_manager; + jmethodID s_controller_interface_get_system_vibrator_manager; + jmethodID s_controller_interface_vibrate; -jclass s_sensor_event_listener_class; -jmethodID s_sensor_event_listener_constructor; -jmethodID s_sensor_event_listener_constructor_input_device; -jmethodID s_sensor_event_listener_set_device_qualifier; -jmethodID s_sensor_event_listener_request_unsuspend_sensor; -jmethodID s_sensor_event_listener_get_axis_names; -jmethodID s_sensor_event_listener_get_negative_axes; + jclass s_sensor_event_listener_class; + jmethodID s_sensor_event_listener_constructor; + jmethodID s_sensor_event_listener_constructor_input_device; + jmethodID s_sensor_event_listener_set_device_qualifier; + jmethodID s_sensor_event_listener_request_unsuspend_sensor; + jmethodID s_sensor_event_listener_get_axis_names; + jmethodID s_sensor_event_listener_get_negative_axes; -jclass s_dolphin_vibrator_manager_class; -jmethodID s_dolphin_vibrator_manager_get_vibrator; -jmethodID s_dolphin_vibrator_manager_get_vibrator_ids; + jclass s_dolphin_vibrator_manager_class; + jmethodID s_dolphin_vibrator_manager_get_vibrator; + jmethodID s_dolphin_vibrator_manager_get_vibrator_ids; -jintArray s_keycodes_array; + jintArray s_keycodes_array; -using Clock = std::chrono::steady_clock; -constexpr Clock::duration ACTIVE_INPUT_TIMEOUT = std::chrono::milliseconds(1000); + using Clock = std::chrono::steady_clock; + constexpr Clock::duration ACTIVE_INPUT_TIMEOUT = std::chrono::milliseconds(1000); -std::unordered_map s_device_id_to_device_qualifier; + std::unordered_map s_device_id_to_device_qualifier; -constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31 + constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31 -const std::array KEYCODE_NAMES = { - "Unknown", - "Soft Left", - "Soft Right", - "Home", - "Back", - "Call", - "End Call", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "Star", - "Pound", - "Up", - "Down", - "Left", - "Right", - "Center", - "Volume Up", - "Volume Down", - "Power", - "Camera", - "Clear", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "Comma", - "Period", - "Left Alt", - "Right Alt", - "Left Shift", - "Right Shift", - "Tab", - "Space", - "Sym", - "Explorer", - "Envelope", - "Enter", - "Backspace", - "Grave", - "Minus", - "Equals", - "Left Bracket", - "Right Bracket", - "Backslash", - "Semicolon", - "Apostrophe", - "Slash", - "At", - "Num", - "Headset Hook", - "Focus", - "Plus", - "Menu", - "Notification", - "Search", - "Play Pause", - "Stop", - "Next", - "Previous", - "Rewind", - "Fast Forward", - "Mute", - "Page Up", - "Page Down", - "Emoji", - "Switch Charset", - "Button A", - "Button B", - "Button C", - "Button X", - "Button Y", - "Button Z", - "Button L1", - "Button R1", - "Button L2", - "Button R2", - "Button L3", - "Button R3", - "Start", - "Select", - "Mode", - "Escape", - "Delete", - "Left Ctrl", - "Right Ctrl", - "Caps Lock", - "Scroll Lock", - "Left Meta", - "Right Meta", - "Fn", - "PrtSc SysRq", - "Pause Break", - "Move Home", - "Move End", - "Insert", - "Forward", - "Play", - "Pause", - "Close", - "Eject", - "Record", - "F1", - "F2", - "F3", - "F4", - "F5", - "F6", - "F7", - "F8", - "F9", - "F10", - "F11", - "F12", - "Num Lock", - "Numpad 0", - "Numpad 1", - "Numpad 2", - "Numpad 3", - "Numpad 4", - "Numpad 5", - "Numpad 6", - "Numpad 7", - "Numpad 8", - "Numpad 9", - "Numpad Divide", - "Numpad Multiply", - "Numpad Subtract", - "Numpad Add", - "Numpad Dot", - "Numpad Comma", - "Numpad Enter", - "Numpad Equals", - "Numpad Left Paren", - "Numpad Right Paren", - "Volume Mute", - "Info", - "Channel Up", - "Channel Down", - "Zoom In", - "Zoom Out", - "TV", - "Window", - "Guide", - "DVR", - "Bookmark", - "Captions", - "Settings", - "TV Power", - "TV Input", - "STB Power", - "STB Input", - "AVR Power", - "AVR Input", - "Prog Red", - "Prog Green", - "Prog Yellow", - "Prog Blue", - "App Switch", - "Button 1", - "Button 2", - "Button 3", - "Button 4", - "Button 5", - "Button 6", - "Button 7", - "Button 8", - "Button 9", - "Button 10", - "Button 11", - "Button 12", - "Button 13", - "Button 14", - "Button 15", - "Button 16", - "Language Switch", - "Manner Mode", - "3D Mode", - "Contacts", - "Calendar", - "Music", - "Calculator", - "Zenkaku Hankaku", - "Eisu", - "Henkan", - "Muhenkan", - "Katakana Hiragana", - "Yen", - "Ro", - "Kana", - "Assist", - "Brightness Down", - "Brightness Up", - "Audio Track", - "Sleep", - "Wakeup", - "Pairing", - "Top Menu", - "11", - "12", - "Last Channel", - "Data Service", - "Voice Assist", - "Radio Service", - "Teletext", - "Number Entry", - "Terrestrial Analog", - "Terrestrial Digital", - "Satellite", - "Satellite BS", - "Satellite CS", - "Satellite Service", - "Network", - "Antenna Cable", - "Input HDMI 1", - "Input HDMI 2", - "Input HDMI 3", - "Input HDMI 4", - "Input Composite 1", - "Input Composite 2", - "Input Component 1", - "Input Component 2", - "Input VGA 1", - "Audio Description", - "Audio Description Mix Up", - "Audio Description Mix Down", - "Zoom Mode", - "Contents Menu", - "Media Context Menu", - "Timer Programming", - "Help", - "Navigate Previous", - "Navigate Next", - "Navigate In", - "Navigate Out", - "Stem Primary", - "Stem 1", - "Stem 2", - "Stem 3", - "Up Left", - "Down Left", - "Up Right", - "Down Right", - "Skip Forward", - "Skip Backward", - "Step Forward", - "Step Backward", - "Soft Sleep", - "Cut", - "Copy", - "Paste", - "System Navigation Up", - "System Navigation Down", - "System Navigation Left", - "System Navigation Right", - "All Apps", - "Refresh", - "Thumbs Up", - "Thumbs Down", - "Profile Switch", -}; + const std::array KEYCODE_NAMES = { + "Unknown", + "Soft Left", + "Soft Right", + "Home", + "Back", + "Call", + "End Call", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "Star", + "Pound", + "Up", + "Down", + "Left", + "Right", + "Center", + "Volume Up", + "Volume Down", + "Power", + "Camera", + "Clear", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "Comma", + "Period", + "Left Alt", + "Right Alt", + "Left Shift", + "Right Shift", + "Tab", + "Space", + "Sym", + "Explorer", + "Envelope", + "Enter", + "Backspace", + "Grave", + "Minus", + "Equals", + "Left Bracket", + "Right Bracket", + "Backslash", + "Semicolon", + "Apostrophe", + "Slash", + "At", + "Num", + "Headset Hook", + "Focus", + "Plus", + "Menu", + "Notification", + "Search", + "Play Pause", + "Stop", + "Next", + "Previous", + "Rewind", + "Fast Forward", + "Mute", + "Page Up", + "Page Down", + "Emoji", + "Switch Charset", + "Button A", + "Button B", + "Button C", + "Button X", + "Button Y", + "Button Z", + "Button L1", + "Button R1", + "Button L2", + "Button R2", + "Button L3", + "Button R3", + "Start", + "Select", + "Mode", + "Escape", + "Delete", + "Left Ctrl", + "Right Ctrl", + "Caps Lock", + "Scroll Lock", + "Left Meta", + "Right Meta", + "Fn", + "PrtSc SysRq", + "Pause Break", + "Move Home", + "Move End", + "Insert", + "Forward", + "Play", + "Pause", + "Close", + "Eject", + "Record", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "Num Lock", + "Numpad 0", + "Numpad 1", + "Numpad 2", + "Numpad 3", + "Numpad 4", + "Numpad 5", + "Numpad 6", + "Numpad 7", + "Numpad 8", + "Numpad 9", + "Numpad Divide", + "Numpad Multiply", + "Numpad Subtract", + "Numpad Add", + "Numpad Dot", + "Numpad Comma", + "Numpad Enter", + "Numpad Equals", + "Numpad Left Paren", + "Numpad Right Paren", + "Volume Mute", + "Info", + "Channel Up", + "Channel Down", + "Zoom In", + "Zoom Out", + "TV", + "Window", + "Guide", + "DVR", + "Bookmark", + "Captions", + "Settings", + "TV Power", + "TV Input", + "STB Power", + "STB Input", + "AVR Power", + "AVR Input", + "Prog Red", + "Prog Green", + "Prog Yellow", + "Prog Blue", + "App Switch", + "Button 1", + "Button 2", + "Button 3", + "Button 4", + "Button 5", + "Button 6", + "Button 7", + "Button 8", + "Button 9", + "Button 10", + "Button 11", + "Button 12", + "Button 13", + "Button 14", + "Button 15", + "Button 16", + "Language Switch", + "Manner Mode", + "3D Mode", + "Contacts", + "Calendar", + "Music", + "Calculator", + "Zenkaku Hankaku", + "Eisu", + "Henkan", + "Muhenkan", + "Katakana Hiragana", + "Yen", + "Ro", + "Kana", + "Assist", + "Brightness Down", + "Brightness Up", + "Audio Track", + "Sleep", + "Wakeup", + "Pairing", + "Top Menu", + "11", + "12", + "Last Channel", + "Data Service", + "Voice Assist", + "Radio Service", + "Teletext", + "Number Entry", + "Terrestrial Analog", + "Terrestrial Digital", + "Satellite", + "Satellite BS", + "Satellite CS", + "Satellite Service", + "Network", + "Antenna Cable", + "Input HDMI 1", + "Input HDMI 2", + "Input HDMI 3", + "Input HDMI 4", + "Input Composite 1", + "Input Composite 2", + "Input Component 1", + "Input Component 2", + "Input VGA 1", + "Audio Description", + "Audio Description Mix Up", + "Audio Description Mix Down", + "Zoom Mode", + "Contents Menu", + "Media Context Menu", + "Timer Programming", + "Help", + "Navigate Previous", + "Navigate Next", + "Navigate In", + "Navigate Out", + "Stem Primary", + "Stem 1", + "Stem 2", + "Stem 3", + "Up Left", + "Down Left", + "Up Right", + "Down Right", + "Skip Forward", + "Skip Backward", + "Step Forward", + "Step Backward", + "Soft Sleep", + "Cut", + "Copy", + "Paste", + "System Navigation Up", + "System Navigation Down", + "System Navigation Left", + "System Navigation Right", + "All Apps", + "Refresh", + "Thumbs Up", + "Thumbs Down", + "Profile Switch", + }; -std::string ConstructKeyName(int keycode) -{ - return std::string(KEYCODE_NAMES[keycode]); -} + std::string ConstructKeyName(int keycode) + { + return std::string(KEYCODE_NAMES[keycode]); + } -std::string ConstructAxisNamePrefix(int source) -{ - // A device is allowed to have two axes with the same axis ID but different source IDs, - // so we have to make sure to include the source in the axis name. + std::string ConstructAxisNamePrefix(int source) + { + // A device is allowed to have two axes with the same axis ID but different source IDs, + // so we have to make sure to include the source in the axis name. - static const std::unordered_map source_names{ - {AINPUT_SOURCE_KEYBOARD, "Keyboard"}, - {AINPUT_SOURCE_DPAD, "Dpad"}, - {AINPUT_SOURCE_GAMEPAD, "Gamepad"}, - {AINPUT_SOURCE_TOUCHSCREEN, "Touch"}, - {AINPUT_SOURCE_MOUSE, "Cursor"}, - {AINPUT_SOURCE_STYLUS, "Stylus"}, - {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BTStylus"}, - {AINPUT_SOURCE_TRACKBALL, "Trackball"}, - {AINPUT_SOURCE_MOUSE_RELATIVE, "Mouse"}, - {AINPUT_SOURCE_TOUCHPAD, "Touchpad"}, - {AINPUT_SOURCE_TOUCH_NAVIGATION, "Touchnav"}, - {AINPUT_SOURCE_JOYSTICK, "Axis"}, // The typical source for all axes on a gamepad - {AINPUT_SOURCE_HDMI, "HDMI"}, - {AINPUT_SOURCE_SENSOR, "Sensor"}, - {AINPUT_SOURCE_ROTARY_ENCODER, "Rotary"}, - }; + static const std::unordered_map source_names{ + {AINPUT_SOURCE_KEYBOARD, "Keyboard"}, + {AINPUT_SOURCE_DPAD, "Dpad"}, + {AINPUT_SOURCE_GAMEPAD, "Gamepad"}, + {AINPUT_SOURCE_TOUCHSCREEN, "Touch"}, + {AINPUT_SOURCE_MOUSE, "Cursor"}, + {AINPUT_SOURCE_STYLUS, "Stylus"}, + {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BTStylus"}, + {AINPUT_SOURCE_TRACKBALL, "Trackball"}, + {AINPUT_SOURCE_MOUSE_RELATIVE, "Mouse"}, + {AINPUT_SOURCE_TOUCHPAD, "Touchpad"}, + {AINPUT_SOURCE_TOUCH_NAVIGATION, "Touchnav"}, + {AINPUT_SOURCE_JOYSTICK, "Axis"}, // The typical source for all axes on a gamepad + {AINPUT_SOURCE_HDMI, "HDMI"}, + {AINPUT_SOURCE_SENSOR, "Sensor"}, + {AINPUT_SOURCE_ROTARY_ENCODER, "Rotary"}, + }; - const auto it = source_names.find(source); - if (it != source_names.end()) - return fmt::format("{} ", it->second); - else - return fmt::format("Axis {:08x}/", source); -} + const auto it = source_names.find(source); + if (it != source_names.end()) + return fmt::format("{} ", it->second); + else + return fmt::format("Axis {:08x}/", source); + } -std::string ConstructAxisName(int source, int axis, bool negative) -{ - const char sign = negative ? '-' : '+'; - return fmt::format("{}{}{}", ConstructAxisNamePrefix(source), axis, sign); -} + std::string ConstructAxisName(int source, int axis, bool negative) + { + const char sign = negative ? '-' : '+'; + return fmt::format("{}{}{}", ConstructAxisNamePrefix(source), axis, sign); + } -std::shared_ptr FindDevice(jint device_id) -{ - const auto it = s_device_id_to_device_qualifier.find(device_id); - if (it == s_device_id_to_device_qualifier.end()) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device ID {}", device_id); - return nullptr; - } + std::shared_ptr FindDevice(jint device_id) + { + const auto it = s_device_id_to_device_qualifier.find(device_id); + if (it == s_device_id_to_device_qualifier.end()) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device ID {}", device_id); + return nullptr; + } - const ciface::Core::DeviceQualifier& qualifier = it->second; - std::shared_ptr device = g_controller_interface.FindDevice(qualifier); - if (!device) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device {}", qualifier.ToString()); - return nullptr; - } + const ciface::Core::DeviceQualifier& qualifier = it->second; + std::shared_ptr device = g_controller_interface.FindDevice(qualifier); + if (!device) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device {}", qualifier.ToString()); + return nullptr; + } - return device; -} + return device; + } -void RegisterDevicesChangedCallbackIfNeeded(JNIEnv* env, jclass controller_interface_class) -{ - static bool registered = false; - if (registered) - return; - registered = true; + void RegisterDevicesChangedCallbackIfNeeded(JNIEnv* env, jclass controller_interface_class) + { + static bool registered = false; + if (registered) + return; + registered = true; - const jclass global_controller_interface_class = - reinterpret_cast(env->NewGlobalRef(controller_interface_class)); - const jmethodID controller_interface_on_devices_changed = - env->GetStaticMethodID(global_controller_interface_class, "onDevicesChanged", "()V"); + const jclass global_controller_interface_class = + reinterpret_cast(env->NewGlobalRef(controller_interface_class)); + const jmethodID controller_interface_on_devices_changed = + env->GetStaticMethodID(global_controller_interface_class, "onDevicesChanged", "()V"); - static Common::EventHook event_hook = g_controller_interface.RegisterDevicesChangedCallback( - [global_controller_interface_class, controller_interface_on_devices_changed] { - IDCache::GetEnvForThread()->CallStaticVoidMethod(global_controller_interface_class, - controller_interface_on_devices_changed); - }); -} + static Common::EventHook event_hook = g_controller_interface.RegisterDevicesChangedCallback( + [global_controller_interface_class, controller_interface_on_devices_changed] { + IDCache::GetEnvForThread()->CallStaticVoidMethod(global_controller_interface_class, + controller_interface_on_devices_changed); + }); + } } // namespace namespace ciface::Android { -class InputBackend final : public ciface::InputBackend -{ -public: - explicit InputBackend(ControllerInterface* controller_interface); - ~InputBackend(); - void PopulateDevices() override; - - static void AddDevice(JNIEnv* env, int device_id); - static void AddSensorDevice(JNIEnv* env); - static void RemoveDevice(int device_id); -}; - -std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) -{ - return std::make_unique(controller_interface); -} - -class AndroidInput : public Core::Device::Input -{ -public: - explicit AndroidInput(std::string name) : m_name(std::move(name)) - { - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Created {}", m_name); - } - - std::string GetName() const override { return m_name; } - - ControlState GetState() const override - { - m_last_polled.store(Clock::now(), std::memory_order_relaxed); - return m_state.load(std::memory_order_relaxed); - } - - void SetState(ControlState state) { m_state.store(state, std::memory_order_relaxed); } - - Clock::time_point GetLastPolled() const { return m_last_polled.load(std::memory_order_relaxed); } - -private: - std::string m_name; - std::atomic m_state = 0; - mutable std::atomic m_last_polled{}; -}; - -class AndroidKey final : public AndroidInput -{ -public: - explicit AndroidKey(int keycode) : AndroidInput(ConstructKeyName(keycode)) {} -}; - -class AndroidAxis : public AndroidInput -{ -public: - AndroidAxis(int source, int axis, bool negative) - : AndroidInput(ConstructAxisName(source, axis, negative)), m_negative(negative) - { - } - - AndroidAxis(std::string name, bool negative) : AndroidInput(std::move(name)), m_negative(negative) - { - } - - ControlState GetState() const override - { - return m_negative ? -AndroidInput::GetState() : AndroidInput::GetState(); - } - -private: - bool m_negative; -}; - -class AndroidSensorAxis final : public AndroidAxis -{ -public: - // This class does not create its own global reference to the passed-in sensor_event_listener. - // That is, it's up to the device that contains this axis to keep sensor_event_listener valid. - // It does however create its own global reference to the passed-in name. - AndroidSensorAxis(JNIEnv* env, jobject sensor_event_listener, jstring j_name, bool negative) - : AndroidAxis(GetJString(env, j_name), negative), - m_sensor_event_listener(sensor_event_listener), - m_j_name(reinterpret_cast(env->NewGlobalRef(j_name))) - { - } - - ~AndroidSensorAxis() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_j_name); } - - bool IsDetectable() const override { return false; } - - ControlState GetState() const override - { - if (m_is_suspended.load(std::memory_order_relaxed)) + class InputBackend final : public ciface::InputBackend { - IDCache::GetEnvForThread()->CallVoidMethod( - m_sensor_event_listener, s_sensor_event_listener_request_unsuspend_sensor, m_j_name); + public: + explicit InputBackend(ControllerInterface* controller_interface); + ~InputBackend(); + void PopulateDevices() override; - // m_is_suspended is intentionally not updated here. To prevent the C++ suspended status from - // ending up desynced with the Java suspended status, we only update m_is_suspended when Java - // calls notifySensorSuspendedState (which calls NotifyIsSuspended). This way, Java is the - // authoritative source for the suspended status, and C++ mirrors it (possibly with a delay). + static void AddDevice(JNIEnv* env, int device_id); + static void AddSensorDevice(JNIEnv* env); + static void RemoveDevice(int device_id); + }; + + std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) + { + return std::make_unique(controller_interface); } - return AndroidAxis::GetState(); - } - - void NotifyIsSuspended(bool is_suspended) - { - m_is_suspended.store(is_suspended, std::memory_order_relaxed); - } - -private: - const jobject m_sensor_event_listener; - const jstring m_j_name; - std::atomic m_is_suspended = true; -}; - -class AndroidMotor : public Core::Device::Output -{ -public: - AndroidMotor(JNIEnv* env, jobject vibrator, jint id) - : m_vibrator(env->NewGlobalRef(vibrator)), m_id(id) - { - } - - ~AndroidMotor() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_vibrator); } - - std::string GetName() const override { return "Motor " + std::to_string(m_id); } - - void SetState(ControlState state) override - { - ControlState old_state = m_state.exchange(state, std::memory_order_relaxed); - - if (old_state < 0.5 && state >= 0.5) + class AndroidInput : public Core::Device::Input { - IDCache::GetEnvForThread()->CallStaticVoidMethod(s_controller_interface_class, - s_controller_interface_vibrate, m_vibrator); - } - } + public: + explicit AndroidInput(std::string name) : m_name(std::move(name)) + { + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Created {}", m_name); + } -private: - const jobject m_vibrator; - const jint m_id; - std::atomic m_state = 0; -}; + std::string GetName() const override { return m_name; } -class AndroidDevice final : public Core::Device -{ -public: - AndroidDevice(JNIEnv* env, jint device_id, jobject input_device) - : m_sensor_event_listener(AddSensors(env, input_device)), - m_source(env->CallIntMethod(input_device, s_input_device_get_sources)), - m_controller_number(env->CallIntMethod(input_device, s_input_device_get_controller_number)), - m_device_id(device_id) - { - jstring j_name = - reinterpret_cast(env->CallObjectMethod(input_device, s_input_device_get_name)); - m_name = GetJString(env, j_name); - env->DeleteLocalRef(j_name); + ControlState GetState() const override + { + m_last_polled.store(Clock::now(), std::memory_order_relaxed); + return m_state.load(std::memory_order_relaxed); + } - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Sources for {}: {:08x}", GetQualifiedName(), m_source); + void SetState(ControlState state) { m_state.store(state, std::memory_order_relaxed); } - AddKeys(env, input_device); - AddAxes(env, input_device); - AddMotors(env, input_device); - } + Clock::time_point GetLastPolled() const { return m_last_polled.load(std::memory_order_relaxed); } - // Constructor for the device added by Dolphin to contain sensor inputs - AndroidDevice(JNIEnv* env, std::string name) - : m_sensor_event_listener(AddSensors(env, nullptr)), m_source(AINPUT_SOURCE_SENSOR), - m_controller_number(0), m_device_id(std::nullopt), m_name(std::move(name)) - { - AddSystemMotors(env); - } + private: + std::string m_name; + std::atomic m_state = 0; + mutable std::atomic m_last_polled{}; + }; - ~AndroidDevice() - { - if (m_sensor_event_listener) - IDCache::GetEnvForThread()->DeleteGlobalRef(m_sensor_event_listener); - } - - std::string GetName() const override { return m_name; } - - std::string GetSource() const override { return SOURCE; } - - std::optional GetPreferredId() const override - { - return m_controller_number != 0 ? std::make_optional(m_controller_number) : std::nullopt; - } - - int GetSortPriority() const override - { - // If m_controller_number is non-zero, Android considers this to be a gamepad - if (m_controller_number != 0) - return 0; - - if ((m_source & AINPUT_SOURCE_KEYBOARD) != 0) - return -1; - - if ((m_source & (AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_MOUSE_RELATIVE)) != 0) - return -2; - - return -3; - } - - std::optional GetDeviceID() const { return m_device_id; } - - jobject GetSensorEventListener() { return m_sensor_event_listener; } - -private: - void AddKeys(JNIEnv* env, jobject input_device) - { - jbooleanArray keys_array = reinterpret_cast( - env->CallObjectMethod(input_device, s_input_device_has_keys, s_keycodes_array)); - jboolean* keys = env->GetBooleanArrayElements(keys_array, nullptr); - jsize keys_count = env->GetArrayLength(keys_array); - for (jsize i = 0; i < keys_count; ++i) + class AndroidKey final : public AndroidInput { - // These specific keys never get delivered to applications, - // so there's no point in letting users try to map them - if (i == AKEYCODE_HOME || i == AKEYCODE_ASSIST || i == AKEYCODE_VOICE_ASSIST) - continue; + public: + explicit AndroidKey(int keycode) : AndroidInput(ConstructKeyName(keycode)) {} + }; - if (keys[i]) - AddInput(new AndroidKey(i)); - } - env->ReleaseBooleanArrayElements(keys_array, keys, JNI_ABORT); - env->DeleteLocalRef(keys_array); - } - - void AddAxes(JNIEnv* env, jobject input_device) - { - jobject motion_ranges_list = - env->CallObjectMethod(input_device, s_input_device_get_motion_ranges); - jint motion_ranges_count = env->CallIntMethod(motion_ranges_list, s_list_size); - for (jint i = 0; i < motion_ranges_count; ++i) + class AndroidAxis : public AndroidInput { - jobject motion_range = env->CallObjectMethod(motion_ranges_list, s_list_get, i); + public: + AndroidAxis(int source, int axis, bool negative) + : AndroidInput(ConstructAxisName(source, axis, negative)), m_negative(negative) + { + } - jint source = env->CallIntMethod(motion_range, s_motion_range_get_source); - jint axis = env->CallIntMethod(motion_range, s_motion_range_get_axis); - jfloat min = env->CallFloatMethod(motion_range, s_motion_range_get_min); - jfloat max = env->CallFloatMethod(motion_range, s_motion_range_get_max); + AndroidAxis(std::string name, bool negative) : AndroidInput(std::move(name)), m_negative(negative) + { + } - env->DeleteLocalRef(motion_range); + ControlState GetState() const override + { + return m_negative ? -AndroidInput::GetState() : AndroidInput::GetState(); + } - AndroidAxis* positive = nullptr; - AndroidAxis* negative = nullptr; - if (max > 0) - positive = new AndroidAxis(source, axis, false); - if (min < 0) - negative = new AndroidAxis(source, axis, true); + private: + bool m_negative; + }; - if (positive && negative) - AddFullAnalogSurfaceInputs(positive, negative); - else if (positive || negative) - AddInput(positive ? positive : negative); - } - env->DeleteLocalRef(motion_ranges_list); - } - - jobject AddSensors(JNIEnv* env, jobject input_device) - { - jobject local_sensor_event_listener; - if (input_device) + class AndroidSensorAxis final : public AndroidAxis { - local_sensor_event_listener = - env->NewObject(s_sensor_event_listener_class, - s_sensor_event_listener_constructor_input_device, input_device); - } - else + public: + // This class does not create its own global reference to the passed-in sensor_event_listener. + // That is, it's up to the device that contains this axis to keep sensor_event_listener valid. + // It does however create its own global reference to the passed-in name. + AndroidSensorAxis(JNIEnv* env, jobject sensor_event_listener, jstring j_name, bool negative) + : AndroidAxis(GetJString(env, j_name), negative), + m_sensor_event_listener(sensor_event_listener), + m_j_name(reinterpret_cast(env->NewGlobalRef(j_name))) + { + } + + ~AndroidSensorAxis() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_j_name); } + + bool IsDetectable() const override { return false; } + + ControlState GetState() const override + { + if (m_is_suspended.load(std::memory_order_relaxed)) + { + IDCache::GetEnvForThread()->CallVoidMethod( + m_sensor_event_listener, s_sensor_event_listener_request_unsuspend_sensor, m_j_name); + + // m_is_suspended is intentionally not updated here. To prevent the C++ suspended status from + // ending up desynced with the Java suspended status, we only update m_is_suspended when Java + // calls notifySensorSuspendedState (which calls NotifyIsSuspended). This way, Java is the + // authoritative source for the suspended status, and C++ mirrors it (possibly with a delay). + } + + return AndroidAxis::GetState(); + } + + void NotifyIsSuspended(bool is_suspended) + { + m_is_suspended.store(is_suspended, std::memory_order_relaxed); + } + + private: + const jobject m_sensor_event_listener; + const jstring m_j_name; + std::atomic m_is_suspended = true; + }; + + class AndroidMotor : public Core::Device::Output { - local_sensor_event_listener = - env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor); - } + public: + AndroidMotor(JNIEnv* env, jobject vibrator, jint id) + : m_vibrator(env->NewGlobalRef(vibrator)), m_id(id) + { + } - jobject sensor_event_listener = env->NewGlobalRef(local_sensor_event_listener); + ~AndroidMotor() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_vibrator); } - env->DeleteLocalRef(local_sensor_event_listener); + std::string GetName() const override { return "Motor " + std::to_string(m_id); } - jobjectArray j_axis_names = reinterpret_cast( - env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_axis_names)); + void SetState(ControlState state) override + { + ControlState old_state = m_state.exchange(state, std::memory_order_relaxed); - jbooleanArray j_negative_axes = reinterpret_cast( - env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_negative_axes)); - jboolean* negative_axes = env->GetBooleanArrayElements(j_negative_axes, nullptr); + if (old_state < 0.5 && state >= 0.5) + { + IDCache::GetEnvForThread()->CallStaticVoidMethod(s_controller_interface_class, + s_controller_interface_vibrate, m_vibrator); + } + } - const jsize axis_count = env->GetArrayLength(j_axis_names); - ASSERT(axis_count == env->GetArrayLength(j_negative_axes)); - for (jsize i = 0; i < axis_count; ++i) + private: + const jobject m_vibrator; + const jint m_id; + std::atomic m_state = 0; + }; + + class AndroidDevice final : public Core::Device { - const jstring axis_name = - reinterpret_cast(env->GetObjectArrayElement(j_axis_names, i)); - AddInput(new AndroidSensorAxis(env, sensor_event_listener, axis_name, negative_axes[i])); - env->DeleteLocalRef(axis_name); - } + public: + AndroidDevice(JNIEnv* env, jint device_id, jobject input_device) + : m_sensor_event_listener(AddSensors(env, input_device)), + m_source(env->CallIntMethod(input_device, s_input_device_get_sources)), + m_controller_number(env->CallIntMethod(input_device, s_input_device_get_controller_number)), + m_device_id(device_id) + { + jstring j_name = + reinterpret_cast(env->CallObjectMethod(input_device, s_input_device_get_name)); + m_name = GetJString(env, j_name); + env->DeleteLocalRef(j_name); - env->DeleteLocalRef(j_axis_names); - env->ReleaseBooleanArrayElements(j_negative_axes, negative_axes, 0); - env->DeleteLocalRef(j_negative_axes); + jstring j_descriptor = + reinterpret_cast(env->CallObjectMethod(input_device, s_input_device_get_descriptor)); + m_descriptor = GetJString(env, j_descriptor); + env->DeleteLocalRef(j_descriptor); - return sensor_event_listener; - } + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Sources for {}: {:08x}", GetQualifiedName(), m_source); - void AddMotors(JNIEnv* env, jobject input_device) - { - jobject vibrator_manager = env->CallStaticObjectMethod( - s_controller_interface_class, s_controller_interface_get_vibrator_manager, input_device); - AddMotorsFromManager(env, vibrator_manager); - env->DeleteLocalRef(vibrator_manager); - } + AddKeys(env, input_device); + AddAxes(env, input_device); + AddMotors(env, input_device); + } - void AddSystemMotors(JNIEnv* env) - { - jobject vibrator_manager = env->CallStaticObjectMethod( - s_controller_interface_class, s_controller_interface_get_system_vibrator_manager); - AddMotorsFromManager(env, vibrator_manager); - env->DeleteLocalRef(vibrator_manager); - } + // Constructor for the device added by Dolphin to contain sensor inputs + AndroidDevice(JNIEnv* env, std::string name) + : m_sensor_event_listener(AddSensors(env, nullptr)), m_source(AINPUT_SOURCE_SENSOR), + m_controller_number(0), m_device_id(std::nullopt), m_name(std::move(name)) + { + AddSystemMotors(env); + } - void AddMotorsFromManager(JNIEnv* env, jobject vibrator_manager) - { - jintArray j_vibrator_ids = reinterpret_cast( - env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator_ids)); - jint* vibrator_ids = env->GetIntArrayElements(j_vibrator_ids, nullptr); + ~AndroidDevice() + { + if (m_sensor_event_listener) + IDCache::GetEnvForThread()->DeleteGlobalRef(m_sensor_event_listener); + } - jint size = env->GetArrayLength(j_vibrator_ids); - for (jint i = 0; i < size; ++i) - { - jobject vibrator = - env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator, i); - AddOutput(new AndroidMotor(env, vibrator, i)); - env->DeleteLocalRef(vibrator); - } + std::string GetName() const override { return m_name; } - env->ReleaseIntArrayElements(j_vibrator_ids, vibrator_ids, 0); - env->DeleteLocalRef(j_vibrator_ids); - } + std::string GetSource() const override { return SOURCE; } - const jobject m_sensor_event_listener; - const int m_source; - const int m_controller_number; - const std::optional m_device_id; - std::string m_name; -}; + std::optional GetPreferredId() const override + { + return m_controller_number != 0 ? std::make_optional(m_controller_number) : std::nullopt; + } + + int GetSortPriority() const override + { + // If m_controller_number is non-zero, Android considers this to be a gamepad + if (m_controller_number != 0) + return 0; + + if ((m_source & AINPUT_SOURCE_KEYBOARD) != 0) + return -1; + + if ((m_source & (AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_MOUSE_RELATIVE)) != 0) + return -2; + + return -3; + } + + std::optional GetDeviceID() const { return m_device_id; } + + std::string GetDescriptor() const { return m_descriptor; } + + jobject GetSensorEventListener() { return m_sensor_event_listener; } + + private: + void AddKeys(JNIEnv* env, jobject input_device) + { + jbooleanArray keys_array = reinterpret_cast( + env->CallObjectMethod(input_device, s_input_device_has_keys, s_keycodes_array)); + jboolean* keys = env->GetBooleanArrayElements(keys_array, nullptr); + jsize keys_count = env->GetArrayLength(keys_array); + for (jsize i = 0; i < keys_count; ++i) + { + // These specific keys never get delivered to applications, + // so there's no point in letting users try to map them + if (i == AKEYCODE_HOME || i == AKEYCODE_ASSIST || i == AKEYCODE_VOICE_ASSIST) + continue; + + if (keys[i]) + AddInput(new AndroidKey(i)); + } + env->ReleaseBooleanArrayElements(keys_array, keys, JNI_ABORT); + env->DeleteLocalRef(keys_array); + } + + void AddAxes(JNIEnv* env, jobject input_device) + { + jobject motion_ranges_list = + env->CallObjectMethod(input_device, s_input_device_get_motion_ranges); + jint motion_ranges_count = env->CallIntMethod(motion_ranges_list, s_list_size); + for (jint i = 0; i < motion_ranges_count; ++i) + { + jobject motion_range = env->CallObjectMethod(motion_ranges_list, s_list_get, i); + + jint source = env->CallIntMethod(motion_range, s_motion_range_get_source); + jint axis = env->CallIntMethod(motion_range, s_motion_range_get_axis); + jfloat min = env->CallFloatMethod(motion_range, s_motion_range_get_min); + jfloat max = env->CallFloatMethod(motion_range, s_motion_range_get_max); + + env->DeleteLocalRef(motion_range); + + AndroidAxis* positive = nullptr; + AndroidAxis* negative = nullptr; + if (max > 0) + positive = new AndroidAxis(source, axis, false); + if (min < 0) + negative = new AndroidAxis(source, axis, true); + + if (positive && negative) + AddFullAnalogSurfaceInputs(positive, negative); + else if (positive || negative) + AddInput(positive ? positive : negative); + } + env->DeleteLocalRef(motion_ranges_list); + } + + jobject AddSensors(JNIEnv* env, jobject input_device) + { + jobject local_sensor_event_listener; + if (input_device) + { + local_sensor_event_listener = + env->NewObject(s_sensor_event_listener_class, + s_sensor_event_listener_constructor_input_device, input_device); + } + else + { + local_sensor_event_listener = + env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor); + } + + jobject sensor_event_listener = env->NewGlobalRef(local_sensor_event_listener); + + env->DeleteLocalRef(local_sensor_event_listener); + + jobjectArray j_axis_names = reinterpret_cast( + env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_axis_names)); + + jbooleanArray j_negative_axes = reinterpret_cast( + env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_negative_axes)); + jboolean* negative_axes = env->GetBooleanArrayElements(j_negative_axes, nullptr); + + const jsize axis_count = env->GetArrayLength(j_axis_names); + ASSERT(axis_count == env->GetArrayLength(j_negative_axes)); + for (jsize i = 0; i < axis_count; ++i) + { + const jstring axis_name = + reinterpret_cast(env->GetObjectArrayElement(j_axis_names, i)); + AddInput(new AndroidSensorAxis(env, sensor_event_listener, axis_name, negative_axes[i])); + env->DeleteLocalRef(axis_name); + } + + env->DeleteLocalRef(j_axis_names); + env->ReleaseBooleanArrayElements(j_negative_axes, negative_axes, 0); + env->DeleteLocalRef(j_negative_axes); + + return sensor_event_listener; + } + + void AddMotors(JNIEnv* env, jobject input_device) + { + jobject vibrator_manager = env->CallStaticObjectMethod( + s_controller_interface_class, s_controller_interface_get_vibrator_manager, input_device); + AddMotorsFromManager(env, vibrator_manager); + env->DeleteLocalRef(vibrator_manager); + } + + void AddSystemMotors(JNIEnv* env) + { + jobject vibrator_manager = env->CallStaticObjectMethod( + s_controller_interface_class, s_controller_interface_get_system_vibrator_manager); + AddMotorsFromManager(env, vibrator_manager); + env->DeleteLocalRef(vibrator_manager); + } + + void AddMotorsFromManager(JNIEnv* env, jobject vibrator_manager) + { + jintArray j_vibrator_ids = reinterpret_cast( + env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator_ids)); + jint* vibrator_ids = env->GetIntArrayElements(j_vibrator_ids, nullptr); + + jint size = env->GetArrayLength(j_vibrator_ids); + for (jint i = 0; i < size; ++i) + { + jobject vibrator = + env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator, i); + AddOutput(new AndroidMotor(env, vibrator, i)); + env->DeleteLocalRef(vibrator); + } + + env->ReleaseIntArrayElements(j_vibrator_ids, vibrator_ids, 0); + env->DeleteLocalRef(j_vibrator_ids); + } + + const jobject m_sensor_event_listener; + const int m_source; + const int m_controller_number; + const std::optional m_device_id; + std::string m_name; + std::string m_descriptor; + }; // Creates an array that contains every possible keycode -static jintArray CreateKeyCodesArray(JNIEnv* env) -{ - jintArray keycodes_array = env->NewIntArray(MAX_KEYCODE + 1); + static jintArray CreateKeyCodesArray(JNIEnv* env) + { + jintArray keycodes_array = env->NewIntArray(MAX_KEYCODE + 1); - int* keycodes = env->GetIntArrayElements(keycodes_array, nullptr); - for (int i = 0; i <= MAX_KEYCODE; ++i) - keycodes[i] = i; - env->ReleaseIntArrayElements(keycodes_array, keycodes, 0); + int* keycodes = env->GetIntArrayElements(keycodes_array, nullptr); + for (int i = 0; i <= MAX_KEYCODE; ++i) + keycodes[i] = i; + env->ReleaseIntArrayElements(keycodes_array, keycodes, 0); - return keycodes_array; -} + return keycodes_array; + } -InputBackend::InputBackend(ControllerInterface* controller_interface) - : ciface::InputBackend(controller_interface) -{ - JNIEnv* env = IDCache::GetEnvForThread(); + InputBackend::InputBackend(ControllerInterface* controller_interface) + : ciface::InputBackend(controller_interface) + { + JNIEnv* env = IDCache::GetEnvForThread(); - const jclass list_class = env->FindClass("java/util/List"); - s_list_class = reinterpret_cast(env->NewGlobalRef(list_class)); - s_list_get = env->GetMethodID(s_list_class, "get", "(I)Ljava/lang/Object;"); - s_list_size = env->GetMethodID(s_list_class, "size", "()I"); - env->DeleteLocalRef(list_class); + const jclass list_class = env->FindClass("java/util/List"); + s_list_class = reinterpret_cast(env->NewGlobalRef(list_class)); + s_list_get = env->GetMethodID(s_list_class, "get", "(I)Ljava/lang/Object;"); + s_list_size = env->GetMethodID(s_list_class, "size", "()I"); + env->DeleteLocalRef(list_class); - const jclass input_device_class = env->FindClass("android/view/InputDevice"); - s_input_device_class = reinterpret_cast(env->NewGlobalRef(input_device_class)); - s_input_device_get_device_ids = - env->GetStaticMethodID(s_input_device_class, "getDeviceIds", "()[I"); - s_input_device_get_device = - env->GetStaticMethodID(s_input_device_class, "getDevice", "(I)Landroid/view/InputDevice;"); - s_input_device_get_controller_number = - env->GetMethodID(s_input_device_class, "getControllerNumber", "()I"); - s_input_device_get_motion_ranges = - env->GetMethodID(s_input_device_class, "getMotionRanges", "()Ljava/util/List;"); - s_input_device_get_name = - env->GetMethodID(s_input_device_class, "getName", "()Ljava/lang/String;"); - s_input_device_get_sources = env->GetMethodID(s_input_device_class, "getSources", "()I"); - s_input_device_has_keys = env->GetMethodID(s_input_device_class, "hasKeys", "([I)[Z"); - env->DeleteLocalRef(input_device_class); + const jclass input_device_class = env->FindClass("android/view/InputDevice"); + s_input_device_class = reinterpret_cast(env->NewGlobalRef(input_device_class)); + s_input_device_get_device_ids = + env->GetStaticMethodID(s_input_device_class, "getDeviceIds", "()[I"); + s_input_device_get_device = + env->GetStaticMethodID(s_input_device_class, "getDevice", "(I)Landroid/view/InputDevice;"); + s_input_device_get_controller_number = + env->GetMethodID(s_input_device_class, "getControllerNumber", "()I"); + s_input_device_get_motion_ranges = + env->GetMethodID(s_input_device_class, "getMotionRanges", "()Ljava/util/List;"); + s_input_device_get_name = + env->GetMethodID(s_input_device_class, "getName", "()Ljava/lang/String;"); + s_input_device_get_descriptor = + env->GetMethodID(s_input_device_class, "getDescriptor", "()Ljava/lang/String;"); + s_input_device_get_sources = env->GetMethodID(s_input_device_class, "getSources", "()I"); + s_input_device_has_keys = env->GetMethodID(s_input_device_class, "hasKeys", "([I)[Z"); + env->DeleteLocalRef(input_device_class); - const jclass motion_range_class = env->FindClass("android/view/InputDevice$MotionRange"); - s_motion_range_class = reinterpret_cast(env->NewGlobalRef(motion_range_class)); - s_motion_range_get_axis = env->GetMethodID(s_motion_range_class, "getAxis", "()I"); - s_motion_range_get_max = env->GetMethodID(s_motion_range_class, "getMax", "()F"); - s_motion_range_get_min = env->GetMethodID(s_motion_range_class, "getMin", "()F"); - s_motion_range_get_source = env->GetMethodID(s_motion_range_class, "getSource", "()I"); - env->DeleteLocalRef(motion_range_class); + const jclass motion_range_class = env->FindClass("android/view/InputDevice$MotionRange"); + s_motion_range_class = reinterpret_cast(env->NewGlobalRef(motion_range_class)); + s_motion_range_get_axis = env->GetMethodID(s_motion_range_class, "getAxis", "()I"); + s_motion_range_get_max = env->GetMethodID(s_motion_range_class, "getMax", "()F"); + s_motion_range_get_min = env->GetMethodID(s_motion_range_class, "getMin", "()F"); + s_motion_range_get_source = env->GetMethodID(s_motion_range_class, "getSource", "()I"); + env->DeleteLocalRef(motion_range_class); - const jclass input_event_class = env->FindClass("android/view/InputEvent"); - s_input_event_class = reinterpret_cast(env->NewGlobalRef(input_event_class)); - s_input_event_get_device_id = env->GetMethodID(s_input_event_class, "getDeviceId", "()I"); - env->DeleteLocalRef(input_event_class); + const jclass input_event_class = env->FindClass("android/view/InputEvent"); + s_input_event_class = reinterpret_cast(env->NewGlobalRef(input_event_class)); + s_input_event_get_device_id = env->GetMethodID(s_input_event_class, "getDeviceId", "()I"); + env->DeleteLocalRef(input_event_class); - const jclass key_event_class = env->FindClass("android/view/KeyEvent"); - s_key_event_class = reinterpret_cast(env->NewGlobalRef(key_event_class)); - s_key_event_get_action = env->GetMethodID(s_key_event_class, "getAction", "()I"); - s_key_event_get_keycode = env->GetMethodID(s_key_event_class, "getKeyCode", "()I"); - env->DeleteLocalRef(key_event_class); + const jclass key_event_class = env->FindClass("android/view/KeyEvent"); + s_key_event_class = reinterpret_cast(env->NewGlobalRef(key_event_class)); + s_key_event_get_action = env->GetMethodID(s_key_event_class, "getAction", "()I"); + s_key_event_get_keycode = env->GetMethodID(s_key_event_class, "getKeyCode", "()I"); + env->DeleteLocalRef(key_event_class); - const jclass motion_event_class = env->FindClass("android/view/MotionEvent"); - s_motion_event_class = reinterpret_cast(env->NewGlobalRef(motion_event_class)); - s_motion_event_get_axis_value = env->GetMethodID(s_motion_event_class, "getAxisValue", "(I)F"); - s_motion_event_get_source = env->GetMethodID(s_motion_event_class, "getSource", "()I"); - env->DeleteLocalRef(motion_event_class); + const jclass motion_event_class = env->FindClass("android/view/MotionEvent"); + s_motion_event_class = reinterpret_cast(env->NewGlobalRef(motion_event_class)); + s_motion_event_get_axis_value = env->GetMethodID(s_motion_event_class, "getAxisValue", "(I)F"); + s_motion_event_get_source = env->GetMethodID(s_motion_event_class, "getSource", "()I"); + env->DeleteLocalRef(motion_event_class); - const jclass controller_interface_class = - env->FindClass("org/dolphinemu/dolphinemu/features/input/model/ControllerInterface"); - s_controller_interface_class = - reinterpret_cast(env->NewGlobalRef(controller_interface_class)); - s_controller_interface_register_input_device_listener = - env->GetStaticMethodID(s_controller_interface_class, "registerInputDeviceListener", "()V"); - s_controller_interface_unregister_input_device_listener = - env->GetStaticMethodID(s_controller_interface_class, "unregisterInputDeviceListener", "()V"); - s_controller_interface_get_vibrator_manager = - env->GetStaticMethodID(s_controller_interface_class, "getVibratorManager", - "(Landroid/view/InputDevice;)Lorg/dolphinemu/dolphinemu/features/" - "input/model/DolphinVibratorManager;"); - s_controller_interface_get_system_vibrator_manager = env->GetStaticMethodID( - s_controller_interface_class, "getSystemVibratorManager", - "()Lorg/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager;"); - s_controller_interface_vibrate = - env->GetStaticMethodID(s_controller_interface_class, "vibrate", "(Landroid/os/Vibrator;)V"); - env->DeleteLocalRef(controller_interface_class); + const jclass controller_interface_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/ControllerInterface"); + s_controller_interface_class = + reinterpret_cast(env->NewGlobalRef(controller_interface_class)); + s_controller_interface_register_input_device_listener = + env->GetStaticMethodID(s_controller_interface_class, "registerInputDeviceListener", "()V"); + s_controller_interface_unregister_input_device_listener = + env->GetStaticMethodID(s_controller_interface_class, "unregisterInputDeviceListener", "()V"); + s_controller_interface_get_vibrator_manager = + env->GetStaticMethodID(s_controller_interface_class, "getVibratorManager", + "(Landroid/view/InputDevice;)Lorg/dolphinemu/dolphinemu/features/" + "input/model/DolphinVibratorManager;"); + s_controller_interface_get_system_vibrator_manager = env->GetStaticMethodID( + s_controller_interface_class, "getSystemVibratorManager", + "()Lorg/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager;"); + s_controller_interface_vibrate = + env->GetStaticMethodID(s_controller_interface_class, "vibrate", "(Landroid/os/Vibrator;)V"); + env->DeleteLocalRef(controller_interface_class); - const jclass sensor_event_listener_class = - env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener"); - s_sensor_event_listener_class = - reinterpret_cast(env->NewGlobalRef(sensor_event_listener_class)); - s_sensor_event_listener_constructor = - env->GetMethodID(s_sensor_event_listener_class, "", "()V"); - s_sensor_event_listener_constructor_input_device = - env->GetMethodID(s_sensor_event_listener_class, "", "(Landroid/view/InputDevice;)V"); - s_sensor_event_listener_set_device_qualifier = env->GetMethodID( - s_sensor_event_listener_class, "setDeviceQualifier", "(Ljava/lang/String;)V"); - s_sensor_event_listener_request_unsuspend_sensor = env->GetMethodID( - s_sensor_event_listener_class, "requestUnsuspendSensor", "(Ljava/lang/String;)V"); - s_sensor_event_listener_get_axis_names = - env->GetMethodID(s_sensor_event_listener_class, "getAxisNames", "()[Ljava/lang/String;"); - s_sensor_event_listener_get_negative_axes = - env->GetMethodID(s_sensor_event_listener_class, "getNegativeAxes", "()[Z"); - env->DeleteLocalRef(sensor_event_listener_class); + const jclass sensor_event_listener_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener"); + s_sensor_event_listener_class = + reinterpret_cast(env->NewGlobalRef(sensor_event_listener_class)); + s_sensor_event_listener_constructor = + env->GetMethodID(s_sensor_event_listener_class, "", "()V"); + s_sensor_event_listener_constructor_input_device = + env->GetMethodID(s_sensor_event_listener_class, "", "(Landroid/view/InputDevice;)V"); + s_sensor_event_listener_set_device_qualifier = env->GetMethodID( + s_sensor_event_listener_class, "setDeviceQualifier", "(Ljava/lang/String;)V"); + s_sensor_event_listener_request_unsuspend_sensor = env->GetMethodID( + s_sensor_event_listener_class, "requestUnsuspendSensor", "(Ljava/lang/String;)V"); + s_sensor_event_listener_get_axis_names = + env->GetMethodID(s_sensor_event_listener_class, "getAxisNames", "()[Ljava/lang/String;"); + s_sensor_event_listener_get_negative_axes = + env->GetMethodID(s_sensor_event_listener_class, "getNegativeAxes", "()[Z"); + env->DeleteLocalRef(sensor_event_listener_class); - const jclass dolphin_vibrator_manager_class = - env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager"); - s_dolphin_vibrator_manager_class = - reinterpret_cast(env->NewGlobalRef(dolphin_vibrator_manager_class)); - s_dolphin_vibrator_manager_get_vibrator = - env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibrator", "(I)Landroid/os/Vibrator;"); - s_dolphin_vibrator_manager_get_vibrator_ids = - env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibratorIds", "()[I"); - env->DeleteLocalRef(dolphin_vibrator_manager_class); + const jclass dolphin_vibrator_manager_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager"); + s_dolphin_vibrator_manager_class = + reinterpret_cast(env->NewGlobalRef(dolphin_vibrator_manager_class)); + s_dolphin_vibrator_manager_get_vibrator = + env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibrator", "(I)Landroid/os/Vibrator;"); + s_dolphin_vibrator_manager_get_vibrator_ids = + env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibratorIds", "()[I"); + env->DeleteLocalRef(dolphin_vibrator_manager_class); - jintArray keycodes_array = CreateKeyCodesArray(env); - s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); - env->DeleteLocalRef(keycodes_array); + jintArray keycodes_array = CreateKeyCodesArray(env); + s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); + env->DeleteLocalRef(keycodes_array); - env->CallStaticVoidMethod(s_controller_interface_class, - s_controller_interface_register_input_device_listener); + env->CallStaticVoidMethod(s_controller_interface_class, + s_controller_interface_register_input_device_listener); - RegisterDevicesChangedCallbackIfNeeded(env, s_controller_interface_class); -} + RegisterDevicesChangedCallbackIfNeeded(env, s_controller_interface_class); + } -InputBackend::~InputBackend() -{ - JNIEnv* env = IDCache::GetEnvForThread(); + InputBackend::~InputBackend() + { + JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(s_controller_interface_class, - s_controller_interface_unregister_input_device_listener); + env->CallStaticVoidMethod(s_controller_interface_class, + s_controller_interface_unregister_input_device_listener); - env->DeleteGlobalRef(s_input_device_class); - env->DeleteGlobalRef(s_motion_range_class); - env->DeleteGlobalRef(s_input_event_class); - env->DeleteGlobalRef(s_key_event_class); - env->DeleteGlobalRef(s_motion_event_class); - env->DeleteGlobalRef(s_controller_interface_class); - env->DeleteGlobalRef(s_sensor_event_listener_class); - env->DeleteGlobalRef(s_dolphin_vibrator_manager_class); - env->DeleteGlobalRef(s_keycodes_array); -} + env->DeleteGlobalRef(s_input_device_class); + env->DeleteGlobalRef(s_motion_range_class); + env->DeleteGlobalRef(s_input_event_class); + env->DeleteGlobalRef(s_key_event_class); + env->DeleteGlobalRef(s_motion_event_class); + env->DeleteGlobalRef(s_controller_interface_class); + env->DeleteGlobalRef(s_sensor_event_listener_class); + env->DeleteGlobalRef(s_dolphin_vibrator_manager_class); + env->DeleteGlobalRef(s_keycodes_array); + } -void InputBackend::AddDevice(JNIEnv* env, jint device_id) -{ - // Remove the device in case it already exists (maybe it's possible for a device to connect, - // be processed by PopulateDevices, and then be processed by onInputDeviceAdded) - RemoveDevice(device_id); + void InputBackend::AddDevice(JNIEnv* env, jint device_id) + { + // Remove the device in case it already exists (maybe it's possible for a device to connect, + // be processed by PopulateDevices, and then be processed by onInputDeviceAdded) + RemoveDevice(device_id); - jobject input_device = - env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device, device_id); + jobject input_device = + env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device, device_id); - if (!input_device) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device with ID {}", device_id); - return; - } + if (!input_device) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device with ID {}", device_id); + return; + } - auto device = std::make_shared(env, device_id, input_device); + auto device = std::make_shared(env, device_id, input_device); - env->DeleteLocalRef(input_device); + env->DeleteLocalRef(input_device); - if (device->Inputs().empty() && device->Outputs().empty()) - return; + if (device->Inputs().empty() && device->Outputs().empty()) + return; - g_controller_interface.AddDevice(device); + g_controller_interface.AddDevice(device); - Core::DeviceQualifier qualifier; - qualifier.FromDevice(device.get()); + Core::DeviceQualifier qualifier; + qualifier.FromDevice(device.get()); - INFO_LOG_FMT(CONTROLLERINTERFACE, "Added device ID {} as {}", device_id, - device->GetQualifiedName()); - s_device_id_to_device_qualifier.emplace(device_id, qualifier); + INFO_LOG_FMT(CONTROLLERINTERFACE, "Added device ID {} as {}", device_id, + device->GetQualifiedName()); + s_device_id_to_device_qualifier.emplace(device_id, qualifier); - jstring j_qualifier = ToJString(env, qualifier.ToString()); - env->CallVoidMethod(device->GetSensorEventListener(), - s_sensor_event_listener_set_device_qualifier, j_qualifier); - env->DeleteLocalRef(j_qualifier); -} + jstring j_qualifier = ToJString(env, qualifier.ToString()); + env->CallVoidMethod(device->GetSensorEventListener(), + s_sensor_event_listener_set_device_qualifier, j_qualifier); + env->DeleteLocalRef(j_qualifier); + } -void InputBackend::AddSensorDevice(JNIEnv* env) -{ - // Device sensors (accelerometer, etc.) aren't associated with any Android InputDevice. - // Create an otherwise empty Dolphin input device so that they have somewhere to live. + void InputBackend::AddSensorDevice(JNIEnv* env) + { + // Device sensors (accelerometer, etc.) aren't associated with any Android InputDevice. + // Create an otherwise empty Dolphin input device so that they have somewhere to live. - auto device = std::make_shared(env, "Device Sensors"); + auto device = std::make_shared(env, "Device Sensors"); - if (device->Inputs().empty() && device->Outputs().empty()) - return; + if (device->Inputs().empty() && device->Outputs().empty()) + return; - g_controller_interface.AddDevice(device); + g_controller_interface.AddDevice(device); - Core::DeviceQualifier qualifier; - qualifier.FromDevice(device.get()); + Core::DeviceQualifier qualifier; + qualifier.FromDevice(device.get()); - INFO_LOG_FMT(CONTROLLERINTERFACE, "Added sensor device as {}", device->GetQualifiedName()); + INFO_LOG_FMT(CONTROLLERINTERFACE, "Added sensor device as {}", device->GetQualifiedName()); - jstring j_qualifier = ToJString(env, qualifier.ToString()); - env->CallVoidMethod(device->GetSensorEventListener(), - s_sensor_event_listener_set_device_qualifier, j_qualifier); - env->DeleteLocalRef(j_qualifier); -} + jstring j_qualifier = ToJString(env, qualifier.ToString()); + env->CallVoidMethod(device->GetSensorEventListener(), + s_sensor_event_listener_set_device_qualifier, j_qualifier); + env->DeleteLocalRef(j_qualifier); + } -void InputBackend::RemoveDevice(jint device_id) -{ - g_controller_interface.RemoveDevice([device_id](const ciface::Core::Device* device) { - return device->GetSource() == SOURCE && - static_cast(device)->GetDeviceID() == device_id; - }); + void InputBackend::RemoveDevice(jint device_id) + { + g_controller_interface.RemoveDevice([device_id](const ciface::Core::Device* device) { + return device->GetSource() == SOURCE && + static_cast(device)->GetDeviceID() == device_id; + }); - s_device_id_to_device_qualifier.erase(device_id); -} + s_device_id_to_device_qualifier.erase(device_id); + } -void InputBackend::PopulateDevices() -{ - INFO_LOG_FMT(CONTROLLERINTERFACE, "Android populating devices"); + void InputBackend::PopulateDevices() + { + INFO_LOG_FMT(CONTROLLERINTERFACE, "Android populating devices"); - JNIEnv* env = IDCache::GetEnvForThread(); + JNIEnv* env = IDCache::GetEnvForThread(); - jintArray device_ids_array = reinterpret_cast( - env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device_ids)); - int* device_ids = env->GetIntArrayElements(device_ids_array, nullptr); - jsize device_ids_count = env->GetArrayLength(device_ids_array); - for (jsize i = 0; i < device_ids_count; ++i) - AddDevice(env, device_ids[i]); - env->ReleaseIntArrayElements(device_ids_array, device_ids, JNI_ABORT); - env->DeleteLocalRef(device_ids_array); + jintArray device_ids_array = reinterpret_cast( + env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device_ids)); + int* device_ids = env->GetIntArrayElements(device_ids_array, nullptr); + jsize device_ids_count = env->GetArrayLength(device_ids_array); + for (jsize i = 0; i < device_ids_count; ++i) + AddDevice(env, device_ids[i]); + env->ReleaseIntArrayElements(device_ids_array, device_ids, JNI_ABORT); + env->DeleteLocalRef(device_ids_array); - AddSensorDevice(env); -} + AddSensorDevice(env); + } } // namespace ciface::Android @@ -1044,179 +1055,193 @@ extern "C" { JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchKeyEventNative( - JNIEnv* env, jclass, jobject key_event) + JNIEnv* env, jclass, jobject key_event) { - const jint action = env->CallIntMethod(key_event, s_key_event_get_action); - ControlState state; - switch (action) - { - case AKEY_EVENT_ACTION_DOWN: - state = 1; - break; - case AKEY_EVENT_ACTION_UP: - state = 0; - break; - default: - return JNI_FALSE; - } + const jint action = env->CallIntMethod(key_event, s_key_event_get_action); + ControlState state; + switch (action) + { + case AKEY_EVENT_ACTION_DOWN: + state = 1; + break; + case AKEY_EVENT_ACTION_UP: + state = 0; + break; + default: + return JNI_FALSE; + } - const jint device_id = env->CallIntMethod(key_event, s_input_event_get_device_id); - const std::shared_ptr device = FindDevice(device_id); - if (!device) - return JNI_FALSE; + const jint device_id = env->CallIntMethod(key_event, s_input_event_get_device_id); + const std::shared_ptr device = FindDevice(device_id); + if (!device) + return JNI_FALSE; - const jint keycode = env->CallIntMethod(key_event, s_key_event_get_keycode); - const std::string input_name = ConstructKeyName(keycode); + const jint keycode = env->CallIntMethod(key_event, s_key_event_get_keycode); + const std::string input_name = ConstructKeyName(keycode); - ciface::Core::Device::Input* input = device->FindInput(input_name); - if (!input) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find input {} in device {}", input_name, - device->GetQualifiedName()); - return false; - } + ciface::Core::Device::Input* input = device->FindInput(input_name); + if (!input) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find input {} in device {}", input_name, + device->GetQualifiedName()); + return false; + } - auto casted_input = static_cast(input); - casted_input->SetState(state); - const Clock::time_point last_polled = casted_input->GetLastPolled(); + auto casted_input = static_cast(input); + casted_input->SetState(state); + const Clock::time_point last_polled = casted_input->GetLastPolled(); - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, device->GetQualifiedName(), - state); + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, device->GetQualifiedName(), + state); - return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; + return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchGenericMotionEventNative( - JNIEnv* env, jclass, jobject motion_event) + JNIEnv* env, jclass, jobject motion_event) { - const jint device_id = env->CallIntMethod(motion_event, s_input_event_get_device_id); - const std::shared_ptr device = FindDevice(device_id); - if (!device) - return JNI_FALSE; + const jint device_id = env->CallIntMethod(motion_event, s_input_event_get_device_id); + const std::shared_ptr device = FindDevice(device_id); + if (!device) + return JNI_FALSE; - const jint source = env->CallIntMethod(motion_event, s_motion_event_get_source); - const std::string axis_name_prefix = ConstructAxisNamePrefix(source); + const jint source = env->CallIntMethod(motion_event, s_motion_event_get_source); + const std::string axis_name_prefix = ConstructAxisNamePrefix(source); - Clock::time_point last_polled{}; + Clock::time_point last_polled{}; - for (ciface::Core::Device::Input* input : device->Inputs()) - { - const std::string input_name = input->GetName(); - if (input_name.starts_with(axis_name_prefix)) + for (ciface::Core::Device::Input* input : device->Inputs()) { - const std::string axis_id_str = input_name.substr( - axis_name_prefix.size(), input_name.size() - axis_name_prefix.size() - sizeof('+')); + const std::string input_name = input->GetName(); + if (input_name.starts_with(axis_name_prefix)) + { + const std::string axis_id_str = input_name.substr( + axis_name_prefix.size(), input_name.size() - axis_name_prefix.size() - sizeof('+')); - int axis_id; - if (!TryParse(axis_id_str, &axis_id)) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Failed to parse \"{}\" from \"{}\" as axis ID", - axis_id_str, input_name); - continue; - } + int axis_id; + if (!TryParse(axis_id_str, &axis_id)) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Failed to parse \"{}\" from \"{}\" as axis ID", + axis_id_str, input_name); + continue; + } - float value = env->CallFloatMethod(motion_event, s_motion_event_get_axis_value, axis_id); + float value = env->CallFloatMethod(motion_event, s_motion_event_get_axis_value, axis_id); - auto casted_input = static_cast(input); - casted_input->SetState(value); - last_polled = std::max(last_polled, casted_input->GetLastPolled()); + auto casted_input = static_cast(input); + casted_input->SetState(value); + last_polled = std::max(last_polled, casted_input->GetLastPolled()); - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, - device->GetQualifiedName(), value); + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, + device->GetQualifiedName(), value); + } } - } - return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; + return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEventNative( - JNIEnv* env, jclass, jstring j_device_qualifier, jstring j_axis_name, jfloat value) + JNIEnv* env, jclass, jstring j_device_qualifier, jstring j_axis_name, jfloat value) { - ciface::Core::DeviceQualifier device_qualifier; - device_qualifier.FromString(GetJString(env, j_device_qualifier)); - const std::shared_ptr device = - g_controller_interface.FindDevice(device_qualifier); - if (!device) - return JNI_FALSE; + ciface::Core::DeviceQualifier device_qualifier; + device_qualifier.FromString(GetJString(env, j_device_qualifier)); + const std::shared_ptr device = + g_controller_interface.FindDevice(device_qualifier); + if (!device) + return JNI_FALSE; - const std::string axis_name = GetJString(env, j_axis_name); + const std::string axis_name = GetJString(env, j_axis_name); - Clock::time_point last_polled{}; + Clock::time_point last_polled{}; - for (ciface::Core::Device::Input* input : device->Inputs()) - { - const std::string input_name = input->GetName(); - if (input_name == axis_name) + for (ciface::Core::Device::Input* input : device->Inputs()) { - auto casted_input = static_cast(input); - casted_input->SetState(value); - last_polled = std::max(last_polled, casted_input->GetLastPolled()); + const std::string input_name = input->GetName(); + if (input_name == axis_name) + { + auto casted_input = static_cast(input); + casted_input->SetState(value); + last_polled = std::max(last_polled, casted_input->GetLastPolled()); + } } - } - return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; + return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_notifySensorSuspendedState( - JNIEnv* env, jclass, jstring j_device_qualifier, jobjectArray j_axis_names, jboolean suspended) + JNIEnv* env, jclass, jstring j_device_qualifier, jobjectArray j_axis_names, jboolean suspended) { - ciface::Core::DeviceQualifier device_qualifier; - device_qualifier.FromString(GetJString(env, j_device_qualifier)); - const std::shared_ptr device = - g_controller_interface.FindDevice(device_qualifier); - if (!device) - return; +ciface::Core::DeviceQualifier device_qualifier; +device_qualifier.FromString(GetJString(env, j_device_qualifier)); +const std::shared_ptr device = + g_controller_interface.FindDevice(device_qualifier); +if (!device) +return; - const std::vector axis_names = JStringArrayToVector(env, j_axis_names); +const std::vector axis_names = JStringArrayToVector(env, j_axis_names); - for (ciface::Core::Device::Input* input : device->Inputs()) - { - if (Common::Contains(axis_names, input->GetName())) - { - auto casted_input = static_cast(input); - casted_input->NotifyIsSuspended(static_cast(suspended)); - } - } +for (ciface::Core::Device::Input* input : device->Inputs()) +{ +if (Common::Contains(axis_names, input->GetName())) +{ +auto casted_input = static_cast(input); +casted_input->NotifyIsSuspended(static_cast(suspended)); +} +} } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_00024InputDeviceListener_onInputDeviceAdded( - JNIEnv* env, jobject, jint device_id) + JNIEnv* env, jobject, jint device_id) { - ciface::Android::InputBackend::AddDevice(env, device_id); +ciface::Android::InputBackend::AddDevice(env, device_id); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_00024InputDeviceListener_onInputDeviceRemoved( - JNIEnv*, jobject, jint device_id) + JNIEnv*, jobject, jint device_id) { - ciface::Android::InputBackend::RemoveDevice(device_id); +ciface::Android::InputBackend::RemoveDevice(device_id); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_00024InputDeviceListener_onInputDeviceChanged( - JNIEnv* env, jobject, jint device_id) + JNIEnv* env, jobject, jint device_id) { - // AddDevice will automatically remove the existing device - ciface::Android::InputBackend::AddDevice(env, device_id); +// AddDevice will automatically remove the existing device +ciface::Android::InputBackend::AddDevice(env, device_id); } JNIEXPORT jobjectArray JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getAllDeviceStrings( - JNIEnv* env, jclass) + JNIEnv* env, jclass) { - return SpanToJStringArray(env, g_controller_interface.GetAllDeviceStrings()); + return SpanToJStringArray(env, g_controller_interface.GetAllDeviceStrings()); } JNIEXPORT jobject JNICALL -Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getDevice( - JNIEnv* env, jclass, jstring j_device_string) + Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getDevice( + JNIEnv* env, jclass, jstring j_device_string) { - ciface::Core::DeviceQualifier qualifier; - qualifier.FromString(GetJString(env, j_device_string)); - return CoreDeviceToJava(env, g_controller_interface.FindDevice(qualifier)); +ciface::Core::DeviceQualifier qualifier; +qualifier.FromString(GetJString(env, j_device_string)); +return CoreDeviceToJava(env, g_controller_interface.FindDevice(qualifier)); } + +JNIEXPORT jstring JNICALL + Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getDescriptorForDevice( + JNIEnv* env, jclass, jstring j_device_string) +{ +ciface::Core::DeviceQualifier qualifier; +qualifier.FromString(GetJString(env, j_device_string)); +auto device = g_controller_interface.FindDevice(qualifier); +if (!device || device->GetSource() != SOURCE) +return ToJString(env, ""); + +return ToJString(env, static_cast(device.get())->GetDescriptor()); +} + } From 33ef73e79228994ace0a502ec8743d123bb3f686 Mon Sep 17 00:00:00 2001 From: hae-carrie-perez Date: Fri, 20 Mar 2026 11:42:08 -0400 Subject: [PATCH 2/2] Fix formatting issues per review feedback Fix formatting issues per review feedback --- .../input/model/ControllerInterface.kt | 4 +- .../settings/ui/SettingsFragmentPresenter.kt | 35 +- .../app/src/main/res/values/strings.xml | 4 +- .../ControllerInterface/Android/Android.cpp | 2056 ++++++++--------- 4 files changed, 1050 insertions(+), 1049 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt index ff9fa58844..70a2aebbff 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt @@ -98,8 +98,8 @@ object ControllerInterface { external fun getAllDeviceStrings(): Array external fun getDevice(deviceString: String): CoreDevice? - - external fun getDescriptorForDevice(deviceString: String): String + + external fun getDescriptorForDevice(deviceString: String): String private fun onInputStateChanged() { // When a single SensorEvent is dispatched, this method is likely to get called many times. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt index 1110f7a0f4..d6ef39d234 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt @@ -25,6 +25,7 @@ import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroupContainer import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface import org.dolphinemu.dolphinemu.features.input.model.view.InputDeviceSetting import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialog @@ -32,7 +33,6 @@ import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialogPresenter import org.dolphinemu.dolphinemu.features.settings.model.* import org.dolphinemu.dolphinemu.features.settings.model.view.* import org.dolphinemu.dolphinemu.features.settings.model.AchievementModel.logout -import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting import org.dolphinemu.dolphinemu.model.GpuDriverMetadata import org.dolphinemu.dolphinemu.utils.* @@ -504,16 +504,16 @@ class SettingsFragmentPresenter( override val isOverridden: Boolean get() = BooleanSetting.MAIN_DSP_HLE.isOverridden || - BooleanSetting.MAIN_DSP_JIT.isOverridden + BooleanSetting.MAIN_DSP_JIT.isOverridden override val isRuntimeEditable: Boolean get() = BooleanSetting.MAIN_DSP_HLE.isRuntimeEditable && - BooleanSetting.MAIN_DSP_JIT.isRuntimeEditable + BooleanSetting.MAIN_DSP_JIT.isRuntimeEditable override fun delete(settings: Settings): Boolean { // Not short circuiting return BooleanSetting.MAIN_DSP_HLE.delete(settings) and - BooleanSetting.MAIN_DSP_JIT.delete(settings) + BooleanSetting.MAIN_DSP_JIT.delete(settings) } } @@ -972,8 +972,8 @@ class SettingsFragmentPresenter( 0, false ) { - fragmentView.showDialogFragment(LoginDialog(this)) - loadSettingsList() + fragmentView.showDialogFragment(LoginDialog(this)) + loadSettingsList() }) } else { sl.add( @@ -985,8 +985,8 @@ class SettingsFragmentPresenter( 0, false ) { - logout() - loadSettingsList() + logout() + loadSettingsList() }) } sl.add( @@ -1083,16 +1083,16 @@ class SettingsFragmentPresenter( override val isOverridden: Boolean get() = BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.isOverridden || - BooleanSetting.MAIN_SYNC_GPU.isOverridden + BooleanSetting.MAIN_SYNC_GPU.isOverridden override val isRuntimeEditable: Boolean get() = BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.isRuntimeEditable && - BooleanSetting.MAIN_SYNC_GPU.isRuntimeEditable + BooleanSetting.MAIN_SYNC_GPU.isRuntimeEditable override fun delete(settings: Settings): Boolean { // Not short circuiting return BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.delete(settings) and - BooleanSetting.MAIN_SYNC_GPU.delete(settings) + BooleanSetting.MAIN_SYNC_GPU.delete(settings) } } @@ -2209,7 +2209,7 @@ class SettingsFragmentPresenter( BooleanSetting.MAIN_DEBUG_JIT_ENABLE_PROFILING, R.string.debug_jit_enable_block_profiling, 0 - ) + ) ) sl.add( RunRunnable( @@ -2547,6 +2547,7 @@ class SettingsFragmentPresenter( controller ) ) + sl.add( InputStringSetting( context, @@ -2648,11 +2649,11 @@ class SettingsFragmentPresenter( * @param groupTypeFilter If this is non-null, only groups whose types match this are considered. */ private fun addControllerMappingSettings( - sl: ArrayList, - controller: EmulatedController, - groupTypeFilter: Set? + sl: ArrayList, + controller: EmulatedController, + groupTypeFilter: Set? ) { - addContainerMappingSettings(sl, controller, controller, groupTypeFilter) + addContainerMappingSettings(sl, controller, controller, groupTypeFilter) } /** @@ -2747,7 +2748,7 @@ class SettingsFragmentPresenter( val defaultDevice = controller.getDefaultDevice() hasOldControllerSettings = defaultDevice.startsWith("Android/") && - defaultDevice.endsWith("/Touchscreen") + defaultDevice.endsWith("/Touchscreen") fragmentView.setOldControllerSettingsWarningVisibility(hasOldControllerSettings) } diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 08e00a5e42..03754a6fa6 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -32,8 +32,8 @@ Extension Device - Controller Label - Give this controller a unique name + Controller Label + Give this controller a unique name Create Mappings for Other Devices Detects inputs from all devices, not just the selected device. Profile diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 95cfc3647b..f17262ecc9 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -31,1023 +31,1023 @@ namespace { - std::string SOURCE = "Android"; +std::string SOURCE = "Android"; - jclass s_list_class; - jmethodID s_list_get; - jmethodID s_list_size; +jclass s_list_class; +jmethodID s_list_get; +jmethodID s_list_size; - jclass s_input_device_class; - jmethodID s_input_device_get_device_ids; - jmethodID s_input_device_get_device; - jmethodID s_input_device_get_controller_number; - jmethodID s_input_device_get_motion_ranges; - jmethodID s_input_device_get_name; - jmethodID s_input_device_get_descriptor; - jmethodID s_input_device_get_sources; - jmethodID s_input_device_has_keys; +jclass s_input_device_class; +jmethodID s_input_device_get_device_ids; +jmethodID s_input_device_get_device; +jmethodID s_input_device_get_controller_number; +jmethodID s_input_device_get_motion_ranges; +jmethodID s_input_device_get_name; +jmethodID s_input_device_get_descriptor; +jmethodID s_input_device_get_sources; +jmethodID s_input_device_has_keys; - jclass s_motion_range_class; - jmethodID s_motion_range_get_axis; - jmethodID s_motion_range_get_max; - jmethodID s_motion_range_get_min; - jmethodID s_motion_range_get_source; +jclass s_motion_range_class; +jmethodID s_motion_range_get_axis; +jmethodID s_motion_range_get_max; +jmethodID s_motion_range_get_min; +jmethodID s_motion_range_get_source; - jclass s_input_event_class; - jmethodID s_input_event_get_device_id; +jclass s_input_event_class; +jmethodID s_input_event_get_device_id; - jclass s_key_event_class; - jmethodID s_key_event_get_action; - jmethodID s_key_event_get_keycode; +jclass s_key_event_class; +jmethodID s_key_event_get_action; +jmethodID s_key_event_get_keycode; - jclass s_motion_event_class; - jmethodID s_motion_event_get_axis_value; - jmethodID s_motion_event_get_source; +jclass s_motion_event_class; +jmethodID s_motion_event_get_axis_value; +jmethodID s_motion_event_get_source; - jclass s_controller_interface_class; - jmethodID s_controller_interface_register_input_device_listener; - jmethodID s_controller_interface_unregister_input_device_listener; - jmethodID s_controller_interface_get_vibrator_manager; - jmethodID s_controller_interface_get_system_vibrator_manager; - jmethodID s_controller_interface_vibrate; +jclass s_controller_interface_class; +jmethodID s_controller_interface_register_input_device_listener; +jmethodID s_controller_interface_unregister_input_device_listener; +jmethodID s_controller_interface_get_vibrator_manager; +jmethodID s_controller_interface_get_system_vibrator_manager; +jmethodID s_controller_interface_vibrate; - jclass s_sensor_event_listener_class; - jmethodID s_sensor_event_listener_constructor; - jmethodID s_sensor_event_listener_constructor_input_device; - jmethodID s_sensor_event_listener_set_device_qualifier; - jmethodID s_sensor_event_listener_request_unsuspend_sensor; - jmethodID s_sensor_event_listener_get_axis_names; - jmethodID s_sensor_event_listener_get_negative_axes; +jclass s_sensor_event_listener_class; +jmethodID s_sensor_event_listener_constructor; +jmethodID s_sensor_event_listener_constructor_input_device; +jmethodID s_sensor_event_listener_set_device_qualifier; +jmethodID s_sensor_event_listener_request_unsuspend_sensor; +jmethodID s_sensor_event_listener_get_axis_names; +jmethodID s_sensor_event_listener_get_negative_axes; - jclass s_dolphin_vibrator_manager_class; - jmethodID s_dolphin_vibrator_manager_get_vibrator; - jmethodID s_dolphin_vibrator_manager_get_vibrator_ids; +jclass s_dolphin_vibrator_manager_class; +jmethodID s_dolphin_vibrator_manager_get_vibrator; +jmethodID s_dolphin_vibrator_manager_get_vibrator_ids; - jintArray s_keycodes_array; +jintArray s_keycodes_array; - using Clock = std::chrono::steady_clock; - constexpr Clock::duration ACTIVE_INPUT_TIMEOUT = std::chrono::milliseconds(1000); +using Clock = std::chrono::steady_clock; +constexpr Clock::duration ACTIVE_INPUT_TIMEOUT = std::chrono::milliseconds(1000); - std::unordered_map s_device_id_to_device_qualifier; +std::unordered_map s_device_id_to_device_qualifier; - constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31 +constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31 - const std::array KEYCODE_NAMES = { - "Unknown", - "Soft Left", - "Soft Right", - "Home", - "Back", - "Call", - "End Call", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "Star", - "Pound", - "Up", - "Down", - "Left", - "Right", - "Center", - "Volume Up", - "Volume Down", - "Power", - "Camera", - "Clear", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "Comma", - "Period", - "Left Alt", - "Right Alt", - "Left Shift", - "Right Shift", - "Tab", - "Space", - "Sym", - "Explorer", - "Envelope", - "Enter", - "Backspace", - "Grave", - "Minus", - "Equals", - "Left Bracket", - "Right Bracket", - "Backslash", - "Semicolon", - "Apostrophe", - "Slash", - "At", - "Num", - "Headset Hook", - "Focus", - "Plus", - "Menu", - "Notification", - "Search", - "Play Pause", - "Stop", - "Next", - "Previous", - "Rewind", - "Fast Forward", - "Mute", - "Page Up", - "Page Down", - "Emoji", - "Switch Charset", - "Button A", - "Button B", - "Button C", - "Button X", - "Button Y", - "Button Z", - "Button L1", - "Button R1", - "Button L2", - "Button R2", - "Button L3", - "Button R3", - "Start", - "Select", - "Mode", - "Escape", - "Delete", - "Left Ctrl", - "Right Ctrl", - "Caps Lock", - "Scroll Lock", - "Left Meta", - "Right Meta", - "Fn", - "PrtSc SysRq", - "Pause Break", - "Move Home", - "Move End", - "Insert", - "Forward", - "Play", - "Pause", - "Close", - "Eject", - "Record", - "F1", - "F2", - "F3", - "F4", - "F5", - "F6", - "F7", - "F8", - "F9", - "F10", - "F11", - "F12", - "Num Lock", - "Numpad 0", - "Numpad 1", - "Numpad 2", - "Numpad 3", - "Numpad 4", - "Numpad 5", - "Numpad 6", - "Numpad 7", - "Numpad 8", - "Numpad 9", - "Numpad Divide", - "Numpad Multiply", - "Numpad Subtract", - "Numpad Add", - "Numpad Dot", - "Numpad Comma", - "Numpad Enter", - "Numpad Equals", - "Numpad Left Paren", - "Numpad Right Paren", - "Volume Mute", - "Info", - "Channel Up", - "Channel Down", - "Zoom In", - "Zoom Out", - "TV", - "Window", - "Guide", - "DVR", - "Bookmark", - "Captions", - "Settings", - "TV Power", - "TV Input", - "STB Power", - "STB Input", - "AVR Power", - "AVR Input", - "Prog Red", - "Prog Green", - "Prog Yellow", - "Prog Blue", - "App Switch", - "Button 1", - "Button 2", - "Button 3", - "Button 4", - "Button 5", - "Button 6", - "Button 7", - "Button 8", - "Button 9", - "Button 10", - "Button 11", - "Button 12", - "Button 13", - "Button 14", - "Button 15", - "Button 16", - "Language Switch", - "Manner Mode", - "3D Mode", - "Contacts", - "Calendar", - "Music", - "Calculator", - "Zenkaku Hankaku", - "Eisu", - "Henkan", - "Muhenkan", - "Katakana Hiragana", - "Yen", - "Ro", - "Kana", - "Assist", - "Brightness Down", - "Brightness Up", - "Audio Track", - "Sleep", - "Wakeup", - "Pairing", - "Top Menu", - "11", - "12", - "Last Channel", - "Data Service", - "Voice Assist", - "Radio Service", - "Teletext", - "Number Entry", - "Terrestrial Analog", - "Terrestrial Digital", - "Satellite", - "Satellite BS", - "Satellite CS", - "Satellite Service", - "Network", - "Antenna Cable", - "Input HDMI 1", - "Input HDMI 2", - "Input HDMI 3", - "Input HDMI 4", - "Input Composite 1", - "Input Composite 2", - "Input Component 1", - "Input Component 2", - "Input VGA 1", - "Audio Description", - "Audio Description Mix Up", - "Audio Description Mix Down", - "Zoom Mode", - "Contents Menu", - "Media Context Menu", - "Timer Programming", - "Help", - "Navigate Previous", - "Navigate Next", - "Navigate In", - "Navigate Out", - "Stem Primary", - "Stem 1", - "Stem 2", - "Stem 3", - "Up Left", - "Down Left", - "Up Right", - "Down Right", - "Skip Forward", - "Skip Backward", - "Step Forward", - "Step Backward", - "Soft Sleep", - "Cut", - "Copy", - "Paste", - "System Navigation Up", - "System Navigation Down", - "System Navigation Left", - "System Navigation Right", - "All Apps", - "Refresh", - "Thumbs Up", - "Thumbs Down", - "Profile Switch", - }; +const std::array KEYCODE_NAMES = { + "Unknown", + "Soft Left", + "Soft Right", + "Home", + "Back", + "Call", + "End Call", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "Star", + "Pound", + "Up", + "Down", + "Left", + "Right", + "Center", + "Volume Up", + "Volume Down", + "Power", + "Camera", + "Clear", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "Comma", + "Period", + "Left Alt", + "Right Alt", + "Left Shift", + "Right Shift", + "Tab", + "Space", + "Sym", + "Explorer", + "Envelope", + "Enter", + "Backspace", + "Grave", + "Minus", + "Equals", + "Left Bracket", + "Right Bracket", + "Backslash", + "Semicolon", + "Apostrophe", + "Slash", + "At", + "Num", + "Headset Hook", + "Focus", + "Plus", + "Menu", + "Notification", + "Search", + "Play Pause", + "Stop", + "Next", + "Previous", + "Rewind", + "Fast Forward", + "Mute", + "Page Up", + "Page Down", + "Emoji", + "Switch Charset", + "Button A", + "Button B", + "Button C", + "Button X", + "Button Y", + "Button Z", + "Button L1", + "Button R1", + "Button L2", + "Button R2", + "Button L3", + "Button R3", + "Start", + "Select", + "Mode", + "Escape", + "Delete", + "Left Ctrl", + "Right Ctrl", + "Caps Lock", + "Scroll Lock", + "Left Meta", + "Right Meta", + "Fn", + "PrtSc SysRq", + "Pause Break", + "Move Home", + "Move End", + "Insert", + "Forward", + "Play", + "Pause", + "Close", + "Eject", + "Record", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "Num Lock", + "Numpad 0", + "Numpad 1", + "Numpad 2", + "Numpad 3", + "Numpad 4", + "Numpad 5", + "Numpad 6", + "Numpad 7", + "Numpad 8", + "Numpad 9", + "Numpad Divide", + "Numpad Multiply", + "Numpad Subtract", + "Numpad Add", + "Numpad Dot", + "Numpad Comma", + "Numpad Enter", + "Numpad Equals", + "Numpad Left Paren", + "Numpad Right Paren", + "Volume Mute", + "Info", + "Channel Up", + "Channel Down", + "Zoom In", + "Zoom Out", + "TV", + "Window", + "Guide", + "DVR", + "Bookmark", + "Captions", + "Settings", + "TV Power", + "TV Input", + "STB Power", + "STB Input", + "AVR Power", + "AVR Input", + "Prog Red", + "Prog Green", + "Prog Yellow", + "Prog Blue", + "App Switch", + "Button 1", + "Button 2", + "Button 3", + "Button 4", + "Button 5", + "Button 6", + "Button 7", + "Button 8", + "Button 9", + "Button 10", + "Button 11", + "Button 12", + "Button 13", + "Button 14", + "Button 15", + "Button 16", + "Language Switch", + "Manner Mode", + "3D Mode", + "Contacts", + "Calendar", + "Music", + "Calculator", + "Zenkaku Hankaku", + "Eisu", + "Henkan", + "Muhenkan", + "Katakana Hiragana", + "Yen", + "Ro", + "Kana", + "Assist", + "Brightness Down", + "Brightness Up", + "Audio Track", + "Sleep", + "Wakeup", + "Pairing", + "Top Menu", + "11", + "12", + "Last Channel", + "Data Service", + "Voice Assist", + "Radio Service", + "Teletext", + "Number Entry", + "Terrestrial Analog", + "Terrestrial Digital", + "Satellite", + "Satellite BS", + "Satellite CS", + "Satellite Service", + "Network", + "Antenna Cable", + "Input HDMI 1", + "Input HDMI 2", + "Input HDMI 3", + "Input HDMI 4", + "Input Composite 1", + "Input Composite 2", + "Input Component 1", + "Input Component 2", + "Input VGA 1", + "Audio Description", + "Audio Description Mix Up", + "Audio Description Mix Down", + "Zoom Mode", + "Contents Menu", + "Media Context Menu", + "Timer Programming", + "Help", + "Navigate Previous", + "Navigate Next", + "Navigate In", + "Navigate Out", + "Stem Primary", + "Stem 1", + "Stem 2", + "Stem 3", + "Up Left", + "Down Left", + "Up Right", + "Down Right", + "Skip Forward", + "Skip Backward", + "Step Forward", + "Step Backward", + "Soft Sleep", + "Cut", + "Copy", + "Paste", + "System Navigation Up", + "System Navigation Down", + "System Navigation Left", + "System Navigation Right", + "All Apps", + "Refresh", + "Thumbs Up", + "Thumbs Down", + "Profile Switch", +}; - std::string ConstructKeyName(int keycode) - { - return std::string(KEYCODE_NAMES[keycode]); - } +std::string ConstructKeyName(int keycode) +{ + return std::string(KEYCODE_NAMES[keycode]); +} - std::string ConstructAxisNamePrefix(int source) - { - // A device is allowed to have two axes with the same axis ID but different source IDs, - // so we have to make sure to include the source in the axis name. +std::string ConstructAxisNamePrefix(int source) +{ + // A device is allowed to have two axes with the same axis ID but different source IDs, + // so we have to make sure to include the source in the axis name. - static const std::unordered_map source_names{ - {AINPUT_SOURCE_KEYBOARD, "Keyboard"}, - {AINPUT_SOURCE_DPAD, "Dpad"}, - {AINPUT_SOURCE_GAMEPAD, "Gamepad"}, - {AINPUT_SOURCE_TOUCHSCREEN, "Touch"}, - {AINPUT_SOURCE_MOUSE, "Cursor"}, - {AINPUT_SOURCE_STYLUS, "Stylus"}, - {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BTStylus"}, - {AINPUT_SOURCE_TRACKBALL, "Trackball"}, - {AINPUT_SOURCE_MOUSE_RELATIVE, "Mouse"}, - {AINPUT_SOURCE_TOUCHPAD, "Touchpad"}, - {AINPUT_SOURCE_TOUCH_NAVIGATION, "Touchnav"}, - {AINPUT_SOURCE_JOYSTICK, "Axis"}, // The typical source for all axes on a gamepad - {AINPUT_SOURCE_HDMI, "HDMI"}, - {AINPUT_SOURCE_SENSOR, "Sensor"}, - {AINPUT_SOURCE_ROTARY_ENCODER, "Rotary"}, - }; + static const std::unordered_map source_names{ + {AINPUT_SOURCE_KEYBOARD, "Keyboard"}, + {AINPUT_SOURCE_DPAD, "Dpad"}, + {AINPUT_SOURCE_GAMEPAD, "Gamepad"}, + {AINPUT_SOURCE_TOUCHSCREEN, "Touch"}, + {AINPUT_SOURCE_MOUSE, "Cursor"}, + {AINPUT_SOURCE_STYLUS, "Stylus"}, + {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BTStylus"}, + {AINPUT_SOURCE_TRACKBALL, "Trackball"}, + {AINPUT_SOURCE_MOUSE_RELATIVE, "Mouse"}, + {AINPUT_SOURCE_TOUCHPAD, "Touchpad"}, + {AINPUT_SOURCE_TOUCH_NAVIGATION, "Touchnav"}, + {AINPUT_SOURCE_JOYSTICK, "Axis"}, // The typical source for all axes on a gamepad + {AINPUT_SOURCE_HDMI, "HDMI"}, + {AINPUT_SOURCE_SENSOR, "Sensor"}, + {AINPUT_SOURCE_ROTARY_ENCODER, "Rotary"}, + }; - const auto it = source_names.find(source); - if (it != source_names.end()) - return fmt::format("{} ", it->second); - else - return fmt::format("Axis {:08x}/", source); - } + const auto it = source_names.find(source); + if (it != source_names.end()) + return fmt::format("{} ", it->second); + else + return fmt::format("Axis {:08x}/", source); +} - std::string ConstructAxisName(int source, int axis, bool negative) - { - const char sign = negative ? '-' : '+'; - return fmt::format("{}{}{}", ConstructAxisNamePrefix(source), axis, sign); - } +std::string ConstructAxisName(int source, int axis, bool negative) +{ + const char sign = negative ? '-' : '+'; + return fmt::format("{}{}{}", ConstructAxisNamePrefix(source), axis, sign); +} - std::shared_ptr FindDevice(jint device_id) - { - const auto it = s_device_id_to_device_qualifier.find(device_id); - if (it == s_device_id_to_device_qualifier.end()) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device ID {}", device_id); - return nullptr; - } +std::shared_ptr FindDevice(jint device_id) +{ + const auto it = s_device_id_to_device_qualifier.find(device_id); + if (it == s_device_id_to_device_qualifier.end()) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device ID {}", device_id); + return nullptr; + } - const ciface::Core::DeviceQualifier& qualifier = it->second; - std::shared_ptr device = g_controller_interface.FindDevice(qualifier); - if (!device) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device {}", qualifier.ToString()); - return nullptr; - } + const ciface::Core::DeviceQualifier& qualifier = it->second; + std::shared_ptr device = g_controller_interface.FindDevice(qualifier); + if (!device) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device {}", qualifier.ToString()); + return nullptr; + } - return device; - } + return device; +} - void RegisterDevicesChangedCallbackIfNeeded(JNIEnv* env, jclass controller_interface_class) - { - static bool registered = false; - if (registered) - return; - registered = true; +void RegisterDevicesChangedCallbackIfNeeded(JNIEnv* env, jclass controller_interface_class) +{ + static bool registered = false; + if (registered) + return; + registered = true; - const jclass global_controller_interface_class = - reinterpret_cast(env->NewGlobalRef(controller_interface_class)); - const jmethodID controller_interface_on_devices_changed = - env->GetStaticMethodID(global_controller_interface_class, "onDevicesChanged", "()V"); + const jclass global_controller_interface_class = + reinterpret_cast(env->NewGlobalRef(controller_interface_class)); + const jmethodID controller_interface_on_devices_changed = + env->GetStaticMethodID(global_controller_interface_class, "onDevicesChanged", "()V"); - static Common::EventHook event_hook = g_controller_interface.RegisterDevicesChangedCallback( - [global_controller_interface_class, controller_interface_on_devices_changed] { - IDCache::GetEnvForThread()->CallStaticVoidMethod(global_controller_interface_class, - controller_interface_on_devices_changed); - }); - } + static Common::EventHook event_hook = g_controller_interface.RegisterDevicesChangedCallback( + [global_controller_interface_class, controller_interface_on_devices_changed] { + IDCache::GetEnvForThread()->CallStaticVoidMethod(global_controller_interface_class, + controller_interface_on_devices_changed); + }); +} } // namespace namespace ciface::Android { - class InputBackend final : public ciface::InputBackend - { - public: - explicit InputBackend(ControllerInterface* controller_interface); - ~InputBackend(); - void PopulateDevices() override; +class InputBackend final : public ciface::InputBackend +{ +public: + explicit InputBackend(ControllerInterface* controller_interface); + ~InputBackend(); + void PopulateDevices() override; - static void AddDevice(JNIEnv* env, int device_id); - static void AddSensorDevice(JNIEnv* env); - static void RemoveDevice(int device_id); - }; + static void AddDevice(JNIEnv* env, int device_id); + static void AddSensorDevice(JNIEnv* env); + static void RemoveDevice(int device_id); +}; - std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) +std::unique_ptr CreateInputBackend(ControllerInterface* controller_interface) +{ + return std::make_unique(controller_interface); +} + +class AndroidInput : public Core::Device::Input +{ +public: + explicit AndroidInput(std::string name) : m_name(std::move(name)) + { + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Created {}", m_name); + } + + std::string GetName() const override { return m_name; } + + ControlState GetState() const override + { + m_last_polled.store(Clock::now(), std::memory_order_relaxed); + return m_state.load(std::memory_order_relaxed); + } + + void SetState(ControlState state) { m_state.store(state, std::memory_order_relaxed); } + + Clock::time_point GetLastPolled() const { return m_last_polled.load(std::memory_order_relaxed); } + +private: + std::string m_name; + std::atomic m_state = 0; + mutable std::atomic m_last_polled{}; +}; + +class AndroidKey final : public AndroidInput +{ +public: + explicit AndroidKey(int keycode) : AndroidInput(ConstructKeyName(keycode)) {} +}; + +class AndroidAxis : public AndroidInput +{ +public: + AndroidAxis(int source, int axis, bool negative) + : AndroidInput(ConstructAxisName(source, axis, negative)), m_negative(negative) + { + } + + AndroidAxis(std::string name, bool negative) : AndroidInput(std::move(name)), m_negative(negative) + { + } + + ControlState GetState() const override + { + return m_negative ? -AndroidInput::GetState() : AndroidInput::GetState(); + } + +private: + bool m_negative; +}; + +class AndroidSensorAxis final : public AndroidAxis +{ +public: + // This class does not create its own global reference to the passed-in sensor_event_listener. + // That is, it's up to the device that contains this axis to keep sensor_event_listener valid. + // It does however create its own global reference to the passed-in name. + AndroidSensorAxis(JNIEnv* env, jobject sensor_event_listener, jstring j_name, bool negative) + : AndroidAxis(GetJString(env, j_name), negative), + m_sensor_event_listener(sensor_event_listener), + m_j_name(reinterpret_cast(env->NewGlobalRef(j_name))) + { + } + + ~AndroidSensorAxis() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_j_name); } + + bool IsDetectable() const override { return false; } + + ControlState GetState() const override + { + if (m_is_suspended.load(std::memory_order_relaxed)) { - return std::make_unique(controller_interface); + IDCache::GetEnvForThread()->CallVoidMethod( + m_sensor_event_listener, s_sensor_event_listener_request_unsuspend_sensor, m_j_name); + + // m_is_suspended is intentionally not updated here. To prevent the C++ suspended status from + // ending up desynced with the Java suspended status, we only update m_is_suspended when Java + // calls notifySensorSuspendedState (which calls NotifyIsSuspended). This way, Java is the + // authoritative source for the suspended status, and C++ mirrors it (possibly with a delay). } - class AndroidInput : public Core::Device::Input + return AndroidAxis::GetState(); + } + + void NotifyIsSuspended(bool is_suspended) + { + m_is_suspended.store(is_suspended, std::memory_order_relaxed); + } + +private: + const jobject m_sensor_event_listener; + const jstring m_j_name; + std::atomic m_is_suspended = true; +}; + +class AndroidMotor : public Core::Device::Output +{ +public: + AndroidMotor(JNIEnv* env, jobject vibrator, jint id) + : m_vibrator(env->NewGlobalRef(vibrator)), m_id(id) + { + } + + ~AndroidMotor() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_vibrator); } + + std::string GetName() const override { return "Motor " + std::to_string(m_id); } + + void SetState(ControlState state) override + { + ControlState old_state = m_state.exchange(state, std::memory_order_relaxed); + + if (old_state < 0.5 && state >= 0.5) { - public: - explicit AndroidInput(std::string name) : m_name(std::move(name)) - { - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Created {}", m_name); - } + IDCache::GetEnvForThread()->CallStaticVoidMethod(s_controller_interface_class, + s_controller_interface_vibrate, m_vibrator); + } + } - std::string GetName() const override { return m_name; } +private: + const jobject m_vibrator; + const jint m_id; + std::atomic m_state = 0; +}; - ControlState GetState() const override - { - m_last_polled.store(Clock::now(), std::memory_order_relaxed); - return m_state.load(std::memory_order_relaxed); - } +class AndroidDevice final : public Core::Device +{ +public: + AndroidDevice(JNIEnv* env, jint device_id, jobject input_device) + : m_sensor_event_listener(AddSensors(env, input_device)), + m_source(env->CallIntMethod(input_device, s_input_device_get_sources)), + m_controller_number(env->CallIntMethod(input_device, s_input_device_get_controller_number)), + m_device_id(device_id) + { + jstring j_name = + reinterpret_cast(env->CallObjectMethod(input_device, s_input_device_get_name)); + m_name = GetJString(env, j_name); + env->DeleteLocalRef(j_name); - void SetState(ControlState state) { m_state.store(state, std::memory_order_relaxed); } + jstring j_descriptor = reinterpret_cast( + env->CallObjectMethod(input_device, s_input_device_get_descriptor)); + m_descriptor = GetJString(env, j_descriptor); + env->DeleteLocalRef(j_descriptor); - Clock::time_point GetLastPolled() const { return m_last_polled.load(std::memory_order_relaxed); } + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Sources for {}: {:08x}", GetQualifiedName(), m_source); - private: - std::string m_name; - std::atomic m_state = 0; - mutable std::atomic m_last_polled{}; - }; + AddKeys(env, input_device); + AddAxes(env, input_device); + AddMotors(env, input_device); + } - class AndroidKey final : public AndroidInput + // Constructor for the device added by Dolphin to contain sensor inputs + AndroidDevice(JNIEnv* env, std::string name) + : m_sensor_event_listener(AddSensors(env, nullptr)), m_source(AINPUT_SOURCE_SENSOR), + m_controller_number(0), m_device_id(std::nullopt), m_name(std::move(name)) + { + AddSystemMotors(env); + } + + ~AndroidDevice() + { + if (m_sensor_event_listener) + IDCache::GetEnvForThread()->DeleteGlobalRef(m_sensor_event_listener); + } + + std::string GetName() const override { return m_name; } + + std::string GetSource() const override { return SOURCE; } + + std::optional GetPreferredId() const override + { + return m_controller_number != 0 ? std::make_optional(m_controller_number) : std::nullopt; + } + + int GetSortPriority() const override + { + // If m_controller_number is non-zero, Android considers this to be a gamepad + if (m_controller_number != 0) + return 0; + + if ((m_source & AINPUT_SOURCE_KEYBOARD) != 0) + return -1; + + if ((m_source & (AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_MOUSE_RELATIVE)) != 0) + return -2; + + return -3; + } + + std::optional GetDeviceID() const { return m_device_id; } + + std::string GetDescriptor() const { return m_descriptor; } + + jobject GetSensorEventListener() { return m_sensor_event_listener; } + +private: + void AddKeys(JNIEnv* env, jobject input_device) + { + jbooleanArray keys_array = reinterpret_cast( + env->CallObjectMethod(input_device, s_input_device_has_keys, s_keycodes_array)); + jboolean* keys = env->GetBooleanArrayElements(keys_array, nullptr); + jsize keys_count = env->GetArrayLength(keys_array); + for (jsize i = 0; i < keys_count; ++i) { - public: - explicit AndroidKey(int keycode) : AndroidInput(ConstructKeyName(keycode)) {} - }; + // These specific keys never get delivered to applications, + // so there's no point in letting users try to map them + if (i == AKEYCODE_HOME || i == AKEYCODE_ASSIST || i == AKEYCODE_VOICE_ASSIST) + continue; - class AndroidAxis : public AndroidInput + if (keys[i]) + AddInput(new AndroidKey(i)); + } + env->ReleaseBooleanArrayElements(keys_array, keys, JNI_ABORT); + env->DeleteLocalRef(keys_array); + } + + void AddAxes(JNIEnv* env, jobject input_device) + { + jobject motion_ranges_list = + env->CallObjectMethod(input_device, s_input_device_get_motion_ranges); + jint motion_ranges_count = env->CallIntMethod(motion_ranges_list, s_list_size); + for (jint i = 0; i < motion_ranges_count; ++i) { - public: - AndroidAxis(int source, int axis, bool negative) - : AndroidInput(ConstructAxisName(source, axis, negative)), m_negative(negative) - { - } + jobject motion_range = env->CallObjectMethod(motion_ranges_list, s_list_get, i); - AndroidAxis(std::string name, bool negative) : AndroidInput(std::move(name)), m_negative(negative) - { - } + jint source = env->CallIntMethod(motion_range, s_motion_range_get_source); + jint axis = env->CallIntMethod(motion_range, s_motion_range_get_axis); + jfloat min = env->CallFloatMethod(motion_range, s_motion_range_get_min); + jfloat max = env->CallFloatMethod(motion_range, s_motion_range_get_max); - ControlState GetState() const override - { - return m_negative ? -AndroidInput::GetState() : AndroidInput::GetState(); - } + env->DeleteLocalRef(motion_range); - private: - bool m_negative; - }; + AndroidAxis* positive = nullptr; + AndroidAxis* negative = nullptr; + if (max > 0) + positive = new AndroidAxis(source, axis, false); + if (min < 0) + negative = new AndroidAxis(source, axis, true); - class AndroidSensorAxis final : public AndroidAxis + if (positive && negative) + AddFullAnalogSurfaceInputs(positive, negative); + else if (positive || negative) + AddInput(positive ? positive : negative); + } + env->DeleteLocalRef(motion_ranges_list); + } + + jobject AddSensors(JNIEnv* env, jobject input_device) + { + jobject local_sensor_event_listener; + if (input_device) { - public: - // This class does not create its own global reference to the passed-in sensor_event_listener. - // That is, it's up to the device that contains this axis to keep sensor_event_listener valid. - // It does however create its own global reference to the passed-in name. - AndroidSensorAxis(JNIEnv* env, jobject sensor_event_listener, jstring j_name, bool negative) - : AndroidAxis(GetJString(env, j_name), negative), - m_sensor_event_listener(sensor_event_listener), - m_j_name(reinterpret_cast(env->NewGlobalRef(j_name))) - { - } - - ~AndroidSensorAxis() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_j_name); } - - bool IsDetectable() const override { return false; } - - ControlState GetState() const override - { - if (m_is_suspended.load(std::memory_order_relaxed)) - { - IDCache::GetEnvForThread()->CallVoidMethod( - m_sensor_event_listener, s_sensor_event_listener_request_unsuspend_sensor, m_j_name); - - // m_is_suspended is intentionally not updated here. To prevent the C++ suspended status from - // ending up desynced with the Java suspended status, we only update m_is_suspended when Java - // calls notifySensorSuspendedState (which calls NotifyIsSuspended). This way, Java is the - // authoritative source for the suspended status, and C++ mirrors it (possibly with a delay). - } - - return AndroidAxis::GetState(); - } - - void NotifyIsSuspended(bool is_suspended) - { - m_is_suspended.store(is_suspended, std::memory_order_relaxed); - } - - private: - const jobject m_sensor_event_listener; - const jstring m_j_name; - std::atomic m_is_suspended = true; - }; - - class AndroidMotor : public Core::Device::Output + local_sensor_event_listener = + env->NewObject(s_sensor_event_listener_class, + s_sensor_event_listener_constructor_input_device, input_device); + } + else { - public: - AndroidMotor(JNIEnv* env, jobject vibrator, jint id) - : m_vibrator(env->NewGlobalRef(vibrator)), m_id(id) - { - } + local_sensor_event_listener = + env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor); + } - ~AndroidMotor() { IDCache::GetEnvForThread()->DeleteGlobalRef(m_vibrator); } + jobject sensor_event_listener = env->NewGlobalRef(local_sensor_event_listener); - std::string GetName() const override { return "Motor " + std::to_string(m_id); } + env->DeleteLocalRef(local_sensor_event_listener); - void SetState(ControlState state) override - { - ControlState old_state = m_state.exchange(state, std::memory_order_relaxed); + jobjectArray j_axis_names = reinterpret_cast( + env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_axis_names)); - if (old_state < 0.5 && state >= 0.5) - { - IDCache::GetEnvForThread()->CallStaticVoidMethod(s_controller_interface_class, - s_controller_interface_vibrate, m_vibrator); - } - } + jbooleanArray j_negative_axes = reinterpret_cast( + env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_negative_axes)); + jboolean* negative_axes = env->GetBooleanArrayElements(j_negative_axes, nullptr); - private: - const jobject m_vibrator; - const jint m_id; - std::atomic m_state = 0; - }; - - class AndroidDevice final : public Core::Device + const jsize axis_count = env->GetArrayLength(j_axis_names); + ASSERT(axis_count == env->GetArrayLength(j_negative_axes)); + for (jsize i = 0; i < axis_count; ++i) { - public: - AndroidDevice(JNIEnv* env, jint device_id, jobject input_device) - : m_sensor_event_listener(AddSensors(env, input_device)), - m_source(env->CallIntMethod(input_device, s_input_device_get_sources)), - m_controller_number(env->CallIntMethod(input_device, s_input_device_get_controller_number)), - m_device_id(device_id) - { - jstring j_name = - reinterpret_cast(env->CallObjectMethod(input_device, s_input_device_get_name)); - m_name = GetJString(env, j_name); - env->DeleteLocalRef(j_name); + const jstring axis_name = + reinterpret_cast(env->GetObjectArrayElement(j_axis_names, i)); + AddInput(new AndroidSensorAxis(env, sensor_event_listener, axis_name, negative_axes[i])); + env->DeleteLocalRef(axis_name); + } - jstring j_descriptor = - reinterpret_cast(env->CallObjectMethod(input_device, s_input_device_get_descriptor)); - m_descriptor = GetJString(env, j_descriptor); - env->DeleteLocalRef(j_descriptor); + env->DeleteLocalRef(j_axis_names); + env->ReleaseBooleanArrayElements(j_negative_axes, negative_axes, 0); + env->DeleteLocalRef(j_negative_axes); - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Sources for {}: {:08x}", GetQualifiedName(), m_source); + return sensor_event_listener; + } - AddKeys(env, input_device); - AddAxes(env, input_device); - AddMotors(env, input_device); - } + void AddMotors(JNIEnv* env, jobject input_device) + { + jobject vibrator_manager = env->CallStaticObjectMethod( + s_controller_interface_class, s_controller_interface_get_vibrator_manager, input_device); + AddMotorsFromManager(env, vibrator_manager); + env->DeleteLocalRef(vibrator_manager); + } - // Constructor for the device added by Dolphin to contain sensor inputs - AndroidDevice(JNIEnv* env, std::string name) - : m_sensor_event_listener(AddSensors(env, nullptr)), m_source(AINPUT_SOURCE_SENSOR), - m_controller_number(0), m_device_id(std::nullopt), m_name(std::move(name)) - { - AddSystemMotors(env); - } + void AddSystemMotors(JNIEnv* env) + { + jobject vibrator_manager = env->CallStaticObjectMethod( + s_controller_interface_class, s_controller_interface_get_system_vibrator_manager); + AddMotorsFromManager(env, vibrator_manager); + env->DeleteLocalRef(vibrator_manager); + } - ~AndroidDevice() - { - if (m_sensor_event_listener) - IDCache::GetEnvForThread()->DeleteGlobalRef(m_sensor_event_listener); - } + void AddMotorsFromManager(JNIEnv* env, jobject vibrator_manager) + { + jintArray j_vibrator_ids = reinterpret_cast( + env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator_ids)); + jint* vibrator_ids = env->GetIntArrayElements(j_vibrator_ids, nullptr); - std::string GetName() const override { return m_name; } + jint size = env->GetArrayLength(j_vibrator_ids); + for (jint i = 0; i < size; ++i) + { + jobject vibrator = + env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator, i); + AddOutput(new AndroidMotor(env, vibrator, i)); + env->DeleteLocalRef(vibrator); + } - std::string GetSource() const override { return SOURCE; } + env->ReleaseIntArrayElements(j_vibrator_ids, vibrator_ids, 0); + env->DeleteLocalRef(j_vibrator_ids); + } - std::optional GetPreferredId() const override - { - return m_controller_number != 0 ? std::make_optional(m_controller_number) : std::nullopt; - } - - int GetSortPriority() const override - { - // If m_controller_number is non-zero, Android considers this to be a gamepad - if (m_controller_number != 0) - return 0; - - if ((m_source & AINPUT_SOURCE_KEYBOARD) != 0) - return -1; - - if ((m_source & (AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_MOUSE_RELATIVE)) != 0) - return -2; - - return -3; - } - - std::optional GetDeviceID() const { return m_device_id; } - - std::string GetDescriptor() const { return m_descriptor; } - - jobject GetSensorEventListener() { return m_sensor_event_listener; } - - private: - void AddKeys(JNIEnv* env, jobject input_device) - { - jbooleanArray keys_array = reinterpret_cast( - env->CallObjectMethod(input_device, s_input_device_has_keys, s_keycodes_array)); - jboolean* keys = env->GetBooleanArrayElements(keys_array, nullptr); - jsize keys_count = env->GetArrayLength(keys_array); - for (jsize i = 0; i < keys_count; ++i) - { - // These specific keys never get delivered to applications, - // so there's no point in letting users try to map them - if (i == AKEYCODE_HOME || i == AKEYCODE_ASSIST || i == AKEYCODE_VOICE_ASSIST) - continue; - - if (keys[i]) - AddInput(new AndroidKey(i)); - } - env->ReleaseBooleanArrayElements(keys_array, keys, JNI_ABORT); - env->DeleteLocalRef(keys_array); - } - - void AddAxes(JNIEnv* env, jobject input_device) - { - jobject motion_ranges_list = - env->CallObjectMethod(input_device, s_input_device_get_motion_ranges); - jint motion_ranges_count = env->CallIntMethod(motion_ranges_list, s_list_size); - for (jint i = 0; i < motion_ranges_count; ++i) - { - jobject motion_range = env->CallObjectMethod(motion_ranges_list, s_list_get, i); - - jint source = env->CallIntMethod(motion_range, s_motion_range_get_source); - jint axis = env->CallIntMethod(motion_range, s_motion_range_get_axis); - jfloat min = env->CallFloatMethod(motion_range, s_motion_range_get_min); - jfloat max = env->CallFloatMethod(motion_range, s_motion_range_get_max); - - env->DeleteLocalRef(motion_range); - - AndroidAxis* positive = nullptr; - AndroidAxis* negative = nullptr; - if (max > 0) - positive = new AndroidAxis(source, axis, false); - if (min < 0) - negative = new AndroidAxis(source, axis, true); - - if (positive && negative) - AddFullAnalogSurfaceInputs(positive, negative); - else if (positive || negative) - AddInput(positive ? positive : negative); - } - env->DeleteLocalRef(motion_ranges_list); - } - - jobject AddSensors(JNIEnv* env, jobject input_device) - { - jobject local_sensor_event_listener; - if (input_device) - { - local_sensor_event_listener = - env->NewObject(s_sensor_event_listener_class, - s_sensor_event_listener_constructor_input_device, input_device); - } - else - { - local_sensor_event_listener = - env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor); - } - - jobject sensor_event_listener = env->NewGlobalRef(local_sensor_event_listener); - - env->DeleteLocalRef(local_sensor_event_listener); - - jobjectArray j_axis_names = reinterpret_cast( - env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_axis_names)); - - jbooleanArray j_negative_axes = reinterpret_cast( - env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_negative_axes)); - jboolean* negative_axes = env->GetBooleanArrayElements(j_negative_axes, nullptr); - - const jsize axis_count = env->GetArrayLength(j_axis_names); - ASSERT(axis_count == env->GetArrayLength(j_negative_axes)); - for (jsize i = 0; i < axis_count; ++i) - { - const jstring axis_name = - reinterpret_cast(env->GetObjectArrayElement(j_axis_names, i)); - AddInput(new AndroidSensorAxis(env, sensor_event_listener, axis_name, negative_axes[i])); - env->DeleteLocalRef(axis_name); - } - - env->DeleteLocalRef(j_axis_names); - env->ReleaseBooleanArrayElements(j_negative_axes, negative_axes, 0); - env->DeleteLocalRef(j_negative_axes); - - return sensor_event_listener; - } - - void AddMotors(JNIEnv* env, jobject input_device) - { - jobject vibrator_manager = env->CallStaticObjectMethod( - s_controller_interface_class, s_controller_interface_get_vibrator_manager, input_device); - AddMotorsFromManager(env, vibrator_manager); - env->DeleteLocalRef(vibrator_manager); - } - - void AddSystemMotors(JNIEnv* env) - { - jobject vibrator_manager = env->CallStaticObjectMethod( - s_controller_interface_class, s_controller_interface_get_system_vibrator_manager); - AddMotorsFromManager(env, vibrator_manager); - env->DeleteLocalRef(vibrator_manager); - } - - void AddMotorsFromManager(JNIEnv* env, jobject vibrator_manager) - { - jintArray j_vibrator_ids = reinterpret_cast( - env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator_ids)); - jint* vibrator_ids = env->GetIntArrayElements(j_vibrator_ids, nullptr); - - jint size = env->GetArrayLength(j_vibrator_ids); - for (jint i = 0; i < size; ++i) - { - jobject vibrator = - env->CallObjectMethod(vibrator_manager, s_dolphin_vibrator_manager_get_vibrator, i); - AddOutput(new AndroidMotor(env, vibrator, i)); - env->DeleteLocalRef(vibrator); - } - - env->ReleaseIntArrayElements(j_vibrator_ids, vibrator_ids, 0); - env->DeleteLocalRef(j_vibrator_ids); - } - - const jobject m_sensor_event_listener; - const int m_source; - const int m_controller_number; - const std::optional m_device_id; - std::string m_name; - std::string m_descriptor; - }; + const jobject m_sensor_event_listener; + const int m_source; + const int m_controller_number; + const std::optional m_device_id; + std::string m_name; + std::string m_descriptor; +}; // Creates an array that contains every possible keycode - static jintArray CreateKeyCodesArray(JNIEnv* env) - { - jintArray keycodes_array = env->NewIntArray(MAX_KEYCODE + 1); +static jintArray CreateKeyCodesArray(JNIEnv* env) +{ + jintArray keycodes_array = env->NewIntArray(MAX_KEYCODE + 1); - int* keycodes = env->GetIntArrayElements(keycodes_array, nullptr); - for (int i = 0; i <= MAX_KEYCODE; ++i) - keycodes[i] = i; - env->ReleaseIntArrayElements(keycodes_array, keycodes, 0); + int* keycodes = env->GetIntArrayElements(keycodes_array, nullptr); + for (int i = 0; i <= MAX_KEYCODE; ++i) + keycodes[i] = i; + env->ReleaseIntArrayElements(keycodes_array, keycodes, 0); - return keycodes_array; - } + return keycodes_array; +} - InputBackend::InputBackend(ControllerInterface* controller_interface) - : ciface::InputBackend(controller_interface) - { - JNIEnv* env = IDCache::GetEnvForThread(); +InputBackend::InputBackend(ControllerInterface* controller_interface) + : ciface::InputBackend(controller_interface) +{ + JNIEnv* env = IDCache::GetEnvForThread(); - const jclass list_class = env->FindClass("java/util/List"); - s_list_class = reinterpret_cast(env->NewGlobalRef(list_class)); - s_list_get = env->GetMethodID(s_list_class, "get", "(I)Ljava/lang/Object;"); - s_list_size = env->GetMethodID(s_list_class, "size", "()I"); - env->DeleteLocalRef(list_class); + const jclass list_class = env->FindClass("java/util/List"); + s_list_class = reinterpret_cast(env->NewGlobalRef(list_class)); + s_list_get = env->GetMethodID(s_list_class, "get", "(I)Ljava/lang/Object;"); + s_list_size = env->GetMethodID(s_list_class, "size", "()I"); + env->DeleteLocalRef(list_class); - const jclass input_device_class = env->FindClass("android/view/InputDevice"); - s_input_device_class = reinterpret_cast(env->NewGlobalRef(input_device_class)); - s_input_device_get_device_ids = - env->GetStaticMethodID(s_input_device_class, "getDeviceIds", "()[I"); - s_input_device_get_device = - env->GetStaticMethodID(s_input_device_class, "getDevice", "(I)Landroid/view/InputDevice;"); - s_input_device_get_controller_number = - env->GetMethodID(s_input_device_class, "getControllerNumber", "()I"); - s_input_device_get_motion_ranges = - env->GetMethodID(s_input_device_class, "getMotionRanges", "()Ljava/util/List;"); - s_input_device_get_name = - env->GetMethodID(s_input_device_class, "getName", "()Ljava/lang/String;"); - s_input_device_get_descriptor = - env->GetMethodID(s_input_device_class, "getDescriptor", "()Ljava/lang/String;"); - s_input_device_get_sources = env->GetMethodID(s_input_device_class, "getSources", "()I"); - s_input_device_has_keys = env->GetMethodID(s_input_device_class, "hasKeys", "([I)[Z"); - env->DeleteLocalRef(input_device_class); + const jclass input_device_class = env->FindClass("android/view/InputDevice"); + s_input_device_class = reinterpret_cast(env->NewGlobalRef(input_device_class)); + s_input_device_get_device_ids = + env->GetStaticMethodID(s_input_device_class, "getDeviceIds", "()[I"); + s_input_device_get_device = + env->GetStaticMethodID(s_input_device_class, "getDevice", "(I)Landroid/view/InputDevice;"); + s_input_device_get_controller_number = + env->GetMethodID(s_input_device_class, "getControllerNumber", "()I"); + s_input_device_get_motion_ranges = + env->GetMethodID(s_input_device_class, "getMotionRanges", "()Ljava/util/List;"); + s_input_device_get_name = + env->GetMethodID(s_input_device_class, "getName", "()Ljava/lang/String;"); + s_input_device_get_descriptor = + env->GetMethodID(s_input_device_class, "getDescriptor", "()Ljava/lang/String;"); + s_input_device_get_sources = env->GetMethodID(s_input_device_class, "getSources", "()I"); + s_input_device_has_keys = env->GetMethodID(s_input_device_class, "hasKeys", "([I)[Z"); + env->DeleteLocalRef(input_device_class); - const jclass motion_range_class = env->FindClass("android/view/InputDevice$MotionRange"); - s_motion_range_class = reinterpret_cast(env->NewGlobalRef(motion_range_class)); - s_motion_range_get_axis = env->GetMethodID(s_motion_range_class, "getAxis", "()I"); - s_motion_range_get_max = env->GetMethodID(s_motion_range_class, "getMax", "()F"); - s_motion_range_get_min = env->GetMethodID(s_motion_range_class, "getMin", "()F"); - s_motion_range_get_source = env->GetMethodID(s_motion_range_class, "getSource", "()I"); - env->DeleteLocalRef(motion_range_class); + const jclass motion_range_class = env->FindClass("android/view/InputDevice$MotionRange"); + s_motion_range_class = reinterpret_cast(env->NewGlobalRef(motion_range_class)); + s_motion_range_get_axis = env->GetMethodID(s_motion_range_class, "getAxis", "()I"); + s_motion_range_get_max = env->GetMethodID(s_motion_range_class, "getMax", "()F"); + s_motion_range_get_min = env->GetMethodID(s_motion_range_class, "getMin", "()F"); + s_motion_range_get_source = env->GetMethodID(s_motion_range_class, "getSource", "()I"); + env->DeleteLocalRef(motion_range_class); - const jclass input_event_class = env->FindClass("android/view/InputEvent"); - s_input_event_class = reinterpret_cast(env->NewGlobalRef(input_event_class)); - s_input_event_get_device_id = env->GetMethodID(s_input_event_class, "getDeviceId", "()I"); - env->DeleteLocalRef(input_event_class); + const jclass input_event_class = env->FindClass("android/view/InputEvent"); + s_input_event_class = reinterpret_cast(env->NewGlobalRef(input_event_class)); + s_input_event_get_device_id = env->GetMethodID(s_input_event_class, "getDeviceId", "()I"); + env->DeleteLocalRef(input_event_class); - const jclass key_event_class = env->FindClass("android/view/KeyEvent"); - s_key_event_class = reinterpret_cast(env->NewGlobalRef(key_event_class)); - s_key_event_get_action = env->GetMethodID(s_key_event_class, "getAction", "()I"); - s_key_event_get_keycode = env->GetMethodID(s_key_event_class, "getKeyCode", "()I"); - env->DeleteLocalRef(key_event_class); + const jclass key_event_class = env->FindClass("android/view/KeyEvent"); + s_key_event_class = reinterpret_cast(env->NewGlobalRef(key_event_class)); + s_key_event_get_action = env->GetMethodID(s_key_event_class, "getAction", "()I"); + s_key_event_get_keycode = env->GetMethodID(s_key_event_class, "getKeyCode", "()I"); + env->DeleteLocalRef(key_event_class); - const jclass motion_event_class = env->FindClass("android/view/MotionEvent"); - s_motion_event_class = reinterpret_cast(env->NewGlobalRef(motion_event_class)); - s_motion_event_get_axis_value = env->GetMethodID(s_motion_event_class, "getAxisValue", "(I)F"); - s_motion_event_get_source = env->GetMethodID(s_motion_event_class, "getSource", "()I"); - env->DeleteLocalRef(motion_event_class); + const jclass motion_event_class = env->FindClass("android/view/MotionEvent"); + s_motion_event_class = reinterpret_cast(env->NewGlobalRef(motion_event_class)); + s_motion_event_get_axis_value = env->GetMethodID(s_motion_event_class, "getAxisValue", "(I)F"); + s_motion_event_get_source = env->GetMethodID(s_motion_event_class, "getSource", "()I"); + env->DeleteLocalRef(motion_event_class); - const jclass controller_interface_class = - env->FindClass("org/dolphinemu/dolphinemu/features/input/model/ControllerInterface"); - s_controller_interface_class = - reinterpret_cast(env->NewGlobalRef(controller_interface_class)); - s_controller_interface_register_input_device_listener = - env->GetStaticMethodID(s_controller_interface_class, "registerInputDeviceListener", "()V"); - s_controller_interface_unregister_input_device_listener = - env->GetStaticMethodID(s_controller_interface_class, "unregisterInputDeviceListener", "()V"); - s_controller_interface_get_vibrator_manager = - env->GetStaticMethodID(s_controller_interface_class, "getVibratorManager", - "(Landroid/view/InputDevice;)Lorg/dolphinemu/dolphinemu/features/" - "input/model/DolphinVibratorManager;"); - s_controller_interface_get_system_vibrator_manager = env->GetStaticMethodID( - s_controller_interface_class, "getSystemVibratorManager", - "()Lorg/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager;"); - s_controller_interface_vibrate = - env->GetStaticMethodID(s_controller_interface_class, "vibrate", "(Landroid/os/Vibrator;)V"); - env->DeleteLocalRef(controller_interface_class); + const jclass controller_interface_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/ControllerInterface"); + s_controller_interface_class = + reinterpret_cast(env->NewGlobalRef(controller_interface_class)); + s_controller_interface_register_input_device_listener = + env->GetStaticMethodID(s_controller_interface_class, "registerInputDeviceListener", "()V"); + s_controller_interface_unregister_input_device_listener = + env->GetStaticMethodID(s_controller_interface_class, "unregisterInputDeviceListener", "()V"); + s_controller_interface_get_vibrator_manager = + env->GetStaticMethodID(s_controller_interface_class, "getVibratorManager", + "(Landroid/view/InputDevice;)Lorg/dolphinemu/dolphinemu/features/" + "input/model/DolphinVibratorManager;"); + s_controller_interface_get_system_vibrator_manager = env->GetStaticMethodID( + s_controller_interface_class, "getSystemVibratorManager", + "()Lorg/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager;"); + s_controller_interface_vibrate = + env->GetStaticMethodID(s_controller_interface_class, "vibrate", "(Landroid/os/Vibrator;)V"); + env->DeleteLocalRef(controller_interface_class); - const jclass sensor_event_listener_class = - env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener"); - s_sensor_event_listener_class = - reinterpret_cast(env->NewGlobalRef(sensor_event_listener_class)); - s_sensor_event_listener_constructor = - env->GetMethodID(s_sensor_event_listener_class, "", "()V"); - s_sensor_event_listener_constructor_input_device = - env->GetMethodID(s_sensor_event_listener_class, "", "(Landroid/view/InputDevice;)V"); - s_sensor_event_listener_set_device_qualifier = env->GetMethodID( - s_sensor_event_listener_class, "setDeviceQualifier", "(Ljava/lang/String;)V"); - s_sensor_event_listener_request_unsuspend_sensor = env->GetMethodID( - s_sensor_event_listener_class, "requestUnsuspendSensor", "(Ljava/lang/String;)V"); - s_sensor_event_listener_get_axis_names = - env->GetMethodID(s_sensor_event_listener_class, "getAxisNames", "()[Ljava/lang/String;"); - s_sensor_event_listener_get_negative_axes = - env->GetMethodID(s_sensor_event_listener_class, "getNegativeAxes", "()[Z"); - env->DeleteLocalRef(sensor_event_listener_class); + const jclass sensor_event_listener_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener"); + s_sensor_event_listener_class = + reinterpret_cast(env->NewGlobalRef(sensor_event_listener_class)); + s_sensor_event_listener_constructor = + env->GetMethodID(s_sensor_event_listener_class, "", "()V"); + s_sensor_event_listener_constructor_input_device = + env->GetMethodID(s_sensor_event_listener_class, "", "(Landroid/view/InputDevice;)V"); + s_sensor_event_listener_set_device_qualifier = env->GetMethodID( + s_sensor_event_listener_class, "setDeviceQualifier", "(Ljava/lang/String;)V"); + s_sensor_event_listener_request_unsuspend_sensor = env->GetMethodID( + s_sensor_event_listener_class, "requestUnsuspendSensor", "(Ljava/lang/String;)V"); + s_sensor_event_listener_get_axis_names = + env->GetMethodID(s_sensor_event_listener_class, "getAxisNames", "()[Ljava/lang/String;"); + s_sensor_event_listener_get_negative_axes = + env->GetMethodID(s_sensor_event_listener_class, "getNegativeAxes", "()[Z"); + env->DeleteLocalRef(sensor_event_listener_class); - const jclass dolphin_vibrator_manager_class = - env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager"); - s_dolphin_vibrator_manager_class = - reinterpret_cast(env->NewGlobalRef(dolphin_vibrator_manager_class)); - s_dolphin_vibrator_manager_get_vibrator = - env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibrator", "(I)Landroid/os/Vibrator;"); - s_dolphin_vibrator_manager_get_vibrator_ids = - env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibratorIds", "()[I"); - env->DeleteLocalRef(dolphin_vibrator_manager_class); + const jclass dolphin_vibrator_manager_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager"); + s_dolphin_vibrator_manager_class = + reinterpret_cast(env->NewGlobalRef(dolphin_vibrator_manager_class)); + s_dolphin_vibrator_manager_get_vibrator = + env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibrator", "(I)Landroid/os/Vibrator;"); + s_dolphin_vibrator_manager_get_vibrator_ids = + env->GetMethodID(s_dolphin_vibrator_manager_class, "getVibratorIds", "()[I"); + env->DeleteLocalRef(dolphin_vibrator_manager_class); - jintArray keycodes_array = CreateKeyCodesArray(env); - s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); - env->DeleteLocalRef(keycodes_array); + jintArray keycodes_array = CreateKeyCodesArray(env); + s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); + env->DeleteLocalRef(keycodes_array); - env->CallStaticVoidMethod(s_controller_interface_class, - s_controller_interface_register_input_device_listener); + env->CallStaticVoidMethod(s_controller_interface_class, + s_controller_interface_register_input_device_listener); - RegisterDevicesChangedCallbackIfNeeded(env, s_controller_interface_class); - } + RegisterDevicesChangedCallbackIfNeeded(env, s_controller_interface_class); +} - InputBackend::~InputBackend() - { - JNIEnv* env = IDCache::GetEnvForThread(); +InputBackend::~InputBackend() +{ + JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(s_controller_interface_class, - s_controller_interface_unregister_input_device_listener); + env->CallStaticVoidMethod(s_controller_interface_class, + s_controller_interface_unregister_input_device_listener); - env->DeleteGlobalRef(s_input_device_class); - env->DeleteGlobalRef(s_motion_range_class); - env->DeleteGlobalRef(s_input_event_class); - env->DeleteGlobalRef(s_key_event_class); - env->DeleteGlobalRef(s_motion_event_class); - env->DeleteGlobalRef(s_controller_interface_class); - env->DeleteGlobalRef(s_sensor_event_listener_class); - env->DeleteGlobalRef(s_dolphin_vibrator_manager_class); - env->DeleteGlobalRef(s_keycodes_array); - } + env->DeleteGlobalRef(s_input_device_class); + env->DeleteGlobalRef(s_motion_range_class); + env->DeleteGlobalRef(s_input_event_class); + env->DeleteGlobalRef(s_key_event_class); + env->DeleteGlobalRef(s_motion_event_class); + env->DeleteGlobalRef(s_controller_interface_class); + env->DeleteGlobalRef(s_sensor_event_listener_class); + env->DeleteGlobalRef(s_dolphin_vibrator_manager_class); + env->DeleteGlobalRef(s_keycodes_array); +} - void InputBackend::AddDevice(JNIEnv* env, jint device_id) - { - // Remove the device in case it already exists (maybe it's possible for a device to connect, - // be processed by PopulateDevices, and then be processed by onInputDeviceAdded) - RemoveDevice(device_id); +void InputBackend::AddDevice(JNIEnv* env, jint device_id) +{ + // Remove the device in case it already exists (maybe it's possible for a device to connect, + // be processed by PopulateDevices, and then be processed by onInputDeviceAdded) + RemoveDevice(device_id); - jobject input_device = - env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device, device_id); + jobject input_device = + env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device, device_id); - if (!input_device) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device with ID {}", device_id); - return; - } + if (!input_device) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find device with ID {}", device_id); + return; + } - auto device = std::make_shared(env, device_id, input_device); + auto device = std::make_shared(env, device_id, input_device); - env->DeleteLocalRef(input_device); + env->DeleteLocalRef(input_device); - if (device->Inputs().empty() && device->Outputs().empty()) - return; + if (device->Inputs().empty() && device->Outputs().empty()) + return; - g_controller_interface.AddDevice(device); + g_controller_interface.AddDevice(device); - Core::DeviceQualifier qualifier; - qualifier.FromDevice(device.get()); + Core::DeviceQualifier qualifier; + qualifier.FromDevice(device.get()); - INFO_LOG_FMT(CONTROLLERINTERFACE, "Added device ID {} as {}", device_id, - device->GetQualifiedName()); - s_device_id_to_device_qualifier.emplace(device_id, qualifier); + INFO_LOG_FMT(CONTROLLERINTERFACE, "Added device ID {} as {}", device_id, + device->GetQualifiedName()); + s_device_id_to_device_qualifier.emplace(device_id, qualifier); - jstring j_qualifier = ToJString(env, qualifier.ToString()); - env->CallVoidMethod(device->GetSensorEventListener(), - s_sensor_event_listener_set_device_qualifier, j_qualifier); - env->DeleteLocalRef(j_qualifier); - } + jstring j_qualifier = ToJString(env, qualifier.ToString()); + env->CallVoidMethod(device->GetSensorEventListener(), + s_sensor_event_listener_set_device_qualifier, j_qualifier); + env->DeleteLocalRef(j_qualifier); +} - void InputBackend::AddSensorDevice(JNIEnv* env) - { - // Device sensors (accelerometer, etc.) aren't associated with any Android InputDevice. - // Create an otherwise empty Dolphin input device so that they have somewhere to live. +void InputBackend::AddSensorDevice(JNIEnv* env) +{ + // Device sensors (accelerometer, etc.) aren't associated with any Android InputDevice. + // Create an otherwise empty Dolphin input device so that they have somewhere to live. - auto device = std::make_shared(env, "Device Sensors"); + auto device = std::make_shared(env, "Device Sensors"); - if (device->Inputs().empty() && device->Outputs().empty()) - return; + if (device->Inputs().empty() && device->Outputs().empty()) + return; - g_controller_interface.AddDevice(device); + g_controller_interface.AddDevice(device); - Core::DeviceQualifier qualifier; - qualifier.FromDevice(device.get()); + Core::DeviceQualifier qualifier; + qualifier.FromDevice(device.get()); - INFO_LOG_FMT(CONTROLLERINTERFACE, "Added sensor device as {}", device->GetQualifiedName()); + INFO_LOG_FMT(CONTROLLERINTERFACE, "Added sensor device as {}", device->GetQualifiedName()); - jstring j_qualifier = ToJString(env, qualifier.ToString()); - env->CallVoidMethod(device->GetSensorEventListener(), - s_sensor_event_listener_set_device_qualifier, j_qualifier); - env->DeleteLocalRef(j_qualifier); - } + jstring j_qualifier = ToJString(env, qualifier.ToString()); + env->CallVoidMethod(device->GetSensorEventListener(), + s_sensor_event_listener_set_device_qualifier, j_qualifier); + env->DeleteLocalRef(j_qualifier); +} - void InputBackend::RemoveDevice(jint device_id) - { - g_controller_interface.RemoveDevice([device_id](const ciface::Core::Device* device) { - return device->GetSource() == SOURCE && - static_cast(device)->GetDeviceID() == device_id; - }); +void InputBackend::RemoveDevice(jint device_id) +{ + g_controller_interface.RemoveDevice([device_id](const ciface::Core::Device* device) { + return device->GetSource() == SOURCE && + static_cast(device)->GetDeviceID() == device_id; + }); - s_device_id_to_device_qualifier.erase(device_id); - } + s_device_id_to_device_qualifier.erase(device_id); +} - void InputBackend::PopulateDevices() - { - INFO_LOG_FMT(CONTROLLERINTERFACE, "Android populating devices"); +void InputBackend::PopulateDevices() +{ + INFO_LOG_FMT(CONTROLLERINTERFACE, "Android populating devices"); - JNIEnv* env = IDCache::GetEnvForThread(); + JNIEnv* env = IDCache::GetEnvForThread(); - jintArray device_ids_array = reinterpret_cast( - env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device_ids)); - int* device_ids = env->GetIntArrayElements(device_ids_array, nullptr); - jsize device_ids_count = env->GetArrayLength(device_ids_array); - for (jsize i = 0; i < device_ids_count; ++i) - AddDevice(env, device_ids[i]); - env->ReleaseIntArrayElements(device_ids_array, device_ids, JNI_ABORT); - env->DeleteLocalRef(device_ids_array); + jintArray device_ids_array = reinterpret_cast( + env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device_ids)); + int* device_ids = env->GetIntArrayElements(device_ids_array, nullptr); + jsize device_ids_count = env->GetArrayLength(device_ids_array); + for (jsize i = 0; i < device_ids_count; ++i) + AddDevice(env, device_ids[i]); + env->ReleaseIntArrayElements(device_ids_array, device_ids, JNI_ABORT); + env->DeleteLocalRef(device_ids_array); - AddSensorDevice(env); - } + AddSensorDevice(env); +} } // namespace ciface::Android @@ -1055,193 +1055,193 @@ extern "C" { JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchKeyEventNative( - JNIEnv* env, jclass, jobject key_event) + JNIEnv* env, jclass, jobject key_event) { - const jint action = env->CallIntMethod(key_event, s_key_event_get_action); - ControlState state; - switch (action) - { - case AKEY_EVENT_ACTION_DOWN: - state = 1; - break; - case AKEY_EVENT_ACTION_UP: - state = 0; - break; - default: - return JNI_FALSE; - } + const jint action = env->CallIntMethod(key_event, s_key_event_get_action); + ControlState state; + switch (action) + { + case AKEY_EVENT_ACTION_DOWN: + state = 1; + break; + case AKEY_EVENT_ACTION_UP: + state = 0; + break; + default: + return JNI_FALSE; + } - const jint device_id = env->CallIntMethod(key_event, s_input_event_get_device_id); - const std::shared_ptr device = FindDevice(device_id); - if (!device) - return JNI_FALSE; + const jint device_id = env->CallIntMethod(key_event, s_input_event_get_device_id); + const std::shared_ptr device = FindDevice(device_id); + if (!device) + return JNI_FALSE; - const jint keycode = env->CallIntMethod(key_event, s_key_event_get_keycode); - const std::string input_name = ConstructKeyName(keycode); + const jint keycode = env->CallIntMethod(key_event, s_key_event_get_keycode); + const std::string input_name = ConstructKeyName(keycode); - ciface::Core::Device::Input* input = device->FindInput(input_name); - if (!input) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find input {} in device {}", input_name, - device->GetQualifiedName()); - return false; - } + ciface::Core::Device::Input* input = device->FindInput(input_name); + if (!input) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Could not find input {} in device {}", input_name, + device->GetQualifiedName()); + return false; + } - auto casted_input = static_cast(input); - casted_input->SetState(state); - const Clock::time_point last_polled = casted_input->GetLastPolled(); + auto casted_input = static_cast(input); + casted_input->SetState(state); + const Clock::time_point last_polled = casted_input->GetLastPolled(); - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, device->GetQualifiedName(), - state); + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, device->GetQualifiedName(), + state); - return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; + return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchGenericMotionEventNative( - JNIEnv* env, jclass, jobject motion_event) + JNIEnv* env, jclass, jobject motion_event) { - const jint device_id = env->CallIntMethod(motion_event, s_input_event_get_device_id); - const std::shared_ptr device = FindDevice(device_id); - if (!device) - return JNI_FALSE; + const jint device_id = env->CallIntMethod(motion_event, s_input_event_get_device_id); + const std::shared_ptr device = FindDevice(device_id); + if (!device) + return JNI_FALSE; - const jint source = env->CallIntMethod(motion_event, s_motion_event_get_source); - const std::string axis_name_prefix = ConstructAxisNamePrefix(source); + const jint source = env->CallIntMethod(motion_event, s_motion_event_get_source); + const std::string axis_name_prefix = ConstructAxisNamePrefix(source); - Clock::time_point last_polled{}; + Clock::time_point last_polled{}; - for (ciface::Core::Device::Input* input : device->Inputs()) + for (ciface::Core::Device::Input* input : device->Inputs()) + { + const std::string input_name = input->GetName(); + if (input_name.starts_with(axis_name_prefix)) { - const std::string input_name = input->GetName(); - if (input_name.starts_with(axis_name_prefix)) - { - const std::string axis_id_str = input_name.substr( - axis_name_prefix.size(), input_name.size() - axis_name_prefix.size() - sizeof('+')); + const std::string axis_id_str = input_name.substr( + axis_name_prefix.size(), input_name.size() - axis_name_prefix.size() - sizeof('+')); - int axis_id; - if (!TryParse(axis_id_str, &axis_id)) - { - ERROR_LOG_FMT(CONTROLLERINTERFACE, "Failed to parse \"{}\" from \"{}\" as axis ID", - axis_id_str, input_name); - continue; - } + int axis_id; + if (!TryParse(axis_id_str, &axis_id)) + { + ERROR_LOG_FMT(CONTROLLERINTERFACE, "Failed to parse \"{}\" from \"{}\" as axis ID", + axis_id_str, input_name); + continue; + } - float value = env->CallFloatMethod(motion_event, s_motion_event_get_axis_value, axis_id); + float value = env->CallFloatMethod(motion_event, s_motion_event_get_axis_value, axis_id); - auto casted_input = static_cast(input); - casted_input->SetState(value); - last_polled = std::max(last_polled, casted_input->GetLastPolled()); + auto casted_input = static_cast(input); + casted_input->SetState(value); + last_polled = std::max(last_polled, casted_input->GetLastPolled()); - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, - device->GetQualifiedName(), value); - } + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, + device->GetQualifiedName(), value); } + } - return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; + return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEventNative( - JNIEnv* env, jclass, jstring j_device_qualifier, jstring j_axis_name, jfloat value) + JNIEnv* env, jclass, jstring j_device_qualifier, jstring j_axis_name, jfloat value) { - ciface::Core::DeviceQualifier device_qualifier; - device_qualifier.FromString(GetJString(env, j_device_qualifier)); - const std::shared_ptr device = - g_controller_interface.FindDevice(device_qualifier); - if (!device) - return JNI_FALSE; + ciface::Core::DeviceQualifier device_qualifier; + device_qualifier.FromString(GetJString(env, j_device_qualifier)); + const std::shared_ptr device = + g_controller_interface.FindDevice(device_qualifier); + if (!device) + return JNI_FALSE; - const std::string axis_name = GetJString(env, j_axis_name); + const std::string axis_name = GetJString(env, j_axis_name); - Clock::time_point last_polled{}; + Clock::time_point last_polled{}; - for (ciface::Core::Device::Input* input : device->Inputs()) + for (ciface::Core::Device::Input* input : device->Inputs()) + { + const std::string input_name = input->GetName(); + if (input_name == axis_name) { - const std::string input_name = input->GetName(); - if (input_name == axis_name) - { - auto casted_input = static_cast(input); - casted_input->SetState(value); - last_polled = std::max(last_polled, casted_input->GetLastPolled()); - } + auto casted_input = static_cast(input); + casted_input->SetState(value); + last_polled = std::max(last_polled, casted_input->GetLastPolled()); } + } - return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; + return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_notifySensorSuspendedState( - JNIEnv* env, jclass, jstring j_device_qualifier, jobjectArray j_axis_names, jboolean suspended) + JNIEnv* env, jclass, jstring j_device_qualifier, jobjectArray j_axis_names, jboolean suspended) { -ciface::Core::DeviceQualifier device_qualifier; -device_qualifier.FromString(GetJString(env, j_device_qualifier)); -const std::shared_ptr device = - g_controller_interface.FindDevice(device_qualifier); -if (!device) -return; + ciface::Core::DeviceQualifier device_qualifier; + device_qualifier.FromString(GetJString(env, j_device_qualifier)); + const std::shared_ptr device = + g_controller_interface.FindDevice(device_qualifier); + if (!device) + return; -const std::vector axis_names = JStringArrayToVector(env, j_axis_names); + const std::vector axis_names = JStringArrayToVector(env, j_axis_names); -for (ciface::Core::Device::Input* input : device->Inputs()) -{ -if (Common::Contains(axis_names, input->GetName())) -{ -auto casted_input = static_cast(input); -casted_input->NotifyIsSuspended(static_cast(suspended)); -} -} + for (ciface::Core::Device::Input* input : device->Inputs()) + { + if (Common::Contains(axis_names, input->GetName())) + { + auto casted_input = static_cast(input); + casted_input->NotifyIsSuspended(static_cast(suspended)); + } + } } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_00024InputDeviceListener_onInputDeviceAdded( - JNIEnv* env, jobject, jint device_id) + JNIEnv* env, jobject, jint device_id) { -ciface::Android::InputBackend::AddDevice(env, device_id); + ciface::Android::InputBackend::AddDevice(env, device_id); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_00024InputDeviceListener_onInputDeviceRemoved( - JNIEnv*, jobject, jint device_id) + JNIEnv*, jobject, jint device_id) { -ciface::Android::InputBackend::RemoveDevice(device_id); + ciface::Android::InputBackend::RemoveDevice(device_id); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_00024InputDeviceListener_onInputDeviceChanged( - JNIEnv* env, jobject, jint device_id) + JNIEnv* env, jobject, jint device_id) { -// AddDevice will automatically remove the existing device -ciface::Android::InputBackend::AddDevice(env, device_id); + // AddDevice will automatically remove the existing device + ciface::Android::InputBackend::AddDevice(env, device_id); } JNIEXPORT jobjectArray JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getAllDeviceStrings( - JNIEnv* env, jclass) + JNIEnv* env, jclass) { - return SpanToJStringArray(env, g_controller_interface.GetAllDeviceStrings()); + return SpanToJStringArray(env, g_controller_interface.GetAllDeviceStrings()); } JNIEXPORT jobject JNICALL - Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getDevice( - JNIEnv* env, jclass, jstring j_device_string) +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getDevice( + JNIEnv* env, jclass, jstring j_device_string) { -ciface::Core::DeviceQualifier qualifier; -qualifier.FromString(GetJString(env, j_device_string)); -return CoreDeviceToJava(env, g_controller_interface.FindDevice(qualifier)); + ciface::Core::DeviceQualifier qualifier; + qualifier.FromString(GetJString(env, j_device_string)); + return CoreDeviceToJava(env, g_controller_interface.FindDevice(qualifier)); } JNIEXPORT jstring JNICALL - Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getDescriptorForDevice( - JNIEnv* env, jclass, jstring j_device_string) +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getDescriptorForDevice( + JNIEnv* env, jclass, jstring j_device_string) { -ciface::Core::DeviceQualifier qualifier; -qualifier.FromString(GetJString(env, j_device_string)); -auto device = g_controller_interface.FindDevice(qualifier); -if (!device || device->GetSource() != SOURCE) -return ToJString(env, ""); + ciface::Core::DeviceQualifier qualifier; + qualifier.FromString(GetJString(env, j_device_string)); + auto device = g_controller_interface.FindDevice(qualifier); + if (!device || device->GetSource() != SOURCE) + return ToJString(env, ""); -return ToJString(env, static_cast(device.get())->GetDescriptor()); + return ToJString(env, + static_cast(device.get())->GetDescriptor()); } - }