From 3ffa9f3ba75d63e16d3e52d3bff85e55287b177b Mon Sep 17 00:00:00 2001 From: PlayXboxtion963 Date: Sun, 14 Dec 2025 08:57:02 +0800 Subject: [PATCH] Android: support DualSHockUDPClient for input --- .../features/settings/model/BooleanSetting.kt | 7 +++ .../features/settings/model/Settings.kt | 2 + .../features/settings/model/StringSetting.kt | 7 +++ .../settings/ui/SettingsFragmentPresenter.kt | 60 ++++++++++++++----- .../app/src/main/res/values/strings.xml | 5 ++ Source/Android/jni/Config/NativeConfig.cpp | 4 ++ Source/Android/jni/MainAndroid.cpp | 13 ++++ 7 files changed, 82 insertions(+), 16 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt index bcb61a97f1e..8c37c01c03b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt @@ -931,6 +931,13 @@ enum class BooleanSetting( Settings.SECTION_ACHIEVEMENTS, "ProgressEnabled", true + ), + + SERVERS_ENABLED( + Settings.FILE_DUALSHOCKUDPCLIENT, + Settings.SECTION_INI_SERVER, + key = "Enabled", + false ); override val isOverridden: Boolean diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.kt index 9f91a4244c9..b80021444e3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.kt @@ -106,6 +106,7 @@ class Settings : Closeable { const val FILE_WIIMOTE = "WiimoteNew" const val FILE_ACHIEVEMENTS = "RetroAchievements" const val FILE_GAME_SETTINGS_ONLY = "GameSettingsOnly" + const val FILE_DUALSHOCKUDPCLIENT = "DualShockUDPClient" const val SECTION_INI_ANDROID = "Android" const val SECTION_INI_ANDROID_OVERLAY_BUTTONS = "AndroidOverlayButtons" const val SECTION_INI_GENERAL = "General" @@ -124,5 +125,6 @@ class Settings : Closeable { const val SECTION_STEREOSCOPY = "Stereoscopy" const val SECTION_ANALYTICS = "Analytics" const val SECTION_ACHIEVEMENTS = "Achievements" + const val SECTION_INI_SERVER = "Server" } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.kt index 29640aa92f4..49c5c87c2fd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.kt @@ -92,6 +92,13 @@ enum class StringSetting( Settings.SECTION_ACHIEVEMENTS, "ApiToken", "" + ), + + SERVERS( + Settings.FILE_DUALSHOCKUDPCLIENT, + Settings.SECTION_INI_SERVER, + "Entries", + "" ); override val isOverridden: Boolean 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 09b71966a20..94fdbb0082a 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 @@ -502,16 +502,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) } } @@ -962,8 +962,8 @@ class SettingsFragmentPresenter( 0, false ) { - fragmentView.showDialogFragment(LoginDialog(this)) - loadSettingsList() + fragmentView.showDialogFragment(LoginDialog(this)) + loadSettingsList() }) } else { sl.add( @@ -975,8 +975,8 @@ class SettingsFragmentPresenter( 0, false ) { - logout() - loadSettingsList() + logout() + loadSettingsList() }) } sl.add( @@ -1073,16 +1073,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) } } @@ -1422,6 +1422,32 @@ class SettingsFragmentPresenter( IntSetting.WIIMOTE_BB_SOURCE.setInt(settings, if (newValue) 2 else 0) } }, R.string.real_balance_board, 0)) + + sl.add(SwitchSetting(context, object : AbstractBooleanSetting { + override val isOverridden: Boolean = BooleanSetting.SERVERS_ENABLED.isOverridden + + override val isRuntimeEditable: Boolean = + BooleanSetting.SERVERS_ENABLED.isRuntimeEditable + + override fun delete(settings: Settings): Boolean { + return BooleanSetting.SERVERS_ENABLED.delete(settings) + } + + override val boolean: Boolean get() = BooleanSetting.SERVERS_ENABLED.boolean; + + override fun setBoolean(settings: Settings, newValue: Boolean) { + BooleanSetting.SERVERS_ENABLED.setBoolean(settings, if (newValue) true else false); + } + }, R.string.dualshockudp_client, R.string.dualshockudp_client_description)) + + sl.add( + InputStringSetting( + context, + StringSetting.SERVERS, + R.string.dualshockudp_entries, + R.string.dualshockudp_entries_description + ) + ) } private fun addGraphicsSettings(sl: ArrayList) { @@ -2189,7 +2215,7 @@ class SettingsFragmentPresenter( BooleanSetting.MAIN_DEBUG_JIT_ENABLE_PROFILING, R.string.debug_jit_enable_block_profiling, 0 - ) + ) ) sl.add( RunRunnable( @@ -2355,6 +2381,7 @@ class SettingsFragmentPresenter( addControllerMappingSettings(sl, gcPad, null) } } + 7 -> { // Emulated keyboard controller val gcKeyboard = EmulatedController.getGcKeyboard(gcPadNumber) @@ -2367,6 +2394,7 @@ class SettingsFragmentPresenter( addControllerMappingSettings(sl, gcKeyboard, null) } } + 12 -> { // Adapter sl.add( @@ -2585,11 +2613,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) } /** @@ -2684,7 +2712,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 7ebc5867e3d..7a99f00ef2d 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -24,6 +24,10 @@ Wii Remote Extension 4 Real Balance Board + DualshockUDP Client (Background Input) + DualshockUDP Server Info (Alternate Input Source) + Description:IP:Port; + Wii Remote Buttons @@ -120,6 +124,7 @@ Leave this on if you are using a DolphinBar for real Wii Remote support. Wii Remote Speaker Enable sound output through the speaker on a real Wii Remote (DolphinBar required). + DSU protocol enables the use of input and motion data from compatible sources, like PlayStation, Nintendo Switch and Steam controllers. Allow Mismatched Region Settings Change Discs Automatically Fallback Region diff --git a/Source/Android/jni/Config/NativeConfig.cpp b/Source/Android/jni/Config/NativeConfig.cpp index 8478174dae2..d2bcf0bbad5 100644 --- a/Source/Android/jni/Config/NativeConfig.cpp +++ b/Source/Android/jni/Config/NativeConfig.cpp @@ -52,6 +52,10 @@ static Config::Location GetLocation(JNIEnv* env, jstring file, jstring section, { system = Config::System::Achievements; } + else if (decoded_file == "DualShockUDPClient") + { + system = Config::System::DualShockUDPClient; + } else { ASSERT(false); diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 7c78f49045b..5564c46de26 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -57,6 +57,8 @@ #include "DiscIO/Volume.h" #include "InputCommon/GCAdapter.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" + #include "UICommon/GameFile.h" #include "UICommon/UICommon.h" @@ -562,6 +564,17 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Initialize(J UICommon::InitControllers(WindowSystemInfo(WindowSystemType::Android, nullptr, nullptr, nullptr)); AchievementManager::GetInstance().Init(nullptr); + + // Start a background thread to periodically update controller input (every 10ms). + // This ensures Android keeps controller state updated even without a dedicated input loop. + std::thread([]() { + while (true) + { + g_controller_interface.SetCurrentInputChannel(ciface::InputChannel::Host); + g_controller_interface.UpdateInput(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + }).detach(); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ReportStartToAnalytics(JNIEnv*,