From 0150f521f740fc3b1a00d49b432edd0ed32e31d1 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 14 Aug 2021 15:03:54 +0200 Subject: [PATCH 01/38] ControllerInterface/Android: Rip out ButtonManager ButtonManager is very different from how a normal input backend works, and is making it hard for us to improve controller support on Android. The following commits will add a new input backend in its place. --- .../dolphinemu/dolphinemu/NativeLibrary.java | 2 +- .../activities/EmulationActivity.java | 49 +- Source/Android/jni/MainAndroid.cpp | 12 +- Source/Core/InputCommon/CMakeLists.txt | 4 - .../ControllerInterface/Android/Android.cpp | 19 - .../ControllerInterface/Android/Android.h | 2 - .../Touch/ButtonManager.cpp | 788 ------------------ .../ControllerInterface/Touch/ButtonManager.h | 279 ------- .../ControllerInterface/Touch/Touchscreen.cpp | 264 ------ .../ControllerInterface/Touch/Touchscreen.h | 64 -- 10 files changed, 9 insertions(+), 1474 deletions(-) delete mode 100644 Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.cpp delete mode 100644 Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.h delete mode 100644 Source/Core/InputCommon/ControllerInterface/Touch/Touchscreen.cpp delete mode 100644 Source/Core/InputCommon/ControllerInterface/Touch/Touchscreen.h diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index d6fc236771..abd5e8cc2c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -44,7 +44,7 @@ public final class NativeLibrary } /** - * Button type for use in onTouchEvent + * Button type, for legacy use only */ public static final class ButtonType { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 808adbff26..fb839ed41a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -894,21 +894,8 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP return super.dispatchKeyEvent(event); } - int action; - - switch (event.getAction()) - { - case KeyEvent.ACTION_DOWN: - action = NativeLibrary.ButtonState.PRESSED; - break; - case KeyEvent.ACTION_UP: - action = NativeLibrary.ButtonState.RELEASED; - break; - default: - return false; - } - InputDevice input = event.getDevice(); - return NativeLibrary.onGamePadEvent(input.getDescriptor(), event.getKeyCode(), action); + // TODO + return false; } private void toggleControls() @@ -1271,36 +1258,8 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP return false; } - if (((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)) - { - return super.dispatchGenericMotionEvent(event); - } - - // Don't attempt to do anything if we are disconnecting a device. - if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) - return true; - - InputDevice input = event.getDevice(); - List motions = input.getMotionRanges(); - - for (InputDevice.MotionRange range : motions) - { - int axis = range.getAxis(); - float origValue = event.getAxisValue(axis); - float value = ControllerMappingHelper.scaleAxis(input, axis, origValue); - // If the input is still in the "flat" area, that means it's really zero. - // This is used to compensate for imprecision in joysticks. - if (Math.abs(value) > range.getFlat()) - { - NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, value); - } - else - { - NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, 0.0f); - } - } - - return true; + // TODO + return false; } private void showSubMenu(SaveLoadStateFragment.SaveOrLoad saveOrLoad) diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 01728ce014..fd6ca47359 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -51,8 +51,6 @@ #include "DiscIO/ScrubbedBlob.h" #include "DiscIO/Volume.h" -#include "InputCommon/ControllerInterface/Android/Android.h" -#include "InputCommon/ControllerInterface/Touch/ButtonManager.h" #include "InputCommon/GCAdapter.h" #include "UICommon/GameFile.h" @@ -293,19 +291,20 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunningAndUnpaused(JNIEnv*, jclas JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent( JNIEnv* env, jclass, jstring jDevice, jint Button, jint Action) { - return ButtonManager::GamepadEvent(GetJString(env, jDevice), Button, Action); + // TODO + return JNI_FALSE; } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent( JNIEnv* env, jclass, jstring jDevice, jint Axis, jfloat Value) { - ButtonManager::GamepadAxisEvent(GetJString(env, jDevice), Axis, Value); + // TODO } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetMotionSensorsEnabled( JNIEnv*, jclass, jboolean accelerometer_enabled, jboolean gyroscope_enabled) { - ciface::Android::SetMotionSensorsEnabled(accelerometer_enabled, gyroscope_enabled); + // TODO } JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env, @@ -583,8 +582,6 @@ static void Run(JNIEnv* env, std::unique_ptr&& boot, bool riivol if (BootManager::BootCore(std::move(boot), wsi)) { - ButtonManager::Init(SConfig::GetInstance().GetGameID()); - static constexpr int WAIT_STEP = 25; while (Core::GetState() == Core::State::Starting) std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_STEP)); @@ -604,7 +601,6 @@ static void Run(JNIEnv* env, std::unique_ptr&& boot, bool riivol s_game_metadata_is_valid = false; Core::Shutdown(); - ButtonManager::Shutdown(); host_identity_guard.unlock(); env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 4b2ab2a170..bb3b1b5872 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -143,12 +143,8 @@ elseif(ANDROID) target_sources(inputcommon PRIVATE ControllerInterface/Android/Android.cpp ControllerInterface/Android/Android.h - ControllerInterface/Touch/ButtonManager.cpp - ControllerInterface/Touch/ButtonManager.h ControllerInterface/Touch/InputOverrider.cpp ControllerInterface/Touch/InputOverrider.h - ControllerInterface/Touch/Touchscreen.cpp - ControllerInterface/Touch/Touchscreen.h ) endif() diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 7a7c548cc6..d59dc03588 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -4,29 +4,10 @@ #include "InputCommon/ControllerInterface/Android/Android.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" -#include "InputCommon/ControllerInterface/Touch/Touchscreen.h" namespace ciface::Android { -static bool s_accelerometer_enabled = false; -static bool s_gyroscope_enabled = false; - -void SetMotionSensorsEnabled(bool accelerometer_enabled, bool gyroscope_enabled) -{ - const bool any_changes = - s_accelerometer_enabled != accelerometer_enabled || s_gyroscope_enabled != gyroscope_enabled; - - s_accelerometer_enabled = accelerometer_enabled; - s_gyroscope_enabled = gyroscope_enabled; - - if (any_changes) - g_controller_interface.RefreshDevices(); -} - void PopulateDevices() { - for (int i = 0; i < 8; ++i) - g_controller_interface.AddDevice(std::make_shared( - i, s_accelerometer_enabled, s_gyroscope_enabled)); } } // namespace ciface::Android diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.h b/Source/Core/InputCommon/ControllerInterface/Android/Android.h index 2cc3c5a22b..9f57f5ba2e 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.h +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.h @@ -5,7 +5,5 @@ namespace ciface::Android { -void SetMotionSensorsEnabled(bool accelerometer_enabled, bool gyroscope_enabled); - void PopulateDevices(); } // namespace ciface::Android diff --git a/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.cpp b/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.cpp deleted file mode 100644 index 0111947608..0000000000 --- a/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.cpp +++ /dev/null @@ -1,788 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "InputCommon/ControllerInterface/Touch/ButtonManager.h" - -#include -#include -#include -#include -#include - -#include "Common/FileUtil.h" -#include "Common/IniFile.h" -#include "Common/StringUtil.h" -#include "Common/Thread.h" - -namespace ButtonManager -{ -namespace -{ -constexpr char TOUCHSCREEN_KEY[] = "Touchscreen"; -constexpr std::array CONFIG_STRINGS{{ - // GC - "InputA", - "InputB", - "InputStart", - "InputX", - "InputY", - "InputZ", - "DPadUp", - "DPadDown", - "DPadLeft", - "DPadRight", - "MainUp", - "MainDown", - "MainLeft", - "MainRight", - "CStickUp", - "CStickDown", - "CStickLeft", - "CStickRight", - "InputL", - "InputR", - // Wiimote - "WiimoteA", - "WiimoteB", - "WiimoteMinus", - "WiimotePlus", - "WiimoteHome", - "Wiimote1", - "Wiimote2", - "WiimoteUp", - "WiimoteDown", - "WiimoteLeft", - "WiimoteRight", - "IRUp", - "IRDown", - "IRLeft", - "IRRight", - "IRForward", - "IRBackward", - "IRHide", - "SwingUp", - "SwingDown", - "SwingLeft", - "SwingRight", - "SwingForward", - "SwingBackward", - "TiltForward", - "TiltBackward", - "TiltLeft", - "TiltRight", - "TiltModifier", - "ShakeX", - "ShakeY", - "ShakeZ", - // Nunchuk - "NunchukC", - "NunchukZ", - "NunchukUp", - "NunchukDown", - "NunchukLeft", - "NunchukRight", - "NunchukSwingUp", - "NunchukSwingDown", - "NunchukSwingLeft", - "NunchukSwingRight", - "NunchukSwingForward", - "NunchukSwingBackward", - "NunchukTiltForward", - "NunchukTiltBackward", - "NunchukTiltLeft", - "NunchukTiltRight", - "NunchukTiltModifier", - "NunchukShakeX", - "NunchukShakeY", - "NunchukShakeZ", - // Classic - "ClassicA", - "ClassicB", - "ClassicX", - "ClassicY", - "ClassicMinus", - "ClassicPlus", - "ClassicHome", - "ClassicZL", - "ClassicZR", - "ClassicUp", - "ClassicDown", - "ClassicLeft", - "ClassicRight", - "ClassicLeftStickUp", - "ClassicLeftStickDown", - "ClassicLeftStickLeft", - "ClassicLeftStickRight", - "ClassicRightStickUp", - "ClassicRightStickDown", - "ClassicRightStickLeft", - "ClassicRightStickRight", - "ClassicTriggerL", - "ClassicTriggerR", - // Guitar - "GuitarMinus", - "GuitarPlus", - "GuitarGreen", - "GuitarRed", - "GuitarYellow", - "GuitarBlue", - "GuitarOrange", - "GuitarStrumUp", - "GuitarStrumDown", - "GuitarUp", - "GuitarDown", - "GuitarLeft", - "GuitarRight", - "GuitarWhammy", - // Drums - "DrumsMinus", - "DrumsPlus", - "DrumsRed", - "DrumsYellow", - "DrumsBlue", - "DrumsGreen", - "DrumsOrange", - "DrumsBass", - "DrumsUp", - "DrumsDown", - "DrumsLeft", - "DrumsRight", - // Turntable - "TurntableGreenLeft", - "TurntableRedLeft", - "TurntableBlueLeft", - "TurntableGreenRight", - "TurntableRedRight", - "TurntableBlueRight", - "TurntableMinus", - "TurntablePlus", - "TurntableHome", - "TurntableEuphoria", - "TurntableLeftTLeft", - "TurntableLeftTRight", - "TurntableRightTLeft", - "TurntableRightTRight", - "TurntableUp", - "TurntableDown", - "TurntableLeft", - "TurntableRight", - "TurntableEffDial", - "TurntableCrossLeft", - "TurntableCrossRight", - // Wiimote IMU - "WiimoteAccelLeft", - "WiimoteAccelRight", - "WiimoteAccelForward", - "WiimoteAccelBackward", - "WiimoteAccelUp", - "WiimoteAccelDown", - "WiimoteGyroPitchUp", - "WiimoteGyroPitchDown", - "WiimoteGyroRollLeft", - "WiimoteGyroRollRight", - "WiimoteGyroYawLeft", - "WiimoteGyroYawRight", - // Rumble - "Rumble", -}}; - -constexpr std::array CONFIG_TYPES{{ - // GC - BUTTON_A, - BUTTON_B, - BUTTON_START, - BUTTON_X, - BUTTON_Y, - BUTTON_Z, - BUTTON_UP, - BUTTON_DOWN, - BUTTON_LEFT, - BUTTON_RIGHT, - STICK_MAIN_UP, - STICK_MAIN_DOWN, - STICK_MAIN_LEFT, - STICK_MAIN_RIGHT, - STICK_C_UP, - STICK_C_DOWN, - STICK_C_LEFT, - STICK_C_RIGHT, - TRIGGER_L, - TRIGGER_R, - // Wiimote - WIIMOTE_BUTTON_A, - WIIMOTE_BUTTON_B, - WIIMOTE_BUTTON_MINUS, - WIIMOTE_BUTTON_PLUS, - WIIMOTE_BUTTON_HOME, - WIIMOTE_BUTTON_1, - WIIMOTE_BUTTON_2, - WIIMOTE_UP, - WIIMOTE_DOWN, - WIIMOTE_LEFT, - WIIMOTE_RIGHT, - WIIMOTE_IR_UP, - WIIMOTE_IR_DOWN, - WIIMOTE_IR_LEFT, - WIIMOTE_IR_RIGHT, - WIIMOTE_IR_FORWARD, - WIIMOTE_IR_BACKWARD, - WIIMOTE_IR_HIDE, - WIIMOTE_SWING_UP, - WIIMOTE_SWING_DOWN, - WIIMOTE_SWING_LEFT, - WIIMOTE_SWING_RIGHT, - WIIMOTE_SWING_FORWARD, - WIIMOTE_SWING_BACKWARD, - WIIMOTE_TILT_FORWARD, - WIIMOTE_TILT_BACKWARD, - WIIMOTE_TILT_LEFT, - WIIMOTE_TILT_RIGHT, - WIIMOTE_TILT_MODIFIER, - WIIMOTE_SHAKE_X, - WIIMOTE_SHAKE_Y, - WIIMOTE_SHAKE_Z, - // Nunchuk - NUNCHUK_BUTTON_C, - NUNCHUK_BUTTON_Z, - NUNCHUK_STICK_UP, - NUNCHUK_STICK_DOWN, - NUNCHUK_STICK_LEFT, - NUNCHUK_STICK_RIGHT, - NUNCHUK_SWING_UP, - NUNCHUK_SWING_DOWN, - NUNCHUK_SWING_LEFT, - NUNCHUK_SWING_RIGHT, - NUNCHUK_SWING_FORWARD, - NUNCHUK_SWING_BACKWARD, - NUNCHUK_TILT_FORWARD, - NUNCHUK_TILT_BACKWARD, - NUNCHUK_TILT_LEFT, - NUNCHUK_TILT_RIGHT, - NUNCHUK_TILT_MODIFIER, - NUNCHUK_SHAKE_X, - NUNCHUK_SHAKE_Y, - NUNCHUK_SHAKE_Z, - // Classic - CLASSIC_BUTTON_A, - CLASSIC_BUTTON_B, - CLASSIC_BUTTON_X, - CLASSIC_BUTTON_Y, - CLASSIC_BUTTON_MINUS, - CLASSIC_BUTTON_PLUS, - CLASSIC_BUTTON_HOME, - CLASSIC_BUTTON_ZL, - CLASSIC_BUTTON_ZR, - CLASSIC_DPAD_UP, - CLASSIC_DPAD_DOWN, - CLASSIC_DPAD_LEFT, - CLASSIC_DPAD_RIGHT, - CLASSIC_STICK_LEFT_UP, - CLASSIC_STICK_LEFT_DOWN, - CLASSIC_STICK_LEFT_LEFT, - CLASSIC_STICK_LEFT_RIGHT, - CLASSIC_STICK_RIGHT_UP, - CLASSIC_STICK_RIGHT_DOWN, - CLASSIC_STICK_RIGHT_LEFT, - CLASSIC_STICK_RIGHT_RIGHT, - CLASSIC_TRIGGER_L, - CLASSIC_TRIGGER_R, - // Guitar - GUITAR_BUTTON_MINUS, - GUITAR_BUTTON_PLUS, - GUITAR_FRET_GREEN, - GUITAR_FRET_RED, - GUITAR_FRET_YELLOW, - GUITAR_FRET_BLUE, - GUITAR_FRET_ORANGE, - GUITAR_STRUM_UP, - GUITAR_STRUM_DOWN, - GUITAR_STICK_UP, - GUITAR_STICK_DOWN, - GUITAR_STICK_LEFT, - GUITAR_STICK_RIGHT, - GUITAR_WHAMMY_BAR, - // Drums - DRUMS_BUTTON_MINUS, - DRUMS_BUTTON_PLUS, - DRUMS_PAD_RED, - DRUMS_PAD_YELLOW, - DRUMS_PAD_BLUE, - DRUMS_PAD_GREEN, - DRUMS_PAD_ORANGE, - DRUMS_PAD_BASS, - DRUMS_STICK_UP, - DRUMS_STICK_DOWN, - DRUMS_STICK_LEFT, - DRUMS_STICK_RIGHT, - // Turntable - TURNTABLE_BUTTON_GREEN_LEFT, - TURNTABLE_BUTTON_RED_LEFT, - TURNTABLE_BUTTON_BLUE_LEFT, - TURNTABLE_BUTTON_GREEN_RIGHT, - TURNTABLE_BUTTON_RED_RIGHT, - TURNTABLE_BUTTON_BLUE_RIGHT, - TURNTABLE_BUTTON_MINUS, - TURNTABLE_BUTTON_PLUS, - TURNTABLE_BUTTON_HOME, - TURNTABLE_BUTTON_EUPHORIA, - TURNTABLE_TABLE_LEFT_LEFT, - TURNTABLE_TABLE_LEFT_RIGHT, - TURNTABLE_TABLE_RIGHT_LEFT, - TURNTABLE_TABLE_RIGHT_RIGHT, - TURNTABLE_STICK_UP, - TURNTABLE_STICK_DOWN, - TURNTABLE_STICK_LEFT, - TURNTABLE_STICK_RIGHT, - TURNTABLE_EFFECT_DIAL, - TURNTABLE_CROSSFADE_LEFT, - TURNTABLE_CROSSFADE_RIGHT, - // Wiimote IMU - WIIMOTE_ACCEL_LEFT, - WIIMOTE_ACCEL_RIGHT, - WIIMOTE_ACCEL_FORWARD, - WIIMOTE_ACCEL_BACKWARD, - WIIMOTE_ACCEL_UP, - WIIMOTE_ACCEL_DOWN, - WIIMOTE_GYRO_PITCH_UP, - WIIMOTE_GYRO_PITCH_DOWN, - WIIMOTE_GYRO_ROLL_LEFT, - WIIMOTE_GYRO_ROLL_RIGHT, - WIIMOTE_GYRO_YAW_LEFT, - WIIMOTE_GYRO_YAW_RIGHT, - // Rumble - RUMBLE, -}}; - -std::unordered_map m_controllers; - -void AddBind(const std::string& dev, sBind* bind) -{ - auto it = m_controllers.find(dev); - if (it != m_controllers.end()) - { - it->second->AddBind(bind); - return; - } - m_controllers[dev] = new InputDevice(dev); - m_controllers[dev]->AddBind(bind); -} -} // Anonymous namespace - -void Init(const std::string& game_id) -{ - // Initialize pad 0(gc 1) and pad 4(wii 1) as touch overlay controller - for (int a = 0; a < 5; a += 4) - { - // GC - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_A, BIND_BUTTON, BUTTON_A, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_B, BIND_BUTTON, BUTTON_B, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_START, BIND_BUTTON, BUTTON_START, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_X, BIND_BUTTON, BUTTON_X, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_Y, BIND_BUTTON, BUTTON_Y, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_Z, BIND_BUTTON, BUTTON_Z, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_UP, BIND_BUTTON, BUTTON_UP, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_DOWN, BIND_BUTTON, BUTTON_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_LEFT, BIND_BUTTON, BUTTON_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, BUTTON_RIGHT, BIND_BUTTON, BUTTON_RIGHT, 1.0f)); - - AddBind(TOUCHSCREEN_KEY, new sBind(a, STICK_MAIN_UP, BIND_AXIS, STICK_MAIN_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, STICK_MAIN_DOWN, BIND_AXIS, STICK_MAIN_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, STICK_MAIN_LEFT, BIND_AXIS, STICK_MAIN_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, STICK_MAIN_RIGHT, BIND_AXIS, STICK_MAIN_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, STICK_C_UP, BIND_AXIS, STICK_C_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, STICK_C_DOWN, BIND_AXIS, STICK_C_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, STICK_C_LEFT, BIND_AXIS, STICK_C_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, STICK_C_RIGHT, BIND_AXIS, STICK_C_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, TRIGGER_L, BIND_AXIS, TRIGGER_L, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, TRIGGER_R, BIND_AXIS, TRIGGER_R, 1.0f)); - - // Wiimote - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_BUTTON_A, BIND_BUTTON, WIIMOTE_BUTTON_A, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_BUTTON_B, BIND_BUTTON, WIIMOTE_BUTTON_B, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_BUTTON_MINUS, BIND_BUTTON, WIIMOTE_BUTTON_MINUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_BUTTON_PLUS, BIND_BUTTON, WIIMOTE_BUTTON_PLUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_BUTTON_HOME, BIND_BUTTON, WIIMOTE_BUTTON_HOME, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_BUTTON_1, BIND_BUTTON, WIIMOTE_BUTTON_1, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_BUTTON_2, BIND_BUTTON, WIIMOTE_BUTTON_2, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_UP, BIND_BUTTON, WIIMOTE_UP, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_DOWN, BIND_BUTTON, WIIMOTE_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_LEFT, BIND_BUTTON, WIIMOTE_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_RIGHT, BIND_BUTTON, WIIMOTE_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_IR_HIDE, BIND_BUTTON, WIIMOTE_IR_HIDE, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_TILT_MODIFIER, BIND_BUTTON, WIIMOTE_TILT_MODIFIER, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_SHAKE_X, BIND_BUTTON, WIIMOTE_SHAKE_X, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_SHAKE_Y, BIND_BUTTON, WIIMOTE_SHAKE_Y, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_SHAKE_Z, BIND_BUTTON, WIIMOTE_SHAKE_Z, 1.0f)); - - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_IR_UP, BIND_AXIS, WIIMOTE_IR_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_IR_DOWN, BIND_AXIS, WIIMOTE_IR_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_IR_LEFT, BIND_AXIS, WIIMOTE_IR_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_IR_RIGHT, BIND_AXIS, WIIMOTE_IR_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_IR_FORWARD, BIND_AXIS, WIIMOTE_IR_FORWARD, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_IR_BACKWARD, BIND_AXIS, WIIMOTE_IR_BACKWARD, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_SWING_UP, BIND_AXIS, WIIMOTE_SWING_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_SWING_DOWN, BIND_AXIS, WIIMOTE_SWING_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_SWING_LEFT, BIND_AXIS, WIIMOTE_SWING_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_SWING_RIGHT, BIND_AXIS, WIIMOTE_SWING_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_SWING_FORWARD, BIND_AXIS, WIIMOTE_SWING_FORWARD, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_SWING_BACKWARD, BIND_AXIS, WIIMOTE_SWING_BACKWARD, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_TILT_FORWARD, BIND_AXIS, WIIMOTE_TILT_FORWARD, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_TILT_BACKWARD, BIND_AXIS, WIIMOTE_TILT_BACKWARD, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_TILT_LEFT, BIND_AXIS, WIIMOTE_TILT_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_TILT_RIGHT, BIND_AXIS, WIIMOTE_TILT_RIGHT, 1.0f)); - - // Wii: Nunchuk - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_BUTTON_C, BIND_BUTTON, NUNCHUK_BUTTON_C, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_BUTTON_Z, BIND_BUTTON, NUNCHUK_BUTTON_Z, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_TILT_MODIFIER, BIND_BUTTON, NUNCHUK_TILT_MODIFIER, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_SHAKE_X, BIND_BUTTON, NUNCHUK_SHAKE_X, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_SHAKE_Y, BIND_BUTTON, NUNCHUK_SHAKE_Y, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_SHAKE_Z, BIND_BUTTON, NUNCHUK_SHAKE_Z, 1.0f)); - - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_SWING_UP, BIND_AXIS, NUNCHUK_SWING_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_SWING_DOWN, BIND_AXIS, NUNCHUK_SWING_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_SWING_LEFT, BIND_AXIS, NUNCHUK_SWING_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_SWING_RIGHT, BIND_AXIS, NUNCHUK_SWING_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_SWING_FORWARD, BIND_AXIS, NUNCHUK_SWING_FORWARD, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_SWING_BACKWARD, BIND_BUTTON, NUNCHUK_SWING_BACKWARD, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_TILT_FORWARD, BIND_AXIS, NUNCHUK_TILT_FORWARD, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_TILT_BACKWARD, BIND_AXIS, NUNCHUK_TILT_BACKWARD, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_TILT_LEFT, BIND_AXIS, NUNCHUK_TILT_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_TILT_RIGHT, BIND_AXIS, NUNCHUK_TILT_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_STICK_UP, BIND_AXIS, NUNCHUK_STICK_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, NUNCHUK_STICK_DOWN, BIND_AXIS, NUNCHUK_STICK_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_STICK_LEFT, BIND_AXIS, NUNCHUK_STICK_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, NUNCHUK_STICK_RIGHT, BIND_AXIS, NUNCHUK_STICK_RIGHT, 1.0f)); - - // Wii: Classic - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_BUTTON_A, BIND_BUTTON, CLASSIC_BUTTON_A, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_BUTTON_B, BIND_BUTTON, CLASSIC_BUTTON_B, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_BUTTON_X, BIND_BUTTON, CLASSIC_BUTTON_X, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_BUTTON_Y, BIND_BUTTON, CLASSIC_BUTTON_Y, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_BUTTON_MINUS, BIND_BUTTON, CLASSIC_BUTTON_MINUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_BUTTON_PLUS, BIND_BUTTON, CLASSIC_BUTTON_PLUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_BUTTON_HOME, BIND_BUTTON, CLASSIC_BUTTON_HOME, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_BUTTON_ZL, BIND_BUTTON, CLASSIC_BUTTON_ZL, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_BUTTON_ZR, BIND_BUTTON, CLASSIC_BUTTON_ZR, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_DPAD_UP, BIND_BUTTON, CLASSIC_DPAD_UP, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_DPAD_DOWN, BIND_BUTTON, CLASSIC_DPAD_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_DPAD_LEFT, BIND_BUTTON, CLASSIC_DPAD_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_DPAD_RIGHT, BIND_BUTTON, CLASSIC_DPAD_RIGHT, 1.0f)); - - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_STICK_LEFT_UP, BIND_AXIS, CLASSIC_STICK_LEFT_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_STICK_LEFT_DOWN, BIND_AXIS, CLASSIC_STICK_LEFT_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_STICK_LEFT_LEFT, BIND_AXIS, CLASSIC_STICK_LEFT_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_STICK_LEFT_RIGHT, BIND_AXIS, CLASSIC_STICK_LEFT_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_STICK_RIGHT_UP, BIND_AXIS, CLASSIC_STICK_RIGHT_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_STICK_RIGHT_DOWN, BIND_AXIS, CLASSIC_STICK_RIGHT_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_STICK_RIGHT_LEFT, BIND_AXIS, CLASSIC_STICK_RIGHT_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, CLASSIC_STICK_RIGHT_RIGHT, BIND_AXIS, CLASSIC_STICK_RIGHT_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_TRIGGER_L, BIND_AXIS, CLASSIC_TRIGGER_L, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, CLASSIC_TRIGGER_R, BIND_AXIS, CLASSIC_TRIGGER_R, 1.0f)); - - // Wii: Guitar - AddBind(TOUCHSCREEN_KEY, - new sBind(a, GUITAR_BUTTON_MINUS, BIND_BUTTON, GUITAR_BUTTON_MINUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, GUITAR_BUTTON_PLUS, BIND_BUTTON, GUITAR_BUTTON_PLUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_FRET_GREEN, BIND_BUTTON, GUITAR_FRET_GREEN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_FRET_RED, BIND_BUTTON, GUITAR_FRET_RED, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, GUITAR_FRET_YELLOW, BIND_BUTTON, GUITAR_FRET_YELLOW, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_FRET_BLUE, BIND_BUTTON, GUITAR_FRET_BLUE, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, GUITAR_FRET_ORANGE, BIND_BUTTON, GUITAR_FRET_ORANGE, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_STRUM_UP, BIND_BUTTON, GUITAR_STRUM_UP, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_STRUM_DOWN, BIND_BUTTON, GUITAR_STRUM_DOWN, 1.0f)); - - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_STICK_UP, BIND_AXIS, GUITAR_STICK_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_STICK_DOWN, BIND_AXIS, GUITAR_STICK_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_STICK_LEFT, BIND_AXIS, GUITAR_STICK_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_STICK_RIGHT, BIND_AXIS, GUITAR_STICK_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, GUITAR_WHAMMY_BAR, BIND_AXIS, GUITAR_WHAMMY_BAR, 1.0f)); - - // Wii: Drums - AddBind(TOUCHSCREEN_KEY, - new sBind(a, DRUMS_BUTTON_MINUS, BIND_BUTTON, DRUMS_BUTTON_MINUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_BUTTON_PLUS, BIND_BUTTON, DRUMS_BUTTON_PLUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_PAD_RED, BIND_BUTTON, DRUMS_PAD_RED, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_PAD_YELLOW, BIND_BUTTON, DRUMS_PAD_YELLOW, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_PAD_BLUE, BIND_BUTTON, DRUMS_PAD_BLUE, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_PAD_GREEN, BIND_BUTTON, DRUMS_PAD_GREEN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_PAD_ORANGE, BIND_BUTTON, DRUMS_PAD_ORANGE, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_PAD_BASS, BIND_BUTTON, DRUMS_PAD_BASS, 1.0f)); - - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_STICK_UP, BIND_AXIS, DRUMS_STICK_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_STICK_DOWN, BIND_AXIS, DRUMS_STICK_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_STICK_LEFT, BIND_AXIS, DRUMS_STICK_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, DRUMS_STICK_RIGHT, BIND_AXIS, DRUMS_STICK_RIGHT, 1.0f)); - - // Wii: Turntable - AddBind(TOUCHSCREEN_KEY, new sBind(a, TURNTABLE_BUTTON_GREEN_LEFT, BIND_BUTTON, - TURNTABLE_BUTTON_GREEN_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_BUTTON_RED_LEFT, BIND_BUTTON, TURNTABLE_BUTTON_RED_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, TURNTABLE_BUTTON_BLUE_LEFT, BIND_BUTTON, - TURNTABLE_BUTTON_BLUE_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, TURNTABLE_BUTTON_GREEN_RIGHT, BIND_BUTTON, - TURNTABLE_BUTTON_GREEN_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, TURNTABLE_BUTTON_RED_RIGHT, BIND_BUTTON, - TURNTABLE_BUTTON_RED_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, TURNTABLE_BUTTON_BLUE_RIGHT, BIND_BUTTON, - TURNTABLE_BUTTON_BLUE_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_BUTTON_MINUS, BIND_BUTTON, TURNTABLE_BUTTON_MINUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_BUTTON_PLUS, BIND_BUTTON, TURNTABLE_BUTTON_PLUS, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_BUTTON_HOME, BIND_BUTTON, TURNTABLE_BUTTON_HOME, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_BUTTON_EUPHORIA, BIND_BUTTON, TURNTABLE_BUTTON_EUPHORIA, 1.0f)); - - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_TABLE_LEFT_LEFT, BIND_AXIS, TURNTABLE_TABLE_LEFT_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_TABLE_LEFT_RIGHT, BIND_AXIS, TURNTABLE_TABLE_LEFT_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_TABLE_RIGHT_LEFT, BIND_AXIS, TURNTABLE_TABLE_RIGHT_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, TURNTABLE_TABLE_RIGHT_RIGHT, BIND_AXIS, - TURNTABLE_TABLE_RIGHT_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_STICK_UP, BIND_AXIS, TURNTABLE_STICK_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_STICK_DOWN, BIND_AXIS, TURNTABLE_STICK_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_STICK_LEFT, BIND_AXIS, TURNTABLE_STICK_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_STICK_RIGHT, BIND_AXIS, TURNTABLE_STICK_RIGHT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_EFFECT_DIAL, BIND_AXIS, TURNTABLE_EFFECT_DIAL, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_CROSSFADE_LEFT, BIND_AXIS, TURNTABLE_CROSSFADE_LEFT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, TURNTABLE_CROSSFADE_RIGHT, BIND_AXIS, TURNTABLE_CROSSFADE_RIGHT, 1.0f)); - - // Wiimote IMU - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_ACCEL_LEFT, BIND_AXIS, WIIMOTE_ACCEL_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_ACCEL_RIGHT, BIND_AXIS, WIIMOTE_ACCEL_RIGHT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_ACCEL_FORWARD, BIND_AXIS, WIIMOTE_ACCEL_FORWARD, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_ACCEL_BACKWARD, BIND_AXIS, WIIMOTE_ACCEL_BACKWARD, 1.0f)); - AddBind(TOUCHSCREEN_KEY, new sBind(a, WIIMOTE_ACCEL_UP, BIND_AXIS, WIIMOTE_ACCEL_UP, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_ACCEL_DOWN, BIND_AXIS, WIIMOTE_ACCEL_DOWN, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_GYRO_PITCH_UP, BIND_AXIS, WIIMOTE_GYRO_PITCH_UP, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_GYRO_PITCH_DOWN, BIND_AXIS, WIIMOTE_GYRO_PITCH_DOWN, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_GYRO_ROLL_LEFT, BIND_AXIS, WIIMOTE_GYRO_ROLL_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_GYRO_ROLL_RIGHT, BIND_AXIS, WIIMOTE_GYRO_ROLL_RIGHT, -1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_GYRO_YAW_LEFT, BIND_AXIS, WIIMOTE_GYRO_YAW_LEFT, 1.0f)); - AddBind(TOUCHSCREEN_KEY, - new sBind(a, WIIMOTE_GYRO_YAW_RIGHT, BIND_AXIS, WIIMOTE_GYRO_YAW_RIGHT, -1.0f)); - } - // Init our controller bindings - IniFile ini; - ini.Load(File::GetUserPath(D_CONFIG_IDX) + std::string("Dolphin.ini"), true); - ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + std::string(game_id + ".ini"), true); - for (u32 a = 0; a < CONFIG_STRINGS.size(); ++a) - { - for (int pad_id = 0; pad_id < 8; ++pad_id) - { - std::ostringstream config; - config << CONFIG_STRINGS[a] << "_" << pad_id; - BindType type; - int bindnum; - char dev[128]{}; - bool hasbind = false; - char modifier = '+'; - std::string value; - ini.GetOrCreateSection("Android")->Get(config.str(), &value, "None"); - if (value == "None") - continue; - if (std::string::npos != value.find("Axis")) - { - hasbind = true; - type = BIND_AXIS; - if (value.starts_with("Device ''")) - sscanf(value.c_str(), "Device ''-Axis %d%c", &bindnum, &modifier); - else - sscanf(value.c_str(), "Device '%127[^\']'-Axis %d%c", dev, &bindnum, &modifier); - } - else if (std::string::npos != value.find("Button")) - { - hasbind = true; - type = BIND_BUTTON; - if (value.starts_with("Device ''")) - sscanf(value.c_str(), "Device ''-Button %d", &bindnum); - else - sscanf(value.c_str(), "Device '%127[^\']'-Button %d", dev, &bindnum); - } - if (hasbind) - AddBind(std::string(dev), - new sBind(pad_id, CONFIG_TYPES[a], type, bindnum, modifier == '-' ? -1.0f : 1.0f)); - } - } -} - -bool GetButtonPressed(int pad_id, ButtonType button) -{ - bool pressed = m_controllers[TOUCHSCREEN_KEY]->ButtonValue(pad_id, button); - - for (const auto& ctrl : m_controllers) - pressed |= ctrl.second->ButtonValue(pad_id, button); - - return pressed; -} - -float GetAxisValue(int pad_id, ButtonType axis) -{ - float value = m_controllers[TOUCHSCREEN_KEY]->AxisValue(pad_id, axis); - if (value == 0.0f) - { - for (const auto& ctrl : m_controllers) - { - value = ctrl.second->AxisValue(pad_id, axis); - if (value != 0.0f) - return value; - } - } - return value; -} - -bool GamepadEvent(const std::string& dev, int button, int action) -{ - auto it = m_controllers.find(dev); - if (it != m_controllers.end()) - return it->second->PressEvent(button, action); - return false; -} - -void GamepadAxisEvent(const std::string& dev, int axis, float value) -{ - auto it = m_controllers.find(dev); - if (it != m_controllers.end()) - it->second->AxisEvent(axis, value); -} - -void Shutdown() -{ - for (const auto& controller : m_controllers) - delete controller.second; - m_controllers.clear(); -} - -// InputDevice -bool InputDevice::PressEvent(int button, int action) -{ - bool handled = false; - for (const auto& binding : m_input_binds) - { - if (binding.second->m_bind == button) - { - if (binding.second->m_bind_type == BIND_BUTTON) - m_buttons[binding.second->m_button_type] = action == BUTTON_PRESSED ? true : false; - else - m_axes[binding.second->m_button_type] = action == BUTTON_PRESSED ? 1.0f : 0.0f; - handled = true; - } - } - return handled; -} - -void InputDevice::AxisEvent(int axis, float value) -{ - for (const auto& binding : m_input_binds) - { - if (binding.second->m_bind == axis) - { - if (binding.second->m_bind_type == BIND_AXIS) - m_axes[binding.second->m_button_type] = value; - else - m_buttons[binding.second->m_button_type] = value > 0.5f ? true : false; - } - } -} - -bool InputDevice::ButtonValue(int pad_id, ButtonType button) const -{ - const auto binding = m_input_binds.find(std::make_pair(pad_id, button)); - if (binding == m_input_binds.end()) - return false; - - if (binding->second->m_bind_type == BIND_BUTTON) - { - const auto button = m_buttons.find(binding->second->m_button_type); - if (button == m_buttons.end()) - return false; - return button->second; - } - else - { - const auto axis = m_axes.find(binding->second->m_button_type); - if (axis == m_axes.end()) - return false; - return (axis->second * binding->second->m_neg) > 0.5f; - } -} - -float InputDevice::AxisValue(int pad_id, ButtonType axis) const -{ - const auto binding = m_input_binds.find(std::make_pair(pad_id, axis)); - if (binding == m_input_binds.end()) - return 0.0f; - - if (binding->second->m_bind_type == BIND_AXIS) - { - const auto axis = m_axes.find(binding->second->m_button_type); - if (axis == m_axes.end()) - return 0.0f; - return axis->second * binding->second->m_neg; - } - else - { - const auto button = m_buttons.find(binding->second->m_button_type); - if (button == m_buttons.end()) - return 0.0f; - return button->second == BUTTON_PRESSED ? 1.0f : 0.0f; - } -} -} // namespace ButtonManager diff --git a/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.h b/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.h deleted file mode 100644 index 08c4349c84..0000000000 --- a/Source/Core/InputCommon/ControllerInterface/Touch/ButtonManager.h +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -namespace ButtonManager -{ -enum ButtonType -{ - // GC - BUTTON_A = 0, - BUTTON_B = 1, - BUTTON_START = 2, - BUTTON_X = 3, - BUTTON_Y = 4, - BUTTON_Z = 5, - BUTTON_UP = 6, - BUTTON_DOWN = 7, - BUTTON_LEFT = 8, - BUTTON_RIGHT = 9, - STICK_MAIN = 10, // Used on Java Side - STICK_MAIN_UP = 11, - STICK_MAIN_DOWN = 12, - STICK_MAIN_LEFT = 13, - STICK_MAIN_RIGHT = 14, - STICK_C = 15, // Used on Java Side - STICK_C_UP = 16, - STICK_C_DOWN = 17, - STICK_C_LEFT = 18, - STICK_C_RIGHT = 19, - TRIGGER_L = 20, - TRIGGER_R = 21, - // Wiimote - WIIMOTE_BUTTON_A = 100, - WIIMOTE_BUTTON_B = 101, - WIIMOTE_BUTTON_MINUS = 102, - WIIMOTE_BUTTON_PLUS = 103, - WIIMOTE_BUTTON_HOME = 104, - WIIMOTE_BUTTON_1 = 105, - WIIMOTE_BUTTON_2 = 106, - WIIMOTE_UP = 107, - WIIMOTE_DOWN = 108, - WIIMOTE_LEFT = 109, - WIIMOTE_RIGHT = 110, - WIIMOTE_IR = 111, // To Be Used on Java Side - WIIMOTE_IR_UP = 112, - WIIMOTE_IR_DOWN = 113, - WIIMOTE_IR_LEFT = 114, - WIIMOTE_IR_RIGHT = 115, - WIIMOTE_IR_FORWARD = 116, - WIIMOTE_IR_BACKWARD = 117, - WIIMOTE_IR_HIDE = 118, - WIIMOTE_SWING = 119, // To Be Used on Java Side - WIIMOTE_SWING_UP = 120, - WIIMOTE_SWING_DOWN = 121, - WIIMOTE_SWING_LEFT = 122, - WIIMOTE_SWING_RIGHT = 123, - WIIMOTE_SWING_FORWARD = 124, - WIIMOTE_SWING_BACKWARD = 125, - WIIMOTE_TILT = 126, // To Be Used on Java Side - WIIMOTE_TILT_FORWARD = 127, - WIIMOTE_TILT_BACKWARD = 128, - WIIMOTE_TILT_LEFT = 129, - WIIMOTE_TILT_RIGHT = 130, - WIIMOTE_TILT_MODIFIER = 131, - WIIMOTE_SHAKE_X = 132, - WIIMOTE_SHAKE_Y = 133, - WIIMOTE_SHAKE_Z = 134, - // Nunchuk - NUNCHUK_BUTTON_C = 200, - NUNCHUK_BUTTON_Z = 201, - NUNCHUK_STICK = 202, // To Be Used on Java Side - NUNCHUK_STICK_UP = 203, - NUNCHUK_STICK_DOWN = 204, - NUNCHUK_STICK_LEFT = 205, - NUNCHUK_STICK_RIGHT = 206, - NUNCHUK_SWING = 207, // To Be Used on Java Side - NUNCHUK_SWING_UP = 208, - NUNCHUK_SWING_DOWN = 209, - NUNCHUK_SWING_LEFT = 210, - NUNCHUK_SWING_RIGHT = 211, - NUNCHUK_SWING_FORWARD = 212, - NUNCHUK_SWING_BACKWARD = 213, - NUNCHUK_TILT = 214, // To Be Used on Java Side - NUNCHUK_TILT_FORWARD = 215, - NUNCHUK_TILT_BACKWARD = 216, - NUNCHUK_TILT_LEFT = 217, - NUNCHUK_TILT_RIGHT = 218, - NUNCHUK_TILT_MODIFIER = 219, - NUNCHUK_SHAKE_X = 220, - NUNCHUK_SHAKE_Y = 221, - NUNCHUK_SHAKE_Z = 222, - // Classic - CLASSIC_BUTTON_A = 300, - CLASSIC_BUTTON_B = 301, - CLASSIC_BUTTON_X = 302, - CLASSIC_BUTTON_Y = 303, - CLASSIC_BUTTON_MINUS = 304, - CLASSIC_BUTTON_PLUS = 305, - CLASSIC_BUTTON_HOME = 306, - CLASSIC_BUTTON_ZL = 307, - CLASSIC_BUTTON_ZR = 308, - CLASSIC_DPAD_UP = 309, - CLASSIC_DPAD_DOWN = 310, - CLASSIC_DPAD_LEFT = 311, - CLASSIC_DPAD_RIGHT = 312, - CLASSIC_STICK_LEFT = 313, // To Be Used on Java Side - CLASSIC_STICK_LEFT_UP = 314, - CLASSIC_STICK_LEFT_DOWN = 315, - CLASSIC_STICK_LEFT_LEFT = 316, - CLASSIC_STICK_LEFT_RIGHT = 317, - CLASSIC_STICK_RIGHT = 318, // To Be Used on Java Side - CLASSIC_STICK_RIGHT_UP = 319, - CLASSIC_STICK_RIGHT_DOWN = 320, - CLASSIC_STICK_RIGHT_LEFT = 321, - CLASSIC_STICK_RIGHT_RIGHT = 322, - CLASSIC_TRIGGER_L = 323, - CLASSIC_TRIGGER_R = 324, - // Guitar - GUITAR_BUTTON_MINUS = 400, - GUITAR_BUTTON_PLUS = 401, - GUITAR_FRET_GREEN = 402, - GUITAR_FRET_RED = 403, - GUITAR_FRET_YELLOW = 404, - GUITAR_FRET_BLUE = 405, - GUITAR_FRET_ORANGE = 406, - GUITAR_STRUM_UP = 407, - GUITAR_STRUM_DOWN = 408, - GUITAR_STICK = 409, // To Be Used on Java Side - GUITAR_STICK_UP = 410, - GUITAR_STICK_DOWN = 411, - GUITAR_STICK_LEFT = 412, - GUITAR_STICK_RIGHT = 413, - GUITAR_WHAMMY_BAR = 414, - // Drums - DRUMS_BUTTON_MINUS = 500, - DRUMS_BUTTON_PLUS = 501, - DRUMS_PAD_RED = 502, - DRUMS_PAD_YELLOW = 503, - DRUMS_PAD_BLUE = 504, - DRUMS_PAD_GREEN = 505, - DRUMS_PAD_ORANGE = 506, - DRUMS_PAD_BASS = 507, - DRUMS_STICK = 508, // To Be Used on Java Side - DRUMS_STICK_UP = 509, - DRUMS_STICK_DOWN = 510, - DRUMS_STICK_LEFT = 511, - DRUMS_STICK_RIGHT = 512, - // Turntable - TURNTABLE_BUTTON_GREEN_LEFT = 600, - TURNTABLE_BUTTON_RED_LEFT = 601, - TURNTABLE_BUTTON_BLUE_LEFT = 602, - TURNTABLE_BUTTON_GREEN_RIGHT = 603, - TURNTABLE_BUTTON_RED_RIGHT = 604, - TURNTABLE_BUTTON_BLUE_RIGHT = 605, - TURNTABLE_BUTTON_MINUS = 606, - TURNTABLE_BUTTON_PLUS = 607, - TURNTABLE_BUTTON_HOME = 608, - TURNTABLE_BUTTON_EUPHORIA = 609, - TURNTABLE_TABLE_LEFT = 610, // To Be Used on Java Side - TURNTABLE_TABLE_LEFT_LEFT = 611, - TURNTABLE_TABLE_LEFT_RIGHT = 612, - TURNTABLE_TABLE_RIGHT = 613, // To Be Used on Java Side - TURNTABLE_TABLE_RIGHT_LEFT = 614, - TURNTABLE_TABLE_RIGHT_RIGHT = 615, - TURNTABLE_STICK = 616, // To Be Used on Java Side - TURNTABLE_STICK_UP = 617, - TURNTABLE_STICK_DOWN = 618, - TURNTABLE_STICK_LEFT = 619, - TURNTABLE_STICK_RIGHT = 620, - TURNTABLE_EFFECT_DIAL = 621, - TURNTABLE_CROSSFADE = 622, // To Be Used on Java Side - TURNTABLE_CROSSFADE_LEFT = 623, - TURNTABLE_CROSSFADE_RIGHT = 624, - // Wiimote IMU - WIIMOTE_ACCEL_LEFT = 625, - WIIMOTE_ACCEL_RIGHT = 626, - WIIMOTE_ACCEL_FORWARD = 627, - WIIMOTE_ACCEL_BACKWARD = 628, - WIIMOTE_ACCEL_UP = 629, - WIIMOTE_ACCEL_DOWN = 630, - WIIMOTE_GYRO_PITCH_UP = 631, - WIIMOTE_GYRO_PITCH_DOWN = 632, - WIIMOTE_GYRO_ROLL_LEFT = 633, - WIIMOTE_GYRO_ROLL_RIGHT = 634, - WIIMOTE_GYRO_YAW_LEFT = 635, - WIIMOTE_GYRO_YAW_RIGHT = 636, - // Rumble - RUMBLE = 700, -}; -enum ButtonState -{ - BUTTON_RELEASED = 0, - BUTTON_PRESSED = 1 -}; -enum BindType -{ - BIND_BUTTON = 0, - BIND_AXIS -}; -class Button -{ -private: - ButtonState m_state; - -public: - Button() : m_state(BUTTON_RELEASED) {} - void SetState(ButtonState state) { m_state = state; } - bool Pressed() const { return m_state == BUTTON_PRESSED; } - ~Button() {} -}; -class Axis -{ -private: - float m_value; - -public: - Axis() : m_value(0.0f) {} - void SetValue(float value) { m_value = value; } - float AxisValue() const { return m_value; } - ~Axis() {} -}; - -struct sBind -{ - const int m_pad_id; - const ButtonType m_button_type; - const BindType m_bind_type; - const int m_bind; - const float m_neg; - sBind(int pad_id, ButtonType button_type, BindType bind_type, int bind, float neg) - : m_pad_id(pad_id), m_button_type(button_type), m_bind_type(bind_type), m_bind(bind), - m_neg(neg) - { - } -}; - -class InputDevice -{ -private: - const std::string m_dev; - std::map m_buttons; - std::map m_axes; - - // Key is pad_id and ButtonType - std::map, sBind*> m_input_binds; - -public: - InputDevice(std::string dev) : m_dev(dev) {} - ~InputDevice() - { - for (const auto& bind : m_input_binds) - delete bind.second; - m_input_binds.clear(); - } - void AddBind(sBind* bind) - { - m_input_binds[std::make_pair(bind->m_pad_id, bind->m_button_type)] = bind; - } - bool PressEvent(int button, int action); - void AxisEvent(int axis, float value); - bool ButtonValue(int pad_id, ButtonType button) const; - float AxisValue(int pad_id, ButtonType axis) const; -}; - -void Init(const std::string&); - -// pad_id is numbered 0 to 3 for GC pads and 4 to 7 for Wiimotes -bool GetButtonPressed(int pad_id, ButtonType button); -float GetAxisValue(int pad_id, ButtonType axis); - -bool GamepadEvent(const std::string& dev, int button, int action); -void GamepadAxisEvent(const std::string& dev, int axis, float value); - -void Shutdown(); -} // namespace ButtonManager diff --git a/Source/Core/InputCommon/ControllerInterface/Touch/Touchscreen.cpp b/Source/Core/InputCommon/ControllerInterface/Touch/Touchscreen.cpp deleted file mode 100644 index a4c20b0ae0..0000000000 --- a/Source/Core/InputCommon/ControllerInterface/Touch/Touchscreen.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "InputCommon/ControllerInterface/Touch/Touchscreen.h" - -#include -#include -#ifdef ANDROID -#include -#endif - -#include "InputCommon/ControllerInterface/ControllerInterface.h" - -namespace ciface::Touch -{ -// Touchscreens and stuff -std::string Touchscreen::GetName() const -{ - return "Touchscreen"; -} - -std::string Touchscreen::GetSource() const -{ - return "Android"; -} - -Touchscreen::Touchscreen(int pad_id, bool accelerometer_enabled, bool gyroscope_enabled) - : m_pad_id(pad_id) -{ - // GC - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_A)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_B)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_START)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_X)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_Y)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_Z)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_UP)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_DOWN)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_LEFT)); - AddInput(new Button(m_pad_id, ButtonManager::BUTTON_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::STICK_MAIN_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::STICK_MAIN_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::STICK_MAIN_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::STICK_MAIN_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::STICK_C_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::STICK_C_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::STICK_C_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::STICK_C_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::TRIGGER_L)); - AddInput(new Axis(m_pad_id, ButtonManager::TRIGGER_R)); - - // Wiimote - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_BUTTON_A)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_BUTTON_B)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_BUTTON_MINUS)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_BUTTON_PLUS)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_BUTTON_HOME)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_BUTTON_1)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_BUTTON_2)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_UP)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_DOWN)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_LEFT)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_RIGHT)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_IR_HIDE)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_TILT_MODIFIER)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_SHAKE_X)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_SHAKE_Y)); - AddInput(new Button(m_pad_id, ButtonManager::WIIMOTE_SHAKE_Z)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_IR_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_IR_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_IR_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_IR_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_IR_FORWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_IR_BACKWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_SWING_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_SWING_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_SWING_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_SWING_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_SWING_FORWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_SWING_BACKWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_TILT_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_TILT_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_TILT_FORWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_TILT_BACKWARD)); - - // Wii ext: Nunchuk - AddInput(new Button(m_pad_id, ButtonManager::NUNCHUK_BUTTON_C)); - AddInput(new Button(m_pad_id, ButtonManager::NUNCHUK_BUTTON_Z)); - AddInput(new Button(m_pad_id, ButtonManager::NUNCHUK_TILT_MODIFIER)); - AddInput(new Button(m_pad_id, ButtonManager::NUNCHUK_SHAKE_X)); - AddInput(new Button(m_pad_id, ButtonManager::NUNCHUK_SHAKE_Y)); - AddInput(new Button(m_pad_id, ButtonManager::NUNCHUK_SHAKE_Z)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_STICK_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_STICK_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_STICK_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_STICK_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_SWING_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_SWING_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_SWING_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_SWING_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_SWING_FORWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_SWING_BACKWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_TILT_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_TILT_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_TILT_FORWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::NUNCHUK_TILT_BACKWARD)); - - // Wii ext: Classic - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_A)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_B)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_X)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_Y)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_MINUS)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_PLUS)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_HOME)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_ZL)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_BUTTON_ZR)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_DPAD_UP)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_DPAD_DOWN)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_DPAD_LEFT)); - AddInput(new Button(m_pad_id, ButtonManager::CLASSIC_DPAD_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_STICK_LEFT_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_STICK_LEFT_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_STICK_LEFT_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_STICK_LEFT_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_STICK_RIGHT_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_STICK_RIGHT_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_STICK_RIGHT_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_STICK_RIGHT_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_TRIGGER_L)); - AddInput(new Axis(m_pad_id, ButtonManager::CLASSIC_TRIGGER_R)); - - // Wii-ext: Guitar - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_BUTTON_MINUS)); - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_BUTTON_PLUS)); - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_FRET_GREEN)); - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_FRET_RED)); - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_FRET_YELLOW)); - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_FRET_BLUE)); - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_FRET_ORANGE)); - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_STRUM_UP)); - AddInput(new Button(m_pad_id, ButtonManager::GUITAR_STRUM_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::GUITAR_STICK_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::GUITAR_STICK_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::GUITAR_STICK_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::GUITAR_STICK_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::GUITAR_WHAMMY_BAR)); - - // Wii-ext: Drums - AddInput(new Button(m_pad_id, ButtonManager::DRUMS_BUTTON_MINUS)); - AddInput(new Button(m_pad_id, ButtonManager::DRUMS_BUTTON_PLUS)); - AddInput(new Button(m_pad_id, ButtonManager::DRUMS_PAD_RED)); - AddInput(new Button(m_pad_id, ButtonManager::DRUMS_PAD_YELLOW)); - AddInput(new Button(m_pad_id, ButtonManager::DRUMS_PAD_BLUE)); - AddInput(new Button(m_pad_id, ButtonManager::DRUMS_PAD_GREEN)); - AddInput(new Button(m_pad_id, ButtonManager::DRUMS_PAD_ORANGE)); - AddInput(new Button(m_pad_id, ButtonManager::DRUMS_PAD_BASS)); - AddInput(new Axis(m_pad_id, ButtonManager::DRUMS_STICK_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::DRUMS_STICK_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::DRUMS_STICK_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::DRUMS_STICK_DOWN)); - - // Wii-ext: Turntable - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_GREEN_LEFT)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_RED_LEFT)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_BLUE_LEFT)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_GREEN_RIGHT)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_RED_RIGHT)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_BLUE_RIGHT)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_MINUS)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_PLUS)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_HOME)); - AddInput(new Button(m_pad_id, ButtonManager::TURNTABLE_BUTTON_EUPHORIA)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_TABLE_LEFT_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_TABLE_LEFT_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_TABLE_RIGHT_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_TABLE_RIGHT_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_STICK_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_STICK_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_STICK_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_STICK_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_CROSSFADE_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_CROSSFADE_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::TURNTABLE_EFFECT_DIAL)); - - // Wiimote IMU - // Only add inputs if we actually can receive data from the relevant sensor. - // Whether inputs exist affects what WiimoteEmu gets when calling ControlReference::BoundCount. - if (accelerometer_enabled) - { - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_ACCEL_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_ACCEL_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_ACCEL_FORWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_ACCEL_BACKWARD)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_ACCEL_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_ACCEL_DOWN)); - } - if (gyroscope_enabled) - { - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_GYRO_PITCH_UP)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_GYRO_PITCH_DOWN)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_GYRO_ROLL_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_GYRO_ROLL_RIGHT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_GYRO_YAW_LEFT)); - AddInput(new Axis(m_pad_id, ButtonManager::WIIMOTE_GYRO_YAW_RIGHT)); - } - - // Rumble - AddOutput(new Motor(m_pad_id, ButtonManager::RUMBLE)); -} -// Buttons and stuff - -std::string Touchscreen::Button::GetName() const -{ - std::ostringstream ss; - ss << "Button " << static_cast(m_index); - return ss.str(); -} - -ControlState Touchscreen::Button::GetState() const -{ - return ButtonManager::GetButtonPressed(m_pad_id, m_index); -} - -std::string Touchscreen::Axis::GetName() const -{ - std::ostringstream ss; - ss << "Axis " << static_cast(m_index); - return ss.str(); -} - -ControlState Touchscreen::Axis::GetState() const -{ - return ButtonManager::GetAxisValue(m_pad_id, m_index) * m_neg; -} - -Touchscreen::Motor::~Motor() -{ -} - -std::string Touchscreen::Motor::GetName() const -{ - std::ostringstream ss; - ss << "Rumble " << static_cast(m_index); - return ss.str(); -} - -void Touchscreen::Motor::SetState(ControlState state) -{ - if (state > 0) - { - std::thread(Rumble, m_pad_id, state).detach(); - } -} - -void Touchscreen::Motor::Rumble(int pad_id, double state) -{ -#ifdef ANDROID - JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetDoRumble(), pad_id, - state); -#endif -} -} // namespace ciface::Touch diff --git a/Source/Core/InputCommon/ControllerInterface/Touch/Touchscreen.h b/Source/Core/InputCommon/ControllerInterface/Touch/Touchscreen.h deleted file mode 100644 index 9d12df1a4c..0000000000 --- a/Source/Core/InputCommon/ControllerInterface/Touch/Touchscreen.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "InputCommon/ControllerInterface/ControllerInterface.h" -#include "InputCommon/ControllerInterface/Touch/ButtonManager.h" - -namespace ciface::Touch -{ -class Touchscreen : public Core::Device -{ -private: - class Button : public Input - { - public: - std::string GetName() const override; - Button(int pad_id, ButtonManager::ButtonType index) : m_pad_id(pad_id), m_index(index) {} - ControlState GetState() const override; - - private: - const int m_pad_id; - const ButtonManager::ButtonType m_index; - }; - class Axis : public Input - { - public: - std::string GetName() const override; - bool IsDetectable() const override { return false; } - Axis(int pad_id, ButtonManager::ButtonType index, float neg = 1.0f) - : m_pad_id(pad_id), m_index(index), m_neg(neg) - { - } - ControlState GetState() const override; - - private: - const int m_pad_id; - const ButtonManager::ButtonType m_index; - const float m_neg; - }; - class Motor : public Core::Device::Output - { - public: - Motor(int pad_id, ButtonManager::ButtonType index) : m_pad_id(pad_id), m_index(index) {} - ~Motor(); - std::string GetName() const override; - void SetState(ControlState state) override; - - private: - const int m_pad_id; - const ButtonManager::ButtonType m_index; - static void Rumble(int pad_id, double state); - }; - -public: - Touchscreen(int pad_id, bool accelerometer_enabled, bool gyroscope_enabled); - ~Touchscreen() {} - std::string GetName() const override; - std::string GetSource() const override; - -private: - const int m_pad_id; -}; -} // namespace ciface::Touch From 9e7a2ee0fb307977bb0ae4245c8738ebd33df44b Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 16 Sep 2022 22:03:03 +0200 Subject: [PATCH 02/38] Android: Remove old motion input implementation --- .../dolphinemu/dolphinemu/NativeLibrary.java | 27 ---- .../activities/EmulationActivity.java | 19 --- .../dolphinemu/utils/MotionListener.java | 128 ------------------ Source/Android/jni/MainAndroid.cpp | 19 --- 4 files changed, 193 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/MotionListener.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index abd5e8cc2c..5f3c8f839f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -234,30 +234,6 @@ public final class NativeLibrary // Disallows instantiation. } - /** - * Default touchscreen device - */ - public static final String TouchScreenDevice = "Touchscreen"; - - /** - * Handles button press events for a gamepad. - * - * @param Device The input descriptor of the gamepad. - * @param Button Key code identifying which button was pressed. - * @param Action Mask identifying which action is happening (button pressed down, or button released). - * @return If we handled the button press. - */ - public static native boolean onGamePadEvent(String Device, int Button, int Action); - - /** - * Handles gamepad movement events. - * - * @param Device The device ID of the gamepad. - * @param Axis The axis ID - * @param Value The value of the axis represented by the given ID. - */ - public static native void onGamePadMoveEvent(String Device, int Axis, float Value); - /** * Rumble sent from native. Currently only supports phone rumble. * @@ -277,9 +253,6 @@ public final class NativeLibrary Rumble.checkRumble(padID, state); } - public static native void SetMotionSensorsEnabled(boolean accelerometerEnabled, - boolean gyroscopeEnabled); - /** * Gets the Dolphin version string. * diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index fb839ed41a..18372b1d20 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -64,7 +64,6 @@ import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.IniFile; -import org.dolphinemu.dolphinemu.utils.MotionListener; import org.dolphinemu.dolphinemu.utils.Rumble; import org.dolphinemu.dolphinemu.utils.ThemeHelper; @@ -89,7 +88,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP private EmulationFragment mEmulationFragment; private SharedPreferences mPreferences; - private MotionListener mMotionListener; private Settings mSettings; @@ -359,8 +357,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP updateOrientation(); - mMotionListener = new MotionListener(this); - // Set these options now so that the SurfaceView the game renders into is the right size. enableFullscreenImmersive(); @@ -452,16 +448,12 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP } updateOrientation(); - - if (NativeLibrary.IsGameMetadataValid()) - updateMotionListener(); } @Override protected void onPause() { super.onPause(); - mMotionListener.disable(); } @Override @@ -481,19 +473,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP } setTitle(NativeLibrary.GetCurrentTitleDescription()); - updateMotionListener(); mEmulationFragment.refreshInputOverlay(); } - private void updateMotionListener() - { - if (NativeLibrary.IsEmulatingWii() && IntSetting.MAIN_MOTION_CONTROLS.getInt(mSettings) != 2) - mMotionListener.enable(); - else - mMotionListener.disable(); - } - @Override protected void onDestroy() { @@ -1063,8 +1046,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP { IntSetting.MAIN_MOTION_CONTROLS.setInt(mSettings, indexSelected); - updateMotionListener(); - updateWiimoteNewImuIr(indexSelected); NativeLibrary.ReloadWiimoteConfig(); }) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/MotionListener.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/MotionListener.java deleted file mode 100644 index 0086f3beb5..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/MotionListener.java +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.utils; - -import android.app.Activity; -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.view.Surface; - -import org.dolphinemu.dolphinemu.NativeLibrary; -import org.dolphinemu.dolphinemu.NativeLibrary.ButtonType; - -public class MotionListener implements SensorEventListener -{ - private final Activity mActivity; - private final SensorManager mSensorManager; - private final Sensor mAccelSensor; - private final Sensor mGyroSensor; - - private boolean mEnabled = false; - - // The same sampling period as for Wii Remotes - private static final int SAMPLING_PERIOD_US = 1000000 / 200; - - public MotionListener(Activity activity) - { - mActivity = activity; - mSensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE); - mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - mGyroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); - } - - @Override - public void onSensorChanged(SensorEvent sensorEvent) - { - float x, y; - float z = sensorEvent.values[2]; - int orientation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); - switch (orientation) - { - default: - case Surface.ROTATION_0: - x = -sensorEvent.values[0]; - y = -sensorEvent.values[1]; - break; - case Surface.ROTATION_90: - x = sensorEvent.values[1]; - y = -sensorEvent.values[0]; - break; - case Surface.ROTATION_180: - x = sensorEvent.values[0]; - y = sensorEvent.values[1]; - break; - case Surface.ROTATION_270: - x = -sensorEvent.values[1]; - y = sensorEvent.values[0]; - break; - } - - if (sensorEvent.sensor == mAccelSensor) - { - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_ACCEL_LEFT, x); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_ACCEL_RIGHT, x); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_ACCEL_FORWARD, y); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_ACCEL_BACKWARD, y); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_ACCEL_UP, z); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_ACCEL_DOWN, z); - } - - if (sensorEvent.sensor == mGyroSensor) - { - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_GYRO_PITCH_UP, x); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_GYRO_PITCH_DOWN, x); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_GYRO_ROLL_LEFT, y); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_GYRO_ROLL_RIGHT, y); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_GYRO_YAW_LEFT, z); - NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, - ButtonType.WIIMOTE_GYRO_YAW_RIGHT, z); - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int i) - { - // We don't care about this - } - - public void enable() - { - if (mEnabled) - return; - - if (mAccelSensor != null) - mSensorManager.registerListener(this, mAccelSensor, SAMPLING_PERIOD_US); - if (mGyroSensor != null) - mSensorManager.registerListener(this, mGyroSensor, SAMPLING_PERIOD_US); - - NativeLibrary.SetMotionSensorsEnabled(mAccelSensor != null, mGyroSensor != null); - - mEnabled = true; - } - - public void disable() - { - if (!mEnabled) - return; - - mSensorManager.unregisterListener(this); - - NativeLibrary.SetMotionSensorsEnabled(false, false); - - mEnabled = false; - } -} diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index fd6ca47359..95297664db 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -288,25 +288,6 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunningAndUnpaused(JNIEnv*, jclas return static_cast(Core::GetState() == Core::State::Running); } -JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent( - JNIEnv* env, jclass, jstring jDevice, jint Button, jint Action) -{ - // TODO - return JNI_FALSE; -} - -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent( - JNIEnv* env, jclass, jstring jDevice, jint Axis, jfloat Value) -{ - // TODO -} - -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetMotionSensorsEnabled( - JNIEnv*, jclass, jboolean accelerometer_enabled, jboolean gyroscope_enabled) -{ - // TODO -} - JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env, jclass) { From 68ebb5c33e49aec1b7a49f1b1de94ef2291d319c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 18 Sep 2022 14:33:02 +0200 Subject: [PATCH 03/38] Android: Remove old rumble implementation --- .../dolphinemu/dolphinemu/NativeLibrary.java | 20 ---- .../activities/EmulationActivity.java | 17 ---- .../settings/model/BooleanSetting.java | 2 - .../dolphinemu/dolphinemu/utils/Rumble.java | 97 ------------------- .../res/menu/menu_overlay_controls_gc.xml | 5 - .../res/menu/menu_overlay_controls_wii.xml | 5 - Source/Android/jni/AndroidCommon/IDCache.cpp | 7 -- Source/Android/jni/AndroidCommon/IDCache.h | 1 - 8 files changed, 154 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Rumble.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 5f3c8f839f..1db70b72d9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -18,7 +18,6 @@ import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.dialogs.AlertMessage; import org.dolphinemu.dolphinemu.utils.CompressCallback; import org.dolphinemu.dolphinemu.utils.Log; -import org.dolphinemu.dolphinemu.utils.Rumble; import java.lang.ref.WeakReference; import java.util.LinkedHashMap; @@ -234,25 +233,6 @@ public final class NativeLibrary // Disallows instantiation. } - /** - * Rumble sent from native. Currently only supports phone rumble. - * - * @param padID Ignored for now. Future use would be to pass rumble to a connected controller - * @param state Ignored for now since phone rumble can't just be 'turned' on/off - */ - @Keep - public static void rumble(int padID, double state) - { - final EmulationActivity emulationActivity = sEmulationActivity.get(); - if (emulationActivity == null) - { - Log.warning("[NativeLibrary] EmulationActivity is null"); - return; - } - - Rumble.checkRumble(padID, state); - } - /** * Gets the Dolphin version string. * diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 18372b1d20..a90209b264 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -64,7 +64,6 @@ import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.IniFile; -import org.dolphinemu.dolphinemu.utils.Rumble; import org.dolphinemu.dolphinemu.utils.ThemeHelper; import java.io.File; @@ -157,7 +156,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP public static final int MENU_ACTION_EXIT = 22; public static final int MENU_ACTION_CHANGE_DISC = 23; public static final int MENU_ACTION_JOYSTICK_REL_CENTER = 24; - public static final int MENU_ACTION_RUMBLE = 25; public static final int MENU_ACTION_RESET_OVERLAY = 26; public static final int MENU_SET_IR_RECENTER = 27; public static final int MENU_SET_IR_MODE = 28; @@ -192,7 +190,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP EmulationActivity.MENU_ACTION_CHOOSE_CONTROLLER); buttonsActionsMap.append(R.id.menu_emulation_joystick_rel_center, EmulationActivity.MENU_ACTION_JOYSTICK_REL_CENTER); - buttonsActionsMap.append(R.id.menu_emulation_rumble, EmulationActivity.MENU_ACTION_RUMBLE); buttonsActionsMap .append(R.id.menu_emulation_reset_overlay, EmulationActivity.MENU_ACTION_RESET_OVERLAY); buttonsActionsMap.append(R.id.menu_emulation_ir_recenter, @@ -360,8 +357,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP // Set these options now so that the SurfaceView the game renders into is the right size. enableFullscreenImmersive(); - Rumble.initRumble(this); - ActivityEmulationBinding binding = ActivityEmulationBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); @@ -627,8 +622,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP // Populate the switch value for joystick center on touch menu.findItem(R.id.menu_emulation_joystick_rel_center) .setChecked(BooleanSetting.MAIN_JOYSTICK_REL_CENTER.getBoolean(mSettings)); - menu.findItem(R.id.menu_emulation_rumble) - .setChecked(BooleanSetting.MAIN_PHONE_RUMBLE.getBoolean(mSettings)); if (wii) { menu.findItem(R.id.menu_emulation_ir_recenter) @@ -668,10 +661,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP item.setChecked(!item.isChecked()); toggleJoystickRelCenter(item.isChecked()); break; - case MENU_ACTION_RUMBLE: - item.setChecked(!item.isChecked()); - toggleRumble(item.isChecked()); - break; case MENU_SET_IR_RECENTER: item.setChecked(!item.isChecked()); toggleRecenter(item.isChecked()); @@ -842,12 +831,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP BooleanSetting.MAIN_JOYSTICK_REL_CENTER.setBoolean(mSettings, state); } - private void toggleRumble(boolean state) - { - BooleanSetting.MAIN_PHONE_RUMBLE.setBoolean(mSettings, state); - Rumble.setPhoneVibrator(state, this); - } - private void toggleRecenter(boolean state) { BooleanSetting.MAIN_IR_ALWAYS_RECENTER.setBoolean(mSettings, state); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java index 1194e1f56a..cbaa7a4292 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java @@ -100,8 +100,6 @@ public enum BooleanSetting implements AbstractBooleanSetting "UseBlackBackgrounds", false), MAIN_JOYSTICK_REL_CENTER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "JoystickRelCenter", true), - MAIN_PHONE_RUMBLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "PhoneRumble", true), MAIN_SHOW_INPUT_OVERLAY(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "ShowInputOverlay", true), MAIN_IR_ALWAYS_RECENTER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Rumble.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Rumble.java deleted file mode 100644 index 27d556898a..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Rumble.java +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.utils; - -import android.content.Context; -import android.os.Build; -import android.os.VibrationEffect; -import android.os.Vibrator; -import android.util.SparseArray; -import android.view.InputDevice; - -import org.dolphinemu.dolphinemu.activities.EmulationActivity; -import org.dolphinemu.dolphinemu.features.settings.model.AdHocStringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; - -public class Rumble -{ - private static Vibrator phoneVibrator; - private static final SparseArray emuVibrators = new SparseArray<>(); - - public static void initRumble(EmulationActivity activity) - { - clear(); - - if (BooleanSetting.MAIN_PHONE_RUMBLE.getBooleanGlobal()) - { - setPhoneVibrator(true, activity); - } - - for (int i = 0; i < 8; i++) - { - String deviceName = AdHocStringSetting.getStringGlobal(Settings.FILE_DOLPHIN, - Settings.SECTION_BINDINGS, SettingsFile.KEY_EMU_RUMBLE + i, ""); - - if (!deviceName.isEmpty()) - { - for (int id : InputDevice.getDeviceIds()) - { - InputDevice device = InputDevice.getDevice(id); - if (deviceName.equals(device.getDescriptor())) - { - Vibrator vib = device.getVibrator(); - if (vib != null && vib.hasVibrator()) - emuVibrators.put(i, vib); - } - } - } - } - } - - public static void setPhoneVibrator(boolean set, EmulationActivity activity) - { - if (set) - { - Vibrator vib = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); - if (vib != null && vib.hasVibrator()) - phoneVibrator = vib; - } - else - { - phoneVibrator = null; - } - } - - private static void clear() - { - phoneVibrator = null; - emuVibrators.clear(); - } - - public static void checkRumble(int padId, double state) - { - if (phoneVibrator != null) - doRumble(phoneVibrator); - - if (emuVibrators.get(padId) != null) - doRumble(emuVibrators.get(padId)); - } - - public static void doRumble(Vibrator vib) - { - // Check again that it exists and can vibrate - if (vib != null && vib.hasVibrator()) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - { - vib.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); - } - else - { - vib.vibrate(100); - } - } - } -} diff --git a/Source/Android/app/src/main/res/menu/menu_overlay_controls_gc.xml b/Source/Android/app/src/main/res/menu/menu_overlay_controls_gc.xml index 418d58f008..e55c310b22 100644 --- a/Source/Android/app/src/main/res/menu/menu_overlay_controls_gc.xml +++ b/Source/Android/app/src/main/res/menu/menu_overlay_controls_gc.xml @@ -19,11 +19,6 @@ android:checkable="true" android:title="@string/emulation_control_joystick_rel_center"/> - - diff --git a/Source/Android/app/src/main/res/menu/menu_overlay_controls_wii.xml b/Source/Android/app/src/main/res/menu/menu_overlay_controls_wii.xml index e5182f6ffe..c3dcb47c33 100644 --- a/Source/Android/app/src/main/res/menu/menu_overlay_controls_wii.xml +++ b/Source/Android/app/src/main/res/menu/menu_overlay_controls_wii.xml @@ -19,11 +19,6 @@ android:id="@+id/menu_emulation_joystick_rel_center" android:checkable="true" android:title="@string/emulation_control_joystick_rel_center"/> - - (env->NewGlobalRef(native_library_class)); s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg", "(Ljava/lang/String;Ljava/lang/String;ZZZ)Z"); - s_do_rumble = env->GetStaticMethodID(s_native_library_class, "rumble", "(ID)V"); s_update_touch_pointer = env->GetStaticMethodID(s_native_library_class, "updateTouchPointer", "()V"); s_on_title_changed = env->GetStaticMethodID(s_native_library_class, "onTitleChanged", "()V"); diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 58a0aa17bd..79ab19acaa 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -13,7 +13,6 @@ jclass GetStringClass(); jclass GetNativeLibraryClass(); jmethodID GetDisplayAlertMsg(); -jmethodID GetDoRumble(); jmethodID GetUpdateTouchPointer(); jmethodID GetOnTitleChanged(); jmethodID GetFinishEmulationActivity(); From 792cb6219533bd9ce5154da9a87f6eabddfaa977 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 31 Dec 2021 16:14:15 +0100 Subject: [PATCH 04/38] ControllerInterface/Android: Implement device population --- Source/Core/InputCommon/CMakeLists.txt | 3 + .../ControllerInterface/Android/Android.cpp | 603 +++++++++++++++++- .../ControllerInterface/Android/Android.h | 6 +- .../ControllerInterface.cpp | 4 +- 4 files changed, 612 insertions(+), 4 deletions(-) diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index bb3b1b5872..597a90291f 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -146,6 +146,9 @@ elseif(ANDROID) ControllerInterface/Touch/InputOverrider.cpp ControllerInterface/Touch/InputOverrider.h ) + target_link_libraries(inputcommon PRIVATE + androidcommon + ) endif() if(NOT ANDROID) diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index d59dc03588..681641c2e3 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -1,13 +1,614 @@ -// Copyright 2013 Dolphin Emulator Project +// Copyright 2021 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "InputCommon/ControllerInterface/Android/Android.h" +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "Common/Logging/Log.h" + #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" + +namespace +{ +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_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; + +jintArray s_keycodes_array; + +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", +}; + +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. + + 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); +} + +std::string ConstructAxisName(int source, int axis, bool negative) +{ + const char sign = negative ? '-' : '+'; + return fmt::format("{}{}{}", ConstructAxisNamePrefix(source), axis, sign); +} + +} // namespace + namespace ciface::Android { +class AndroidKey final : public Core::Device::Input +{ +public: + explicit AndroidKey(int keycode) : m_name(ConstructKeyName(keycode)) + { + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Created {}", m_name); + } + + std::string GetName() const override { return m_name; } + + ControlState GetState() const override + { + return 0; // TODO + } + +private: + std::string m_name; +}; + +class AndroidAxis final : public Core::Device::Input +{ +public: + AndroidAxis(int source, int axis, bool negative) + : m_name(ConstructAxisName(source, axis, negative)) + { + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Created {}", m_name); + } + + std::string GetName() const override { return m_name; }; + + ControlState GetState() const override + { + return 0; // TODO + }; + +private: + std::string m_name; +}; + +class AndroidDevice final : public Core::Device +{ +public: + AndroidDevice(JNIEnv* env, jobject 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) + { + jstring j_name = + reinterpret_cast(env->CallObjectMethod(input_device, s_input_device_get_name)); + m_name = GetJString(env, j_name); + env->DeleteLocalRef(j_name); + + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Sources for {}: {:08x}", GetQualifiedName(), m_source); + + AddKeys(env, input_device); + AddAxes(env, input_device); + } + + std::string GetName() const override { return m_name; } + + std::string GetSource() const override { return "Android"; } + + 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; + } + +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) + AddAnalogInputs(positive, negative); + else if (positive || negative) + AddInput(positive ? positive : negative); + } + env->DeleteLocalRef(motion_ranges_list); + } + + const int m_source; + const int m_controller_number; + std::string m_name; +}; + +// Creates an array that contains every possible keycode +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); + + return keycodes_array; +} + +void Init() +{ + 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 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 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); + + jintArray keycodes_array = CreateKeyCodesArray(env); + s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); + env->DeleteLocalRef(keycodes_array); +} + +void Shutdown() +{ + JNIEnv* env = IDCache::GetEnvForThread(); + + env->DeleteGlobalRef(s_input_device_class); + env->DeleteGlobalRef(s_motion_range_class); + env->DeleteGlobalRef(s_keycodes_array); +} + +static void AddDevice(JNIEnv* env, int device_id) +{ + jobject input_device = + env->CallStaticObjectMethod(s_input_device_class, s_input_device_get_device, device_id); + + auto device = std::make_shared(env, input_device); + + env->DeleteLocalRef(input_device); + + if (!device->Inputs().empty() || !device->Outputs().empty()) + g_controller_interface.AddDevice(std::move(device)); +} + void PopulateDevices() { + INFO_LOG_FMT(CONTROLLERINTERFACE, "Android populating devices"); + + 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); } + } // namespace ciface::Android diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.h b/Source/Core/InputCommon/ControllerInterface/Android/Android.h index 9f57f5ba2e..8b8478d7ac 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.h +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.h @@ -1,9 +1,13 @@ -// Copyright 2008 Dolphin Emulator Project +// Copyright 2021 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once namespace ciface::Android { +void Init(); +void Shutdown(); + void PopulateDevices(); + } // namespace ciface::Android diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 6a6affd478..8f58525f0c 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -67,7 +67,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) m_input_backends.emplace_back(ciface::SDL::CreateInputBackend(this)); #endif #ifdef CIFACE_USE_ANDROID -// nothing needed + ciface::Android::Init(); #endif #ifdef CIFACE_USE_EVDEV m_input_backends.emplace_back(ciface::evdev::CreateInputBackend(this)); @@ -237,7 +237,7 @@ void ControllerInterface::Shutdown() ciface::Quartz::DeInit(); #endif #ifdef CIFACE_USE_ANDROID -// nothing needed + ciface::Android::Shutdown(); #endif // Empty the container of input backends to deconstruct and deinitialize them. From ca508e45033c022dbef558a11d170ab7f41c7bf9 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 1 Jan 2022 18:31:50 +0100 Subject: [PATCH 05/38] ControllerInterface/Android: Handle input events Android doesn't let us poll inputs whenever we want. Instead, we listen to input events (activities will have to forward them to the input backend), and store the received values in atomic variables in the Input classes. This is similar in concept to how ButtonManager worked, but without its homegrown second input mapping system. --- .../activities/EmulationActivity.java | 23 ++- .../input/model/ControllerInterface.java | 31 +++ .../ControllerInterface/Android/Android.cpp | 189 ++++++++++++++++-- 3 files changed, 217 insertions(+), 26 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index a90209b264..699e28a9e8 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -11,7 +11,6 @@ import android.os.Build; import android.os.Bundle; import android.util.Pair; import android.util.SparseIntArray; -import android.view.InputDevice; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -42,6 +41,7 @@ import org.dolphinemu.dolphinemu.databinding.ActivityEmulationBinding; import org.dolphinemu.dolphinemu.databinding.DialogInputAdjustBinding; import org.dolphinemu.dolphinemu.databinding.DialogIrSensitivityBinding; import org.dolphinemu.dolphinemu.databinding.DialogSkylandersManagerBinding; +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings; @@ -61,7 +61,6 @@ import org.dolphinemu.dolphinemu.overlay.InputOverlayPointer; import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.ui.main.ThemeProvider; import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; -import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.IniFile; import org.dolphinemu.dolphinemu.utils.ThemeHelper; @@ -855,13 +854,15 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP @Override public boolean dispatchKeyEvent(KeyEvent event) { - if (mMenuVisible || event.getKeyCode() == KeyEvent.KEYCODE_BACK) + if (!mMenuVisible) { - return super.dispatchKeyEvent(event); + if (ControllerInterface.dispatchKeyEvent(event)) + { + return true; + } } - // TODO - return false; + return super.dispatchKeyEvent(event); } private void toggleControls() @@ -1217,13 +1218,15 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP @Override public boolean dispatchGenericMotionEvent(MotionEvent event) { - if (mMenuVisible) + if (!mMenuVisible) { - return false; + if (ControllerInterface.dispatchGenericMotionEvent(event)) + { + return true; + } } - // TODO - return false; + return super.dispatchGenericMotionEvent(event); } private void showSubMenu(SaveLoadStateFragment.SaveOrLoad saveOrLoad) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java new file mode 100644 index 0000000000..80d21eeb57 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import android.view.KeyEvent; +import android.view.MotionEvent; + +/** + * This class interfaces with the native ControllerInterface, + * which is where the emulator core gets inputs from. + */ +public final class ControllerInterface +{ + /** + * Activities which want to pass on inputs to native code + * should call this in their own dispatchKeyEvent method. + * + * @return true if the emulator core seems to be interested in this event. + * false if the event should be passed on to the default dispatchKeyEvent. + */ + public static native boolean dispatchKeyEvent(KeyEvent event); + + /** + * Activities which want to pass on inputs to native code + * should call this in their own dispatchGenericMotionEvent method. + * + * @return true if the emulator core seems to be interested in this event. + * false if the event should be passed on to the default dispatchGenericMotionEvent. + */ + public static native boolean dispatchGenericMotionEvent(MotionEvent event); +} diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 681641c2e3..26aad585fc 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -3,6 +3,7 @@ #include "InputCommon/ControllerInterface/Android/Android.h" +#include #include #include #include @@ -15,6 +16,7 @@ #include #include "Common/Logging/Log.h" +#include "Common/StringUtil.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" @@ -31,6 +33,7 @@ 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_id; jmethodID s_input_device_get_motion_ranges; jmethodID s_input_device_get_name; jmethodID s_input_device_get_sources; @@ -42,8 +45,21 @@ 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; + +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; + jintArray s_keycodes_array; +std::unordered_map s_device_id_to_device_qualifier; + constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31 const std::array KEYCODE_NAMES = { @@ -379,47 +395,70 @@ std::string ConstructAxisName(int source, int axis, bool 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; + } + + 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; +} + } // namespace namespace ciface::Android { -class AndroidKey final : public Core::Device::Input +class AndroidInput : public Core::Device::Input { public: - explicit AndroidKey(int keycode) : m_name(ConstructKeyName(keycode)) + 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 - { - return 0; // TODO - } + ControlState GetState() const override { return m_state.load(std::memory_order_relaxed); } + + void SetState(ControlState state) { m_state.store(state, std::memory_order_relaxed); } private: std::string m_name; + std::atomic m_state = 0; }; -class AndroidAxis final : public Core::Device::Input +class AndroidKey final : public AndroidInput +{ +public: + explicit AndroidKey(int keycode) : AndroidInput(ConstructKeyName(keycode)) {} +}; + +class AndroidAxis final : public AndroidInput { public: AndroidAxis(int source, int axis, bool negative) - : m_name(ConstructAxisName(source, axis, negative)) + : AndroidInput(ConstructAxisName(source, axis, negative)), m_negative(negative) { - DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Created {}", m_name); } - std::string GetName() const override { return m_name; }; - ControlState GetState() const override { - return 0; // TODO - }; + return m_negative ? -AndroidInput::GetState() : AndroidInput::GetState(); + } private: - std::string m_name; + bool m_negative; }; class AndroidDevice final : public Core::Device @@ -552,6 +591,7 @@ void Init() 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_id = env->GetMethodID(s_input_device_class, "getId", "()I"); s_input_device_get_motion_ranges = env->GetMethodID(s_input_device_class, "getMotionRanges", "()Ljava/util/List;"); s_input_device_get_name = @@ -568,6 +608,21 @@ void Init() 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 = + env->GetMethodID(s_input_event_class, "getDevice", "()Landroid/view/InputDevice;"); + + 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"); + + 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"); + jintArray keycodes_array = CreateKeyCodesArray(env); s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); env->DeleteLocalRef(keycodes_array); @@ -579,6 +634,9 @@ void Shutdown() 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_keycodes_array); } @@ -591,8 +649,17 @@ static void AddDevice(JNIEnv* env, int device_id) env->DeleteLocalRef(input_device); - if (!device->Inputs().empty() || !device->Outputs().empty()) - g_controller_interface.AddDevice(std::move(device)); + if (device->Inputs().empty() && device->Outputs().empty()) + return; + + g_controller_interface.AddDevice(device); + + 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); } void PopulateDevices() @@ -612,3 +679,93 @@ void PopulateDevices() } } // namespace ciface::Android + +extern "C" { + +JNIEXPORT jboolean JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchKeyEvent( + 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 jobject input_device = env->CallObjectMethod(key_event, s_input_event_get_device); + const jint device_id = env->CallIntMethod(input_device, s_input_device_get_id); + env->DeleteLocalRef(input_device); + 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); + + 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; + } + + static_cast(input)->SetState(state); + + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, device->GetQualifiedName(), + state); + + // TODO: Return true when appropriate + return JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchGenericMotionEvent( + JNIEnv* env, jclass, jobject motion_event) +{ + const jobject input_device = env->CallObjectMethod(motion_event, s_input_event_get_device); + const jint device_id = env->CallIntMethod(input_device, s_input_device_get_id); + env->DeleteLocalRef(input_device); + 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); + + 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 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; + } + + float value = env->CallFloatMethod(motion_event, s_motion_event_get_axis_value, axis_id); + static_cast(input)->SetState(value); + + DEBUG_LOG_FMT(CONTROLLERINTERFACE, "Set {} of {} to {}", input_name, + device->GetQualifiedName(), value); + } + } + + // TODO: Return true when appropriate + return JNI_FALSE; +} +} From d6af294a2370b1827508cdf1819ad7cedbb39f7b Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 6 Mar 2022 11:51:59 +0100 Subject: [PATCH 06/38] ControllerInterface/Android: Return whether input was handled When Android presents an input event to an app, it wants the app to return true or false depending on whether the app handled the event or not. If the event wasn't handled by the app, it will be passed on to the system, which may decide to take an action depending on what kind of input event it is. For instance, if a B button press is passed on to the system, it will be turned into a Back press. But if an R1 press is passed on to the system, nothing in particular happens. It's important that we get this return value right in Dolphin. For instance, the user generally wouldn't want a B button press to open the EmulationActivity menu, so B button presses usually shouldn't be passed on to the system - but volume button presses usually should be passed on to the system, since it would be hard to adjust the volume otherwise. What ButtonManager did was to pass on input events that are for a button which the user has not mapped, which I think makes sense. But exactly how to implement that is more complicated in the new input backend than in ButtonManager, because now we have a separation between the input backend and the code that keeps track of the user's mappings. What I'm going with in this commit is to treat an input as mapped if it has been polled recently. In part I chose this because it seemed like a simple way of implementing it that wouldn't cause too many layering violations, but it also has two useful side effects: 1. If a controller is not being polled (e.g. GameCube controllers in Wii games that don't use them), its mappings will not be considered. 2. Once sensor input is implemented in the Android input backend, we will be able to use this "polled recently" tracking to power down the sensors at times when the game is using a Wii Remote reporting mode that doesn't include motion data. (Assuming that the sensor inputs only are mapped to Wii Remote motion controls, that is.) --- .../ControllerInterface/Android/Android.cpp | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 26aad585fc..5a16ef56d0 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -3,7 +3,9 @@ #include "InputCommon/ControllerInterface/Android/Android.h" +#include #include +#include #include #include #include @@ -58,6 +60,9 @@ jmethodID s_motion_event_get_source; jintArray s_keycodes_array; +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; constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31 @@ -429,13 +434,20 @@ public: std::string GetName() const override { return m_name; } - ControlState GetState() const override { return m_state.load(std::memory_order_relaxed); } + 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 @@ -718,13 +730,14 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch return false; } - static_cast(input)->SetState(state); + 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); - // TODO: Return true when appropriate - return JNI_FALSE; + return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } JNIEXPORT jboolean JNICALL @@ -741,6 +754,8 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch 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{}; + for (ciface::Core::Device::Input* input : device->Inputs()) { const std::string input_name = input->GetName(); @@ -758,14 +773,16 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch } float value = env->CallFloatMethod(motion_event, s_motion_event_get_axis_value, axis_id); - static_cast(input)->SetState(value); + + 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); } } - // TODO: Return true when appropriate - return JNI_FALSE; + return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } } From 104ea098921846e01eb58ebfa00822b14da46cda Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 6 Mar 2022 20:27:25 +0100 Subject: [PATCH 07/38] ControllerInterface/Android: Implement hotplug --- .../input/model/ControllerInterface.java | 72 +++++++++++++++++++ .../dolphinemu/utils/LooperThread.java | 58 +++++++++++++++ .../ControllerInterface/Android/Android.cpp | 27 +++++++ 3 files changed, 157 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/LooperThread.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java index 80d21eeb57..8a7bc8177f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java @@ -2,15 +2,50 @@ package org.dolphinemu.dolphinemu.features.input.model; +import android.content.Context; +import android.hardware.input.InputManager; +import android.os.Handler; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.annotation.Keep; + +import org.dolphinemu.dolphinemu.DolphinApplication; +import org.dolphinemu.dolphinemu.utils.LooperThread; + /** * This class interfaces with the native ControllerInterface, * which is where the emulator core gets inputs from. */ public final class ControllerInterface { + private static final class InputDeviceListener implements InputManager.InputDeviceListener + { + @Override + public void onInputDeviceAdded(int deviceId) + { + // Simple implementation for now. We could do something fancier if we wanted to. + refreshDevices(); + } + + @Override + public void onInputDeviceRemoved(int deviceId) + { + // Simple implementation for now. We could do something fancier if we wanted to. + refreshDevices(); + } + + @Override + public void onInputDeviceChanged(int deviceId) + { + // Simple implementation for now. We could do something fancier if we wanted to. + refreshDevices(); + } + } + + private static InputDeviceListener mInputDeviceListener; + private static LooperThread mLooperThread; + /** * Activities which want to pass on inputs to native code * should call this in their own dispatchKeyEvent method. @@ -28,4 +63,41 @@ public final class ControllerInterface * false if the event should be passed on to the default dispatchGenericMotionEvent. */ public static native boolean dispatchGenericMotionEvent(MotionEvent event); + + /** + * Rescans for input devices. + */ + public static native void refreshDevices(); + + @Keep + private static void registerInputDeviceListener() + { + if (mLooperThread == null) + { + mLooperThread = new LooperThread("Hotplug thread"); + mLooperThread.start(); + } + + if (mInputDeviceListener == null) + { + InputManager im = (InputManager) + DolphinApplication.getAppContext().getSystemService(Context.INPUT_SERVICE); + + mInputDeviceListener = new InputDeviceListener(); + im.registerInputDeviceListener(mInputDeviceListener, new Handler(mLooperThread.getLooper())); + } + } + + @Keep + private static void unregisterInputDeviceListener() + { + if (mInputDeviceListener != null) + { + InputManager im = (InputManager) + DolphinApplication.getAppContext().getSystemService(Context.INPUT_SERVICE); + + im.unregisterInputDeviceListener(mInputDeviceListener); + mInputDeviceListener = null; + } + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/LooperThread.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/LooperThread.java new file mode 100644 index 0000000000..8a67ec7e89 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/LooperThread.java @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.utils; + +import android.os.Looper; + +public class LooperThread extends Thread +{ + private Looper mLooper; + + public LooperThread() + { + super(); + } + + public LooperThread(String name) + { + super(name); + } + + @Override + public void run() + { + Looper.prepare(); + + synchronized (this) + { + mLooper = Looper.myLooper(); + notifyAll(); + } + + Looper.loop(); + } + + public Looper getLooper() + { + if (!isAlive()) + { + throw new IllegalStateException(); + } + + synchronized (this) + { + while (mLooper == null) + { + try + { + wait(); + } + catch (InterruptedException ignored) + { + } + } + } + + return mLooper; + } +} diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 5a16ef56d0..e5baa4e43e 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -58,6 +58,10 @@ 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; + jintArray s_keycodes_array; using Clock = std::chrono::steady_clock; @@ -635,20 +639,36 @@ void Init() 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"); + 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"); + 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); } void Shutdown() { JNIEnv* env = IDCache::GetEnvForThread(); + 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_keycodes_array); } @@ -785,4 +805,11 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_refreshDevices(JNIEnv* env, + jclass) +{ + g_controller_interface.RefreshDevices(); +} } From 5e51b56d723cdd69bbe1eee39e53accfe609a812 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 16 Sep 2022 22:03:03 +0200 Subject: [PATCH 08/38] ControllerInterface/Android: Implement sensor input --- .../activities/EmulationActivity.java | 5 + .../input/model/ControllerInterface.java | 20 + .../model/DolphinSensorEventListener.java | 361 ++++++++++++++++++ .../input/model/SensorEventRequester.java | 16 + .../ControllerInterface/Android/Android.cpp | 160 +++++++- 5 files changed, 559 insertions(+), 3 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/SensorEventRequester.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 699e28a9e8..adf882d369 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -11,6 +11,7 @@ import android.os.Build; import android.os.Bundle; import android.util.Pair; import android.util.SparseIntArray; +import android.view.Display; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -42,6 +43,7 @@ import org.dolphinemu.dolphinemu.databinding.DialogInputAdjustBinding; import org.dolphinemu.dolphinemu.databinding.DialogIrSensitivityBinding; import org.dolphinemu.dolphinemu.databinding.DialogSkylandersManagerBinding; import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; +import org.dolphinemu.dolphinemu.features.input.model.SensorEventRequester; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings; @@ -442,12 +444,15 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP } updateOrientation(); + + ControllerInterface.enableSensorEvents(() -> getWindowManager().getDefaultDisplay()); } @Override protected void onPause() { super.onPause(); + ControllerInterface.disableSensorEvents(); } @Override diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java index 8a7bc8177f..6038afe0f3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java @@ -64,6 +64,26 @@ public final class ControllerInterface */ public static native boolean dispatchGenericMotionEvent(MotionEvent event); + /** + * {@link DolphinSensorEventListener} calls this for each axis of a received SensorEvent. + */ + public static native void dispatchSensorEvent(String axisName, float value); + + /** + * Enables delivering sensor events to native code. + * + * @param requester The activity or other component which is requesting sensor events to be + * delivered. + */ + public static native void enableSensorEvents(SensorEventRequester requester); + + /** + * Disables delivering sensor events to native code. + * + * Calling this when sensor events are no longer needed will save battery. + */ + public static native void disableSensorEvents(); + /** * Rescans for input devices. */ diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java new file mode 100644 index 0000000000..f72a97d113 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java @@ -0,0 +1,361 @@ +package org.dolphinemu.dolphinemu.features.input.model; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Build; +import android.view.Surface; + +import androidx.annotation.Keep; + +import org.dolphinemu.dolphinemu.DolphinApplication; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +public class DolphinSensorEventListener implements SensorEventListener +{ + // Set of three axes. Creates a negative companion to each axis, and corrects for device rotation. + private static final int AXIS_SET_TYPE_DEVICE_COORDINATES = 0; + // Set of three axes. Creates a negative companion to each axis. + private static final int AXIS_SET_TYPE_OTHER_COORDINATES = 1; + + private static class AxisSetDetails + { + public final int firstAxisOfSet; + public final int axisSetType; + + public AxisSetDetails(int firstAxisOfSet, int axisSetType) + { + this.firstAxisOfSet = firstAxisOfSet; + this.axisSetType = axisSetType; + } + } + + private static class SensorDetails + { + public final int sensorType; + public final String[] axisNames; + public final AxisSetDetails[] axisSetDetails; + + public SensorDetails(int sensorType, String[] axisNames, AxisSetDetails[] axisSetDetails) + { + this.sensorType = sensorType; + this.axisNames = axisNames; + this.axisSetDetails = axisSetDetails; + } + } + + private final SensorManager mSensorManager; + + private final HashMap mSensorDetails = new HashMap<>(); + + private SensorEventRequester mRequester = null; + + // The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS + // permission is 200 Hz. This is also the sampling rate of a Wii Remote, so it fits us perfectly. + private static final int SAMPLING_PERIOD_US = 1000000 / 200; + + @Keep + public DolphinSensorEventListener() + { + mSensorManager = (SensorManager) + DolphinApplication.getAppContext().getSystemService(Context.SENSOR_SERVICE); + + addSensors(); + } + + private void addSensors() + { + tryAddSensor(Sensor.TYPE_ACCELEROMETER, new String[]{"Accel Right", "Accel Left", + "Accel Forward", "Accel Backward", "Accel Up", "Accel Down"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)}); + + tryAddSensor(Sensor.TYPE_GYROSCOPE, new String[]{"Gyro Pitch Up", "Gyro Pitch Down", + "Gyro Roll Right", "Gyro Roll Left", "Gyro Yaw Left", "Gyro Yaw Right"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)}); + + tryAddSensor(Sensor.TYPE_LIGHT, "Light"); + + tryAddSensor(Sensor.TYPE_PRESSURE, "Pressure"); + + tryAddSensor(Sensor.TYPE_TEMPERATURE, "Device Temperature"); + + tryAddSensor(Sensor.TYPE_PROXIMITY, "Proximity"); + + tryAddSensor(Sensor.TYPE_GRAVITY, new String[]{"Gravity Right", "Gravity Left", + "Gravity Forward", "Gravity Backward", "Gravity Up", "Gravity Down"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)}); + + tryAddSensor(Sensor.TYPE_LINEAR_ACCELERATION, + new String[]{"Linear Acceleration Right", "Linear Acceleration Left", + "Linear Acceleration Forward", "Linear Acceleration Backward", + "Linear Acceleration Up", "Linear Acceleration Down"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)}); + + // The values provided by this sensor can be interpreted as an Euler vector or a quaternion. + // The directions of X and Y are flipped to match the Wii Remote coordinate system. + tryAddSensor(Sensor.TYPE_ROTATION_VECTOR, + new String[]{"Rotation Vector X-", "Rotation Vector X+", "Rotation Vector Y-", + "Rotation Vector Y+", "Rotation Vector Z+", + "Rotation Vector Z-", "Rotation Vector R", "Rotation Vector Heading Accuracy"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)}); + + tryAddSensor(Sensor.TYPE_RELATIVE_HUMIDITY, "Relative Humidity"); + + tryAddSensor(Sensor.TYPE_AMBIENT_TEMPERATURE, "Ambient Temperature"); + + // The values provided by this sensor can be interpreted as an Euler vector or a quaternion. + // The directions of X and Y are flipped to match the Wii Remote coordinate system. + tryAddSensor(Sensor.TYPE_GAME_ROTATION_VECTOR, + new String[]{"Game Rotation Vector X-", "Game Rotation Vector X+", + "Game Rotation Vector Y-", "Game Rotation Vector Y+", "Game Rotation Vector Z+", + "Game Rotation Vector Z-", "Game Rotation Vector R"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)}); + + tryAddSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, + new String[]{"Gyro Uncalibrated Pitch Up", "Gyro Uncalibrated Pitch Down", + "Gyro Uncalibrated Roll Right", "Gyro Uncalibrated Roll Left", + "Gyro Uncalibrated Yaw Left", "Gyro Uncalibrated Yaw Right", + "Gyro Drift Pitch Up", "Gyro Drift Pitch Down", "Gyro Drift Roll Right", + "Gyro Drift Roll Left", "Gyro Drift Yaw Left", "Gyro Drift Yaw Right"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES), + new AxisSetDetails(3, AXIS_SET_TYPE_DEVICE_COORDINATES)}); + + tryAddSensor(Sensor.TYPE_HEART_RATE, "Heart Rate"); + + if (Build.VERSION.SDK_INT >= 24) + { + tryAddSensor(Sensor.TYPE_HEART_BEAT, "Heart Beat"); + } + + if (Build.VERSION.SDK_INT >= 26) + { + tryAddSensor(Sensor.TYPE_ACCELEROMETER_UNCALIBRATED, + new String[]{"Accel Uncalibrated Right", "Accel Uncalibrated Left", + "Accel Uncalibrated Forward", "Accel Uncalibrated Backward", + "Accel Uncalibrated Up", "Accel Uncalibrated Down", + "Accel Bias Right", "Accel Bias Left", "Accel Bias Forward", + "Accel Bias Backward", "Accel Bias Up", "Accel Bias Down"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES), + new AxisSetDetails(3, AXIS_SET_TYPE_DEVICE_COORDINATES)}); + } + + if (Build.VERSION.SDK_INT >= 30) + { + tryAddSensor(Sensor.TYPE_HINGE_ANGLE, "Hinge Angle"); + } + + if (Build.VERSION.SDK_INT >= 33) + { + // The values provided by this sensor can be interpreted as an Euler vector. + // The directions of X and Y are flipped to match the Wii Remote coordinate system. + tryAddSensor(Sensor.TYPE_HEAD_TRACKER, + new String[]{"Head Rotation Vector X-", "Head Rotation Vector X+", + "Head Rotation Vector Y-", "Head Rotation Vector Y+", + "Head Rotation Vector Z+", "Head Rotation Vector Z-", + "Head Pitch Up", "Head Pitch Down", "Head Roll Right", "Head Roll Left", + "Head Yaw Left", "Head Yaw Right"}, + new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_OTHER_COORDINATES), + new AxisSetDetails(3, AXIS_SET_TYPE_OTHER_COORDINATES)}); + + tryAddSensor(Sensor.TYPE_HEADING, new String[]{"Heading", "Heading Accuracy"}, + new AxisSetDetails[]{}); + } + } + + private void tryAddSensor(int sensorType, String axisName) + { + tryAddSensor(sensorType, new String[]{axisName}, new AxisSetDetails[]{}); + } + + private void tryAddSensor(int sensorType, String[] axisNames, AxisSetDetails[] axisSetDetails) + { + Sensor sensor = mSensorManager.getDefaultSensor(sensorType); + if (sensor != null) + { + mSensorDetails.put(sensor, new SensorDetails(sensorType, axisNames, axisSetDetails)); + } + } + + @Override + public void onSensorChanged(SensorEvent sensorEvent) + { + final SensorDetails sensorDetails = mSensorDetails.get(sensorEvent.sensor); + + final float[] values = sensorEvent.values; + final String[] axisNames = sensorDetails.axisNames; + final AxisSetDetails[] axisSetDetails = sensorDetails.axisSetDetails; + + int eventAxisIndex = 0; + int detailsAxisIndex = 0; + int detailsAxisSetIndex = 0; + while (eventAxisIndex < values.length && detailsAxisIndex < axisNames.length) + { + if (detailsAxisSetIndex < axisSetDetails.length && + axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex) + { + int rotation = Surface.ROTATION_0; + if (axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES) + { + rotation = mRequester.getDisplay().getRotation(); + } + + float x, y; + switch (rotation) + { + default: + case Surface.ROTATION_0: + x = values[eventAxisIndex]; + y = values[eventAxisIndex + 1]; + break; + case Surface.ROTATION_90: + x = -values[eventAxisIndex + 1]; + y = values[eventAxisIndex]; + break; + case Surface.ROTATION_180: + x = -values[eventAxisIndex]; + y = -values[eventAxisIndex + 1]; + break; + case Surface.ROTATION_270: + x = values[eventAxisIndex + 1]; + y = -values[eventAxisIndex]; + break; + } + + float z = values[eventAxisIndex + 2]; + + ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex], x); + ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 1], x); + ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 2], y); + ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 3], y); + ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 4], z); + ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 5], z); + + eventAxisIndex += 3; + detailsAxisIndex += 6; + detailsAxisSetIndex++; + } + else + { + ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex], + values[eventAxisIndex]); + + eventAxisIndex++; + detailsAxisIndex++; + } + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int i) + { + // We don't care about this + } + + /** + * Enables delivering sensor events to native code. + * + * @param requester The activity or other component which is requesting sensor events to be + * delivered. + */ + @Keep + public void enableSensorEvents(SensorEventRequester requester) + { + if (mRequester != null) + { + throw new IllegalStateException("Attempted to enable sensor events when someone else" + + "had already enabled them"); + } + + mRequester = requester; + + for (Sensor sensor : mSensorDetails.keySet()) + { + mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US); + } + } + + /** + * Disables delivering sensor events to native code. + * + * Calling this when sensor events are no longer needed will save battery. + */ + @Keep + public void disableSensorEvents() + { + mRequester = null; + + mSensorManager.unregisterListener(this); + } + + @Keep + public String[] getAxisNames() + { + ArrayList axisNames = new ArrayList<>(); + + for (SensorDetails sensorDetails : getSensorDetailsSorted()) + { + Collections.addAll(axisNames, sensorDetails.axisNames); + } + + return axisNames.toArray(new String[]{}); + } + + @Keep + public boolean[] getNegativeAxes() + { + ArrayList negativeAxes = new ArrayList<>(); + + for (SensorDetails sensorDetails : getSensorDetailsSorted()) + { + int eventAxisIndex = 0; + int detailsAxisIndex = 0; + int detailsAxisSetIndex = 0; + while (detailsAxisIndex < sensorDetails.axisNames.length) + { + if (detailsAxisSetIndex < sensorDetails.axisSetDetails.length && + sensorDetails.axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex) + { + negativeAxes.add(false); + negativeAxes.add(true); + negativeAxes.add(false); + negativeAxes.add(true); + negativeAxes.add(false); + negativeAxes.add(true); + + eventAxisIndex += 3; + detailsAxisIndex += 6; + detailsAxisSetIndex++; + } + else + { + negativeAxes.add(false); + + eventAxisIndex++; + detailsAxisIndex++; + } + } + } + + boolean[] result = new boolean[negativeAxes.size()]; + for (int i = 0; i < result.length; i++) + { + result[i] = negativeAxes.get(i); + } + + return result; + } + + private List getSensorDetailsSorted() + { + ArrayList sensorDetails = new ArrayList<>(mSensorDetails.values()); + Collections.sort(sensorDetails, Comparator.comparingInt(s -> s.sensorType)); + return sensorDetails; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/SensorEventRequester.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/SensorEventRequester.java new file mode 100644 index 0000000000..83b58015b2 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/SensorEventRequester.java @@ -0,0 +1,16 @@ +package org.dolphinemu.dolphinemu.features.input.model; + +import android.view.Display; + +import androidx.annotation.NonNull; + +public interface SensorEventRequester +{ + /** + * Returns the display the activity is shown on. + * + * This is used for getting the display orientation for rotating the axes of motion events. + */ + @NonNull + Display getDisplay(); +} diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index e5baa4e43e..71b99f9ef3 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -17,6 +17,7 @@ #include #include +#include "Common/Assert.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" @@ -62,12 +63,20 @@ jclass s_controller_interface_class; jmethodID s_controller_interface_register_input_device_listener; jmethodID s_controller_interface_unregister_input_device_listener; +jclass s_sensor_event_listener_class; +jmethodID s_sensor_event_listener_constructor; +jmethodID s_sensor_event_listener_enable_sensor_events; +jmethodID s_sensor_event_listener_disable_sensor_events; +jmethodID s_sensor_event_listener_get_axis_names; +jmethodID s_sensor_event_listener_get_negative_axes; + jintArray s_keycodes_array; 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; +ciface::Core::DeviceQualifier s_sensor_device_qualifier; constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31 @@ -460,7 +469,7 @@ public: explicit AndroidKey(int keycode) : AndroidInput(ConstructKeyName(keycode)) {} }; -class AndroidAxis final : public AndroidInput +class AndroidAxis : public AndroidInput { public: AndroidAxis(int source, int axis, bool negative) @@ -468,6 +477,10 @@ public: { } + AndroidAxis(std::string name, bool negative) : AndroidInput(std::move(name)), m_negative(negative) + { + } + ControlState GetState() const override { return m_negative ? -AndroidInput::GetState() : AndroidInput::GetState(); @@ -477,12 +490,21 @@ private: bool m_negative; }; +class AndroidSensorAxis final : public AndroidAxis +{ +public: + AndroidSensorAxis(std::string name, bool negative) : AndroidAxis(std::move(name), negative) {} + + bool IsDetectable() const override { return false; } +}; + class AndroidDevice final : public Core::Device { public: AndroidDevice(JNIEnv* env, jobject 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_sensor_event_listener(nullptr), + m_source(env->CallIntMethod(input_device, s_input_device_get_sources)), + m_controller_number(env->CallIntMethod(input_device, s_input_device_get_controller_number)) { jstring j_name = reinterpret_cast(env->CallObjectMethod(input_device, s_input_device_get_name)); @@ -495,6 +517,19 @@ public: AddAxes(env, input_device); } + // Constructor for the device added by Dolphin to contain sensor inputs + AndroidDevice(JNIEnv* env, std::string name) + : m_sensor_event_listener(AddSensors(env)), m_source(AINPUT_SOURCE_SENSOR), + m_controller_number(0), m_name(std::move(name)) + { + } + + ~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 "Android"; } @@ -519,6 +554,8 @@ public: return -3; } + jobject GetSensorEventListener() { return m_sensor_event_listener; } + private: void AddKeys(JNIEnv* env, jobject input_device) { @@ -571,6 +608,35 @@ private: env->DeleteLocalRef(motion_ranges_list); } + jobject AddSensors(JNIEnv* env) + { + jobject sensor_event_listener = + env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor); + + jobjectArray j_axis_names = reinterpret_cast( + env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_axis_names)); + std::vector axis_names = JStringArrayToVector(env, j_axis_names); + env->DeleteLocalRef(j_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); + + ASSERT(axis_names.size() == env->GetArrayLength(j_negative_axes)); + for (size_t i = 0; i < axis_names.size(); ++i) + AddInput(new AndroidSensorAxis(axis_names[i], negative_axes[i])); + + env->ReleaseBooleanArrayElements(j_negative_axes, negative_axes, 0); + env->DeleteLocalRef(j_negative_axes); + + jobject global_sensor_event_listener = env->NewGlobalRef(sensor_event_listener); + + env->DeleteLocalRef(sensor_event_listener); + + return global_sensor_event_listener; + } + + const jobject m_sensor_event_listener; const int m_source; const int m_controller_number; std::string m_name; @@ -648,6 +714,22 @@ void Init() s_controller_interface_unregister_input_device_listener = env->GetStaticMethodID(s_controller_interface_class, "unregisterInputDeviceListener", "()V"); + 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_enable_sensor_events = + env->GetMethodID(s_sensor_event_listener_class, "enableSensorEvents", + "(Lorg/dolphinemu/dolphinemu/features/input/model/SensorEventRequester;)V"); + s_sensor_event_listener_disable_sensor_events = + env->GetMethodID(s_sensor_event_listener_class, "disableSensorEvents", "()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"); + jintArray keycodes_array = CreateKeyCodesArray(env); s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); env->DeleteLocalRef(keycodes_array); @@ -669,6 +751,7 @@ void Shutdown() 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_keycodes_array); } @@ -694,6 +777,25 @@ static void AddDevice(JNIEnv* env, int device_id) s_device_id_to_device_qualifier.emplace(device_id, qualifier); } +static void 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"); + + if (device->Inputs().empty() && device->Outputs().empty()) + return; + + g_controller_interface.AddDevice(device); + + Core::DeviceQualifier qualifier; + qualifier.FromDevice(device.get()); + + INFO_LOG_FMT(CONTROLLERINTERFACE, "Added sensor device as {}", device->GetQualifiedName()); + s_sensor_device_qualifier = qualifier; +} + void PopulateDevices() { INFO_LOG_FMT(CONTROLLERINTERFACE, "Android populating devices"); @@ -708,6 +810,8 @@ void PopulateDevices() AddDevice(env, device_ids[i]); env->ReleaseIntArrayElements(device_ids_array, device_ids, JNI_ABORT); env->DeleteLocalRef(device_ids_array); + + AddSensorDevice(env); } } // namespace ciface::Android @@ -806,6 +910,56 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEvent( + JNIEnv* env, jclass, jstring j_axis_name, jfloat value) +{ + const std::shared_ptr device = + g_controller_interface.FindDevice(s_sensor_device_qualifier); + if (!device) + return; + + const std::string axis_name = GetJString(env, j_axis_name); + + for (ciface::Core::Device::Input* input : device->Inputs()) + { + const std::string input_name = input->GetName(); + if (input_name == axis_name) + { + auto casted_input = static_cast(input); + casted_input->SetState(value); + } + } +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_enableSensorEvents( + JNIEnv* env, jclass, jobject j_sensor_event_requester) +{ + const std::shared_ptr device = + g_controller_interface.FindDevice(s_sensor_device_qualifier); + if (!device) + return; + + env->CallVoidMethod( + static_cast(device.get())->GetSensorEventListener(), + s_sensor_event_listener_enable_sensor_events, j_sensor_event_requester); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_disableSensorEvents( + JNIEnv* env, jclass) +{ + const std::shared_ptr device = + g_controller_interface.FindDevice(s_sensor_device_qualifier); + if (!device) + return; + + env->CallVoidMethod( + static_cast(device.get())->GetSensorEventListener(), + s_sensor_event_listener_disable_sensor_events); +} + JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_refreshDevices(JNIEnv* env, jclass) From 36acb17700554f0789d8650b7fba77ac8a7796b6 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 16 Sep 2022 23:02:30 +0200 Subject: [PATCH 09/38] ControllerInterface/Android: Implement sensor input for InputDevices This functionality was added in Android 12 to let apps get motion data for gamepads. --- .../input/model/ControllerInterface.java | 3 +- .../model/DolphinSensorEventListener.java | 67 ++++++++++++++--- .../ControllerInterface/Android/Android.cpp | 72 ++++++++++++------- .../ControllerInterface/CoreDevice.cpp | 12 ++++ .../ControllerInterface/CoreDevice.h | 1 + 5 files changed, 118 insertions(+), 37 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java index 6038afe0f3..96480ab69c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java @@ -67,7 +67,8 @@ public final class ControllerInterface /** * {@link DolphinSensorEventListener} calls this for each axis of a received SensorEvent. */ - public static native void dispatchSensorEvent(String axisName, float value); + public static native void dispatchSensorEvent(String deviceQualifier, String axisName, + float value); /** * Enables delivering sensor events to native code. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java index f72a97d113..21d533a430 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java @@ -6,6 +6,7 @@ import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Build; +import android.view.InputDevice; import android.view.Surface; import androidx.annotation.Keep; @@ -55,6 +56,10 @@ public class DolphinSensorEventListener implements SensorEventListener private final HashMap mSensorDetails = new HashMap<>(); + private final boolean mRotateCoordinatesForScreenOrientation; + + private String mDeviceQualifier = ""; + private SensorEventRequester mRequester = null; // The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS @@ -66,10 +71,28 @@ public class DolphinSensorEventListener implements SensorEventListener { mSensorManager = (SensorManager) DolphinApplication.getAppContext().getSystemService(Context.SENSOR_SERVICE); + mRotateCoordinatesForScreenOrientation = true; addSensors(); } + @Keep + public DolphinSensorEventListener(InputDevice inputDevice) + { + mRotateCoordinatesForScreenOrientation = false; + + if (Build.VERSION.SDK_INT >= 31) + { + mSensorManager = inputDevice.getSensorManager(); + + addSensors(); + } + else + { + mSensorManager = null; + } + } + private void addSensors() { tryAddSensor(Sensor.TYPE_ACCELEROMETER, new String[]{"Accel Right", "Accel Left", @@ -201,7 +224,8 @@ public class DolphinSensorEventListener implements SensorEventListener axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex) { int rotation = Surface.ROTATION_0; - if (axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES) + if (mRotateCoordinatesForScreenOrientation && + axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES) { rotation = mRequester.getDisplay().getRotation(); } @@ -230,12 +254,17 @@ public class DolphinSensorEventListener implements SensorEventListener float z = values[eventAxisIndex + 2]; - ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex], x); - ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 1], x); - ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 2], y); - ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 3], y); - ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 4], z); - ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex + 5], z); + ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex], x); + ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 1], + x); + ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 2], + y); + ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 3], + y); + ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 4], + z); + ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 5], + z); eventAxisIndex += 3; detailsAxisIndex += 6; @@ -243,7 +272,7 @@ public class DolphinSensorEventListener implements SensorEventListener } else { - ControllerInterface.dispatchSensorEvent(axisNames[detailsAxisIndex], + ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex], values[eventAxisIndex]); eventAxisIndex++; @@ -258,6 +287,16 @@ public class DolphinSensorEventListener implements SensorEventListener // We don't care about this } + /** + * The device qualifier set here will be passed on to native code, + * for the purpose of letting native code identify which device this object belongs to. + */ + @Keep + public void setDeviceQualifier(String deviceQualifier) + { + mDeviceQualifier = deviceQualifier; + } + /** * Enables delivering sensor events to native code. * @@ -275,9 +314,12 @@ public class DolphinSensorEventListener implements SensorEventListener mRequester = requester; - for (Sensor sensor : mSensorDetails.keySet()) + if (mSensorManager != null) { - mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US); + for (Sensor sensor : mSensorDetails.keySet()) + { + mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US); + } } } @@ -291,7 +333,10 @@ public class DolphinSensorEventListener implements SensorEventListener { mRequester = null; - mSensorManager.unregisterListener(this); + if (mSensorManager != null) + { + mSensorManager.unregisterListener(this); + } } @Keep diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 71b99f9ef3..cf7e602a77 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -65,6 +65,8 @@ jmethodID s_controller_interface_unregister_input_device_listener; 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_enable_sensor_events; jmethodID s_sensor_event_listener_disable_sensor_events; jmethodID s_sensor_event_listener_get_axis_names; @@ -76,7 +78,6 @@ 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; -ciface::Core::DeviceQualifier s_sensor_device_qualifier; constexpr int MAX_KEYCODE = AKEYCODE_PROFILE_SWITCH; // Up to date as of SDK 31 @@ -502,7 +503,7 @@ class AndroidDevice final : public Core::Device { public: AndroidDevice(JNIEnv* env, jobject input_device) - : m_sensor_event_listener(nullptr), + : 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)) { @@ -519,7 +520,7 @@ public: // Constructor for the device added by Dolphin to contain sensor inputs AndroidDevice(JNIEnv* env, std::string name) - : m_sensor_event_listener(AddSensors(env)), m_source(AINPUT_SOURCE_SENSOR), + : m_sensor_event_listener(AddSensors(env, nullptr)), m_source(AINPUT_SOURCE_SENSOR), m_controller_number(0), m_name(std::move(name)) { } @@ -608,10 +609,20 @@ private: env->DeleteLocalRef(motion_ranges_list); } - jobject AddSensors(JNIEnv* env) + jobject AddSensors(JNIEnv* env, jobject input_device) { - jobject sensor_event_listener = - env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor); + jobject sensor_event_listener; + if (input_device) + { + sensor_event_listener = + env->NewObject(s_sensor_event_listener_class, + s_sensor_event_listener_constructor_input_device, input_device); + } + else + { + sensor_event_listener = + env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor); + } jobjectArray j_axis_names = reinterpret_cast( env->CallObjectMethod(sensor_event_listener, s_sensor_event_listener_get_axis_names)); @@ -720,6 +731,10 @@ void Init() 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_enable_sensor_events = env->GetMethodID(s_sensor_event_listener_class, "enableSensorEvents", "(Lorg/dolphinemu/dolphinemu/features/input/model/SensorEventRequester;)V"); @@ -775,6 +790,11 @@ static void AddDevice(JNIEnv* env, int device_id) 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); } static void AddSensorDevice(JNIEnv* env) @@ -793,7 +813,11 @@ static void AddSensorDevice(JNIEnv* env) qualifier.FromDevice(device.get()); INFO_LOG_FMT(CONTROLLERINTERFACE, "Added sensor device as {}", device->GetQualifiedName()); - s_sensor_device_qualifier = 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 PopulateDevices() @@ -912,10 +936,12 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEvent( - JNIEnv* env, jclass, 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(s_sensor_device_qualifier); + g_controller_interface.FindDevice(device_qualifier); if (!device) return; @@ -936,28 +962,24 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_enableSensorEvents( JNIEnv* env, jclass, jobject j_sensor_event_requester) { - const std::shared_ptr device = - g_controller_interface.FindDevice(s_sensor_device_qualifier); - if (!device) - return; - - env->CallVoidMethod( - static_cast(device.get())->GetSensorEventListener(), - s_sensor_event_listener_enable_sensor_events, j_sensor_event_requester); + for (std::shared_ptr& device : g_controller_interface.GetAllDevices()) + { + env->CallVoidMethod( + static_cast(device.get())->GetSensorEventListener(), + s_sensor_event_listener_enable_sensor_events, j_sensor_event_requester); + } } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_disableSensorEvents( JNIEnv* env, jclass) { - const std::shared_ptr device = - g_controller_interface.FindDevice(s_sensor_device_qualifier); - if (!device) - return; - - env->CallVoidMethod( - static_cast(device.get())->GetSensorEventListener(), - s_sensor_event_listener_disable_sensor_events); + for (std::shared_ptr& device : g_controller_interface.GetAllDevices()) + { + env->CallVoidMethod( + static_cast(device.get())->GetSensorEventListener(), + s_sensor_event_listener_disable_sensor_events); + } } JNIEXPORT void JNICALL diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.cpp b/Source/Core/InputCommon/ControllerInterface/CoreDevice.cpp index 4e46179fc6..222791f03f 100644 --- a/Source/Core/InputCommon/ControllerInterface/CoreDevice.cpp +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.cpp @@ -242,6 +242,18 @@ std::shared_ptr DeviceContainer::FindDevice(const DeviceQualifier& devq) return nullptr; } +std::vector> DeviceContainer::GetAllDevices() const +{ + std::lock_guard lk(m_devices_mutex); + + std::vector> devices; + + for (const auto& d : m_devices) + devices.emplace_back(d); + + return devices; +} + std::vector DeviceContainer::GetAllDeviceStrings() const { std::lock_guard lk(m_devices_mutex); diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h index 2280ddac3b..a2176637ab 100644 --- a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h @@ -226,6 +226,7 @@ public: Device::Input* FindInput(std::string_view name, const Device* def_dev) const; Device::Output* FindOutput(std::string_view name, const Device* def_dev) const; + std::vector> GetAllDevices() const; std::vector GetAllDeviceStrings() const; bool HasDefaultDevice() const; std::string GetDefaultDeviceString() const; From 065481d9899e6debc94bdbbb812512757f8eac62 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 17 Sep 2022 12:12:39 +0200 Subject: [PATCH 10/38] ControllerInterface/Android: Automatically suspend sensors This is a battery-saving measure. Whether a sensor should be suspended is determined in the same way as whether key events and motion events should be handled by the OS rather than consumed by Dolphin. --- .../activities/EmulationActivity.java | 7 +- .../input/model/ControllerInterface.java | 22 ++-- .../model/DolphinSensorEventListener.java | 110 ++++++++++------ .../input/model/SensorEventRequester.java | 16 --- .../ControllerInterface/Android/Android.cpp | 122 ++++++++++++------ 5 files changed, 164 insertions(+), 113 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/SensorEventRequester.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index adf882d369..4a9816c259 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -11,7 +11,6 @@ import android.os.Build; import android.os.Bundle; import android.util.Pair; import android.util.SparseIntArray; -import android.view.Display; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -43,7 +42,7 @@ import org.dolphinemu.dolphinemu.databinding.DialogInputAdjustBinding; import org.dolphinemu.dolphinemu.databinding.DialogIrSensitivityBinding; import org.dolphinemu.dolphinemu.databinding.DialogSkylandersManagerBinding; import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; -import org.dolphinemu.dolphinemu.features.input.model.SensorEventRequester; +import org.dolphinemu.dolphinemu.features.input.model.DolphinSensorEventListener; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings; @@ -445,14 +444,14 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP updateOrientation(); - ControllerInterface.enableSensorEvents(() -> getWindowManager().getDefaultDisplay()); + DolphinSensorEventListener.setDeviceRotation( + getWindowManager().getDefaultDisplay().getRotation()); } @Override protected void onPause() { super.onPause(); - ControllerInterface.disableSensorEvents(); } @Override diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java index 96480ab69c..2b6e1d62ee 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java @@ -66,24 +66,22 @@ public final class ControllerInterface /** * {@link DolphinSensorEventListener} calls this for each axis of a received SensorEvent. + * + * @return true if the emulator core seems to be interested in this event. + * false if the sensor can be suspended to save battery. */ - public static native void dispatchSensorEvent(String deviceQualifier, String axisName, + public static native boolean dispatchSensorEvent(String deviceQualifier, String axisName, float value); /** - * Enables delivering sensor events to native code. + * Called when a sensor is suspended or unsuspended. * - * @param requester The activity or other component which is requesting sensor events to be - * delivered. + * @param deviceQualifier A string used by native code for uniquely identifying devices. + * @param axisNames The name of all axes for the sensor. + * @param suspended Whether the sensor is now suspended. */ - public static native void enableSensorEvents(SensorEventRequester requester); - - /** - * Disables delivering sensor events to native code. - * - * Calling this when sensor events are no longer needed will save battery. - */ - public static native void disableSensorEvents(); + public static native void notifySensorSuspendedState(String deviceQualifier, String[] axisNames, + boolean suspended); /** * Rescans for input devices. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java index 21d533a430..cfc4f34dd7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java @@ -12,12 +12,15 @@ import android.view.Surface; import androidx.annotation.Keep; import org.dolphinemu.dolphinemu.DolphinApplication; +import org.dolphinemu.dolphinemu.utils.Log; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; +import java.util.Map; public class DolphinSensorEventListener implements SensorEventListener { @@ -43,6 +46,7 @@ public class DolphinSensorEventListener implements SensorEventListener public final int sensorType; public final String[] axisNames; public final AxisSetDetails[] axisSetDetails; + public boolean isSuspended = true; public SensorDetails(int sensorType, String[] axisNames, AxisSetDetails[] axisSetDetails) { @@ -52,6 +56,8 @@ public class DolphinSensorEventListener implements SensorEventListener } } + private static int sDeviceRotation = Surface.ROTATION_0; + private final SensorManager mSensorManager; private final HashMap mSensorDetails = new HashMap<>(); @@ -60,8 +66,6 @@ public class DolphinSensorEventListener implements SensorEventListener private String mDeviceQualifier = ""; - private SensorEventRequester mRequester = null; - // The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS // permission is 200 Hz. This is also the sampling rate of a Wii Remote, so it fits us perfectly. private static final int SAMPLING_PERIOD_US = 1000000 / 200; @@ -218,6 +222,7 @@ public class DolphinSensorEventListener implements SensorEventListener int eventAxisIndex = 0; int detailsAxisIndex = 0; int detailsAxisSetIndex = 0; + boolean keepSensorAlive = false; while (eventAxisIndex < values.length && detailsAxisIndex < axisNames.length) { if (detailsAxisSetIndex < axisSetDetails.length && @@ -227,7 +232,7 @@ public class DolphinSensorEventListener implements SensorEventListener if (mRotateCoordinatesForScreenOrientation && axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES) { - rotation = mRequester.getDisplay().getRotation(); + rotation = sDeviceRotation; } float x, y; @@ -254,17 +259,18 @@ public class DolphinSensorEventListener implements SensorEventListener float z = values[eventAxisIndex + 2]; - ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex], x); - ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 1], - x); - ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 2], - y); - ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 3], - y); - ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 4], - z); - ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex + 5], - z); + keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier, + axisNames[detailsAxisIndex], x); + keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier, + axisNames[detailsAxisIndex + 1], x); + keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier, + axisNames[detailsAxisIndex + 2], y); + keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier, + axisNames[detailsAxisIndex + 3], y); + keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier, + axisNames[detailsAxisIndex + 4], z); + keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier, + axisNames[detailsAxisIndex + 5], z); eventAxisIndex += 3; detailsAxisIndex += 6; @@ -272,13 +278,18 @@ public class DolphinSensorEventListener implements SensorEventListener } else { - ControllerInterface.dispatchSensorEvent(mDeviceQualifier, axisNames[detailsAxisIndex], - values[eventAxisIndex]); + keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier, + axisNames[detailsAxisIndex], values[eventAxisIndex]); eventAxisIndex++; detailsAxisIndex++; } } + + if (!keepSensorAlive) + { + setSensorSuspended(sensorEvent.sensor, sensorDetails, true); + } } @Override @@ -298,44 +309,48 @@ public class DolphinSensorEventListener implements SensorEventListener } /** - * Enables delivering sensor events to native code. + * If a sensor has been suspended to save battery, this unsuspends it. + * If the sensor isn't currently suspended, nothing happens. * - * @param requester The activity or other component which is requesting sensor events to be - * delivered. + * @param axisName The name of any of the sensor's axes. */ @Keep - public void enableSensorEvents(SensorEventRequester requester) + public void requestUnsuspendSensor(String axisName) { - if (mRequester != null) + for (Map.Entry entry : mSensorDetails.entrySet()) { - throw new IllegalStateException("Attempted to enable sensor events when someone else" + - "had already enabled them"); - } - - mRequester = requester; - - if (mSensorManager != null) - { - for (Sensor sensor : mSensorDetails.keySet()) + if (Arrays.asList(entry.getValue().axisNames).contains(axisName)) { - mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US); + setSensorSuspended(entry.getKey(), entry.getValue(), false); } } } - /** - * Disables delivering sensor events to native code. - * - * Calling this when sensor events are no longer needed will save battery. - */ - @Keep - public void disableSensorEvents() + private void setSensorSuspended(Sensor sensor, SensorDetails sensorDetails, boolean suspend) { - mRequester = null; + boolean changeOccurred = false; - if (mSensorManager != null) + synchronized (sensorDetails) { - mSensorManager.unregisterListener(this); + if (sensorDetails.isSuspended != suspend) + { + ControllerInterface.notifySensorSuspendedState(mDeviceQualifier, sensorDetails.axisNames, + suspend); + + if (suspend) + mSensorManager.unregisterListener(this, sensor); + else + mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US); + + sensorDetails.isSuspended = suspend; + + changeOccurred = true; + } + } + + if (changeOccurred) + { + Log.info((suspend ? "Suspended sensor " : "Unsuspended sensor ") + sensor.getName()); } } @@ -403,4 +418,17 @@ public class DolphinSensorEventListener implements SensorEventListener Collections.sort(sensorDetails, Comparator.comparingInt(s -> s.sensorType)); return sensorDetails; } + + /** + * Should be called when an activity or other component that uses sensor events is resumed. + * + * Sensor events that contain device coordinates will have the coordinates rotated by the value + * passed to this function. + * + * @param deviceRotation The current rotation of the device (i.e. rotation of the default display) + */ + public static void setDeviceRotation(int deviceRotation) + { + sDeviceRotation = deviceRotation; + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/SensorEventRequester.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/SensorEventRequester.java deleted file mode 100644 index 83b58015b2..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/SensorEventRequester.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.dolphinemu.dolphinemu.features.input.model; - -import android.view.Display; - -import androidx.annotation.NonNull; - -public interface SensorEventRequester -{ - /** - * Returns the display the activity is shown on. - * - * This is used for getting the display orientation for rotating the axes of motion events. - */ - @NonNull - Display getDisplay(); -} diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index cf7e602a77..058a9e3171 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -67,8 +67,7 @@ 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_enable_sensor_events; -jmethodID s_sensor_event_listener_disable_sensor_events; +jmethodID s_sensor_event_listener_request_unsuspend_sensor; jmethodID s_sensor_event_listener_get_axis_names; jmethodID s_sensor_event_listener_get_negative_axes; @@ -494,9 +493,45 @@ private: class AndroidSensorAxis final : public AndroidAxis { public: - AndroidSensorAxis(std::string name, bool negative) : AndroidAxis(std::move(name), negative) {} + // 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 AndroidDevice final : public Core::Device @@ -611,40 +646,45 @@ private: jobject AddSensors(JNIEnv* env, jobject input_device) { - jobject sensor_event_listener; + jobject local_sensor_event_listener; if (input_device) { - sensor_event_listener = + local_sensor_event_listener = env->NewObject(s_sensor_event_listener_class, s_sensor_event_listener_constructor_input_device, input_device); } else { - sensor_event_listener = + 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)); - std::vector axis_names = JStringArrayToVector(env, j_axis_names); - env->DeleteLocalRef(j_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); - ASSERT(axis_names.size() == env->GetArrayLength(j_negative_axes)); - for (size_t i = 0; i < axis_names.size(); ++i) - AddInput(new AndroidSensorAxis(axis_names[i], negative_axes[i])); + 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); - jobject global_sensor_event_listener = env->NewGlobalRef(sensor_event_listener); - - env->DeleteLocalRef(sensor_event_listener); - - return global_sensor_event_listener; + return sensor_event_listener; } const jobject m_sensor_event_listener; @@ -735,11 +775,8 @@ void Init() 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_enable_sensor_events = - env->GetMethodID(s_sensor_event_listener_class, "enableSensorEvents", - "(Lorg/dolphinemu/dolphinemu/features/input/model/SensorEventRequester;)V"); - s_sensor_event_listener_disable_sensor_events = - env->GetMethodID(s_sensor_event_listener_class, "disableSensorEvents", "()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 = @@ -934,7 +971,7 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch return last_polled >= Clock::now() - ACTIVE_INPUT_TIMEOUT; } -JNIEXPORT void JNICALL +JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatchSensorEvent( JNIEnv* env, jclass, jstring j_device_qualifier, jstring j_axis_name, jfloat value) { @@ -943,10 +980,12 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch const std::shared_ptr device = g_controller_interface.FindDevice(device_qualifier); if (!device) - return; + return JNI_FALSE; const std::string axis_name = GetJString(env, j_axis_name); + Clock::time_point last_polled{}; + for (ciface::Core::Device::Input* input : device->Inputs()) { const std::string input_name = input->GetName(); @@ -954,31 +993,34 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_dispatch { 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; } JNIEXPORT void JNICALL -Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_enableSensorEvents( - JNIEnv* env, jclass, jobject j_sensor_event_requester) +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_notifySensorSuspendedState( + JNIEnv* env, jclass, jstring j_device_qualifier, jobjectArray j_axis_names, jboolean suspended) { - for (std::shared_ptr& device : g_controller_interface.GetAllDevices()) - { - env->CallVoidMethod( - static_cast(device.get())->GetSensorEventListener(), - s_sensor_event_listener_enable_sensor_events, j_sensor_event_requester); - } -} + 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; -JNIEXPORT void JNICALL -Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_disableSensorEvents( - JNIEnv* env, jclass) -{ - for (std::shared_ptr& device : g_controller_interface.GetAllDevices()) + const std::vector axis_names = JStringArrayToVector(env, j_axis_names); + + for (ciface::Core::Device::Input* input : device->Inputs()) { - env->CallVoidMethod( - static_cast(device.get())->GetSensorEventListener(), - s_sensor_event_listener_disable_sensor_events); + const std::string input_name = input->GetName(); + if (std::find(axis_names.begin(), axis_names.end(), input_name) != axis_names.end()) + { + auto casted_input = static_cast(input); + casted_input->NotifyIsSuspended(static_cast(suspended)); + } } } From 8e33458f4832daedf04a626d07c4541ad17ee675 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 18 Sep 2022 19:14:15 +0200 Subject: [PATCH 11/38] ControllerInterface/Android: Implement rumble --- .../input/model/ControllerInterface.java | 51 ++++++++++ .../input/model/DolphinVibratorManager.java | 20 ++++ .../model/DolphinVibratorManagerCompat.java | 35 +++++++ .../DolphinVibratorManagerPassthrough.java | 33 +++++++ .../ControllerInterface/Android/Android.cpp | 92 +++++++++++++++++++ 5 files changed, 231 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java index 2b6e1d62ee..0d6a078b4e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java @@ -4,11 +4,18 @@ package org.dolphinemu.dolphinemu.features.input.model; import android.content.Context; import android.hardware.input.InputManager; +import android.os.Build; import android.os.Handler; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.os.VibratorManager; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.dolphinemu.dolphinemu.DolphinApplication; import org.dolphinemu.dolphinemu.utils.LooperThread; @@ -119,4 +126,48 @@ public final class ControllerInterface mInputDeviceListener = null; } } + + @Keep @NonNull + private static DolphinVibratorManager getVibratorManager(InputDevice device) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + return new DolphinVibratorManagerPassthrough(device.getVibratorManager()); + } + else + { + return new DolphinVibratorManagerCompat(device.getVibrator()); + } + } + + @Keep @NonNull + private static DolphinVibratorManager getSystemVibratorManager() + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + VibratorManager vibratorManager = (VibratorManager) + DolphinApplication.getAppContext().getSystemService(Context.VIBRATOR_MANAGER_SERVICE); + + if (vibratorManager != null) + return new DolphinVibratorManagerPassthrough(vibratorManager); + } + + Vibrator vibrator = (Vibrator) + DolphinApplication.getAppContext().getSystemService(Context.VIBRATOR_SERVICE); + + return new DolphinVibratorManagerCompat(vibrator); + } + + @Keep + private static void vibrate(@NonNull Vibrator vibrator) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); + } + else + { + vibrator.vibrate(100); + } + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.java new file mode 100644 index 0000000000..abc04d969d --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.java @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import android.os.Vibrator; + +import androidx.annotation.Keep; +import androidx.annotation.NonNull; + +/** + * A wrapper around {@link android.os.VibratorManager}, for backwards compatibility. + */ +public interface DolphinVibratorManager +{ + @Keep @NonNull + Vibrator getVibrator(int vibratorId); + + @Keep @NonNull + int[] getVibratorIds(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.java new file mode 100644 index 0000000000..40c4fa95d1 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.java @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import android.os.Vibrator; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public final class DolphinVibratorManagerCompat implements DolphinVibratorManager +{ + private final Vibrator mVibrator; + private final int[] mIds; + + public DolphinVibratorManagerCompat(@Nullable Vibrator vibrator) + { + mVibrator = vibrator; + mIds = vibrator != null && vibrator.hasVibrator() ? new int[]{0} : new int[]{}; + } + + @Override @NonNull + public Vibrator getVibrator(int vibratorId) + { + if (vibratorId > mIds.length) + throw new IndexOutOfBoundsException(); + + return mVibrator; + } + + @Override @NonNull + public int[] getVibratorIds() + { + return mIds; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.java new file mode 100644 index 0000000000..2ca747f54f --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.java @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import android.os.Build; +import android.os.Vibrator; +import android.os.VibratorManager; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +@RequiresApi(api = Build.VERSION_CODES.S) +public final class DolphinVibratorManagerPassthrough implements DolphinVibratorManager +{ + private final VibratorManager mVibratorManager; + + public DolphinVibratorManagerPassthrough(@NonNull VibratorManager vibratorManager) + { + mVibratorManager = vibratorManager; + } + + @Override @NonNull + public Vibrator getVibrator(int vibratorId) + { + return mVibratorManager.getVibrator(vibratorId); + } + + @Override @NonNull + public int[] getVibratorIds() + { + return mVibratorManager.getVibratorIds(); + } +} diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 058a9e3171..811343bf73 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -62,6 +62,9 @@ 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_sensor_event_listener_class; jmethodID s_sensor_event_listener_constructor; @@ -71,6 +74,10 @@ 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; + jintArray s_keycodes_array; using Clock = std::chrono::steady_clock; @@ -534,6 +541,35 @@ private: 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) + { + IDCache::GetEnvForThread()->CallStaticVoidMethod(s_controller_interface_class, + s_controller_interface_vibrate, m_vibrator); + } + } + +private: + const jobject m_vibrator; + const jint m_id; + std::atomic m_state = 0; +}; + class AndroidDevice final : public Core::Device { public: @@ -551,6 +587,7 @@ public: AddKeys(env, input_device); AddAxes(env, input_device); + AddMotors(env, input_device); } // Constructor for the device added by Dolphin to contain sensor inputs @@ -558,6 +595,7 @@ public: : m_sensor_event_listener(AddSensors(env, nullptr)), m_source(AINPUT_SOURCE_SENSOR), m_controller_number(0), m_name(std::move(name)) { + AddSystemMotors(env); } ~AndroidDevice() @@ -687,6 +725,41 @@ private: 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; @@ -764,6 +837,15 @@ void Init() 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"); const jclass sensor_event_listener_class = env->FindClass("org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener"); @@ -782,6 +864,15 @@ void Init() s_sensor_event_listener_get_negative_axes = env->GetMethodID(s_sensor_event_listener_class, "getNegativeAxes", "()[Z"); + 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"); + jintArray keycodes_array = CreateKeyCodesArray(env); s_keycodes_array = reinterpret_cast(env->NewGlobalRef(keycodes_array)); env->DeleteLocalRef(keycodes_array); @@ -804,6 +895,7 @@ void Shutdown() 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); } From e0562abb7d5c43fecb161c515b4718b58eed562f Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 5 Feb 2022 15:09:45 +0100 Subject: [PATCH 12/38] ControllerEmu: Add default mappings for Android Unlike PCs, Android doesn't really have any input method (not counting touch) that can reasonably be expected to exist on most devices. Because of this, I don't think shipping with a default mapping for the buttons and sticks of GameCube controllers and Wii Remotes makes sense. I would however like to ship default mappings for a few things: 1. Mapping the Wii Remote's accelerometer and gyroscope to the device's accelerometer and gyroscope. This functionality is useful mainly for people who use the touchscreen, but can also be useful when using a clip-on controller. The disadvantage of having this mapped by default is that games disable pointer input if the accelerometer reports that the Wii Remote is pointed at the ceiling. 2. Mapping GC keyboards for use with a physical keyboard, like on PC. After all, there's no other way of mapping them that makes sense. 3. Mapping rumble to the device's vibrator. Aside from the GC keyboards, this approach is effectively the same as what we were doing before the input overhaul. --- Source/Core/Core/FreeLookManager.cpp | 2 + Source/Core/Core/HW/GBAPadEmu.cpp | 2 + Source/Core/Core/HW/GCKeyboardEmu.cpp | 41 +++++++++++++++++++ Source/Core/Core/HW/GCPadEmu.cpp | 15 ++++--- .../Core/HW/WiimoteEmu/Extension/Nunchuk.cpp | 2 + Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 27 ++++++++++-- 6 files changed, 80 insertions(+), 9 deletions(-) diff --git a/Source/Core/Core/FreeLookManager.cpp b/Source/Core/Core/FreeLookManager.cpp index 432dbb0b92..0dbc71d3d8 100644 --- a/Source/Core/Core/FreeLookManager.cpp +++ b/Source/Core/Core/FreeLookManager.cpp @@ -120,6 +120,7 @@ void FreeLookController::LoadDefaults(const ControllerInterface& ciface) { EmulatedController::LoadDefaults(ciface); +#ifndef ANDROID auto hotkey_string = [](std::vector inputs) { return "@(" + JoinStrings(inputs, "+") + ')'; }; @@ -194,6 +195,7 @@ void FreeLookController::LoadDefaults(const ControllerInterface& ciface) m_rotation_gyro->SetControlExpression(GyroButtons::YawRight, "if(`Click 1`,`RelativeMouse X+` * 0.10, 0)"); #endif +#endif } ControllerEmu::ControlGroup* FreeLookController::GetGroup(FreeLookGroup group) const diff --git a/Source/Core/Core/HW/GBAPadEmu.cpp b/Source/Core/Core/HW/GBAPadEmu.cpp index 274c042c2e..a46177e79f 100644 --- a/Source/Core/Core/HW/GBAPadEmu.cpp +++ b/Source/Core/Core/HW/GBAPadEmu.cpp @@ -83,6 +83,7 @@ void GBAPad::LoadDefaults(const ControllerInterface& ciface) { EmulatedController::LoadDefaults(ciface); +#ifndef ANDROID // Buttons m_buttons->SetControlExpression(0, "`Z`"); // B m_buttons->SetControlExpression(1, "`X`"); // A @@ -101,4 +102,5 @@ void GBAPad::LoadDefaults(const ControllerInterface& ciface) m_dpad->SetControlExpression(1, "`G`"); // Down m_dpad->SetControlExpression(2, "`F`"); // Left m_dpad->SetControlExpression(3, "`H`"); // Right +#endif } diff --git a/Source/Core/Core/HW/GCKeyboardEmu.cpp b/Source/Core/Core/HW/GCKeyboardEmu.cpp index aace009503..0d91708076 100644 --- a/Source/Core/Core/HW/GCKeyboardEmu.cpp +++ b/Source/Core/Core/HW/GCKeyboardEmu.cpp @@ -257,6 +257,47 @@ void GCKeyboard::LoadDefaults(const ControllerInterface& ciface) m_keys5x->SetControlExpression(2, "`Up Arrow`"); m_keys5x->SetControlExpression(3, "`Right Arrow`"); m_keys5x->SetControlExpression(4, "Return"); +#elif ANDROID + m_keys0x->SetControlExpression(0, "`Move Home`"); + m_keys0x->SetControlExpression(1, "`Move End`"); + m_keys0x->SetControlExpression(2, "`Page Up`"); + m_keys0x->SetControlExpression(3, "`Page Down`"); + m_keys0x->SetControlExpression(4, "`Scroll Lock`"); + + m_keys2x->SetControlExpression(9, "`Minus`"); + m_keys2x->SetControlExpression(10, "`Grave`"); + m_keys2x->SetControlExpression(11, "`PrtSc SysRq`"); + m_keys2x->SetControlExpression(12, "`Apostrophe`"); + m_keys2x->SetControlExpression(13, "`Left Bracket`"); + m_keys2x->SetControlExpression(14, "``Equals``"); + m_keys2x->SetControlExpression(15, "`Numpad Multiply`"); + m_keys3x->SetControlExpression(0, "`Right Bracket`"); + m_keys3x->SetControlExpression(1, "`Comma`"); + m_keys3x->SetControlExpression(2, "`Period`"); + m_keys3x->SetControlExpression(3, "`Slash`"); + m_keys3x->SetControlExpression(4, "`Backslash`"); + + m_keys4x->SetControlExpression(1, "`Escape`"); + m_keys4x->SetControlExpression(2, "`Insert`"); + m_keys4x->SetControlExpression(3, "`Delete`"); + m_keys4x->SetControlExpression(4, "`Semicolon`"); + m_keys4x->SetControlExpression(5, "`Backspace`"); + m_keys4x->SetControlExpression(6, "`Tab`"); + m_keys4x->SetControlExpression(7, "`Caps Lock`"); + m_keys4x->SetControlExpression(8, "`Left Shift`"); + m_keys4x->SetControlExpression(9, "`Right Shift`"); + m_keys4x->SetControlExpression(10, "`Left Ctrl`"); + m_keys4x->SetControlExpression(11, "`Right Alt`"); + m_keys4x->SetControlExpression(12, "`Left Meta`"); + m_keys4x->SetControlExpression(13, "`Space`"); + m_keys4x->SetControlExpression(14, "`Right Meta`"); + m_keys4x->SetControlExpression(15, "`Menu`"); + + m_keys5x->SetControlExpression(0, "`Left`"); + m_keys5x->SetControlExpression(1, "`Down`"); + m_keys5x->SetControlExpression(2, "`Up`"); + m_keys5x->SetControlExpression(3, "`Right`"); + m_keys5x->SetControlExpression(4, "`Enter`"); #else // linux m_keys0x->SetControlExpression(0, "Home"); m_keys0x->SetControlExpression(1, "End"); diff --git a/Source/Core/Core/HW/GCPadEmu.cpp b/Source/Core/Core/HW/GCPadEmu.cpp index 470d2b8c2f..9c21d53451 100644 --- a/Source/Core/Core/HW/GCPadEmu.cpp +++ b/Source/Core/Core/HW/GCPadEmu.cpp @@ -170,12 +170,16 @@ void GCPad::LoadDefaults(const ControllerInterface& ciface) { EmulatedController::LoadDefaults(ciface); +#ifdef ANDROID + // Rumble + m_rumble->SetControlExpression(0, "`Android/0/Device Sensors:Motor 0`"); +#else // Buttons - m_buttons->SetControlExpression(0, "`X`"); // A - m_buttons->SetControlExpression(1, "`Z`"); // B - m_buttons->SetControlExpression(2, "`C`"); // X - m_buttons->SetControlExpression(3, "`S`"); // Y - m_buttons->SetControlExpression(4, "`D`"); // Z + m_buttons->SetControlExpression(0, "`X`"); // A + m_buttons->SetControlExpression(1, "`Z`"); // B + m_buttons->SetControlExpression(2, "`C`"); // X + m_buttons->SetControlExpression(3, "`S`"); // Y + m_buttons->SetControlExpression(4, "`D`"); // Z #ifdef _WIN32 m_buttons->SetControlExpression(5, "`RETURN`"); // Start #else @@ -225,6 +229,7 @@ void GCPad::LoadDefaults(const ControllerInterface& ciface) // Triggers m_triggers->SetControlExpression(0, "`Q`"); // L m_triggers->SetControlExpression(1, "`W`"); // R +#endif } bool GCPad::GetMicButton() const diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp index dbad8fbead..daa512c2d3 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp @@ -198,6 +198,7 @@ void Nunchuk::DoState(PointerWrap& p) void Nunchuk::LoadDefaults(const ControllerInterface& ciface) { +#ifndef ANDROID // Stick m_stick->SetControlExpression(0, "W"); // up m_stick->SetControlExpression(1, "S"); // down @@ -228,5 +229,6 @@ void Nunchuk::LoadDefaults(const ControllerInterface& ciface) m_shake->SetControlExpression(i, "`Click 2`"); #endif } +#endif } } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 5bc19f48e8..0c1999c1f9 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -655,6 +655,24 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface) { EmulatedController::LoadDefaults(ciface); +#ifdef ANDROID + // Rumble + m_rumble->SetControlExpression(0, "`Android/0/Device Sensors:Motor 0`"); + + // Motion Source + m_imu_accelerometer->SetControlExpression(0, "`Android/0/Device Sensors:Accel Up`"); + m_imu_accelerometer->SetControlExpression(1, "`Android/0/Device Sensors:Accel Down`"); + m_imu_accelerometer->SetControlExpression(2, "`Android/0/Device Sensors:Accel Left`"); + m_imu_accelerometer->SetControlExpression(3, "`Android/0/Device Sensors:Accel Right`"); + m_imu_accelerometer->SetControlExpression(4, "`Android/0/Device Sensors:Accel Forward`"); + m_imu_accelerometer->SetControlExpression(5, "`Android/0/Device Sensors:Accel Backward`"); + m_imu_gyroscope->SetControlExpression(0, "`Android/0/Device Sensors:Gyro Pitch Up`"); + m_imu_gyroscope->SetControlExpression(1, "`Android/0/Device Sensors:Gyro Pitch Down`"); + m_imu_gyroscope->SetControlExpression(2, "`Android/0/Device Sensors:Gyro Roll Left`"); + m_imu_gyroscope->SetControlExpression(3, "`Android/0/Device Sensors:Gyro Roll Right`"); + m_imu_gyroscope->SetControlExpression(4, "`Android/0/Device Sensors:Gyro Yaw Left`"); + m_imu_gyroscope->SetControlExpression(5, "`Android/0/Device Sensors:Gyro Yaw Right`"); +#else // Buttons #if defined HAVE_X11 && HAVE_X11 // A @@ -672,10 +690,10 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface) // B m_buttons->SetControlExpression(1, "`Click 1`"); #endif - m_buttons->SetControlExpression(2, "`1`"); // 1 - m_buttons->SetControlExpression(3, "`2`"); // 2 - m_buttons->SetControlExpression(4, "Q"); // - - m_buttons->SetControlExpression(5, "E"); // + + m_buttons->SetControlExpression(2, "`1`"); // 1 + m_buttons->SetControlExpression(3, "`2`"); // 2 + m_buttons->SetControlExpression(4, "Q"); // - + m_buttons->SetControlExpression(5, "E"); // + #ifdef _WIN32 m_buttons->SetControlExpression(6, "RETURN"); // Home @@ -729,6 +747,7 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface) m_imu_gyroscope->SetControlExpression(3, "`Gyro Roll Right`"); m_imu_gyroscope->SetControlExpression(4, "`Gyro Yaw Left`"); m_imu_gyroscope->SetControlExpression(5, "`Gyro Yaw Right`"); +#endif // Enable Nunchuk: constexpr ExtensionNumber DEFAULT_EXT = ExtensionNumber::NUNCHUK; From 9a8a3955605c8e1b33b358f258046bc549e88d1f Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 18 Apr 2022 18:19:24 +0200 Subject: [PATCH 13/38] GCPadEmu/WiimoteEmu: Reorder control groups This way, Android (which will show groups in the order they're defined) will show groups in a more logical order similar to DolphinQt. The main thing that was annoying me was how early Rumble was for Wii Remotes. Some of the other changes I'm making in this commit, like the order of Shake/Tilt/Swing, are more arbitrary and were made for consistency with DolphinQt. But there are also places where I didn't go all the way with matching DolphinQt. Most notably, DolphinQt puts sticks before buttons, but I don't see any reason to do that for Android. --- Source/Core/Core/HW/GCPadEmu.cpp | 16 +++--- .../Core/HW/WiimoteEmu/Extension/Drums.cpp | 8 +-- .../Core/HW/WiimoteEmu/Extension/Nunchuk.cpp | 12 ++--- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 52 +++++++++---------- 4 files changed, 43 insertions(+), 45 deletions(-) diff --git a/Source/Core/Core/HW/GCPadEmu.cpp b/Source/Core/Core/HW/GCPadEmu.cpp index 9c21d53451..efcab4fa7a 100644 --- a/Source/Core/Core/HW/GCPadEmu.cpp +++ b/Source/Core/Core/HW/GCPadEmu.cpp @@ -60,14 +60,6 @@ GCPad::GCPad(const unsigned int index) : m_index(index) m_triggers->AddInput(ControllerEmu::Translate, named_trigger); } - // rumble - groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(RUMBLE_GROUP)); - m_rumble->AddOutput(ControllerEmu::Translate, _trans("Motor")); - - // Microphone - groups.emplace_back(m_mic = new ControllerEmu::Buttons(MIC_GROUP)); - m_mic->AddInput(ControllerEmu::Translate, _trans("Button")); - // dpad groups.emplace_back(m_dpad = new ControllerEmu::Buttons(DPAD_GROUP)); for (const char* named_direction : named_directions) @@ -75,6 +67,14 @@ GCPad::GCPad(const unsigned int index) : m_index(index) m_dpad->AddInput(ControllerEmu::Translate, named_direction); } + // Microphone + groups.emplace_back(m_mic = new ControllerEmu::Buttons(MIC_GROUP)); + m_mic->AddInput(ControllerEmu::Translate, _trans("Button")); + + // rumble + groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(RUMBLE_GROUP)); + m_rumble->AddOutput(ControllerEmu::Translate, _trans("Motor")); + // options groups.emplace_back(m_options = new ControllerEmu::ControlGroup(OPTIONS_GROUP)); m_options->AddSetting( diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp index 1451acafbe..ef3f27460d 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp @@ -69,14 +69,14 @@ Drums::Drums() : Extension1stParty("Drums", _trans("Drum Kit")) _trans("%")}, 50); - // Stick. - groups.emplace_back(m_stick = - new ControllerEmu::OctagonAnalogStick(_trans("Stick"), GATE_RADIUS)); - // Buttons. groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); m_buttons->AddInput(ControllerEmu::DoNotTranslate, "-"); m_buttons->AddInput(ControllerEmu::DoNotTranslate, "+"); + + // Stick. + groups.emplace_back(m_stick = + new ControllerEmu::OctagonAnalogStick(_trans("Stick"), GATE_RADIUS)); } void Drums::BuildDesiredExtensionState(DesiredExtensionState* target_state) diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp index daa512c2d3..361f4459f5 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp @@ -45,16 +45,16 @@ Nunchuk::Nunchuk() : Extension1stParty(_trans("Nunchuk")) constexpr auto gate_radius = ControlState(STICK_GATE_RADIUS) / STICK_RADIUS; groups.emplace_back(m_stick = new ControllerEmu::OctagonAnalogStick(STICK_GROUP, gate_radius)); - // swing - groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing"))); + // Shake + // Inverse the default intensity so shake is opposite that of wiimote. + // This is needed by Donkey Kong Country Returns for proper shake action detection. + groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake"), -1)); // tilt groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt"))); - // Shake - // Inverse the default intensity so shake is opposite that of wiimote. - // This is needed by DKCR for proper shake action detection. - groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake"), -1)); + // swing + groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing"))); // accelerometer groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer( diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 0c1999c1f9..3b0018a76b 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -214,21 +214,22 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i } m_buttons->AddInput(ControllerEmu::DoNotTranslate, HOME_BUTTON, "HOME"); - // Pointing (IR) + // D-Pad + groups.emplace_back(m_dpad = new ControllerEmu::Buttons(DPAD_GROUP)); + for (const char* named_direction : named_directions) + { + m_dpad->AddInput(ControllerEmu::Translate, named_direction); + } + // i18n: "Point" refers to the action of pointing a Wii Remote. groups.emplace_back(m_ir = new ControllerEmu::Cursor(IR_GROUP, _trans("Point"))); - groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing"))); - groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt"))); groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake"))); - groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer( - ACCELEROMETER_GROUP, _trans("Accelerometer"))); - groups.emplace_back(m_imu_gyroscope = - new ControllerEmu::IMUGyroscope(GYROSCOPE_GROUP, _trans("Gyroscope"))); - groups.emplace_back(m_imu_ir = new ControllerEmu::IMUCursor("IMUIR", _trans("Point"))); + groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt"))); + groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing"))); + groups.emplace_back(m_imu_ir = new ControllerEmu::IMUCursor("IMUIR", _trans("Point"))); const auto fov_default = Common::DVec2(CameraLogic::CAMERA_FOV_X, CameraLogic::CAMERA_FOV_Y) / MathUtil::TAU * 360; - m_imu_ir->AddSetting(&m_fov_x_setting, // i18n: FOV stands for "Field of view". {_trans("Horizontal FOV"), @@ -237,7 +238,6 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i // i18n: Refers to emulated wii remote camera properties. _trans("Camera field of view (affects sensitivity of pointing).")}, fov_default.x, 0.01, 180); - m_imu_ir->AddSetting(&m_fov_y_setting, // i18n: FOV stands for "Field of view". {_trans("Vertical FOV"), @@ -247,6 +247,21 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i _trans("Camera field of view (affects sensitivity of pointing).")}, fov_default.y, 0.01, 180); + groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer( + ACCELEROMETER_GROUP, _trans("Accelerometer"))); + groups.emplace_back(m_imu_gyroscope = + new ControllerEmu::IMUGyroscope(GYROSCOPE_GROUP, _trans("Gyroscope"))); + + // Hotkeys + groups.emplace_back(m_hotkeys = new ControllerEmu::ModifySettingsButton(_trans("Hotkeys"))); + // hotkeys to temporarily modify the Wii Remote orientation (sideways, upright) + // this setting modifier is toggled + m_hotkeys->AddInput(_trans("Sideways Toggle"), true); + m_hotkeys->AddInput(_trans("Upright Toggle"), true); + // this setting modifier is not toggled + m_hotkeys->AddInput(_trans("Sideways Hold"), false); + m_hotkeys->AddInput(_trans("Upright Hold"), false); + // Extension groups.emplace_back(m_attachments = new ControllerEmu::Attachments(_trans("Extension"))); m_attachments->AddAttachment(std::make_unique()); @@ -266,13 +281,6 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(_trans("Rumble"))); m_rumble->AddOutput(ControllerEmu::Translate, _trans("Motor")); - // D-Pad - groups.emplace_back(m_dpad = new ControllerEmu::Buttons(DPAD_GROUP)); - for (const char* named_direction : named_directions) - { - m_dpad->AddInput(ControllerEmu::Translate, named_direction); - } - // Options groups.emplace_back(m_options = new ControllerEmu::ControlGroup(_trans("Options"))); @@ -297,16 +305,6 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i {"Sideways Wiimote", nullptr, nullptr, _trans("Sideways Wii Remote")}, false); - // Hotkeys - groups.emplace_back(m_hotkeys = new ControllerEmu::ModifySettingsButton(_trans("Hotkeys"))); - // hotkeys to temporarily modify the Wii Remote orientation (sideways, upright) - // this setting modifier is toggled - m_hotkeys->AddInput(_trans("Sideways Toggle"), true); - m_hotkeys->AddInput(_trans("Upright Toggle"), true); - // this setting modifier is not toggled - m_hotkeys->AddInput(_trans("Sideways Hold"), false); - m_hotkeys->AddInput(_trans("Upright Hold"), false); - Reset(); m_config_changed_callback_id = Config::AddConfigChangedCallback([this] { RefreshConfig(); }); From 93657cce5433552dbd0d836bac34e65143384a2c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 30 Dec 2021 18:37:17 +0100 Subject: [PATCH 14/38] Android: Stop extracting pre-defined controller INIs --- .../Android/app/src/main/assets/GCPadNew.ini | 104 ---- .../app/src/main/assets/WiimoteNew.ini | 580 ------------------ .../app/src/main/assets/WiimoteProfile.ini | 144 ----- .../utils/DirectoryInitialization.java | 70 +-- Source/Android/jni/MainAndroid.cpp | 7 +- 5 files changed, 11 insertions(+), 894 deletions(-) delete mode 100644 Source/Android/app/src/main/assets/GCPadNew.ini delete mode 100644 Source/Android/app/src/main/assets/WiimoteNew.ini delete mode 100644 Source/Android/app/src/main/assets/WiimoteProfile.ini diff --git a/Source/Android/app/src/main/assets/GCPadNew.ini b/Source/Android/app/src/main/assets/GCPadNew.ini deleted file mode 100644 index 2bf8ffdac3..0000000000 --- a/Source/Android/app/src/main/assets/GCPadNew.ini +++ /dev/null @@ -1,104 +0,0 @@ -[GCPad1] -Device = Android/0/Touchscreen -Buttons/A = `Button 0` -Buttons/B = `Button 1` -Buttons/Start = `Button 2` -Buttons/X = `Button 3` -Buttons/Y = `Button 4` -Buttons/Z = `Button 5` -D-Pad/Up = `Button 6` -D-Pad/Down = `Button 7` -D-Pad/Left = `Button 8` -D-Pad/Right = `Button 9` -Main Stick/Up = `Axis 11` -Main Stick/Down = `Axis 12` -Main Stick/Left = `Axis 13` -Main Stick/Right = `Axis 14` -C-Stick/Up = `Axis 16` -C-Stick/Down = `Axis 17` -C-Stick/Left = `Axis 18` -C-Stick/Right = `Axis 19` -Triggers/L = `Axis 20` -Triggers/R = `Axis 21` -Triggers/L-Analog = `Axis 20` -Triggers/R-Analog = `Axis 21` -Triggers/Threshold = 90,000000 -Rumble/Motor = `Rumble 700` -[GCPad2] -Device = Android/1/Touchscreen -Buttons/A = `Button 0` -Buttons/B = `Button 1` -Buttons/Start = `Button 2` -Buttons/X = `Button 3` -Buttons/Y = `Button 4` -Buttons/Z = `Button 5` -D-Pad/Up = `Button 6` -D-Pad/Down = `Button 7` -D-Pad/Left = `Button 8` -D-Pad/Right = `Button 9` -Main Stick/Up = `Axis 11` -Main Stick/Down = `Axis 12` -Main Stick/Left = `Axis 13` -Main Stick/Right = `Axis 14` -C-Stick/Up = `Axis 16` -C-Stick/Down = `Axis 17` -C-Stick/Left = `Axis 18` -C-Stick/Right = `Axis 19` -Triggers/L = `Axis 20` -Triggers/R = `Axis 21` -Triggers/L-Analog = `Axis 20` -Triggers/R-Analog = `Axis 21` -Triggers/Threshold = 90,000000 -Rumble/Motor = `Rumble 700` -[GCPad3] -Device = Android/2/Touchscreen -Buttons/A = `Button 0` -Buttons/B = `Button 1` -Buttons/Start = `Button 2` -Buttons/X = `Button 3` -Buttons/Y = `Button 4` -Buttons/Z = `Button 5` -D-Pad/Up = `Button 6` -D-Pad/Down = `Button 7` -D-Pad/Left = `Button 8` -D-Pad/Right = `Button 9` -Main Stick/Up = `Axis 11` -Main Stick/Down = `Axis 12` -Main Stick/Left = `Axis 13` -Main Stick/Right = `Axis 14` -C-Stick/Up = `Axis 16` -C-Stick/Down = `Axis 17` -C-Stick/Left = `Axis 18` -C-Stick/Right = `Axis 19` -Triggers/L = `Axis 20` -Triggers/R = `Axis 21` -Triggers/L-Analog = `Axis 20` -Triggers/R-Analog = `Axis 21` -Triggers/Threshold = 90,000000 -Rumble/Motor = `Rumble 700` -[GCPad4] -Device = Android/3/Touchscreen -Buttons/A = `Button 0` -Buttons/B = `Button 1` -Buttons/Start = `Button 2` -Buttons/X = `Button 3` -Buttons/Y = `Button 4` -Buttons/Z = `Button 5` -D-Pad/Up = `Button 6` -D-Pad/Down = `Button 7` -D-Pad/Left = `Button 8` -D-Pad/Right = `Button 9` -Main Stick/Up = `Axis 11` -Main Stick/Down = `Axis 12` -Main Stick/Left = `Axis 13` -Main Stick/Right = `Axis 14` -C-Stick/Up = `Axis 16` -C-Stick/Down = `Axis 17` -C-Stick/Left = `Axis 18` -C-Stick/Right = `Axis 19` -Triggers/L = `Axis 20` -Triggers/R = `Axis 21` -Triggers/L-Analog = `Axis 20` -Triggers/R-Analog = `Axis 21` -Triggers/Threshold = 90,000000 -Rumble/Motor = `Rumble 700` diff --git a/Source/Android/app/src/main/assets/WiimoteNew.ini b/Source/Android/app/src/main/assets/WiimoteNew.ini deleted file mode 100644 index 7237db7a3d..0000000000 --- a/Source/Android/app/src/main/assets/WiimoteNew.ini +++ /dev/null @@ -1,580 +0,0 @@ -[Wiimote1] -Device = Android/4/Touchscreen -Buttons/A = `Button 100` -Buttons/B = `Button 101` -Buttons/- = `Button 102` -Buttons/+ = `Button 103` -Buttons/Home = `Button 104` -Buttons/1 = `Button 105` -Buttons/2 = `Button 106` -D-Pad/Up = `Button 107` -D-Pad/Down = `Button 108` -D-Pad/Left = `Button 109` -D-Pad/Right = `Button 110` -IR/Up = `Axis 112` -IR/Down = `Axis 113` -IR/Left = `Axis 114` -IR/Right = `Axis 115` -IR/Forward = `Axis 116` -IR/Backward = `Axis 117` -IR/Hide = `Button 118` -IR/Total Pitch = 20 -IR/Total Yaw = 25 -IR/Vertical Offset = 10 -Swing/Up = `Axis 120` -Swing/Down = `Axis 121` -Swing/Left = `Axis 122` -Swing/Right = `Axis 123` -Swing/Forward = `Axis 124` -Swing/Backward = `Axis 125` -Tilt/Forward = `Axis 127` -Tilt/Backward = `Axis 128` -Tilt/Left = `Axis 129` -Tilt/Right = `Axis 130` -Tilt/Modifier = `Button 131` -Tilt/Modifier/Range = 50,000000 -Shake/X = `Button 132` -Shake/Y = `Button 133` -Shake/Z = `Button 134` -Extension = Nunchuk -Nunchuk/Buttons/C = `Button 200` -Nunchuk/Buttons/Z = `Button 201` -Nunchuk/Stick/Up = `Axis 203` -Nunchuk/Stick/Down = `Axis 204` -Nunchuk/Stick/Left = `Axis 205` -Nunchuk/Stick/Right = `Axis 206` -Nunchuk/Swing/Up = `Axis 208` -Nunchuk/Swing/Down = `Axis 209` -Nunchuk/Swing/Left = `Axis 210` -Nunchuk/Swing/Right = `Axis 211` -Nunchuk/Swing/Forward = `Axis 212` -Nunchuk/Swing/Backward = `Axis 213` -Nunchuk/Tilt/Forward = `Axis 215` -Nunchuk/Tilt/Backward = `Axis 216` -Nunchuk/Tilt/Left = `Axis 217` -Nunchuk/Tilt/Right = `Axis 218` -Nunchuk/Tilt/Modifier = `Button 219` -Nunchuk/Tilt/Modifier/Range = 50,000000 -Nunchuk/Shake/X = `Button 220` -Nunchuk/Shake/Y = `Button 221` -Nunchuk/Shake/Z = `Button 222` -Classic/Buttons/A = `Button 300` -Classic/Buttons/B = `Button 301` -Classic/Buttons/X = `Button 302` -Classic/Buttons/Y = `Button 303` -Classic/Buttons/- = `Button 304` -Classic/Buttons/+ = `Button 305` -Classic/Buttons/Home = `Button 306` -Classic/Buttons/ZL = `Button 307` -Classic/Buttons/ZR = `Button 308` -Classic/D-Pad/Up = `Button 309` -Classic/D-Pad/Down = `Button 310` -Classic/D-Pad/Left = `Button 311` -Classic/D-Pad/Right = `Button 312` -Classic/Left Stick/Up = `Axis 314` -Classic/Left Stick/Down = `Axis 315` -Classic/Left Stick/Left = `Axis 316` -Classic/Left Stick/Right = `Axis 317` -Classic/Right Stick/Up = `Axis 319` -Classic/Right Stick/Down = `Axis 320` -Classic/Right Stick/Left = `Axis 321` -Classic/Right Stick/Right = `Axis 322` -Classic/Triggers/L = `Axis 323` -Classic/Triggers/R = `Axis 324` -Classic/Triggers/Threshold = 90,000000 -Guitar/Buttons/- = `Button 400` -Guitar/Buttons/+ = `Button 401` -Guitar/Frets/Green = `Button 402` -Guitar/Frets/Red = `Button 403` -Guitar/Frets/Yellow = `Button 404` -Guitar/Frets/Blue = `Button 405` -Guitar/Frets/Orange = `Button 406` -Guitar/Strum/Up = `Button 407` -Guitar/Strum/Down = `Button 408` -Guitar/Stick/Up = `Axis 410` -Guitar/Stick/Down = `Axis 411` -Guitar/Stick/Left = `Axis 412` -Guitar/Stick/Right = `Axis 413` -Guitar/Whammy/Bar = `Axis 414` -Drums/Buttons/- = `Button 500` -Drums/Buttons/+ = `Button 501` -Drums/Pads/Red = `Button 502` -Drums/Pads/Yellow = `Button 503` -Drums/Pads/Blue = `Button 504` -Drums/Pads/Green = `Button 505` -Drums/Pads/Orange = `Button 506` -Drums/Pads/Bass = `Button 507` -Drums/Stick/Up = `Axis 509` -Drums/Stick/Down = `Axis 510` -Drums/Stick/Left = `Axis 511` -Drums/Stick/Right = `Axis 512` -Turntable/Buttons/Green Left = `Button 600` -Turntable/Buttons/Red Left = `Button 601` -Turntable/Buttons/Blue Left = `Button 602` -Turntable/Buttons/Green Right = `Button 603` -Turntable/Buttons/Red Right = `Button 604` -Turntable/Buttons/Blue Right = `Button 605` -Turntable/Buttons/- = `Button 606` -Turntable/Buttons/+ = `Button 607` -Turntable/Buttons/Home = `Button 608` -Turntable/Buttons/Euphoria = `Button 609` -Turntable/Table Left/Left = `Axis 611` -Turntable/Table Left/Right = `Axis 612` -Turntable/Table Right/Left = `Axis 614` -Turntable/Table Right/Right = `Axis 615` -Turntable/Stick/Up = `Axis 617` -Turntable/Stick/Down = `Axis 618` -Turntable/Stick/Left = `Axis 619` -Turntable/Stick/Right = `Axis 620` -Turntable/Effect/Dial = `Axis 621` -Turntable/Crossfade/Left = `Axis 623` -Turntable/Crossfade/Right = `Axis 624` -IMUAccelerometer/Left = `Axis 625` -IMUAccelerometer/Right = `Axis 626` -IMUAccelerometer/Forward = `Axis 627` -IMUAccelerometer/Backward = `Axis 628` -IMUAccelerometer/Up = `Axis 629` -IMUAccelerometer/Down = `Axis 630` -IMUGyroscope/Pitch Up = `Axis 631` -IMUGyroscope/Pitch Down = `Axis 632` -IMUGyroscope/Roll Left = `Axis 633` -IMUGyroscope/Roll Right = `Axis 634` -IMUGyroscope/Yaw Left = `Axis 635` -IMUGyroscope/Yaw Right = `Axis 636` -Source = 1 -Rumble/Motor = `Rumble 700` -[Wiimote2] -Device = Android/5/Touchscreen -Buttons/A = `Button 100` -Buttons/B = `Button 101` -Buttons/- = `Button 102` -Buttons/+ = `Button 103` -Buttons/Home = `Button 104` -Buttons/1 = `Button 105` -Buttons/2 = `Button 106` -D-Pad/Up = `Button 107` -D-Pad/Down = `Button 108` -D-Pad/Left = `Button 109` -D-Pad/Right = `Button 110` -IR/Up = `Axis 112` -IR/Down = `Axis 113` -IR/Left = `Axis 114` -IR/Right = `Axis 115` -IR/Forward = `Axis 116` -IR/Backward = `Axis 117` -IR/Hide = `Button 118` -IR/Total Pitch = 15 -IR/Total Yaw = 15 -IR/Vertical Offset = 10 -Swing/Up = `Axis 120` -Swing/Down = `Axis 121` -Swing/Left = `Axis 122` -Swing/Right = `Axis 123` -Swing/Forward = `Axis 124` -Swing/Backward = `Axis 125` -Tilt/Forward = `Axis 127` -Tilt/Backward = `Axis 128` -Tilt/Left = `Axis 129` -Tilt/Right = `Axis 130` -Tilt/Modifier = `Button 131` -Tilt/Modifier/Range = 50,000000 -Shake/X = `Button 132` -Shake/Y = `Button 133` -Shake/Z = `Button 134` -Extension = None -Nunchuk/Buttons/C = `Button 200` -Nunchuk/Buttons/Z = `Button 201` -Nunchuk/Stick/Up = `Axis 203` -Nunchuk/Stick/Down = `Axis 204` -Nunchuk/Stick/Left = `Axis 205` -Nunchuk/Stick/Right = `Axis 206` -Nunchuk/Swing/Up = `Axis 208` -Nunchuk/Swing/Down = `Axis 209` -Nunchuk/Swing/Left = `Axis 210` -Nunchuk/Swing/Right = `Axis 211` -Nunchuk/Swing/Forward = `Axis 212` -Nunchuk/Swing/Backward = `Axis 213` -Nunchuk/Tilt/Forward = `Axis 215` -Nunchuk/Tilt/Backward = `Axis 216` -Nunchuk/Tilt/Left = `Axis 217` -Nunchuk/Tilt/Right = `Axis 218` -Nunchuk/Tilt/Modifier = `Button 219` -Nunchuk/Tilt/Modifier/Range = 50,000000 -Nunchuk/Shake/X = `Button 220` -Nunchuk/Shake/Y = `Button 221` -Nunchuk/Shake/Z = `Button 222` -Classic/Buttons/A = `Button 300` -Classic/Buttons/B = `Button 301` -Classic/Buttons/X = `Button 302` -Classic/Buttons/Y = `Button 303` -Classic/Buttons/- = `Button 304` -Classic/Buttons/+ = `Button 305` -Classic/Buttons/Home = `Button 306` -Classic/Buttons/ZL = `Button 307` -Classic/Buttons/ZR = `Button 308` -Classic/D-Pad/Up = `Button 309` -Classic/D-Pad/Down = `Button 310` -Classic/D-Pad/Left = `Button 311` -Classic/D-Pad/Right = `Button 312` -Classic/Left Stick/Up = `Axis 314` -Classic/Left Stick/Down = `Axis 315` -Classic/Left Stick/Left = `Axis 316` -Classic/Left Stick/Right = `Axis 317` -Classic/Right Stick/Up = `Axis 319` -Classic/Right Stick/Down = `Axis 320` -Classic/Right Stick/Left = `Axis 321` -Classic/Right Stick/Right = `Axis 322` -Classic/Triggers/L = `Axis 323` -Classic/Triggers/R = `Axis 324` -Classic/Triggers/Threshold = 90,000000 -Guitar/Buttons/- = `Button 400` -Guitar/Buttons/+ = `Button 401` -Guitar/Frets/Green = `Button 402` -Guitar/Frets/Red = `Button 403` -Guitar/Frets/Yellow = `Button 404` -Guitar/Frets/Blue = `Button 405` -Guitar/Frets/Orange = `Button 406` -Guitar/Strum/Up = `Button 407` -Guitar/Strum/Down = `Button 408` -Guitar/Stick/Up = `Axis 410` -Guitar/Stick/Down = `Axis 411` -Guitar/Stick/Left = `Axis 412` -Guitar/Stick/Right = `Axis 413` -Guitar/Whammy/Bar = `Axis 414` -Drums/Buttons/- = `Button 500` -Drums/Buttons/+ = `Button 501` -Drums/Pads/Red = `Button 502` -Drums/Pads/Yellow = `Button 503` -Drums/Pads/Blue = `Button 504` -Drums/Pads/Green = `Button 505` -Drums/Pads/Orange = `Button 506` -Drums/Pads/Bass = `Button 507` -Drums/Stick/Up = `Axis 509` -Drums/Stick/Down = `Axis 510` -Drums/Stick/Left = `Axis 511` -Drums/Stick/Right = `Axis 512` -Turntable/Buttons/Green Left = `Button 600` -Turntable/Buttons/Red Left = `Button 601` -Turntable/Buttons/Blue Left = `Button 602` -Turntable/Buttons/Green Right = `Button 603` -Turntable/Buttons/Red Right = `Button 604` -Turntable/Buttons/Blue Right = `Button 605` -Turntable/Buttons/- = `Button 606` -Turntable/Buttons/+ = `Button 607` -Turntable/Buttons/Home = `Button 608` -Turntable/Buttons/Euphoria = `Button 609` -Turntable/Table Left/Left = `Axis 611` -Turntable/Table Left/Right = `Axis 612` -Turntable/Table Right/Left = `Axis 614` -Turntable/Table Right/Right = `Axis 615` -Turntable/Stick/Up = `Axis 617` -Turntable/Stick/Down = `Axis 618` -Turntable/Stick/Left = `Axis 619` -Turntable/Stick/Right = `Axis 620` -Turntable/Effect/Dial = `Axis 621` -Turntable/Crossfade/Left = `Axis 623` -Turntable/Crossfade/Right = `Axis 624` -IMUAccelerometer/Left = `Axis 625` -IMUAccelerometer/Right = `Axis 626` -IMUAccelerometer/Forward = `Axis 627` -IMUAccelerometer/Backward = `Axis 628` -IMUAccelerometer/Up = `Axis 629` -IMUAccelerometer/Down = `Axis 630` -IMUGyroscope/Pitch Up = `Axis 631` -IMUGyroscope/Pitch Down = `Axis 632` -IMUGyroscope/Roll Left = `Axis 633` -IMUGyroscope/Roll Right = `Axis 634` -IMUGyroscope/Yaw Left = `Axis 635` -IMUGyroscope/Yaw Right = `Axis 636` -Source = 0 -Rumble/Motor = `Rumble 700` -[Wiimote3] -Device = Android/6/Touchscreen -Buttons/A = `Button 100` -Buttons/B = `Button 101` -Buttons/- = `Button 102` -Buttons/+ = `Button 103` -Buttons/Home = `Button 104` -Buttons/1 = `Button 105` -Buttons/2 = `Button 106` -D-Pad/Up = `Button 107` -D-Pad/Down = `Button 108` -D-Pad/Left = `Button 109` -D-Pad/Right = `Button 110` -IR/Up = `Axis 112` -IR/Down = `Axis 113` -IR/Left = `Axis 114` -IR/Right = `Axis 115` -IR/Forward = `Axis 116` -IR/Backward = `Axis 117` -IR/Hide = `Button 118` -IR/Total Pitch = 15 -IR/Total Yaw = 15 -IR/Vertical Offset = 10 -Swing/Up = `Axis 120` -Swing/Down = `Axis 121` -Swing/Left = `Axis 122` -Swing/Right = `Axis 123` -Swing/Forward = `Axis 124` -Swing/Backward = `Axis 125` -Tilt/Forward = `Axis 127` -Tilt/Backward = `Axis 128` -Tilt/Left = `Axis 129` -Tilt/Right = `Axis 130` -Tilt/Modifier = `Button 131` -Tilt/Modifier/Range = 50,000000 -Shake/X = `Button 132` -Shake/Y = `Button 133` -Shake/Z = `Button 134` -Extension = None -Nunchuk/Buttons/C = `Button 200` -Nunchuk/Buttons/Z = `Button 201` -Nunchuk/Stick/Up = `Axis 203` -Nunchuk/Stick/Down = `Axis 204` -Nunchuk/Stick/Left = `Axis 205` -Nunchuk/Stick/Right = `Axis 206` -Nunchuk/Swing/Up = `Axis 208` -Nunchuk/Swing/Down = `Axis 209` -Nunchuk/Swing/Left = `Axis 210` -Nunchuk/Swing/Right = `Axis 211` -Nunchuk/Swing/Forward = `Axis 212` -Nunchuk/Swing/Backward = `Axis 213` -Nunchuk/Tilt/Forward = `Axis 215` -Nunchuk/Tilt/Backward = `Axis 216` -Nunchuk/Tilt/Left = `Axis 217` -Nunchuk/Tilt/Right = `Axis 218` -Nunchuk/Tilt/Modifier = `Button 219` -Nunchuk/Tilt/Modifier/Range = 50,000000 -Nunchuk/Shake/X = `Button 220` -Nunchuk/Shake/Y = `Button 221` -Nunchuk/Shake/Z = `Button 222` -Classic/Buttons/A = `Button 300` -Classic/Buttons/B = `Button 301` -Classic/Buttons/X = `Button 302` -Classic/Buttons/Y = `Button 303` -Classic/Buttons/- = `Button 304` -Classic/Buttons/+ = `Button 305` -Classic/Buttons/Home = `Button 306` -Classic/Buttons/ZL = `Button 307` -Classic/Buttons/ZR = `Button 308` -Classic/D-Pad/Up = `Button 309` -Classic/D-Pad/Down = `Button 310` -Classic/D-Pad/Left = `Button 311` -Classic/D-Pad/Right = `Button 312` -Classic/Left Stick/Up = `Axis 314` -Classic/Left Stick/Down = `Axis 315` -Classic/Left Stick/Left = `Axis 316` -Classic/Left Stick/Right = `Axis 317` -Classic/Right Stick/Up = `Axis 319` -Classic/Right Stick/Down = `Axis 320` -Classic/Right Stick/Left = `Axis 321` -Classic/Right Stick/Right = `Axis 322` -Classic/Triggers/L = `Axis 323` -Classic/Triggers/R = `Axis 324` -Classic/Triggers/Threshold = 90,000000 -Guitar/Buttons/- = `Button 400` -Guitar/Buttons/+ = `Button 401` -Guitar/Frets/Green = `Button 402` -Guitar/Frets/Red = `Button 403` -Guitar/Frets/Yellow = `Button 404` -Guitar/Frets/Blue = `Button 405` -Guitar/Frets/Orange = `Button 406` -Guitar/Strum/Up = `Button 407` -Guitar/Strum/Down = `Button 408` -Guitar/Stick/Up = `Axis 410` -Guitar/Stick/Down = `Axis 411` -Guitar/Stick/Left = `Axis 412` -Guitar/Stick/Right = `Axis 413` -Guitar/Whammy/Bar = `Axis 414` -Drums/Buttons/- = `Button 500` -Drums/Buttons/+ = `Button 501` -Drums/Pads/Red = `Button 502` -Drums/Pads/Yellow = `Button 503` -Drums/Pads/Blue = `Button 504` -Drums/Pads/Green = `Button 505` -Drums/Pads/Orange = `Button 506` -Drums/Pads/Bass = `Button 507` -Drums/Stick/Up = `Axis 509` -Drums/Stick/Down = `Axis 510` -Drums/Stick/Left = `Axis 511` -Drums/Stick/Right = `Axis 512` -Turntable/Buttons/Green Left = `Button 600` -Turntable/Buttons/Red Left = `Button 601` -Turntable/Buttons/Blue Left = `Button 602` -Turntable/Buttons/Green Right = `Button 603` -Turntable/Buttons/Red Right = `Button 604` -Turntable/Buttons/Blue Right = `Button 605` -Turntable/Buttons/- = `Button 606` -Turntable/Buttons/+ = `Button 607` -Turntable/Buttons/Home = `Button 608` -Turntable/Buttons/Euphoria = `Button 609` -Turntable/Table Left/Left = `Axis 611` -Turntable/Table Left/Right = `Axis 612` -Turntable/Table Right/Left = `Axis 614` -Turntable/Table Right/Right = `Axis 615` -Turntable/Stick/Up = `Axis 617` -Turntable/Stick/Down = `Axis 618` -Turntable/Stick/Left = `Axis 619` -Turntable/Stick/Right = `Axis 620` -Turntable/Effect/Dial = `Axis 621` -Turntable/Crossfade/Left = `Axis 623` -Turntable/Crossfade/Right = `Axis 624` -IMUAccelerometer/Left = `Axis 625` -IMUAccelerometer/Right = `Axis 626` -IMUAccelerometer/Forward = `Axis 627` -IMUAccelerometer/Backward = `Axis 628` -IMUAccelerometer/Up = `Axis 629` -IMUAccelerometer/Down = `Axis 630` -IMUGyroscope/Pitch Up = `Axis 631` -IMUGyroscope/Pitch Down = `Axis 632` -IMUGyroscope/Roll Left = `Axis 633` -IMUGyroscope/Roll Right = `Axis 634` -IMUGyroscope/Yaw Left = `Axis 635` -IMUGyroscope/Yaw Right = `Axis 636` -Source = 0 -Rumble/Motor = `Rumble 700` -[Wiimote4] -Device = Android/7/Touchscreen -Buttons/A = `Button 100` -Buttons/B = `Button 101` -Buttons/- = `Button 102` -Buttons/+ = `Button 103` -Buttons/Home = `Button 104` -Buttons/1 = `Button 105` -Buttons/2 = `Button 106` -D-Pad/Up = `Button 107` -D-Pad/Down = `Button 108` -D-Pad/Left = `Button 109` -D-Pad/Right = `Button 110` -IR/Up = `Axis 112` -IR/Down = `Axis 113` -IR/Left = `Axis 114` -IR/Right = `Axis 115` -IR/Forward = `Axis 116` -IR/Backward = `Axis 117` -IR/Hide = `Button 118` -IR/Total Pitch = 15 -IR/Total Yaw = 15 -IR/Vertical Offset = 10 -Swing/Up = `Axis 120` -Swing/Down = `Axis 121` -Swing/Left = `Axis 122` -Swing/Right = `Axis 123` -Swing/Forward = `Axis 124` -Swing/Backward = `Axis 125` -Tilt/Forward = `Axis 127` -Tilt/Backward = `Axis 128` -Tilt/Left = `Axis 129` -Tilt/Right = `Axis 130` -Tilt/Modifier = `Button 131` -Tilt/Modifier/Range = 50,000000 -Shake/X = `Button 132` -Shake/Y = `Button 133` -Shake/Z = `Button 134` -Extension = None -Nunchuk/Buttons/C = `Button 200` -Nunchuk/Buttons/Z = `Button 201` -Nunchuk/Stick/Up = `Axis 203` -Nunchuk/Stick/Down = `Axis 204` -Nunchuk/Stick/Left = `Axis 205` -Nunchuk/Stick/Right = `Axis 206` -Nunchuk/Swing/Up = `Axis 208` -Nunchuk/Swing/Down = `Axis 209` -Nunchuk/Swing/Left = `Axis 210` -Nunchuk/Swing/Right = `Axis 211` -Nunchuk/Swing/Forward = `Axis 212` -Nunchuk/Swing/Backward = `Axis 213` -Nunchuk/Tilt/Forward = `Axis 215` -Nunchuk/Tilt/Backward = `Axis 216` -Nunchuk/Tilt/Left = `Axis 217` -Nunchuk/Tilt/Right = `Axis 218` -Nunchuk/Tilt/Modifier = `Button 219` -Nunchuk/Tilt/Modifier/Range = 50,000000 -Nunchuk/Shake/X = `Button 220` -Nunchuk/Shake/Y = `Button 221` -Nunchuk/Shake/Z = `Button 222` -Classic/Buttons/A = `Button 300` -Classic/Buttons/B = `Button 301` -Classic/Buttons/X = `Button 302` -Classic/Buttons/Y = `Button 303` -Classic/Buttons/- = `Button 304` -Classic/Buttons/+ = `Button 305` -Classic/Buttons/Home = `Button 306` -Classic/Buttons/ZL = `Button 307` -Classic/Buttons/ZR = `Button 308` -Classic/D-Pad/Up = `Button 309` -Classic/D-Pad/Down = `Button 310` -Classic/D-Pad/Left = `Button 311` -Classic/D-Pad/Right = `Button 312` -Classic/Left Stick/Up = `Axis 314` -Classic/Left Stick/Down = `Axis 315` -Classic/Left Stick/Left = `Axis 316` -Classic/Left Stick/Right = `Axis 317` -Classic/Right Stick/Up = `Axis 319` -Classic/Right Stick/Down = `Axis 320` -Classic/Right Stick/Left = `Axis 321` -Classic/Right Stick/Right = `Axis 322` -Classic/Triggers/L = `Axis 323` -Classic/Triggers/R = `Axis 324` -Classic/Triggers/Threshold = 90,000000 -Guitar/Buttons/- = `Button 400` -Guitar/Buttons/+ = `Button 401` -Guitar/Frets/Green = `Button 402` -Guitar/Frets/Red = `Button 403` -Guitar/Frets/Yellow = `Button 404` -Guitar/Frets/Blue = `Button 405` -Guitar/Frets/Orange = `Button 406` -Guitar/Strum/Up = `Button 407` -Guitar/Strum/Down = `Button 408` -Guitar/Stick/Up = `Axis 410` -Guitar/Stick/Down = `Axis 411` -Guitar/Stick/Left = `Axis 412` -Guitar/Stick/Right = `Axis 413` -Guitar/Whammy/Bar = `Axis 414` -Drums/Buttons/- = `Button 500` -Drums/Buttons/+ = `Button 501` -Drums/Pads/Red = `Button 502` -Drums/Pads/Yellow = `Button 503` -Drums/Pads/Blue = `Button 504` -Drums/Pads/Green = `Button 505` -Drums/Pads/Orange = `Button 506` -Drums/Pads/Bass = `Button 507` -Drums/Stick/Up = `Axis 509` -Drums/Stick/Down = `Axis 510` -Drums/Stick/Left = `Axis 511` -Drums/Stick/Right = `Axis 512` -Turntable/Buttons/Green Left = `Button 600` -Turntable/Buttons/Red Left = `Button 601` -Turntable/Buttons/Blue Left = `Button 602` -Turntable/Buttons/Green Right = `Button 603` -Turntable/Buttons/Red Right = `Button 604` -Turntable/Buttons/Blue Right = `Button 605` -Turntable/Buttons/- = `Button 606` -Turntable/Buttons/+ = `Button 607` -Turntable/Buttons/Home = `Button 608` -Turntable/Buttons/Euphoria = `Button 609` -Turntable/Table Left/Left = `Axis 611` -Turntable/Table Left/Right = `Axis 612` -Turntable/Table Right/Left = `Axis 614` -Turntable/Table Right/Right = `Axis 615` -Turntable/Stick/Up = `Axis 617` -Turntable/Stick/Down = `Axis 618` -Turntable/Stick/Left = `Axis 619` -Turntable/Stick/Right = `Axis 620` -Turntable/Effect/Dial = `Axis 621` -Turntable/Crossfade/Left = `Axis 623` -Turntable/Crossfade/Right = `Axis 624` -IMUAccelerometer/Left = `Axis 625` -IMUAccelerometer/Right = `Axis 626` -IMUAccelerometer/Forward = `Axis 627` -IMUAccelerometer/Backward = `Axis 628` -IMUAccelerometer/Up = `Axis 629` -IMUAccelerometer/Down = `Axis 630` -IMUGyroscope/Pitch Up = `Axis 631` -IMUGyroscope/Pitch Down = `Axis 632` -IMUGyroscope/Roll Left = `Axis 633` -IMUGyroscope/Roll Right = `Axis 634` -IMUGyroscope/Yaw Left = `Axis 635` -IMUGyroscope/Yaw Right = `Axis 636` -Source = 0 -Rumble/Motor = `Rumble 700` diff --git a/Source/Android/app/src/main/assets/WiimoteProfile.ini b/Source/Android/app/src/main/assets/WiimoteProfile.ini deleted file mode 100644 index 89f7d87f5b..0000000000 --- a/Source/Android/app/src/main/assets/WiimoteProfile.ini +++ /dev/null @@ -1,144 +0,0 @@ -[Profile] -Device = Android/4/Touchscreen -Buttons/A = `Button 100` -Buttons/B = `Button 101` -Buttons/- = `Button 102` -Buttons/+ = `Button 103` -Buttons/Home = `Button 104` -Buttons/1 = `Button 105` -Buttons/2 = `Button 106` -D-Pad/Up = `Button 107` -D-Pad/Down = `Button 108` -D-Pad/Left = `Button 109` -D-Pad/Right = `Button 110` -IR/Up = `Axis 112` -IR/Down = `Axis 113` -IR/Left = `Axis 114` -IR/Right = `Axis 115` -IR/Forward = `Axis 116` -IR/Backward = `Axis 117` -IR/Hide = `Button 118` -IR/Total Pitch = 20 -IR/Total Yaw = 25 -IR/Vertical Offset = 10 -Swing/Up = `Axis 120` -Swing/Down = `Axis 121` -Swing/Left = `Axis 122` -Swing/Right = `Axis 123` -Swing/Forward = `Axis 124` -Swing/Backward = `Axis 125` -Tilt/Forward = `Axis 127` -Tilt/Backward = `Axis 128` -Tilt/Left = `Axis 129` -Tilt/Right = `Axis 130` -Tilt/Modifier = `Button 131` -Tilt/Modifier/Range = 50,000000 -Shake/X = `Button 132` -Shake/Y = `Button 133` -Shake/Z = `Button 134` -Extension = Nunchuk -Nunchuk/Buttons/C = `Button 200` -Nunchuk/Buttons/Z = `Button 201` -Nunchuk/Stick/Up = `Axis 203` -Nunchuk/Stick/Down = `Axis 204` -Nunchuk/Stick/Left = `Axis 205` -Nunchuk/Stick/Right = `Axis 206` -Nunchuk/Swing/Up = `Axis 208` -Nunchuk/Swing/Down = `Axis 209` -Nunchuk/Swing/Left = `Axis 210` -Nunchuk/Swing/Right = `Axis 211` -Nunchuk/Swing/Forward = `Axis 212` -Nunchuk/Swing/Backward = `Axis 213` -Nunchuk/Tilt/Forward = `Axis 215` -Nunchuk/Tilt/Backward = `Axis 216` -Nunchuk/Tilt/Left = `Axis 217` -Nunchuk/Tilt/Right = `Axis 218` -Nunchuk/Tilt/Modifier = `Button 219` -Nunchuk/Tilt/Modifier/Range = 50,000000 -Nunchuk/Shake/X = `Button 220` -Nunchuk/Shake/Y = `Button 221` -Nunchuk/Shake/Z = `Button 222` -Classic/Buttons/A = `Button 300` -Classic/Buttons/B = `Button 301` -Classic/Buttons/X = `Button 302` -Classic/Buttons/Y = `Button 303` -Classic/Buttons/- = `Button 304` -Classic/Buttons/+ = `Button 305` -Classic/Buttons/Home = `Button 306` -Classic/Buttons/ZL = `Button 307` -Classic/Buttons/ZR = `Button 308` -Classic/D-Pad/Up = `Button 309` -Classic/D-Pad/Down = `Button 310` -Classic/D-Pad/Left = `Button 311` -Classic/D-Pad/Right = `Button 312` -Classic/Left Stick/Up = `Axis 314` -Classic/Left Stick/Down = `Axis 315` -Classic/Left Stick/Left = `Axis 316` -Classic/Left Stick/Right = `Axis 317` -Classic/Right Stick/Up = `Axis 319` -Classic/Right Stick/Down = `Axis 320` -Classic/Right Stick/Left = `Axis 321` -Classic/Right Stick/Right = `Axis 322` -Classic/Triggers/L = `Axis 323` -Classic/Triggers/R = `Axis 324` -Classic/Triggers/Threshold = 90,000000 -Guitar/Buttons/- = `Button 400` -Guitar/Buttons/+ = `Button 401` -Guitar/Frets/Green = `Button 402` -Guitar/Frets/Red = `Button 403` -Guitar/Frets/Yellow = `Button 404` -Guitar/Frets/Blue = `Button 405` -Guitar/Frets/Orange = `Button 406` -Guitar/Strum/Up = `Button 407` -Guitar/Strum/Down = `Button 408` -Guitar/Stick/Up = `Axis 410` -Guitar/Stick/Down = `Axis 411` -Guitar/Stick/Left = `Axis 412` -Guitar/Stick/Right = `Axis 413` -Guitar/Whammy/Bar = `Axis 414` -Drums/Buttons/- = `Button 500` -Drums/Buttons/+ = `Button 501` -Drums/Pads/Red = `Button 502` -Drums/Pads/Yellow = `Button 503` -Drums/Pads/Blue = `Button 504` -Drums/Pads/Green = `Button 505` -Drums/Pads/Orange = `Button 506` -Drums/Pads/Bass = `Button 507` -Drums/Stick/Up = `Axis 509` -Drums/Stick/Down = `Axis 510` -Drums/Stick/Left = `Axis 511` -Drums/Stick/Right = `Axis 512` -Turntable/Buttons/Green Left = `Button 600` -Turntable/Buttons/Red Left = `Button 601` -Turntable/Buttons/Blue Left = `Button 602` -Turntable/Buttons/Green Right = `Button 603` -Turntable/Buttons/Red Right = `Button 604` -Turntable/Buttons/Blue Right = `Button 605` -Turntable/Buttons/- = `Button 606` -Turntable/Buttons/+ = `Button 607` -Turntable/Buttons/Home = `Button 608` -Turntable/Buttons/Euphoria = `Button 609` -Turntable/Table Left/Left = `Axis 611` -Turntable/Table Left/Right = `Axis 612` -Turntable/Table Right/Left = `Axis 614` -Turntable/Table Right/Right = `Axis 615` -Turntable/Stick/Up = `Axis 617` -Turntable/Stick/Down = `Axis 618` -Turntable/Stick/Left = `Axis 619` -Turntable/Stick/Right = `Axis 620` -Turntable/Effect/Dial = `Axis 621` -Turntable/Crossfade/Left = `Axis 623` -Turntable/Crossfade/Right = `Axis 624` -IMUAccelerometer/Left = `Axis 625` -IMUAccelerometer/Right = `Axis 626` -IMUAccelerometer/Forward = `Axis 627` -IMUAccelerometer/Backward = `Axis 628` -IMUAccelerometer/Up = `Axis 629` -IMUAccelerometer/Down = `Axis 630` -IMUGyroscope/Pitch Up = `Axis 631` -IMUGyroscope/Pitch Down = `Axis 632` -IMUGyroscope/Roll Left = `Axis 633` -IMUGyroscope/Roll Right = `Axis 634` -IMUGyroscope/Yaw Left = `Axis 635` -IMUGyroscope/Yaw Right = `Axis 636` -Rumble/Motor = `Rumble 700` diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java index b1584df1be..c4bfdbd7bf 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java @@ -31,13 +31,16 @@ import java.io.InputStream; import java.io.OutputStream; /** - * A service that spawns its own thread in order to copy several binary and shader files - * from the Dolphin APK to the external file system. + * A class that spawns its own thread in order perform initialization. + * + * The initialization steps include: + * - Extracting the Sys directory from the APK so it can be accessed using regular file APIs + * - Letting the native code know where on external storage it should place the User directory + * - Running the native code's init steps (which include things like populating the User directory) */ public final class DirectoryInitialization { public static final String EXTRA_STATE = "directoryState"; - private static final int WiimoteNewVersion = 5; // Last changed in PR 8907 private static final MutableLiveData directoryState = new MutableLiveData<>(DirectoryInitializationState.NOT_YET_INITIALIZED); private static volatile boolean areDirectoriesAvailable = false; @@ -73,8 +76,7 @@ public final class DirectoryInitialization System.exit(1); } - initializeInternalStorage(context); - boolean wiimoteIniWritten = initializeExternalStorage(context); + extractSysDirectory(context); NativeLibrary.Initialize(); NativeLibrary.ReportStartToAnalytics(); @@ -82,7 +84,8 @@ public final class DirectoryInitialization checkThemeSettings(context); - if (wiimoteIniWritten) + // TODO: Does doing this still make sense? + if (false) { // This has to be done after calling NativeLibrary.Initialize(), // as it relies on the config system @@ -131,7 +134,7 @@ public final class DirectoryInitialization return true; } - private static void initializeInternalStorage(Context context) + private static void extractSysDirectory(Context context) { File sysDirectory = new File(context.getFilesDir(), "Sys"); @@ -153,45 +156,6 @@ public final class DirectoryInitialization SetSysDirectory(sysDirectory.getPath()); } - // Returns whether the WiimoteNew.ini file was written to - private static boolean initializeExternalStorage(Context context) - { - // Create User directory structure and copy some NAND files from the extracted Sys directory. - CreateUserDirectories(); - - // GCPadNew.ini and WiimoteNew.ini must contain specific values in order for controller - // input to work as intended (they aren't user configurable), so we overwrite them just - // in case the user has tried to modify them manually. - // - // ...Except WiimoteNew.ini contains the user configurable settings for Wii Remote - // extensions in addition to all of its lines that aren't user configurable, so since we - // don't want to lose the selected extensions, we don't overwrite that file if it exists. - // - // TODO: Redo the Android controller system so that we don't have to extract these INIs. - String configDirectory = NativeLibrary.GetUserDirectory() + File.separator + "Config"; - String profileDirectory = - NativeLibrary.GetUserDirectory() + File.separator + "Config/Profiles/Wiimote/"; - createWiimoteProfileDirectory(profileDirectory); - - copyAsset("GCPadNew.ini", new File(configDirectory, "GCPadNew.ini"), true, context); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - boolean overwriteWiimoteIni = prefs.getInt("WiimoteNewVersion", 0) != WiimoteNewVersion; - boolean wiimoteIniWritten = copyAsset("WiimoteNew.ini", - new File(configDirectory, "WiimoteNew.ini"), overwriteWiimoteIni, context); - if (overwriteWiimoteIni) - { - SharedPreferences.Editor sPrefsEditor = prefs.edit(); - sPrefsEditor.putInt("WiimoteNewVersion", WiimoteNewVersion); - sPrefsEditor.apply(); - } - - copyAsset("WiimoteProfile.ini", new File(profileDirectory, "WiimoteProfile.ini"), true, - context); - - return wiimoteIniWritten; - } - private static void deleteDirectoryRecursively(@NonNull final File file) { if (file.isDirectory()) @@ -322,18 +286,6 @@ public final class DirectoryInitialization } } - private static void createWiimoteProfileDirectory(String directory) - { - File wiiPath = new File(directory); - if (!wiiPath.isDirectory()) - { - if (!wiiPath.mkdirs()) - { - Log.error("[DirectoryInitialization] Failed to create folder " + wiiPath.getAbsolutePath()); - } - } - } - public static boolean preferOldFolderPicker(Context context) { // As of January 2021, ACTION_OPEN_DOCUMENT_TREE seems to be broken on the Nvidia Shield TV @@ -433,7 +385,5 @@ public final class DirectoryInitialization } } - private static native void CreateUserDirectories(); - private static native void SetSysDirectory(String path); } diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 95297664db..375d470eb5 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -355,12 +355,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_DirectoryInitializat File::SetSysDirectory(path); } -JNIEXPORT void JNICALL -Java_org_dolphinemu_dolphinemu_utils_DirectoryInitialization_CreateUserDirectories(JNIEnv*, jclass) -{ - UICommon::CreateDirectories(); -} - JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory( JNIEnv* env, jclass, jstring jDirectory) { @@ -510,6 +504,7 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_UpdateGCAdapterScanThread(JNIEnv*, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Initialize(JNIEnv*, jclass) { + UICommon::CreateDirectories(); Common::RegisterMsgAlertHandler(&MsgAlert); Common::AndroidSetReportHandler(&ReportSend); DolphinAnalytics::AndroidSetGetValFunc(&GetAnalyticValue); From 304d8989660c798156df58334c274ccfa75072b0 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 30 Dec 2021 18:40:00 +0100 Subject: [PATCH 15/38] Android: Remove copyAsset's overwrite parameter Because of the previous commit, this is no longer needed. --- .../utils/DirectoryInitialization.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java index c4bfdbd7bf..cf85e12740 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java @@ -145,7 +145,7 @@ public final class DirectoryInitialization // There is no extracted Sys directory, or there is a Sys directory from another // version of Dolphin that might contain outdated files. Let's (re-)extract Sys. deleteDirectoryRecursively(sysDirectory); - copyAssetFolder("Sys", sysDirectory, true, context); + copyAssetFolder("Sys", sysDirectory, context); SharedPreferences.Editor editor = preferences.edit(); editor.putString("sysDirectoryVersion", revision); @@ -209,21 +209,18 @@ public final class DirectoryInitialization return new File(context.getExternalCacheDir(), "gamelist.cache"); } - private static boolean copyAsset(String asset, File output, Boolean overwrite, Context context) + private static boolean copyAsset(String asset, File output, Context context) { Log.verbose("[DirectoryInitialization] Copying File " + asset + " to " + output); try { - if (!output.exists() || overwrite) + try (InputStream in = context.getAssets().open(asset)) { - try (InputStream in = context.getAssets().open(asset)) + try (OutputStream out = new FileOutputStream(output)) { - try (OutputStream out = new FileOutputStream(output)) - { - copyFile(in, out); - return true; - } + copyFile(in, out); + return true; } } } @@ -235,8 +232,7 @@ public final class DirectoryInitialization return false; } - private static void copyAssetFolder(String assetFolder, File outputFolder, Boolean overwrite, - Context context) + private static void copyAssetFolder(String assetFolder, File outputFolder, Context context) { Log.verbose("[DirectoryInitialization] Copying Folder " + assetFolder + " to " + outputFolder); @@ -263,9 +259,8 @@ public final class DirectoryInitialization createdFolder = true; } copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file), - overwrite, context); - copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), overwrite, context); + copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), context); } } catch (IOException e) From dd8976f18dd5fb0c44fb40f3a24803f6cc5009b2 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 23 Jan 2022 21:47:57 +0100 Subject: [PATCH 16/38] Android: Remove old input mapping implementation This old code is of no use now that ButtonManager is gone. --- .../dolphinemu/dialogs/MotionAlertDialog.java | 6 +- .../settings/model/LegacyFloatSetting.java | 28 - .../features/settings/model/Settings.java | 54 -- .../model/WiimoteProfileBooleanSetting.java | 70 --- .../model/WiimoteProfileStringSetting.java | 70 --- .../ui/SettingsFragmentPresenter.java | 492 +----------------- .../features/settings/utils/SettingsFile.java | 158 ------ .../utils/ControllerMappingHelper.java | 65 --- .../app/src/main/res/values/strings.xml | 93 ---- 9 files changed, 5 insertions(+), 1031 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/LegacyFloatSetting.java delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileBooleanSetting.java delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileStringSetting.java delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java index b0316f94cc..a111155181 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java @@ -12,7 +12,6 @@ import androidx.appcompat.app.AlertDialog; import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; -import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.Log; import java.util.ArrayList; @@ -50,7 +49,7 @@ public final class MotionAlertDialog extends AlertDialog Log.debug("[MotionAlertDialog] Received key event: " + event.getAction()); if (event.getAction() == KeyEvent.ACTION_UP) { - if (!ControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode)) + if (true) { setting.onKeyInput(mAdapter.getSettings(), event); dismiss(); @@ -116,8 +115,7 @@ public final class MotionAlertDialog extends AlertDialog { InputDevice.MotionRange range = motionRanges.get(i); int axis = range.getAxis(); - float origValue = event.getAxisValue(axis); - float value = ControllerMappingHelper.scaleAxis(input, axis, origValue); + float value = event.getAxisValue(axis); if (firstEvent) { mPreviousValues.add(value); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/LegacyFloatSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/LegacyFloatSetting.java deleted file mode 100644 index 0b92cba802..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/LegacyFloatSetting.java +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public class LegacyFloatSetting extends AbstractLegacySetting implements AbstractFloatSetting -{ - private final float mDefaultValue; - - public LegacyFloatSetting(String file, String section, String key, float defaultValue) - { - super(file, section, key); - mDefaultValue = defaultValue; - } - - @Override - public float getFloat(@NonNull Settings settings) - { - return settings.getSection(mFile, mSection).getFloat(mKey, mDefaultValue); - } - - @Override - public void setFloat(@NonNull Settings settings, float newValue) - { - settings.getSection(mFile, mSection).setFloat(mKey, newValue); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java index ebdaf70691..7d3d5a5b19 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java @@ -24,7 +24,6 @@ public class Settings implements Closeable public static final String FILE_SYSCONF = "SYSCONF"; public static final String FILE_GFX = "GFX"; public static final String FILE_LOGGER = "Logger"; - public static final String FILE_GCPAD = "GCPadNew"; public static final String FILE_WIIMOTE = "WiimoteNew"; public static final String SECTION_INI_ANDROID = "Android"; @@ -46,8 +45,6 @@ public class Settings implements Closeable public static final String SECTION_STEREOSCOPY = "Stereoscopy"; - public static final String SECTION_WIIMOTE = "Wiimote"; - public static final String SECTION_BINDINGS = "Android"; public static final String SECTION_CONTROLS = "Controls"; public static final String SECTION_PROFILE = "Profile"; @@ -64,7 +61,6 @@ public class Settings implements Closeable FILE_WIIMOTE}; private Map mIniFiles = new HashMap<>(); - private final Map mWiimoteProfileFiles = new HashMap<>(); private boolean mLoadedRecursiveIsoPathsValue = false; @@ -99,50 +95,6 @@ public class Settings implements Closeable return mIsWii; } - public IniFile getWiimoteProfile(String profile, int padID) - { - IniFile wiimoteProfileIni = mWiimoteProfileFiles.computeIfAbsent(profile, profileComputed -> - { - IniFile newIni = new IniFile(); - newIni.load(SettingsFile.getWiiProfile(profileComputed), false); - return newIni; - }); - - if (!wiimoteProfileIni.exists(SECTION_PROFILE)) - { - String defaultWiiProfilePath = DirectoryInitialization.getUserDirectory() + - "/Config/Profiles/Wiimote/WiimoteProfile.ini"; - - wiimoteProfileIni.load(defaultWiiProfilePath, false); - - wiimoteProfileIni - .setString(SECTION_PROFILE, "Device", "Android/" + (padID + 4) + "/Touchscreen"); - } - - return wiimoteProfileIni; - } - - public void enableWiimoteProfile(Settings settings, String profile, String profileKey) - { - getWiimoteControlsSection(settings).setString(profileKey, profile); - } - - public boolean disableWiimoteProfile(Settings settings, String profileKey) - { - return getWiimoteControlsSection(settings).delete(profileKey); - } - - public boolean isWiimoteProfileEnabled(Settings settings, String profile, - String profileKey) - { - return profile.equals(getWiimoteControlsSection(settings).getString(profileKey, "")); - } - - private IniFile.Section getWiimoteControlsSection(Settings settings) - { - return settings.getSection(GAME_SETTINGS_PLACEHOLDER_FILE_NAME, SECTION_CONTROLS); - } - public int getWriteLayer() { return isGameSpecific() ? NativeConfig.LAYER_LOCAL_GAME : NativeConfig.LAYER_BASE_OR_CURRENT; @@ -224,7 +176,6 @@ public class Settings implements Closeable { // Notify the native code of the changes to legacy settings NativeLibrary.ReloadConfig(); - NativeLibrary.ReloadWiimoteConfig(); } // LogManager does use the new config system, but doesn't pick up on changes automatically @@ -251,11 +202,6 @@ public class Settings implements Closeable NativeConfig.save(NativeConfig.LAYER_LOCAL_GAME); } - - for (Map.Entry entry : mWiimoteProfileFiles.entrySet()) - { - entry.getValue().save(SettingsFile.getWiiProfile(entry.getKey())); - } } public void clearSettings() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileBooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileBooleanSetting.java deleted file mode 100644 index 1705e969c4..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileBooleanSetting.java +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; - -// This stuff is pretty ugly. It's a kind of workaround for certain controller settings -// not actually being available as game-specific settings. -public class WiimoteProfileBooleanSetting implements AbstractBooleanSetting -{ - private final int mPadID; - - private final String mSection; - private final String mKey; - private final boolean mDefaultValue; - - private final String mProfileKey; - - private final String mProfile; - - public WiimoteProfileBooleanSetting(String gameID, int padID, String section, String key, - boolean defaultValue) - { - mPadID = padID; - mSection = section; - mKey = key; - mDefaultValue = defaultValue; - - mProfileKey = SettingsFile.KEY_WIIMOTE_PROFILE + (padID + 1); - - mProfile = gameID + "_Wii" + padID; - } - - @Override - public boolean isOverridden(@NonNull Settings settings) - { - return settings.isWiimoteProfileEnabled(settings, mProfile, mProfileKey); - } - - @Override - public boolean isRuntimeEditable() - { - return false; - } - - @Override - public boolean delete(@NonNull Settings settings) - { - return settings.disableWiimoteProfile(settings, mProfileKey); - } - - @Override - public boolean getBoolean(@NonNull Settings settings) - { - if (settings.isWiimoteProfileEnabled(settings, mProfile, mProfileKey)) - return settings.getWiimoteProfile(mProfile, mPadID).getBoolean(mSection, mKey, mDefaultValue); - else - return mDefaultValue; - } - - @Override - public void setBoolean(@NonNull Settings settings, boolean newValue) - { - settings.getWiimoteProfile(mProfile, mPadID).setBoolean(mSection, mKey, newValue); - - settings.enableWiimoteProfile(settings, mProfile, mProfileKey); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileStringSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileStringSetting.java deleted file mode 100644 index c62df5638d..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileStringSetting.java +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; - -// This stuff is pretty ugly. It's a kind of workaround for certain controller settings -// not actually being available as game-specific settings. -public class WiimoteProfileStringSetting implements AbstractStringSetting -{ - private final int mPadID; - - private final String mSection; - private final String mKey; - private final String mDefaultValue; - - private final String mProfileKey; - - private final String mProfile; - - public WiimoteProfileStringSetting(String gameID, int padID, String section, String key, - String defaultValue) - { - mPadID = padID; - mSection = section; - mKey = key; - mDefaultValue = defaultValue; - - mProfileKey = SettingsFile.KEY_WIIMOTE_PROFILE + (padID + 1); - - mProfile = gameID + "_Wii" + padID; - } - - @Override - public boolean isOverridden(@NonNull Settings settings) - { - return settings.isWiimoteProfileEnabled(settings, mProfile, mProfileKey); - } - - @Override - public boolean isRuntimeEditable() - { - return false; - } - - @Override - public boolean delete(@NonNull Settings settings) - { - return settings.disableWiimoteProfile(settings, mProfileKey); - } - - @NonNull @Override - public String getString(@NonNull Settings settings) - { - if (settings.isWiimoteProfileEnabled(settings, mProfile, mProfileKey)) - return settings.getWiimoteProfile(mProfile, mPadID).getString(mSection, mKey, mDefaultValue); - else - return mDefaultValue; - } - - @Override - public void setString(@NonNull Settings settings, @NonNull String newValue) - { - settings.getWiimoteProfile(mProfile, mPadID).setString(mSection, mKey, newValue); - - settings.enableWiimoteProfile(settings, mProfile, mProfileKey); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 3604d53087..9d1a783979 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -16,36 +16,30 @@ import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.UserDataActivity; import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting; import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting; import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; -import org.dolphinemu.dolphinemu.features.settings.model.LegacyStringSetting; import org.dolphinemu.dolphinemu.features.settings.model.PostProcessing; import org.dolphinemu.dolphinemu.features.settings.model.ScaledIntSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.WiimoteProfileStringSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.HyperLinkHeaderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.InvertedSwitchSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.LogSwitchSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.PercentSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.RunRunnable; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions; import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting; -import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.utils.BooleanSupplier; import org.dolphinemu.dolphinemu.utils.EGLHelper; @@ -1079,62 +1073,7 @@ public final class SettingsFragmentPresenter { if (gcPadType == 6) // Emulated { - sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_A + gcPadNumber, R.string.button_a, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_B + gcPadNumber, R.string.button_b, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_X + gcPadNumber, R.string.button_x, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_Y + gcPadNumber, R.string.button_y, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_Z + gcPadNumber, R.string.button_z, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_START + gcPadNumber, R.string.button_start, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.controller_control, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_CONTROL_UP + gcPadNumber, R.string.generic_up, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_CONTROL_DOWN + gcPadNumber, R.string.generic_down, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_CONTROL_LEFT + gcPadNumber, R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_CONTROL_RIGHT + gcPadNumber, R.string.generic_right, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.controller_c, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_C_UP + gcPadNumber, R.string.generic_up, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_C_DOWN + gcPadNumber, R.string.generic_down, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_C_LEFT + gcPadNumber, R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_C_RIGHT + gcPadNumber, R.string.generic_right, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.controller_trig, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_TRIGGER_L + gcPadNumber, R.string.trigger_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_TRIGGER_R + gcPadNumber, R.string.trigger_right, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.controller_dpad, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_DPAD_UP + gcPadNumber, R.string.generic_up, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_DPAD_DOWN + gcPadNumber, R.string.generic_down, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_DPAD_LEFT + gcPadNumber, R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_GCBIND_DPAD_RIGHT + gcPadNumber, R.string.generic_right, mGameID)); - - - sl.add(new HeaderSetting(mContext, R.string.emulation_control_rumble, 0)); - sl.add(new RumbleBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_EMU_RUMBLE + gcPadNumber, R.string.emulation_control_rumble, - mGameID)); + // TODO } else if (gcPadType == 12) // Adapter { @@ -1147,438 +1086,13 @@ public final class SettingsFragmentPresenter private void addWiimoteSubSettings(ArrayList sl, int wiimoteNumber) { - // Bindings use controller numbers 4-7 (0-3 are GameCube), but the extension setting uses 1-4. - // But game specific extension settings are saved in their own profile. These profiles - // do not have any way to specify the controller that is loaded outside of knowing the filename - // of the profile that was loaded. - AbstractStringSetting extension; - final String defaultExtension = "None"; - if (mGameID.isEmpty()) - { - extension = new LegacyStringSetting(Settings.FILE_WIIMOTE, - Settings.SECTION_WIIMOTE + (wiimoteNumber - 3), SettingsFile.KEY_WIIMOTE_EXTENSION, - defaultExtension); - } - else - { - extension = new WiimoteProfileStringSetting(mGameID, wiimoteNumber - 4, - Settings.SECTION_PROFILE, SettingsFile.KEY_WIIMOTE_EXTENSION, defaultExtension); - } - - sl.add(new StringSingleChoiceSetting(mContext, extension, R.string.wiimote_extensions, 0, - R.array.wiimoteExtensionsEntries, R.array.wiimoteExtensionsValues, - MenuTag.getWiimoteExtensionMenuTag(wiimoteNumber))); - - sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_A + wiimoteNumber, R.string.button_a, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_B + wiimoteNumber, R.string.button_b, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_1 + wiimoteNumber, R.string.button_one, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_2 + wiimoteNumber, R.string.button_two, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_MINUS + wiimoteNumber, R.string.button_minus, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_PLUS + wiimoteNumber, R.string.button_plus, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_HOME + wiimoteNumber, R.string.button_home, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.wiimote_ir, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_IR_UP + wiimoteNumber, R.string.generic_up, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_IR_DOWN + wiimoteNumber, R.string.generic_down, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_IR_LEFT + wiimoteNumber, R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_IR_RIGHT + wiimoteNumber, R.string.generic_right, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_IR_FORWARD + wiimoteNumber, R.string.generic_forward, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_IR_BACKWARD + wiimoteNumber, R.string.generic_backward, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_IR_HIDE + wiimoteNumber, R.string.ir_hide, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.wiimote_swing, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SWING_UP + wiimoteNumber, R.string.generic_up, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SWING_DOWN + wiimoteNumber, R.string.generic_down, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SWING_LEFT + wiimoteNumber, R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SWING_RIGHT + wiimoteNumber, R.string.generic_right, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SWING_FORWARD + wiimoteNumber, R.string.generic_forward, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SWING_BACKWARD + wiimoteNumber, R.string.generic_backward, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.wiimote_tilt, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TILT_FORWARD + wiimoteNumber, R.string.generic_forward, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TILT_BACKWARD + wiimoteNumber, R.string.generic_backward, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TILT_LEFT + wiimoteNumber, R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TILT_RIGHT + wiimoteNumber, R.string.generic_right, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TILT_MODIFIER + wiimoteNumber, R.string.tilt_modifier, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.wiimote_shake, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SHAKE_X + wiimoteNumber, R.string.shake_x, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SHAKE_Y + wiimoteNumber, R.string.shake_y, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_SHAKE_Z + wiimoteNumber, R.string.shake_z, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.controller_dpad, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DPAD_UP + wiimoteNumber, R.string.generic_up, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DPAD_DOWN + wiimoteNumber, R.string.generic_down, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DPAD_LEFT + wiimoteNumber, R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DPAD_RIGHT + wiimoteNumber, R.string.generic_right, mGameID)); - - - sl.add(new HeaderSetting(mContext, R.string.emulation_control_rumble, 0)); - sl.add(new RumbleBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_EMU_RUMBLE + wiimoteNumber, R.string.emulation_control_rumble, - mGameID)); + // TODO } private void addExtensionTypeSettings(ArrayList sl, int wiimoteNumber, int extentionType) { - switch (extentionType) - { - case 1: // Nunchuk - sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_C + wiimoteNumber, R.string.nunchuk_button_c, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_Z + wiimoteNumber, R.string.button_z, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.generic_stick, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_UP + wiimoteNumber, R.string.generic_up, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_DOWN + wiimoteNumber, R.string.generic_down, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_RIGHT + wiimoteNumber, R.string.generic_right, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.wiimote_swing, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_UP + wiimoteNumber, R.string.generic_up, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_DOWN + wiimoteNumber, R.string.generic_down, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_RIGHT + wiimoteNumber, - R.string.generic_right, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_FORWARD + wiimoteNumber, - R.string.generic_forward, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_BACKWARD + wiimoteNumber, - R.string.generic_backward, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.wiimote_tilt, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_FORWARD + wiimoteNumber, - R.string.generic_forward, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_BACKWARD + wiimoteNumber, - R.string.generic_backward, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_RIGHT + wiimoteNumber, R.string.generic_right, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_MODIFIER + wiimoteNumber, - R.string.tilt_modifier, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.wiimote_shake, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SHAKE_X + wiimoteNumber, R.string.shake_x, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SHAKE_Y + wiimoteNumber, R.string.shake_y, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_NUNCHUK_SHAKE_Z + wiimoteNumber, R.string.shake_z, - mGameID)); - break; - case 2: // Classic - sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_A + wiimoteNumber, R.string.button_a, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_B + wiimoteNumber, R.string.button_b, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_X + wiimoteNumber, R.string.button_x, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_Y + wiimoteNumber, R.string.button_y, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_ZL + wiimoteNumber, R.string.classic_button_zl, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_ZR + wiimoteNumber, R.string.classic_button_zr, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_MINUS + wiimoteNumber, R.string.button_minus, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_PLUS + wiimoteNumber, R.string.button_plus, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_HOME + wiimoteNumber, R.string.button_home, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.classic_leftstick, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_LEFT_UP + wiimoteNumber, R.string.generic_up, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_LEFT_DOWN + wiimoteNumber, R.string.generic_down, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_LEFT_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_LEFT_RIGHT + wiimoteNumber, R.string.generic_right, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.classic_rightstick, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_RIGHT_UP + wiimoteNumber, R.string.generic_up, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_RIGHT_DOWN + wiimoteNumber, R.string.generic_down, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_RIGHT_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_RIGHT_RIGHT + wiimoteNumber, - R.string.generic_right, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.controller_trig, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_TRIGGER_L + wiimoteNumber, R.string.trigger_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_TRIGGER_R + wiimoteNumber, R.string.trigger_right, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.controller_dpad, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_DPAD_UP + wiimoteNumber, R.string.generic_up, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_DPAD_DOWN + wiimoteNumber, R.string.generic_down, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_DPAD_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_CLASSIC_DPAD_RIGHT + wiimoteNumber, R.string.generic_right, - mGameID)); - break; - case 3: // Guitar - sl.add(new HeaderSetting(mContext, R.string.guitar_frets, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_FRET_GREEN + wiimoteNumber, R.string.generic_green, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_FRET_RED + wiimoteNumber, R.string.generic_red, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_FRET_YELLOW + wiimoteNumber, - R.string.generic_yellow, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_FRET_BLUE + wiimoteNumber, R.string.generic_blue, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_FRET_ORANGE + wiimoteNumber, - R.string.generic_orange, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.guitar_strum, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_STRUM_UP + wiimoteNumber, R.string.generic_up, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_STRUM_DOWN + wiimoteNumber, R.string.generic_down, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_MINUS + wiimoteNumber, R.string.button_minus, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_PLUS + wiimoteNumber, R.string.button_plus, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.generic_stick, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_STICK_UP + wiimoteNumber, R.string.generic_up, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_STICK_DOWN + wiimoteNumber, R.string.generic_down, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_STICK_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_STICK_RIGHT + wiimoteNumber, R.string.generic_right, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.guitar_whammy, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_GUITAR_WHAMMY_BAR + wiimoteNumber, R.string.generic_right, - mGameID)); - break; - case 4: // Drums - sl.add(new HeaderSetting(mContext, R.string.drums_pads, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_PAD_RED + wiimoteNumber, R.string.generic_red, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_PAD_YELLOW + wiimoteNumber, R.string.generic_yellow, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_PAD_BLUE + wiimoteNumber, R.string.generic_blue, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_PAD_GREEN + wiimoteNumber, R.string.generic_green, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_PAD_ORANGE + wiimoteNumber, R.string.generic_orange, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_PAD_BASS + wiimoteNumber, R.string.drums_pad_bass, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.generic_stick, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_STICK_UP + wiimoteNumber, R.string.generic_up, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_STICK_DOWN + wiimoteNumber, R.string.generic_down, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_STICK_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_STICK_RIGHT + wiimoteNumber, R.string.generic_right, - mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_MINUS + wiimoteNumber, R.string.button_minus, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_DRUMS_PLUS + wiimoteNumber, R.string.button_plus, - mGameID)); - break; - case 5: // Turntable - sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_GREEN_LEFT + wiimoteNumber, - R.string.turntable_button_green_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_RED_LEFT + wiimoteNumber, - R.string.turntable_button_red_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_BLUE_LEFT + wiimoteNumber, - R.string.turntable_button_blue_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_GREEN_RIGHT + wiimoteNumber, - R.string.turntable_button_green_right, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_RED_RIGHT + wiimoteNumber, - R.string.turntable_button_red_right, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_BLUE_RIGHT + wiimoteNumber, - R.string.turntable_button_blue_right, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_MINUS + wiimoteNumber, - R.string.button_minus, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_PLUS + wiimoteNumber, - R.string.button_plus, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_EUPHORIA + wiimoteNumber, - R.string.turntable_button_euphoria, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.turntable_table_left, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_LEFT_LEFT + wiimoteNumber, R.string.generic_left, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_LEFT_RIGHT + wiimoteNumber, - R.string.generic_right, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.turntable_table_right, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_RIGHT_LEFT + wiimoteNumber, - R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_RIGHT_RIGHT + wiimoteNumber, - R.string.generic_right, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.generic_stick, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_STICK_UP + wiimoteNumber, R.string.generic_up, - mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_STICK_DOWN + wiimoteNumber, - R.string.generic_down, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_STICK_LEFT + wiimoteNumber, - R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_STICK_RIGHT + wiimoteNumber, - R.string.generic_right, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.turntable_effect, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_EFFECT_DIAL + wiimoteNumber, - R.string.turntable_effect_dial, mGameID)); - - sl.add(new HeaderSetting(mContext, R.string.turntable_crossfade, 0)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_CROSSFADE_LEFT + wiimoteNumber, - R.string.generic_left, mGameID)); - sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS, - SettingsFile.KEY_WIIBIND_TURNTABLE_CROSSFADE_RIGHT + wiimoteNumber, - R.string.generic_right, mGameID)); - break; - } + // TODO } private static int getLogVerbosityEntries() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java index 87f58b4d02..9362b4e980 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java @@ -22,158 +22,9 @@ public final class SettingsFile public static final String KEY_GCPAD_PLAYER_1 = "SIDevice0"; - public static final String KEY_GCBIND_A = "InputA_"; - public static final String KEY_GCBIND_B = "InputB_"; - public static final String KEY_GCBIND_X = "InputX_"; - public static final String KEY_GCBIND_Y = "InputY_"; - public static final String KEY_GCBIND_Z = "InputZ_"; - public static final String KEY_GCBIND_START = "InputStart_"; - public static final String KEY_GCBIND_CONTROL_UP = "MainUp_"; - public static final String KEY_GCBIND_CONTROL_DOWN = "MainDown_"; - public static final String KEY_GCBIND_CONTROL_LEFT = "MainLeft_"; - public static final String KEY_GCBIND_CONTROL_RIGHT = "MainRight_"; - public static final String KEY_GCBIND_C_UP = "CStickUp_"; - public static final String KEY_GCBIND_C_DOWN = "CStickDown_"; - public static final String KEY_GCBIND_C_LEFT = "CStickLeft_"; - public static final String KEY_GCBIND_C_RIGHT = "CStickRight_"; - public static final String KEY_GCBIND_TRIGGER_L = "InputL_"; - public static final String KEY_GCBIND_TRIGGER_R = "InputR_"; - public static final String KEY_GCBIND_DPAD_UP = "DPadUp_"; - public static final String KEY_GCBIND_DPAD_DOWN = "DPadDown_"; - public static final String KEY_GCBIND_DPAD_LEFT = "DPadLeft_"; - public static final String KEY_GCBIND_DPAD_RIGHT = "DPadRight_"; - - public static final String KEY_EMU_RUMBLE = "EmuRumble"; - - public static final String KEY_WIIMOTE_EXTENSION = "Extension"; - - // Controller keys for game specific settings - public static final String KEY_WIIMOTE_PROFILE = "WiimoteProfile"; - - public static final String KEY_WIIBIND_A = "WiimoteA_"; - public static final String KEY_WIIBIND_B = "WiimoteB_"; - public static final String KEY_WIIBIND_1 = "Wiimote1_"; - public static final String KEY_WIIBIND_2 = "Wiimote2_"; - public static final String KEY_WIIBIND_MINUS = "WiimoteMinus_"; - public static final String KEY_WIIBIND_PLUS = "WiimotePlus_"; - public static final String KEY_WIIBIND_HOME = "WiimoteHome_"; - public static final String KEY_WIIBIND_IR_UP = "IRUp_"; - public static final String KEY_WIIBIND_IR_DOWN = "IRDown_"; - public static final String KEY_WIIBIND_IR_LEFT = "IRLeft_"; - public static final String KEY_WIIBIND_IR_RIGHT = "IRRight_"; - public static final String KEY_WIIBIND_IR_FORWARD = "IRForward_"; - public static final String KEY_WIIBIND_IR_BACKWARD = "IRBackward_"; - public static final String KEY_WIIBIND_IR_HIDE = "IRHide_"; public static final String KEY_WIIBIND_IR_PITCH = "IRTotalPitch"; public static final String KEY_WIIBIND_IR_YAW = "IRTotalYaw"; public static final String KEY_WIIBIND_IR_VERTICAL_OFFSET = "IRVerticalOffset"; - public static final String KEY_WIIBIND_SWING_UP = "SwingUp_"; - public static final String KEY_WIIBIND_SWING_DOWN = "SwingDown_"; - public static final String KEY_WIIBIND_SWING_LEFT = "SwingLeft_"; - public static final String KEY_WIIBIND_SWING_RIGHT = "SwingRight_"; - public static final String KEY_WIIBIND_SWING_FORWARD = "SwingForward_"; - public static final String KEY_WIIBIND_SWING_BACKWARD = "SwingBackward_"; - public static final String KEY_WIIBIND_TILT_FORWARD = "TiltForward_"; - public static final String KEY_WIIBIND_TILT_BACKWARD = "TiltBackward_"; - public static final String KEY_WIIBIND_TILT_LEFT = "TiltLeft_"; - public static final String KEY_WIIBIND_TILT_RIGHT = "TiltRight_"; - public static final String KEY_WIIBIND_TILT_MODIFIER = "TiltModifier_"; - public static final String KEY_WIIBIND_SHAKE_X = "ShakeX_"; - public static final String KEY_WIIBIND_SHAKE_Y = "ShakeY_"; - public static final String KEY_WIIBIND_SHAKE_Z = "ShakeZ_"; - public static final String KEY_WIIBIND_DPAD_UP = "WiimoteUp_"; - public static final String KEY_WIIBIND_DPAD_DOWN = "WiimoteDown_"; - public static final String KEY_WIIBIND_DPAD_LEFT = "WiimoteLeft_"; - public static final String KEY_WIIBIND_DPAD_RIGHT = "WiimoteRight_"; - public static final String KEY_WIIBIND_NUNCHUK_C = "NunchukC_"; - public static final String KEY_WIIBIND_NUNCHUK_Z = "NunchukZ_"; - public static final String KEY_WIIBIND_NUNCHUK_UP = "NunchukUp_"; - public static final String KEY_WIIBIND_NUNCHUK_DOWN = "NunchukDown_"; - public static final String KEY_WIIBIND_NUNCHUK_LEFT = "NunchukLeft_"; - public static final String KEY_WIIBIND_NUNCHUK_RIGHT = "NunchukRight_"; - public static final String KEY_WIIBIND_NUNCHUK_SWING_UP = "NunchukSwingUp_"; - public static final String KEY_WIIBIND_NUNCHUK_SWING_DOWN = "NunchukSwingDown_"; - public static final String KEY_WIIBIND_NUNCHUK_SWING_LEFT = "NunchukSwingLeft_"; - public static final String KEY_WIIBIND_NUNCHUK_SWING_RIGHT = "NunchukSwingRight_"; - public static final String KEY_WIIBIND_NUNCHUK_SWING_FORWARD = "NunchukSwingForward_"; - public static final String KEY_WIIBIND_NUNCHUK_SWING_BACKWARD = "NunchukSwingBackward_"; - public static final String KEY_WIIBIND_NUNCHUK_TILT_FORWARD = "NunchukTiltForward_"; - public static final String KEY_WIIBIND_NUNCHUK_TILT_BACKWARD = "NunchukTiltBackward_"; - public static final String KEY_WIIBIND_NUNCHUK_TILT_LEFT = "NunchukTiltLeft_"; - public static final String KEY_WIIBIND_NUNCHUK_TILT_RIGHT = "NunchukTiltRight_"; - public static final String KEY_WIIBIND_NUNCHUK_TILT_MODIFIER = "NunchukTiltModifier_"; - public static final String KEY_WIIBIND_NUNCHUK_SHAKE_X = "NunchukShakeX_"; - public static final String KEY_WIIBIND_NUNCHUK_SHAKE_Y = "NunchukShakeY_"; - public static final String KEY_WIIBIND_NUNCHUK_SHAKE_Z = "NunchukShakeZ_"; - public static final String KEY_WIIBIND_CLASSIC_A = "ClassicA_"; - public static final String KEY_WIIBIND_CLASSIC_B = "ClassicB_"; - public static final String KEY_WIIBIND_CLASSIC_X = "ClassicX_"; - public static final String KEY_WIIBIND_CLASSIC_Y = "ClassicY_"; - public static final String KEY_WIIBIND_CLASSIC_ZL = "ClassicZL_"; - public static final String KEY_WIIBIND_CLASSIC_ZR = "ClassicZR_"; - public static final String KEY_WIIBIND_CLASSIC_MINUS = "ClassicMinus_"; - public static final String KEY_WIIBIND_CLASSIC_PLUS = "ClassicPlus_"; - public static final String KEY_WIIBIND_CLASSIC_HOME = "ClassicHome_"; - public static final String KEY_WIIBIND_CLASSIC_LEFT_UP = "ClassicLeftStickUp_"; - public static final String KEY_WIIBIND_CLASSIC_LEFT_DOWN = "ClassicLeftStickDown_"; - public static final String KEY_WIIBIND_CLASSIC_LEFT_LEFT = "ClassicLeftStickLeft_"; - public static final String KEY_WIIBIND_CLASSIC_LEFT_RIGHT = "ClassicLeftStickRight_"; - public static final String KEY_WIIBIND_CLASSIC_RIGHT_UP = "ClassicRightStickUp_"; - public static final String KEY_WIIBIND_CLASSIC_RIGHT_DOWN = "ClassicRightStickDown_"; - public static final String KEY_WIIBIND_CLASSIC_RIGHT_LEFT = "ClassicRightStickLeft_"; - public static final String KEY_WIIBIND_CLASSIC_RIGHT_RIGHT = "ClassicRightStickRight_"; - public static final String KEY_WIIBIND_CLASSIC_TRIGGER_L = "ClassicTriggerL_"; - public static final String KEY_WIIBIND_CLASSIC_TRIGGER_R = "ClassicTriggerR_"; - public static final String KEY_WIIBIND_CLASSIC_DPAD_UP = "ClassicUp_"; - public static final String KEY_WIIBIND_CLASSIC_DPAD_DOWN = "ClassicDown_"; - public static final String KEY_WIIBIND_CLASSIC_DPAD_LEFT = "ClassicLeft_"; - public static final String KEY_WIIBIND_CLASSIC_DPAD_RIGHT = "ClassicRight_"; - public static final String KEY_WIIBIND_GUITAR_FRET_GREEN = "GuitarGreen_"; - public static final String KEY_WIIBIND_GUITAR_FRET_RED = "GuitarRed_"; - public static final String KEY_WIIBIND_GUITAR_FRET_YELLOW = "GuitarYellow_"; - public static final String KEY_WIIBIND_GUITAR_FRET_BLUE = "GuitarBlue_"; - public static final String KEY_WIIBIND_GUITAR_FRET_ORANGE = "GuitarOrange_"; - public static final String KEY_WIIBIND_GUITAR_STRUM_UP = "GuitarStrumUp_"; - public static final String KEY_WIIBIND_GUITAR_STRUM_DOWN = "GuitarStrumDown_"; - public static final String KEY_WIIBIND_GUITAR_MINUS = "GuitarMinus_"; - public static final String KEY_WIIBIND_GUITAR_PLUS = "GuitarPlus_"; - public static final String KEY_WIIBIND_GUITAR_STICK_UP = "GuitarUp_"; - public static final String KEY_WIIBIND_GUITAR_STICK_DOWN = "GuitarDown_"; - public static final String KEY_WIIBIND_GUITAR_STICK_LEFT = "GuitarLeft_"; - public static final String KEY_WIIBIND_GUITAR_STICK_RIGHT = "GuitarRight_"; - public static final String KEY_WIIBIND_GUITAR_WHAMMY_BAR = "GuitarWhammy_"; - public static final String KEY_WIIBIND_DRUMS_PAD_RED = "DrumsRed_"; - public static final String KEY_WIIBIND_DRUMS_PAD_YELLOW = "DrumsYellow_"; - public static final String KEY_WIIBIND_DRUMS_PAD_BLUE = "DrumsBlue_"; - public static final String KEY_WIIBIND_DRUMS_PAD_GREEN = "DrumsGreen_"; - public static final String KEY_WIIBIND_DRUMS_PAD_ORANGE = "DrumsOrange_"; - public static final String KEY_WIIBIND_DRUMS_PAD_BASS = "DrumsBass_"; - public static final String KEY_WIIBIND_DRUMS_MINUS = "DrumsMinus_"; - public static final String KEY_WIIBIND_DRUMS_PLUS = "DrumsPlus_"; - public static final String KEY_WIIBIND_DRUMS_STICK_UP = "DrumsUp_"; - public static final String KEY_WIIBIND_DRUMS_STICK_DOWN = "DrumsDown_"; - public static final String KEY_WIIBIND_DRUMS_STICK_LEFT = "DrumsLeft_"; - public static final String KEY_WIIBIND_DRUMS_STICK_RIGHT = "DrumsRight_"; - public static final String KEY_WIIBIND_TURNTABLE_GREEN_LEFT = "TurntableGreenLeft_"; - public static final String KEY_WIIBIND_TURNTABLE_RED_LEFT = "TurntableRedLeft_"; - public static final String KEY_WIIBIND_TURNTABLE_BLUE_LEFT = "TurntableBlueLeft_"; - public static final String KEY_WIIBIND_TURNTABLE_GREEN_RIGHT = "TurntableGreenRight_"; - public static final String KEY_WIIBIND_TURNTABLE_RED_RIGHT = "TurntableRedRight_"; - public static final String KEY_WIIBIND_TURNTABLE_BLUE_RIGHT = "TurntableBlueRight_"; - public static final String KEY_WIIBIND_TURNTABLE_MINUS = "TurntableMinus_"; - public static final String KEY_WIIBIND_TURNTABLE_PLUS = "TurntablePlus_"; - public static final String KEY_WIIBIND_TURNTABLE_EUPHORIA = "TurntableEuphoria_"; - public static final String KEY_WIIBIND_TURNTABLE_LEFT_LEFT = "TurntableLeftLeft_"; - public static final String KEY_WIIBIND_TURNTABLE_LEFT_RIGHT = "TurntableLeftRight_"; - public static final String KEY_WIIBIND_TURNTABLE_RIGHT_LEFT = "TurntableRightLeft_"; - public static final String KEY_WIIBIND_TURNTABLE_RIGHT_RIGHT = "TurntableRightRight_"; - public static final String KEY_WIIBIND_TURNTABLE_STICK_UP = "TurntableUp_"; - public static final String KEY_WIIBIND_TURNTABLE_STICK_DOWN = "TurntableDown_"; - public static final String KEY_WIIBIND_TURNTABLE_STICK_LEFT = "TurntableLeft_"; - public static final String KEY_WIIBIND_TURNTABLE_STICK_RIGHT = "TurntableRight_"; - public static final String KEY_WIIBIND_TURNTABLE_EFFECT_DIAL = "TurntableEffDial_"; - public static final String KEY_WIIBIND_TURNTABLE_CROSSFADE_LEFT = "TurntableCrossLeft_"; - public static final String KEY_WIIBIND_TURNTABLE_CROSSFADE_RIGHT = "TurntableCrossRight_"; private static BiMap sectionsMap = new BiMap<>(); @@ -283,13 +134,4 @@ public final class SettingsFile return new File( DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini"); } - - public static File getWiiProfile(String profile) - { - String wiiConfigPath = - DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" + - profile + ".ini"; - - return new File(wiiConfigPath); - } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java deleted file mode 100644 index 9e31a46a96..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.utils; - -import android.view.InputDevice; -import android.view.KeyEvent; -import android.view.MotionEvent; - -/** - * Some controllers have incorrect mappings. This class has special-case fixes for them. - */ -public class ControllerMappingHelper -{ - /** - * Some controllers report extra button presses that can be ignored. - */ - public static boolean shouldKeyBeIgnored(InputDevice inputDevice, int keyCode) - { - if (isDualShock4(inputDevice)) - { - // The two analog triggers generate analog motion events as well as a keycode. - // We always prefer to use the analog values, so throw away the button press - return keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2; - } - return false; - } - - /** - * Scale an axis to be zero-centered with a proper range. - */ - public static float scaleAxis(InputDevice inputDevice, int axis, float value) - { - if (isDualShock4(inputDevice)) - { - // Android doesn't have correct mappings for this controller's triggers. It reports them - // as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0] - // Scale them to properly zero-centered with a range of [0.0, 1.0]. - if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) - { - return (value + 1) / 2.0f; - } - } - else if (isXboxOneWireless(inputDevice)) - { - // Same as the DualShock 4, the mappings are missing. - if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) - { - return (value + 1) / 2.0f; - } - } - return value; - } - - private static boolean isDualShock4(InputDevice inputDevice) - { - // Sony DualShock 4 controller - return inputDevice.getVendorId() == 0x54c && inputDevice.getProductId() == 0x9cc; - } - - private static boolean isXboxOneWireless(InputDevice inputDevice) - { - // Microsoft Xbox One controller - return inputDevice.getVendorId() == 0x45e && inputDevice.getProductId() == 0x2e0; - } -} diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index e81092cbb9..cca14f13a8 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -11,14 +11,6 @@ GameCube Controller 3 GameCube Controller 4 - Control Stick - C Stick - Triggers - D-Pad - Modifier Range - Analog Radius (High value = High sensitivity) - Analog Threshold (Low value = High sensitivity) - Wii Remote 1 Wii Remote 2 Wii Remote 3 @@ -30,85 +22,9 @@ Wii Remote Extension 4 Extension - Choose and bind the Wii Remote extension. - IR - Swing - Tilt - Shake Input Binding Press or move an input to bind it to %1$s. - Press or move any input to set rumble. - - - Buttons - Up - Down - Left - Right - Forward - Backward - Stick - Green - Red - Yellow - Blue - Orange - - - A - B - START - X - Y - Z - L - R - - - 1 - 2 - - - + - HOME - Hide - Modifier - X - Y - Z - - - C - - - ZL - ZR - Left Stick - Right Stick - - - Frets - Strum - Whammy - Bar - - - Pads - Bass - - - Green Left - Red Left - Blue Left - Green Right - Red Right - Blue Right - Euphoria - Table Left - Table Right - Effect - Dial - Crossfade Settings @@ -528,7 +444,6 @@ Save and Exit Game Properties Game Properties: %1$s - Extension Bindings Junk Data Found The settings file for this game contains extraneous data added by an old version of Dolphin. This will likely prevent global settings from working as intended.\n\nWould you like to fix this by deleting the settings file for this game? All game-specific settings and cheats that you have added will be removed. This cannot be undone. @@ -626,7 +541,6 @@ It can efficiently compress both junk data and encrypted Wii data. Opacity Adjust Controls Relative Stick Center - Rumble Choose Controller Press Back to access the menu.\nLong press Back to exit emulation. Reset Overlay @@ -643,13 +557,6 @@ It can efficiently compress both junk data and encrypted Wii data. Bongo Controller Enable this if you are using bongos on this port. - - General - Controllers - - - Device rumble not found - Due to the Scoped Storage policy in Android 11 and newer, you can\'t change this path. Loading Settings… This setting can\'t be changed while a game is running. From 2c529b9db12a60eb3640975f6ea4802237e118fa Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 23 Jan 2022 21:47:57 +0100 Subject: [PATCH 17/38] Android: Add new input mapping implementation --- .../dolphinemu/dialogs/MotionAlertDialog.java | 169 ------------------ .../features/input/model/MappingCommon.java | 24 +++ .../input/model/controlleremu/Control.java | 27 +++ .../model/controlleremu/ControlGroup.java | 29 +++ .../model/controlleremu/ControlReference.java | 37 ++++ .../controlleremu/EmulatedController.java | 36 ++++ .../view/InputMappingControlSetting.java | 50 ++++++ .../features/input/ui/MotionAlertDialog.java | 95 ++++++++++ ...InputMappingControlSettingViewHolder.java} | 28 ++- .../features/settings/model/Settings.java | 3 + .../settings/model/view/HeaderSetting.java | 5 + .../model/view/InputBindingSetting.java | 108 ----------- .../model/view/RumbleBindingSetting.java | 69 ------- .../settings/model/view/SettingsItem.java | 3 +- .../features/settings/ui/MenuTag.java | 16 +- .../features/settings/ui/SettingsAdapter.java | 28 ++- .../settings/ui/SettingsFragment.java | 16 +- .../ui/SettingsFragmentPresenter.java | 44 +++-- .../viewholder/RumbleBindingViewHolder.java | 67 ------- .../app/src/main/res/values/strings.xml | 16 +- Source/Android/jni/AndroidCommon/IDCache.cpp | 109 +++++++++++ Source/Android/jni/AndroidCommon/IDCache.h | 16 ++ Source/Android/jni/CMakeLists.txt | 9 + Source/Android/jni/Input/Control.cpp | 51 ++++++ Source/Android/jni/Input/Control.h | 13 ++ Source/Android/jni/Input/ControlGroup.cpp | 51 ++++++ Source/Android/jni/Input/ControlGroup.h | 13 ++ Source/Android/jni/Input/ControlReference.cpp | 54 ++++++ Source/Android/jni/Input/ControlReference.h | 11 ++ .../Android/jni/Input/EmulatedController.cpp | 81 +++++++++ Source/Android/jni/Input/MappingCommon.cpp | 56 ++++++ 31 files changed, 847 insertions(+), 487 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java rename Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/{settings/ui/viewholder/InputBindingSettingViewHolder.java => input/ui/viewholder/InputMappingControlSettingViewHolder.java} (51%) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java create mode 100644 Source/Android/jni/Input/Control.cpp create mode 100644 Source/Android/jni/Input/Control.h create mode 100644 Source/Android/jni/Input/ControlGroup.cpp create mode 100644 Source/Android/jni/Input/ControlGroup.h create mode 100644 Source/Android/jni/Input/ControlReference.cpp create mode 100644 Source/Android/jni/Input/ControlReference.h create mode 100644 Source/Android/jni/Input/EmulatedController.cpp create mode 100644 Source/Android/jni/Input/MappingCommon.cpp diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java deleted file mode 100644 index a111155181..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.dialogs; - -import android.content.Context; -import android.view.InputDevice; -import android.view.KeyEvent; -import android.view.MotionEvent; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; -import org.dolphinemu.dolphinemu.utils.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * {@link AlertDialog} derivative that listens for - * motion events from controllers and joysticks. - */ -public final class MotionAlertDialog extends AlertDialog -{ - // The selected input preference - private final InputBindingSetting setting; - private final ArrayList mPreviousValues = new ArrayList<>(); - private int mPrevDeviceId = 0; - private boolean mWaitingForEvent = true; - private SettingsAdapter mAdapter; - - /** - * Constructor - * - * @param context The current {@link Context}. - * @param setting The Preference to show this dialog for. - */ - public MotionAlertDialog(Context context, InputBindingSetting setting, SettingsAdapter adapter) - { - super(context); - - this.setting = setting; - mAdapter = adapter; - } - - public boolean onKeyEvent(int keyCode, KeyEvent event) - { - Log.debug("[MotionAlertDialog] Received key event: " + event.getAction()); - if (event.getAction() == KeyEvent.ACTION_UP) - { - if (true) - { - setting.onKeyInput(mAdapter.getSettings(), event); - dismiss(); - } - // Even if we ignore the key, we still consume it. Thus return true regardless. - return true; - } - return false; - } - - @Override - public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) - { - // Intended for devices with no touchscreen or mouse - if (keyCode == KeyEvent.KEYCODE_BACK) - { - setting.clearValue(mAdapter.getSettings()); - dismiss(); - return true; - } - return super.onKeyLongPress(keyCode, event); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) - { - // Handle this key if we care about it, otherwise pass it down the framework - return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event); - } - - @Override - public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) - { - // Handle this event if we care about it, otherwise pass it down the framework - return onMotionEvent(event) || super.dispatchGenericMotionEvent(event); - } - - private boolean onMotionEvent(MotionEvent event) - { - if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) - return false; - if (event.getAction() != MotionEvent.ACTION_MOVE) - return false; - - InputDevice input = event.getDevice(); - - List motionRanges = input.getMotionRanges(); - - if (input.getId() != mPrevDeviceId) - { - mPreviousValues.clear(); - } - mPrevDeviceId = input.getId(); - boolean firstEvent = mPreviousValues.isEmpty(); - - int numMovedAxis = 0; - float axisMoveValue = 0.0f; - InputDevice.MotionRange lastMovedRange = null; - char lastMovedDir = '?'; - if (mWaitingForEvent) - { - for (int i = 0; i < motionRanges.size(); i++) - { - InputDevice.MotionRange range = motionRanges.get(i); - int axis = range.getAxis(); - float value = event.getAxisValue(axis); - if (firstEvent) - { - mPreviousValues.add(value); - } - else - { - float previousValue = mPreviousValues.get(i); - - // Only handle the axes that are not neutral (more than 0.5) - // but ignore any axis that has a constant value (e.g. always 1) - if (Math.abs(value) > 0.5f && value != previousValue) - { - // It is common to have multiple axes with the same physical input. For example, - // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. - // To handle this, we ignore an axis motion that's the exact same as a motion - // we already saw. This way, we ignore axes with two names, but catch the case - // where a joystick is moved in two directions. - // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html - if (value != axisMoveValue) - { - axisMoveValue = value; - numMovedAxis++; - lastMovedRange = range; - lastMovedDir = value < 0.0f ? '-' : '+'; - } - } - // Special case for d-pads (axis value jumps between 0 and 1 without any values - // in between). Without this, the user would need to press the d-pad twice - // due to the first press being caught by the "if (firstEvent)" case further up. - else if (Math.abs(value) < 0.25f && Math.abs(previousValue) > 0.75f) - { - numMovedAxis++; - lastMovedRange = range; - lastMovedDir = previousValue < 0.0f ? '-' : '+'; - } - } - - mPreviousValues.set(i, value); - } - - // If only one axis moved, that's the winner. - if (numMovedAxis == 1) - { - mWaitingForEvent = false; - setting.onMotionInput(mAdapter.getSettings(), input, lastMovedRange, lastMovedDir); - dismiss(); - } - } - return true; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java new file mode 100644 index 0000000000..cb136a908d --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +public final class MappingCommon +{ + private MappingCommon() + { + } + + /** + * Waits until the user presses one or more inputs or until a timeout, + * then returns the pressed inputs. + * + * When this is being called, a separate thread must be calling ControllerInterface's + * dispatchKeyEvent and dispatchGenericMotionEvent, otherwise no inputs will be registered. + * + * @return The input(s) pressed by the user in the form of an InputCommon expression, + * or an empty string if there were no inputs. + */ + public static native String detectInput(); + + public static native void save(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java new file mode 100644 index 0000000000..6033e17e37 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::Control. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class Control +{ + @Keep + private final long mPointer; + + @Keep + private Control(long pointer) + { + mPointer = pointer; + } + + public native String getUiName(); + + public native ControlReference getControlReference(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java new file mode 100644 index 0000000000..ddfce9a737 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::ControlGroup. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class ControlGroup +{ + @Keep + private final long mPointer; + + @Keep + private ControlGroup(long pointer) + { + mPointer = pointer; + } + + public native String getUiName(); + + public native int getControlCount(); + + public native Control getControl(int i); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java new file mode 100644 index 0000000000..fd01cc1705 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; +import androidx.annotation.Nullable; + +/** + * Represents a C++ ControlReference. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class ControlReference +{ + @Keep + private final long mPointer; + + @Keep + private ControlReference(long pointer) + { + mPointer = pointer; + } + + public native double getState(); + + public native String getExpression(); + + /** + * Sets the expression for this control reference. + * + * @param expr The new expression + * @return null on success, a human-readable error on failure + */ + @Nullable + public native String setExpression(String expr); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java new file mode 100644 index 0000000000..b0fbd0fd92 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::EmulatedController. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class EmulatedController +{ + @Keep + private final long mPointer; + + @Keep + private EmulatedController(long pointer) + { + mPointer = pointer; + } + + public native int getGroupCount(); + + public native ControlGroup getGroup(int index); + + public native void updateSingleControlReference(ControlReference controlReference); + + public static native EmulatedController getGcPad(int controllerIndex); + + public static native EmulatedController getWiimote(int controllerIndex); + + public static native EmulatedController getWiimoteAttachment(int controllerIndex, + int attachmentIndex); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java new file mode 100644 index 0000000000..8888e2430b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.view; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.Control; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; + +public final class InputMappingControlSetting extends SettingsItem +{ + private final ControlReference mControlReference; + private final EmulatedController mController; + + public InputMappingControlSetting(Control control, EmulatedController controller) + { + super(control.getUiName(), ""); + mControlReference = control.getControlReference(); + mController = controller; + } + + public String getValue() + { + return mControlReference.getExpression(); + } + + public void setValue(String expr) + { + mControlReference.setExpression(expr); + mController.updateSingleControlReference(mControlReference); + } + + public void clearValue() + { + setValue(""); + } + + @Override + public int getType() + { + return TYPE_INPUT_MAPPING_CONTROL; + } + + @Override + public AbstractSetting getSetting() + { + return null; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java new file mode 100644 index 0000000000..f36d9bc478 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.ui; + +import android.app.Activity; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; +import org.dolphinemu.dolphinemu.features.input.model.MappingCommon; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; + +/** + * {@link AlertDialog} derivative that listens for + * motion events from controllers and joysticks. + */ +public final class MotionAlertDialog extends AlertDialog +{ + private final Activity mActivity; + private final InputMappingControlSetting mSetting; + private boolean mRunning = false; + + /** + * Constructor + * + * @param activity The current {@link Activity}. + * @param setting The setting to show this dialog for. + */ + public MotionAlertDialog(Activity activity, InputMappingControlSetting setting) + { + super(activity); + + mActivity = activity; + mSetting = setting; + } + + @Override + protected void onStart() + { + super.onStart(); + + mRunning = true; + new Thread(() -> + { + String result = MappingCommon.detectInput(); + mActivity.runOnUiThread(() -> + { + if (mRunning) + { + mSetting.setValue(result); + dismiss(); + } + }); + }).start(); + } + + @Override + protected void onStop() + { + super.onStop(); + mRunning = false; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) + { + ControllerInterface.dispatchKeyEvent(event); + + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isLongPress()) + { + // Special case: Let the user cancel by long-pressing Back (intended for non-touch devices) + mSetting.clearValue(); + dismiss(); + } + + return true; + } + + @Override + public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) + { + if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) + { + // Special case: Let the user cancel by touching an on-screen button + return super.dispatchGenericMotionEvent(event); + } + + ControllerInterface.dispatchGenericMotionEvent(event); + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java similarity index 51% rename from Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java rename to Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java index b61fcc458b..72ddcb3fd3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java @@ -1,46 +1,38 @@ // SPDX-License-Identifier: GPL-2.0-or-later -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; +package org.dolphinemu.dolphinemu.features.input.ui.viewholder; -import android.content.Context; -import android.content.SharedPreferences; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.preference.PreferenceManager; import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; +import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder; -public final class InputBindingSettingViewHolder extends SettingViewHolder +public final class InputMappingControlSettingViewHolder extends SettingViewHolder { - private InputBindingSetting mItem; - - private final Context mContext; + private InputMappingControlSetting mItem; private final ListItemSettingBinding mBinding; - public InputBindingSettingViewHolder(@NonNull ListItemSettingBinding binding, - SettingsAdapter adapter, Context context) + public InputMappingControlSettingViewHolder(@NonNull ListItemSettingBinding binding, + SettingsAdapter adapter) { super(binding.getRoot(), adapter); mBinding = binding; - mContext = context; } @Override public void bind(SettingsItem item) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - - mItem = (InputBindingSetting) item; + mItem = (InputMappingControlSetting) item; mBinding.textSettingName.setText(mItem.getName()); - mBinding.textSettingDescription - .setText(sharedPreferences.getString(mItem.getKey() + mItem.getGameId(), "")); + mBinding.textSettingDescription.setText(mItem.getValue()); setStyle(mBinding.textSettingName, mItem); } @@ -54,7 +46,7 @@ public final class InputBindingSettingViewHolder extends SettingViewHolder return; } - getAdapter().onInputBindingClick(mItem, getBindingAdapterPosition()); + getAdapter().onInputMappingClick(mItem, getBindingAdapterPosition()); setStyle(mBinding.textSettingName, mItem); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java index 7d3d5a5b19..9a94048838 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java @@ -8,6 +8,7 @@ import android.widget.Toast; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.input.model.MappingCommon; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.services.GameFileCacheManager; @@ -170,6 +171,8 @@ public class Settings implements Closeable SettingsFile.saveFile(entry.getKey(), entry.getValue(), view); } + MappingCommon.save(); + NativeConfig.save(NativeConfig.LAYER_BASE); if (!NativeLibrary.IsRunning()) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java index 14c5f49021..b65f45837b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java @@ -13,6 +13,11 @@ public class HeaderSetting extends SettingsItem super(context, titleId, descriptionId); } + public HeaderSetting(CharSequence title, CharSequence description) + { + super(title, description); + } + @Override public int getType() { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java deleted file mode 100644 index 175986d3ba..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; -import android.content.SharedPreferences; -import android.view.InputDevice; -import android.view.KeyEvent; - -import androidx.preference.PreferenceManager; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public class InputBindingSetting extends SettingsItem -{ - private String mFile; - private String mSection; - private String mKey; - - private String mGameId; - - public InputBindingSetting(Context context, String file, String section, String key, int titleId, - String gameId) - { - super(context, titleId, 0); - mFile = file; - mSection = section; - mKey = key; - mGameId = gameId; - } - - public String getKey() - { - return mKey; - } - - public String getValue(Settings settings) - { - return settings.getSection(mFile, mSection).getString(mKey, ""); - } - - /** - * Saves the provided key input setting both to the INI file (so native code can use it) and as - * an Android preference (so it persists correctly and is human-readable.) - * - * @param keyEvent KeyEvent of this key press. - */ - public void onKeyInput(Settings settings, KeyEvent keyEvent) - { - InputDevice device = keyEvent.getDevice(); - String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode(); - String uiString = device.getName() + ": Button " + keyEvent.getKeyCode(); - setValue(settings, bindStr, uiString); - } - - /** - * Saves the provided motion input setting both to the INI file (so native code can use it) and as - * an Android preference (so it persists correctly and is human-readable.) - * - * @param device InputDevice from which the input event originated. - * @param motionRange MotionRange of the movement - * @param axisDir Either '-' or '+' - */ - public void onMotionInput(Settings settings, InputDevice device, - InputDevice.MotionRange motionRange, char axisDir) - { - String bindStr = - "Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir; - String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir; - setValue(settings, bindStr, uiString); - } - - public void setValue(Settings settings, String bind, String ui) - { - SharedPreferences - preferences = - PreferenceManager.getDefaultSharedPreferences(DolphinApplication.getAppContext()); - SharedPreferences.Editor editor = preferences.edit(); - editor.putString(mKey + mGameId, ui); - editor.apply(); - - settings.getSection(mFile, mSection).setString(mKey, bind); - } - - public void clearValue(Settings settings) - { - setValue(settings, "", ""); - } - - @Override - public int getType() - { - return TYPE_INPUT_BINDING; - } - - public String getGameId() - { - return mGameId; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java deleted file mode 100644 index 6c3e11e17b..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; -import android.os.Vibrator; -import android.view.InputDevice; -import android.view.KeyEvent; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.utils.Rumble; - -public class RumbleBindingSetting extends InputBindingSetting -{ - public RumbleBindingSetting(Context context, String file, String section, String key, int titleId, - String gameId) - { - super(context, file, section, key, titleId, gameId); - } - - /** - * Just need the device when saving rumble. - */ - @Override - public void onKeyInput(Settings settings, KeyEvent keyEvent) - { - saveRumble(settings, keyEvent.getDevice()); - } - - /** - * Just need the device when saving rumble. - */ - @Override - public void onMotionInput(Settings settings, InputDevice device, - InputDevice.MotionRange motionRange, char axisDir) - { - saveRumble(settings, device); - } - - private void saveRumble(Settings settings, InputDevice device) - { - Vibrator vibrator = device.getVibrator(); - if (vibrator != null && vibrator.hasVibrator()) - { - setValue(settings, device.getDescriptor(), device.getName()); - Rumble.doRumble(vibrator); - } - else - { - setValue(settings, "", - DolphinApplication.getAppContext().getString(R.string.rumble_not_found)); - } - } - - @Override - public int getType() - { - return TYPE_RUMBLE_BINDING; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java index 7b41c8e3f8..9c7b89c12f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java @@ -21,9 +21,8 @@ public abstract class SettingsItem public static final int TYPE_SINGLE_CHOICE = 2; public static final int TYPE_SLIDER = 3; public static final int TYPE_SUBMENU = 4; - public static final int TYPE_INPUT_BINDING = 5; + public static final int TYPE_INPUT_MAPPING_CONTROL = 5; public static final int TYPE_STRING_SINGLE_CHOICE = 6; - public static final int TYPE_RUMBLE_BINDING = 7; public static final int TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8; public static final int TYPE_FILE_PICKER = 9; public static final int TYPE_RUN_RUNNABLE = 10; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java index 6fc30a381b..1d4fe28af9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java @@ -31,14 +31,14 @@ public enum MenuTag GCPAD_2("gcpad", 1), GCPAD_3("gcpad", 2), GCPAD_4("gcpad", 3), - WIIMOTE_1("wiimote", 4), - WIIMOTE_2("wiimote", 5), - WIIMOTE_3("wiimote", 6), - WIIMOTE_4("wiimote", 7), - WIIMOTE_EXTENSION_1("wiimote_extension", 4), - WIIMOTE_EXTENSION_2("wiimote_extension", 5), - WIIMOTE_EXTENSION_3("wiimote_extension", 6), - WIIMOTE_EXTENSION_4("wiimote_extension", 7); + WIIMOTE_1("wiimote", 0), + WIIMOTE_2("wiimote", 1), + WIIMOTE_3("wiimote", 2), + WIIMOTE_4("wiimote", 3), + WIIMOTE_EXTENSION_1("wiimote_extension", 0), + WIIMOTE_EXTENSION_2("wiimote_extension", 1), + WIIMOTE_EXTENSION_3("wiimote_extension", 2), + WIIMOTE_EXTENSION_4("wiimote_extension", 3); private String tag; private int subType = -1; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index a7f028afe3..8fe3fe4602 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -37,15 +37,15 @@ import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding; -import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog; +import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; +import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions; @@ -57,9 +57,7 @@ import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.DateTimeSetting import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputBindingSettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputStringSettingViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RumbleBindingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RunRunnableViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder; @@ -124,13 +122,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter item.clearValue(getSettings())); + (dialogInterface, i) -> item.clearValue()); dialog.setOnDismissListener(dialog1 -> { notifyItemChanged(position); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java index 00699e3a0c..bb73093656 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java @@ -67,14 +67,14 @@ public final class SettingsFragment extends Fragment implements SettingsFragment titles.put(MenuTag.GCPAD_2, R.string.controller_1); titles.put(MenuTag.GCPAD_3, R.string.controller_2); titles.put(MenuTag.GCPAD_4, R.string.controller_3); - titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_4); - titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_5); - titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_6); - titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_7); - titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_4); - titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_5); - titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_6); - titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_7); + titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_0); + titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_1); + titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_2); + titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_3); + titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_0); + titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_1); + titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_2); + titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_3); } private FragmentSettingsBinding mBinding; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 9d1a783979..01f4b52b71 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -14,6 +14,9 @@ import androidx.appcompat.app.AppCompatActivity; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.UserDataActivity; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting; @@ -795,14 +798,14 @@ public final class SettingsFragmentPresenter private void addWiimoteSettings(ArrayList sl) { - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_4, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(4))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_5, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(5))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_6, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(6))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_7, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(7))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_0, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(0))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_1, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(1))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_2, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(2))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_3, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(3))); } private void addGraphicsSettings(ArrayList sl) @@ -1073,7 +1076,7 @@ public final class SettingsFragmentPresenter { if (gcPadType == 6) // Emulated { - // TODO + addControllerSettings(sl, EmulatedController.getGcPad(gcPadNumber)); } else if (gcPadType == 12) // Adapter { @@ -1086,13 +1089,30 @@ public final class SettingsFragmentPresenter private void addWiimoteSubSettings(ArrayList sl, int wiimoteNumber) { - // TODO + addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber)); } private void addExtensionTypeSettings(ArrayList sl, int wiimoteNumber, - int extentionType) + int extensionType) { - // TODO + addControllerSettings(sl, + EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType)); + } + + private void addControllerSettings(ArrayList sl, EmulatedController controller) + { + int groupCount = controller.getGroupCount(); + for (int i = 0; i < groupCount; i++) + { + ControlGroup group = controller.getGroup(i); + sl.add(new HeaderSetting(group.getUiName(), "")); + + int controlCount = group.getControlCount(); + for (int j = 0; j < controlCount; j++) + { + sl.add(new InputMappingControlSetting(group.getControl(j), controller)); + } + } } private static int getLogVerbosityEntries() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java deleted file mode 100644 index 3b7a6b0f75..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.content.Context; -import android.content.SharedPreferences; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.PreferenceManager; - -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public class RumbleBindingViewHolder extends SettingViewHolder -{ - private RumbleBindingSetting mItem; - - private final Context mContext; - - private final ListItemSettingBinding mBinding; - - public RumbleBindingViewHolder(@NonNull ListItemSettingBinding binding, SettingsAdapter adapter, - Context context) - { - super(binding.getRoot(), adapter); - mBinding = binding; - mContext = context; - } - - @Override - public void bind(SettingsItem item) - { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - - mItem = (RumbleBindingSetting) item; - - mBinding.textSettingName.setText(item.getName()); - mBinding.textSettingDescription - .setText(sharedPreferences.getString(mItem.getKey() + mItem.getGameId(), "")); - - setStyle(mBinding.textSettingName, mItem); - } - - @Override - public void onClick(View clicked) - { - if (!mItem.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - getAdapter().onInputBindingClick(mItem, getBindingAdapterPosition()); - - setStyle(mBinding.textSettingName, mItem); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } -} diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index cca14f13a8..a1c7a32f95 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -11,15 +11,15 @@ GameCube Controller 3 GameCube Controller 4 - Wii Remote 1 - Wii Remote 2 - Wii Remote 3 - Wii Remote 4 + Wii Remote 1 + Wii Remote 2 + Wii Remote 3 + Wii Remote 4 - Wii Remote Extension 1 - Wii Remote Extension 2 - Wii Remote Extension 3 - Wii Remote Extension 4 + Wii Remote Extension 1 + Wii Remote Extension 2 + Wii Remote Extension 3 + Wii Remote Extension 4 Extension diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 8e43016c62..54d3f765c9 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -83,6 +83,22 @@ static jfieldID s_riivolution_patches_pointer; static jclass s_wii_update_cb_class; static jmethodID s_wii_update_cb_run; +static jclass s_control_class; +static jfieldID s_control_pointer; +static jmethodID s_control_constructor; + +static jclass s_control_group_class; +static jfieldID s_control_group_pointer; +static jmethodID s_control_group_constructor; + +static jclass s_control_reference_class; +static jfieldID s_control_reference_pointer; +static jmethodID s_control_reference_constructor; + +static jclass s_emulated_controller_class; +static jfieldID s_emulated_controller_pointer; +static jmethodID s_emulated_controller_constructor; + namespace IDCache { JNIEnv* GetEnvForThread() @@ -383,6 +399,66 @@ jmethodID GetWiiUpdateCallbackFunction() return s_wii_update_cb_run; } +jclass GetControlClass() +{ + return s_control_class; +} + +jfieldID GetControlPointer() +{ + return s_control_pointer; +} + +jmethodID GetControlConstructor() +{ + return s_control_constructor; +} + +jclass GetControlGroupClass() +{ + return s_control_group_class; +} + +jfieldID GetControlGroupPointer() +{ + return s_control_group_pointer; +} + +jmethodID GetControlGroupConstructor() +{ + return s_control_group_constructor; +} + +jclass GetControlReferenceClass() +{ + return s_control_reference_class; +} + +jfieldID GetControlReferencePointer() +{ + return s_control_reference_pointer; +} + +jmethodID GetControlReferenceConstructor() +{ + return s_control_reference_constructor; +} + +jclass GetEmulatedControllerClass() +{ + return s_emulated_controller_class; +} + +jfieldID GetEmulatedControllerPointer() +{ + return s_emulated_controller_pointer; +} + +jmethodID GetEmulatedControllerConstructor() +{ + return s_emulated_controller_constructor; +} + } // namespace IDCache extern "C" { @@ -541,6 +617,35 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) s_wii_update_cb_run = env->GetMethodID(s_wii_update_cb_class, "run", "(IIJ)Z"); env->DeleteLocalRef(wii_update_cb_class); + const jclass control_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control"); + s_control_class = reinterpret_cast(env->NewGlobalRef(control_class)); + s_control_pointer = env->GetFieldID(control_class, "mPointer", "J"); + s_control_constructor = env->GetMethodID(control_class, "", "(J)V"); + env->DeleteLocalRef(control_class); + + const jclass control_group_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup"); + s_control_group_class = reinterpret_cast(env->NewGlobalRef(control_group_class)); + s_control_group_pointer = env->GetFieldID(control_group_class, "mPointer", "J"); + s_control_group_constructor = env->GetMethodID(control_group_class, "", "(J)V"); + env->DeleteLocalRef(control_group_class); + + const jclass control_reference_class = env->FindClass( + "org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference"); + s_control_reference_class = reinterpret_cast(env->NewGlobalRef(control_reference_class)); + s_control_reference_pointer = env->GetFieldID(control_reference_class, "mPointer", "J"); + s_control_reference_constructor = env->GetMethodID(control_reference_class, "", "(J)V"); + env->DeleteLocalRef(control_reference_class); + + const jclass emulated_controller_class = env->FindClass( + "org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController"); + s_emulated_controller_class = + reinterpret_cast(env->NewGlobalRef(emulated_controller_class)); + s_emulated_controller_pointer = env->GetFieldID(emulated_controller_class, "mPointer", "J"); + s_emulated_controller_constructor = env->GetMethodID(emulated_controller_class, "", "(J)V"); + env->DeleteLocalRef(emulated_controller_class); + return JNI_VERSION; } @@ -568,5 +673,9 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) env->DeleteGlobalRef(s_graphics_mod_class); env->DeleteGlobalRef(s_riivolution_patches_class); env->DeleteGlobalRef(s_wii_update_cb_class); + env->DeleteGlobalRef(s_control_class); + env->DeleteGlobalRef(s_control_group_class); + env->DeleteGlobalRef(s_control_reference_class); + env->DeleteGlobalRef(s_emulated_controller_class); } } diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 79ab19acaa..bd727c12a9 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -82,4 +82,20 @@ jfieldID GetRiivolutionPatchesPointer(); jclass GetWiiUpdateCallbackClass(); jmethodID GetWiiUpdateCallbackFunction(); +jclass GetControlClass(); +jfieldID GetControlPointer(); +jmethodID GetControlConstructor(); + +jclass GetControlGroupClass(); +jfieldID GetControlGroupPointer(); +jmethodID GetControlGroupConstructor(); + +jclass GetControlReferenceClass(); +jfieldID GetControlReferencePointer(); +jmethodID GetControlReferenceConstructor(); + +jclass GetEmulatedControllerClass(); +jfieldID GetEmulatedControllerPointer(); +jmethodID GetEmulatedControllerConstructor(); + } // namespace IDCache diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index 1c419aefcf..575847290e 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -10,7 +10,15 @@ add_library(main SHARED GameList/GameFile.cpp GameList/GameFile.h GameList/GameFileCache.cpp + Input/Control.cpp + Input/Control.h + Input/ControlGroup.cpp + Input/ControlGroup.h + Input/ControlReference.cpp + Input/ControlReference.h + Input/EmulatedController.cpp Input/InputOverrider.cpp + Input/MappingCommon.cpp IniFile.cpp MainAndroid.cpp RiivolutionPatches.cpp @@ -23,6 +31,7 @@ PRIVATE androidcommon common core + inputcommon uicommon ) diff --git a/Source/Android/jni/Input/Control.cpp b/Source/Android/jni/Input/Control.cpp new file mode 100644 index 0000000000..3a0496b92e --- /dev/null +++ b/Source/Android/jni/Input/Control.cpp @@ -0,0 +1,51 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "jni/Input/Control.h" + +#include + +#include + +#include "Common/MsgHandler.h" +#include "InputCommon/ControlReference/ControlReference.h" +#include "InputCommon/ControllerEmu/Control/Control.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" +#include "jni/Input/ControlReference.h" + +static ControllerEmu::Control* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetControlPointer())); +} + +jobject ControlToJava(JNIEnv* env, ControllerEmu::Control* control) +{ + if (!control) + return nullptr; + + return env->NewObject(IDCache::GetControlClass(), IDCache::GetControlConstructor(), + reinterpret_cast(control)); +} + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_Control_getUiName(JNIEnv* env, + jobject obj) +{ + ControllerEmu::Control* control = GetPointer(env, obj); + std::string ui_name = control->ui_name; + if (control->translate == ControllerEmu::Translatability::Translate) + ui_name = Common::GetStringT(ui_name.c_str()); + return ToJString(env, ui_name); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_Control_getControlReference( + JNIEnv* env, jobject obj) +{ + return ControlReferenceToJava(env, GetPointer(env, obj)->control_ref.get()); +} +} diff --git a/Source/Android/jni/Input/Control.h b/Source/Android/jni/Input/Control.h new file mode 100644 index 0000000000..c27732dd38 --- /dev/null +++ b/Source/Android/jni/Input/Control.h @@ -0,0 +1,13 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace ControllerEmu +{ +class Control; +} + +jobject ControlToJava(JNIEnv* env, ControllerEmu::Control* control); diff --git a/Source/Android/jni/Input/ControlGroup.cpp b/Source/Android/jni/Input/ControlGroup.cpp new file mode 100644 index 0000000000..0e56774e84 --- /dev/null +++ b/Source/Android/jni/Input/ControlGroup.cpp @@ -0,0 +1,51 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "jni/Input/ControlGroup.h" + +#include + +#include "Common/MsgHandler.h" +#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" +#include "jni/Input/Control.h" + +static ControllerEmu::ControlGroup* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetControlGroupPointer())); +} + +jobject ControlGroupToJava(JNIEnv* env, ControllerEmu::ControlGroup* group) +{ + if (!group) + return nullptr; + + return env->NewObject(IDCache::GetControlGroupClass(), IDCache::GetControlGroupConstructor(), + reinterpret_cast(group)); +} + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getUiName( + JNIEnv* env, jobject obj) +{ + return ToJString(env, Common::GetStringT(GetPointer(env, obj)->ui_name.c_str())); +} + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getControlCount( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->controls.size()); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getControl( + JNIEnv* env, jobject obj, jint i) +{ + return ControlToJava(env, GetPointer(env, obj)->controls[i].get()); +} +} diff --git a/Source/Android/jni/Input/ControlGroup.h b/Source/Android/jni/Input/ControlGroup.h new file mode 100644 index 0000000000..b7ec0e78d1 --- /dev/null +++ b/Source/Android/jni/Input/ControlGroup.h @@ -0,0 +1,13 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace ControllerEmu +{ +class ControlGroup; +} + +jobject ControlGroupToJava(JNIEnv* env, ControllerEmu::ControlGroup* group); diff --git a/Source/Android/jni/Input/ControlReference.cpp b/Source/Android/jni/Input/ControlReference.cpp new file mode 100644 index 0000000000..8bf9351109 --- /dev/null +++ b/Source/Android/jni/Input/ControlReference.cpp @@ -0,0 +1,54 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "jni/Input/ControlReference.h" + +#include + +#include + +#include "InputCommon/ControlReference/ControlReference.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" + +ControlReference* ControlReferenceFromJava(JNIEnv* env, jobject control_reference) +{ + return reinterpret_cast( + env->GetLongField(control_reference, IDCache::GetControlReferencePointer())); +} + +jobject ControlReferenceToJava(JNIEnv* env, ControlReference* control_reference) +{ + if (!control_reference) + return nullptr; + + return env->NewObject(IDCache::GetControlReferenceClass(), + IDCache::GetControlReferenceConstructor(), + reinterpret_cast(control_reference)); +} + +extern "C" { + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_getState( + JNIEnv* env, jobject obj) +{ + return ControlReferenceFromJava(env, obj)->GetState(); +} + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_getExpression( + JNIEnv* env, jobject obj) +{ + return ToJString(env, ControlReferenceFromJava(env, obj)->GetExpression()); +} + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_setExpression( + JNIEnv* env, jobject obj, jstring expr) +{ + const std::optional result = + ControlReferenceFromJava(env, obj)->SetExpression(GetJString(env, expr)); + return result ? ToJString(env, *result) : nullptr; +} +} diff --git a/Source/Android/jni/Input/ControlReference.h b/Source/Android/jni/Input/ControlReference.h new file mode 100644 index 0000000000..c8bfb7041a --- /dev/null +++ b/Source/Android/jni/Input/ControlReference.h @@ -0,0 +1,11 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +class ControlReference; + +jobject ControlReferenceToJava(JNIEnv* env, ControlReference* control_reference); +ControlReference* ControlReferenceFromJava(JNIEnv* env, jobject control_reference); diff --git a/Source/Android/jni/Input/EmulatedController.cpp b/Source/Android/jni/Input/EmulatedController.cpp new file mode 100644 index 0000000000..af6ba3b48a --- /dev/null +++ b/Source/Android/jni/Input/EmulatedController.cpp @@ -0,0 +1,81 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "Core/HW/GCPad.h" +#include "Core/HW/Wiimote.h" +#include "Core/HW/WiimoteEmu/WiimoteEmu.h" +#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h" +#include "InputCommon/ControllerEmu/ControllerEmu.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/InputConfig.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" +#include "jni/Input/Control.h" +#include "jni/Input/ControlGroup.h" +#include "jni/Input/ControlReference.h" + +static ControllerEmu::EmulatedController* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetEmulatedControllerPointer())); +} + +static jobject EmulatedControllerToJava(JNIEnv* env, ControllerEmu::EmulatedController* controller) +{ + if (!controller) + return nullptr; + + return env->NewObject(IDCache::GetEmulatedControllerClass(), + IDCache::GetEmulatedControllerConstructor(), + reinterpret_cast(controller)); +} + +extern "C" { + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroupCount( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->groups.size()); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroup( + JNIEnv* env, jobject obj, jint controller_index) +{ + return ControlGroupToJava(env, GetPointer(env, obj)->groups[controller_index].get()); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_updateSingleControlReference( + JNIEnv* env, jobject obj, jobject control_reference) +{ + return GetPointer(env, obj)->UpdateSingleControlReference( + g_controller_interface, ControlReferenceFromJava(env, control_reference)); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGcPad( + JNIEnv* env, jclass, jint controller_index) +{ + return EmulatedControllerToJava(env, Pad::GetConfig()->GetController(controller_index)); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getWiimote( + JNIEnv* env, jclass, jint controller_index) +{ + return EmulatedControllerToJava(env, Wiimote::GetConfig()->GetController(controller_index)); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getWiimoteAttachment( + JNIEnv* env, jclass, jint controller_index, jint attachment_index) +{ + auto* attachments = static_cast( + Wiimote::GetWiimoteGroup(controller_index, WiimoteEmu::WiimoteGroup::Attachments)); + return EmulatedControllerToJava(env, attachments->GetAttachmentList()[attachment_index].get()); +} +} diff --git a/Source/Android/jni/Input/MappingCommon.cpp b/Source/Android/jni/Input/MappingCommon.cpp new file mode 100644 index 0000000000..c02f1c2ae5 --- /dev/null +++ b/Source/Android/jni/Input/MappingCommon.cpp @@ -0,0 +1,56 @@ +// Copyright 2022 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include + +#include "Core/FreeLookManager.h" +#include "Core/HW/GBAPad.h" +#include "Core/HW/GCKeyboard.h" +#include "Core/HW/GCPad.h" +#include "Core/HW/Wiimote.h" +#include "Core/HotkeyManager.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/ControllerInterface/MappingCommon.h" +#include "jni/AndroidCommon/AndroidCommon.h" + +namespace +{ +constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3); +constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0); +constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5); +} // namespace + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_detectInput(JNIEnv* env, jclass) +{ + const std::vector device_strings = g_controller_interface.GetAllDeviceStrings(); + const ciface::Core::DeviceQualifier default_device{}; + + auto detections = + g_controller_interface.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME, + INPUT_DETECT_CONFIRMATION_TIME, INPUT_DETECT_MAXIMUM_TIME); + + ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections); + + return ToJString(env, ciface::MappingCommon::BuildExpression(detections, default_device, + ciface::MappingCommon::Quote::On)); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_save(JNIEnv* env, jclass) +{ + Pad::GetConfig()->SaveConfig(); + Pad::GetGBAConfig()->SaveConfig(); + Keyboard::GetConfig()->SaveConfig(); + Wiimote::GetConfig()->SaveConfig(); + HotkeyManagerEmu::GetConfig()->SaveConfig(); + FreeLook::GetInputConfig()->SaveConfig(); +} +}; From 1c26a85e35f94169ddc83e8174d6824aec82d322 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 18 Apr 2022 16:11:14 +0200 Subject: [PATCH 18/38] Android: Add NumericSetting support --- .../model/InputMappingBooleanSetting.java | 48 ++++++ .../model/InputMappingDoubleSetting.java | 49 ++++++ .../model/controlleremu/ControlGroup.java | 4 + .../model/controlleremu/NumericSetting.java | 89 +++++++++++ .../model/view/FloatSliderSetting.java | 7 + .../settings/model/view/SliderSetting.java | 8 + .../ui/SettingsFragmentPresenter.java | 26 ++++ Source/Android/jni/AndroidCommon/IDCache.cpp | 27 ++++ Source/Android/jni/AndroidCommon/IDCache.h | 4 + Source/Android/jni/CMakeLists.txt | 2 + Source/Android/jni/Input/ControlGroup.cpp | 15 ++ Source/Android/jni/Input/NumericSetting.cpp | 139 ++++++++++++++++++ Source/Android/jni/Input/NumericSetting.h | 15 ++ 13 files changed, 433 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java create mode 100644 Source/Android/jni/Input/NumericSetting.cpp create mode 100644 Source/Android/jni/Input/NumericSetting.h diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java new file mode 100644 index 0000000000..7241d6e125 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; +import org.dolphinemu.dolphinemu.features.settings.model.Settings; + +public class InputMappingBooleanSetting implements AbstractBooleanSetting +{ + private final NumericSetting mNumericSetting; + + public InputMappingBooleanSetting(NumericSetting numericSetting) + { + mNumericSetting = numericSetting; + } + + @Override + public boolean getBoolean(Settings settings) + { + return mNumericSetting.getBooleanValue(); + } + + @Override + public void setBoolean(Settings settings, boolean newValue) + { + mNumericSetting.setBooleanValue(newValue); + } + + @Override + public boolean isOverridden(Settings settings) + { + return false; + } + + @Override + public boolean isRuntimeEditable() + { + return true; + } + + @Override + public boolean delete(Settings settings) + { + mNumericSetting.setBooleanValue(mNumericSetting.getBooleanDefaultValue()); + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java new file mode 100644 index 0000000000..e84b61a045 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting; +import org.dolphinemu.dolphinemu.features.settings.model.Settings; + +// Yes, floats are not the same thing as doubles... They're close enough, though +public class InputMappingDoubleSetting implements AbstractFloatSetting +{ + private final NumericSetting mNumericSetting; + + public InputMappingDoubleSetting(NumericSetting numericSetting) + { + mNumericSetting = numericSetting; + } + + @Override + public float getFloat(Settings settings) + { + return (float) mNumericSetting.getDoubleValue(); + } + + @Override + public void setFloat(Settings settings, float newValue) + { + mNumericSetting.setDoubleValue(newValue); + } + + @Override + public boolean isOverridden(Settings settings) + { + return false; + } + + @Override + public boolean isRuntimeEditable() + { + return true; + } + + @Override + public boolean delete(Settings settings) + { + mNumericSetting.setDoubleValue(mNumericSetting.getDoubleDefaultValue()); + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java index ddfce9a737..19423d5f14 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java @@ -26,4 +26,8 @@ public class ControlGroup public native int getControlCount(); public native Control getControl(int i); + + public native int getNumericSettingCount(); + + public native NumericSetting getNumericSetting(int i); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java new file mode 100644 index 0000000000..9baa2c41d7 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::NumericSetting. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class NumericSetting +{ + public static final int TYPE_INT = 0; + public static final int TYPE_DOUBLE = 1; + public static final int TYPE_BOOLEAN = 2; + + @Keep + private final long mPointer; + + @Keep + private NumericSetting(long pointer) + { + mPointer = pointer; + } + + /** + * @return The name used in the UI. + */ + public native String getUiName(); + + /** + * @return A string applied to the number in the UI (unit of measure). + */ + public native String getUiSuffix(); + + /** + * @return Detailed description of the setting. + */ + public native String getUiDescription(); + + /** + * @return TYPE_INT, TYPE_DOUBLE or TYPE_BOOLEAN + */ + public native int getType(); + + public native ControlReference getControlReference(); + + /** + * If the type is TYPE_DOUBLE, gets the current value. Otherwise, undefined behavior! + */ + public native double getDoubleValue(); + + /** + * If the type is TYPE_DOUBLE, sets the current value. Otherwise, undefined behavior! + */ + public native void setDoubleValue(double value); + + /** + * If the type is TYPE_DOUBLE, gets the default value. Otherwise, undefined behavior! + */ + public native double getDoubleDefaultValue(); + + /** + * If the type is TYPE_DOUBLE, returns the minimum valid value. Otherwise, undefined behavior! + */ + public native double getDoubleMin(); + + /** + * If the type is TYPE_DOUBLE, returns the maximum valid value. Otherwise, undefined behavior! + */ + public native double getDoubleMax(); + + /** + * If the type is TYPE_BOOLEAN, gets the current value. Otherwise, undefined behavior! + */ + public native boolean getBooleanValue(); + + /** + * If the type is TYPE_BOOLEAN, sets the current value. Otherwise, undefined behavior! + */ + public native void setBooleanValue(boolean value); + + /** + * If the type is TYPE_BOOLEAN, gets the default value. Otherwise, undefined behavior! + */ + public native boolean getBooleanDefaultValue(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java index 167c5a471b..5b91517f75 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java @@ -19,6 +19,13 @@ public class FloatSliderSetting extends SliderSetting mSetting = setting; } + public FloatSliderSetting(AbstractFloatSetting setting, CharSequence name, + CharSequence description, int min, int max, String units) + { + super(name, description, min, max, units); + mSetting = setting; + } + public int getSelectedValue(Settings settings) { return Math.round(mSetting.getFloat(settings)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java index 48961839c0..e63fde3bad 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java @@ -23,6 +23,14 @@ public abstract class SliderSetting extends SettingsItem mStepSize = stepSize; } + public SliderSetting(CharSequence name, CharSequence description, int min, int max, String units) + { + super(name, description); + mMin = min; + mMax = max; + mUnits = units; + } + public abstract int getSelectedValue(Settings settings); public int getMin() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 01f4b52b71..775c9b3f8a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -14,8 +14,11 @@ import androidx.appcompat.app.AppCompatActivity; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.UserDataActivity; +import org.dolphinemu.dolphinemu.features.input.model.InputMappingBooleanSetting; +import org.dolphinemu.dolphinemu.features.input.model.InputMappingDoubleSetting; import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; 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.view.InputMappingControlSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; @@ -30,6 +33,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; +import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.HyperLinkHeaderSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting; @@ -1112,6 +1116,28 @@ public final class SettingsFragmentPresenter { sl.add(new InputMappingControlSetting(group.getControl(j), controller)); } + + int numericSettingCount = group.getNumericSettingCount(); + for (int j = 0; j < numericSettingCount; j++) + { + addNumericSetting(sl, group.getNumericSetting(j)); + } + } + } + + private void addNumericSetting(ArrayList sl, NumericSetting setting) + { + switch (setting.getType()) + { + case NumericSetting.TYPE_DOUBLE: + sl.add(new FloatSliderSetting(new InputMappingDoubleSetting(setting), setting.getUiName(), + "", (int) Math.ceil(setting.getDoubleMin()), + (int) Math.floor(setting.getDoubleMax()), setting.getUiSuffix())); + break; + case NumericSetting.TYPE_BOOLEAN: + sl.add(new SwitchSetting(new InputMappingBooleanSetting(setting), setting.getUiName(), + setting.getUiDescription())); + break; } } diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 54d3f765c9..6e547beaed 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -87,6 +87,10 @@ static jclass s_control_class; static jfieldID s_control_pointer; static jmethodID s_control_constructor; +static jclass s_numeric_setting_class; +static jfieldID s_numeric_setting_pointer; +static jmethodID s_numeric_setting_constructor; + static jclass s_control_group_class; static jfieldID s_control_group_pointer; static jmethodID s_control_group_constructor; @@ -459,6 +463,21 @@ jmethodID GetEmulatedControllerConstructor() return s_emulated_controller_constructor; } +jclass GetNumericSettingClass() +{ + return s_numeric_setting_class; +} + +jfieldID GetNumericSettingPointer() +{ + return s_numeric_setting_pointer; +} + +jmethodID GetNumericSettingConstructor() +{ + return s_numeric_setting_constructor; +} + } // namespace IDCache extern "C" { @@ -646,6 +665,13 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) s_emulated_controller_constructor = env->GetMethodID(emulated_controller_class, "", "(J)V"); env->DeleteLocalRef(emulated_controller_class); + const jclass numeric_setting_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting"); + s_numeric_setting_class = reinterpret_cast(env->NewGlobalRef(numeric_setting_class)); + s_numeric_setting_pointer = env->GetFieldID(numeric_setting_class, "mPointer", "J"); + s_numeric_setting_constructor = env->GetMethodID(numeric_setting_class, "", "(J)V"); + env->DeleteLocalRef(numeric_setting_class); + return JNI_VERSION; } @@ -677,5 +703,6 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) env->DeleteGlobalRef(s_control_group_class); env->DeleteGlobalRef(s_control_reference_class); env->DeleteGlobalRef(s_emulated_controller_class); + env->DeleteGlobalRef(s_numeric_setting_class); } } diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index bd727c12a9..2a6462c380 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -98,4 +98,8 @@ jclass GetEmulatedControllerClass(); jfieldID GetEmulatedControllerPointer(); jmethodID GetEmulatedControllerConstructor(); +jclass GetNumericSettingClass(); +jfieldID GetNumericSettingPointer(); +jmethodID GetNumericSettingConstructor(); + } // namespace IDCache diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index 575847290e..96dbb9d014 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -19,6 +19,8 @@ add_library(main SHARED Input/EmulatedController.cpp Input/InputOverrider.cpp Input/MappingCommon.cpp + Input/NumericSetting.cpp + Input/NumericSetting.h IniFile.cpp MainAndroid.cpp RiivolutionPatches.cpp diff --git a/Source/Android/jni/Input/ControlGroup.cpp b/Source/Android/jni/Input/ControlGroup.cpp index 0e56774e84..01405a0e8b 100644 --- a/Source/Android/jni/Input/ControlGroup.cpp +++ b/Source/Android/jni/Input/ControlGroup.cpp @@ -10,6 +10,7 @@ #include "jni/AndroidCommon/AndroidCommon.h" #include "jni/AndroidCommon/IDCache.h" #include "jni/Input/Control.h" +#include "jni/Input/NumericSetting.h" static ControllerEmu::ControlGroup* GetPointer(JNIEnv* env, jobject obj) { @@ -48,4 +49,18 @@ Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_g { return ControlToJava(env, GetPointer(env, obj)->controls[i].get()); } + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getNumericSettingCount( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->numeric_settings.size()); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getNumericSetting( + JNIEnv* env, jobject obj, jint i) +{ + return NumericSettingToJava(env, GetPointer(env, obj)->numeric_settings[i].get()); +} } diff --git a/Source/Android/jni/Input/NumericSetting.cpp b/Source/Android/jni/Input/NumericSetting.cpp new file mode 100644 index 0000000000..32805d15f8 --- /dev/null +++ b/Source/Android/jni/Input/NumericSetting.cpp @@ -0,0 +1,139 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "jni/Input/NumericSetting.h" + +#include + +#include + +#include "Common/Assert.h" +#include "Common/MsgHandler.h" +#include "InputCommon/ControlReference/ControlReference.h" +#include "InputCommon/ControllerEmu/Setting/NumericSetting.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" +#include "jni/Input/ControlReference.h" + +static const char* NullStringToEmptyString(const char* str) +{ + return str ? str : ""; +} + +static ControllerEmu::NumericSettingBase* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetNumericSettingPointer())); +} + +template +static ControllerEmu::NumericSetting* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast*>( + env->GetLongField(obj, IDCache::GetNumericSettingPointer())); +} + +jobject NumericSettingToJava(JNIEnv* env, ControllerEmu::NumericSettingBase* numeric_setting) +{ + if (!numeric_setting) + return nullptr; + + return env->NewObject(IDCache::GetNumericSettingClass(), IDCache::GetNumericSettingConstructor(), + reinterpret_cast(numeric_setting)); +} + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiName( + JNIEnv* env, jobject obj) +{ + return ToJString(env, Common::GetStringT(GetPointer(env, obj)->GetUIName())); +} + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiSuffix( + JNIEnv* env, jobject obj) +{ + const char* str = NullStringToEmptyString(GetPointer(env, obj)->GetUISuffix()); + return ToJString(env, Common::GetStringT(str)); +} + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiDescription( + JNIEnv* env, jobject obj) +{ + const char* str = NullStringToEmptyString(GetPointer(env, obj)->GetUIDescription()); + return ToJString(env, Common::GetStringT(str)); +} + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getType( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->GetType()); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getControlReference( + JNIEnv* env, jobject obj) +{ + return ControlReferenceToJava(env, &GetPointer(env, obj)->GetInputReference()); +} + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleValue( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetValue(); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_setDoubleValue( + JNIEnv* env, jobject obj, jdouble value) +{ + GetPointer(env, obj)->SetValue(value); +} + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleDefaultValue( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetDefaultValue(); +} + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleMin( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetMinValue(); +} + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleMax( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetMaxValue(); +} + +JNIEXPORT jboolean JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getBooleanValue( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->GetValue()); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_setBooleanValue( + JNIEnv* env, jobject obj, jboolean value) +{ + GetPointer(env, obj)->SetValue(static_cast(value)); +} + +JNIEXPORT jboolean JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getBooleanDefaultValue( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->GetDefaultValue()); +} +} diff --git a/Source/Android/jni/Input/NumericSetting.h b/Source/Android/jni/Input/NumericSetting.h new file mode 100644 index 0000000000..a7f3a2124a --- /dev/null +++ b/Source/Android/jni/Input/NumericSetting.h @@ -0,0 +1,15 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace ControllerEmu +{ +class NumericSettingBase; +} + +class ControlReference; + +jobject NumericSettingToJava(JNIEnv* env, ControllerEmu::NumericSettingBase* control); From 0dd8bbbbbb91f701f284561f244fa4e6f29b5f61 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 18 Apr 2022 17:17:48 +0200 Subject: [PATCH 19/38] Android: Add extension selection --- .../input/model/InputMappingIntSetting.java | 48 +++++++++++++++++++ .../model/controlleremu/ControlGroup.java | 23 +++++++++ .../model/controlleremu/NumericSetting.java | 15 ++++++ .../ui/SettingsFragmentPresenter.java | 10 ++++ .../app/src/main/res/values/arrays.xml | 16 +++---- Source/Android/jni/Input/ControlGroup.cpp | 16 +++++++ Source/Android/jni/Input/NumericSetting.cpp | 21 ++++++++ 7 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.java new file mode 100644 index 0000000000..989f2b7cde --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.java @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; +import org.dolphinemu.dolphinemu.features.settings.model.Settings; + +public class InputMappingIntSetting implements AbstractIntSetting +{ + private final NumericSetting mNumericSetting; + + public InputMappingIntSetting(NumericSetting numericSetting) + { + mNumericSetting = numericSetting; + } + + @Override + public int getInt(Settings settings) + { + return mNumericSetting.getIntValue(); + } + + @Override + public void setInt(Settings settings, int newValue) + { + mNumericSetting.setIntValue(newValue); + } + + @Override + public boolean isOverridden(Settings settings) + { + return false; + } + + @Override + public boolean isRuntimeEditable() + { + return true; + } + + @Override + public boolean delete(Settings settings) + { + mNumericSetting.setIntValue(mNumericSetting.getIntDefaultValue()); + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java index 19423d5f14..0a7681af6d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java @@ -12,6 +12,21 @@ import androidx.annotation.Keep; */ public class ControlGroup { + public static final int TYPE_OTHER = 0; + public static final int TYPE_STICK = 1; + public static final int TYPE_MIXED_TRIGGERS = 2; + public static final int TYPE_BUTTONS = 3; + public static final int TYPE_FORCE = 4; + public static final int TYPE_ATTACHMENTS = 5; + public static final int TYPE_TILT = 6; + public static final int TYPE_CURSOR = 7; + public static final int TYPE_TRIGGERS = 8; + public static final int TYPE_SLIDER = 9; + public static final int TYPE_SHAKE = 10; + public static final int TYPE_IMU_ACCELEROMETER = 11; + public static final int TYPE_IMU_GYROSCOPE = 12; + public static final int TYPE_IMU_CURSOR = 13; + @Keep private final long mPointer; @@ -23,6 +38,8 @@ public class ControlGroup public native String getUiName(); + public native int getGroupType(); + public native int getControlCount(); public native Control getControl(int i); @@ -30,4 +47,10 @@ public class ControlGroup public native int getNumericSettingCount(); public native NumericSetting getNumericSetting(int i); + + /** + * If getGroupType returns TYPE_ATTACHMENTS, this returns the attachment selection setting. + * Otherwise, undefined behavior! + */ + public native NumericSetting getAttachmentSetting(); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java index 9baa2c41d7..bcb9dce181 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java @@ -47,6 +47,21 @@ public class NumericSetting public native ControlReference getControlReference(); + /** + * If the type is TYPE_INT, gets the current value. Otherwise, undefined behavior! + */ + public native int getIntValue(); + + /** + * If the type is TYPE_INT, sets the current value. Otherwise, undefined behavior! + */ + public native void setIntValue(int value); + + /** + * If the type is TYPE_INT, gets the default value. Otherwise, undefined behavior! + */ + public native int getIntDefaultValue(); + /** * If the type is TYPE_DOUBLE, gets the current value. Otherwise, undefined behavior! */ diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 775c9b3f8a..fded8e56de 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -16,6 +16,7 @@ import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.UserDataActivity; import org.dolphinemu.dolphinemu.features.input.model.InputMappingBooleanSetting; import org.dolphinemu.dolphinemu.features.input.model.InputMappingDoubleSetting; +import org.dolphinemu.dolphinemu.features.input.model.InputMappingIntSetting; import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; @@ -1117,6 +1118,15 @@ public final class SettingsFragmentPresenter sl.add(new InputMappingControlSetting(group.getControl(j), controller)); } + if (group.getGroupType() == ControlGroup.TYPE_ATTACHMENTS) + { + NumericSetting attachmentSetting = group.getAttachmentSetting(); + sl.add(new SingleChoiceSetting(mContext, new InputMappingIntSetting(attachmentSetting), + R.string.wiimote_extensions, 0, R.array.wiimoteExtensionsEntries, + R.array.wiimoteExtensionsValues, + MenuTag.getWiimoteExtensionMenuTag(mControllerNumber))); + } + int numericSettingCount = group.getNumericSettingCount(); for (int j = 0; j < numericSettingCount; j++) { diff --git a/Source/Android/app/src/main/res/values/arrays.xml b/Source/Android/app/src/main/res/values/arrays.xml index 4e27b8ade3..5afaf8ba2c 100644 --- a/Source/Android/app/src/main/res/values/arrays.xml +++ b/Source/Android/app/src/main/res/values/arrays.xml @@ -210,14 +210,14 @@ @string/extension_drums @string/extension_turntable - - None - Nunchuk - Classic - Guitar - Drums - Turntable - + + 0 + 1 + 2 + 3 + 4 + 5 + diff --git a/Source/Android/jni/Input/ControlGroup.cpp b/Source/Android/jni/Input/ControlGroup.cpp index 01405a0e8b..0b69145b36 100644 --- a/Source/Android/jni/Input/ControlGroup.cpp +++ b/Source/Android/jni/Input/ControlGroup.cpp @@ -6,6 +6,7 @@ #include #include "Common/MsgHandler.h" +#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h" #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" #include "jni/AndroidCommon/AndroidCommon.h" #include "jni/AndroidCommon/IDCache.h" @@ -36,6 +37,13 @@ Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_g return ToJString(env, Common::GetStringT(GetPointer(env, obj)->ui_name.c_str())); } +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getGroupType( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->type); +} + JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getControlCount( JNIEnv* env, jobject obj) @@ -63,4 +71,12 @@ Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_g { return NumericSettingToJava(env, GetPointer(env, obj)->numeric_settings[i].get()); } + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getAttachmentSetting( + JNIEnv* env, jobject obj) +{ + auto* group = reinterpret_cast(GetPointer(env, obj)); + return NumericSettingToJava(env, &group->GetSelectionSetting()); +} } diff --git a/Source/Android/jni/Input/NumericSetting.cpp b/Source/Android/jni/Input/NumericSetting.cpp index 32805d15f8..1625d172ae 100644 --- a/Source/Android/jni/Input/NumericSetting.cpp +++ b/Source/Android/jni/Input/NumericSetting.cpp @@ -81,6 +81,27 @@ Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting return ControlReferenceToJava(env, &GetPointer(env, obj)->GetInputReference()); } +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getIntValue( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetValue(); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_setIntValue( + JNIEnv* env, jobject obj, jint value) +{ + GetPointer(env, obj)->SetValue(value); +} + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getIntDefaultValue( + JNIEnv* env, jobject obj) +{ + return GetPointer(env, obj)->GetDefaultValue(); +} + JNIEXPORT jdouble JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleValue( JNIEnv* env, jobject obj) From e7530a1cb91d9b49257fbfb75cb000757b212489 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 18 Apr 2022 17:57:29 +0200 Subject: [PATCH 20/38] Android: Split Wii Remote settings into submenus Too much stuff on one screen otherwise. This split matches DolphinQt. --- .../features/settings/ui/MenuTag.java | 29 +++++++- .../settings/ui/SettingsFragment.java | 12 +++ .../ui/SettingsFragmentPresenter.java | 74 +++++++++++++++++-- .../app/src/main/res/values/strings.xml | 4 + 4 files changed, 113 insertions(+), 6 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java index 1d4fe28af9..f97a013074 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java @@ -38,7 +38,19 @@ public enum MenuTag WIIMOTE_EXTENSION_1("wiimote_extension", 0), WIIMOTE_EXTENSION_2("wiimote_extension", 1), WIIMOTE_EXTENSION_3("wiimote_extension", 2), - WIIMOTE_EXTENSION_4("wiimote_extension", 3); + WIIMOTE_EXTENSION_4("wiimote_extension", 3), + WIIMOTE_GENERAL_1("wiimote_general", 0), + WIIMOTE_GENERAL_2("wiimote_general", 1), + WIIMOTE_GENERAL_3("wiimote_general", 2), + WIIMOTE_GENERAL_4("wiimote_general", 3), + WIIMOTE_MOTION_SIMULATION_1("wiimote_motion_simulation", 0), + WIIMOTE_MOTION_SIMULATION_2("wiimote_motion_simulation", 1), + WIIMOTE_MOTION_SIMULATION_3("wiimote_motion_simulation", 2), + WIIMOTE_MOTION_SIMULATION_4("wiimote_motion_simulation", 3), + WIIMOTE_MOTION_INPUT_1("wiimote_motion_input", 0), + WIIMOTE_MOTION_INPUT_2("wiimote_motion_input", 1), + WIIMOTE_MOTION_INPUT_3("wiimote_motion_input", 2), + WIIMOTE_MOTION_INPUT_4("wiimote_motion_input", 3); private String tag; private int subType = -1; @@ -112,6 +124,21 @@ public enum MenuTag return getMenuTag("wiimote_extension", subtype); } + public static MenuTag getWiimoteGeneralMenuTag(int subtype) + { + return getMenuTag("wiimote_general", subtype); + } + + public static MenuTag getWiimoteMotionSimulationMenuTag(int subtype) + { + return getMenuTag("wiimote_motion_simulation", subtype); + } + + public static MenuTag getWiimoteMotionInputMenuTag(int subtype) + { + return getMenuTag("wiimote_motion_input", subtype); + } + private static MenuTag getMenuTag(String tag, int subtype) { for (MenuTag menuTag : MenuTag.values()) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java index bb73093656..11ed282cf4 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java @@ -75,6 +75,18 @@ public final class SettingsFragment extends Fragment implements SettingsFragment titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_1); titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_2); titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_3); + titles.put(MenuTag.WIIMOTE_GENERAL_1, R.string.wiimote_general); + titles.put(MenuTag.WIIMOTE_GENERAL_2, R.string.wiimote_general); + titles.put(MenuTag.WIIMOTE_GENERAL_3, R.string.wiimote_general); + titles.put(MenuTag.WIIMOTE_GENERAL_4, R.string.wiimote_general); + titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_1, R.string.wiimote_motion_simulation); + titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_2, R.string.wiimote_motion_simulation); + titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_3, R.string.wiimote_motion_simulation); + titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_4, R.string.wiimote_motion_simulation); + titles.put(MenuTag.WIIMOTE_MOTION_INPUT_1, R.string.wiimote_motion_input); + titles.put(MenuTag.WIIMOTE_MOTION_INPUT_2, R.string.wiimote_motion_input); + titles.put(MenuTag.WIIMOTE_MOTION_INPUT_3, R.string.wiimote_motion_input); + titles.put(MenuTag.WIIMOTE_MOTION_INPUT_4, R.string.wiimote_motion_input); } private FragmentSettingsBinding mBinding; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index fded8e56de..3f60937810 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -10,6 +10,7 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.collection.ArraySet; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; @@ -56,8 +57,11 @@ import org.dolphinemu.dolphinemu.utils.ThreadUtil; import org.dolphinemu.dolphinemu.utils.WiiUtils; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; public final class SettingsFragmentPresenter { @@ -248,6 +252,27 @@ public final class SettingsFragmentPresenter addExtensionTypeSettings(sl, mControllerNumber, mControllerType); break; + case WIIMOTE_GENERAL_1: + case WIIMOTE_GENERAL_2: + case WIIMOTE_GENERAL_3: + case WIIMOTE_GENERAL_4: + addWiimoteGeneralSubSettings(sl, mControllerNumber); + break; + + case WIIMOTE_MOTION_SIMULATION_1: + case WIIMOTE_MOTION_SIMULATION_2: + case WIIMOTE_MOTION_SIMULATION_3: + case WIIMOTE_MOTION_SIMULATION_4: + addWiimoteMotionSimulationSubSettings(sl, mControllerNumber); + break; + + case WIIMOTE_MOTION_INPUT_1: + case WIIMOTE_MOTION_INPUT_2: + case WIIMOTE_MOTION_INPUT_3: + case WIIMOTE_MOTION_INPUT_4: + addWiimoteMotionInputSubSettings(sl, mControllerNumber); + break; + default: throw new UnsupportedOperationException("Unimplemented menu"); } @@ -1081,7 +1106,7 @@ public final class SettingsFragmentPresenter { if (gcPadType == 6) // Emulated { - addControllerSettings(sl, EmulatedController.getGcPad(gcPadNumber)); + addControllerSettings(sl, EmulatedController.getGcPad(gcPadNumber), null); } else if (gcPadType == 12) // Adapter { @@ -1094,22 +1119,61 @@ public final class SettingsFragmentPresenter private void addWiimoteSubSettings(ArrayList sl, int wiimoteNumber) { - addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber)); + sl.add(new HeaderSetting(mContext, R.string.wiimote, 0)); + sl.add(new SubmenuSetting(mContext, R.string.wiimote_general, + MenuTag.getWiimoteGeneralMenuTag(wiimoteNumber))); + sl.add(new SubmenuSetting(mContext, R.string.wiimote_motion_simulation, + MenuTag.getWiimoteMotionSimulationMenuTag(wiimoteNumber))); + sl.add(new SubmenuSetting(mContext, R.string.wiimote_motion_input, + MenuTag.getWiimoteMotionInputMenuTag(wiimoteNumber))); + + addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), + Collections.singleton(ControlGroup.TYPE_ATTACHMENTS)); } private void addExtensionTypeSettings(ArrayList sl, int wiimoteNumber, int extensionType) { addControllerSettings(sl, - EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType)); + EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType), null); } - private void addControllerSettings(ArrayList sl, EmulatedController controller) + private void addWiimoteGeneralSubSettings(ArrayList sl, int wiimoteNumber) + { + addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), + new ArraySet<>(Arrays.asList(ControlGroup.TYPE_BUTTONS, ControlGroup.TYPE_OTHER))); + } + + private void addWiimoteMotionSimulationSubSettings(ArrayList sl, int wiimoteNumber) + { + addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), + new ArraySet<>(Arrays.asList(ControlGroup.TYPE_FORCE, ControlGroup.TYPE_TILT, + ControlGroup.TYPE_CURSOR, ControlGroup.TYPE_SHAKE))); + } + + private void addWiimoteMotionInputSubSettings(ArrayList sl, int wiimoteNumber) + { + addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), + new ArraySet<>(Arrays.asList(ControlGroup.TYPE_IMU_ACCELEROMETER, + ControlGroup.TYPE_IMU_GYROSCOPE, ControlGroup.TYPE_IMU_CURSOR))); + } + + /** + * @param sl The list to place controller settings into. + * @param controller The controller to add settings for. + * @param groupTypeFilter If this is non-null, only groups whose types match this are considered. + */ + private void addControllerSettings(ArrayList sl, EmulatedController controller, + Set groupTypeFilter) { int groupCount = controller.getGroupCount(); for (int i = 0; i < groupCount; i++) { ControlGroup group = controller.getGroup(i); + int groupType = group.getGroupType(); + if (groupTypeFilter != null && !groupTypeFilter.contains(groupType)) + continue; + sl.add(new HeaderSetting(group.getUiName(), "")); int controlCount = group.getControlCount(); @@ -1118,7 +1182,7 @@ public final class SettingsFragmentPresenter sl.add(new InputMappingControlSetting(group.getControl(j), controller)); } - if (group.getGroupType() == ControlGroup.TYPE_ATTACHMENTS) + if (groupType == ControlGroup.TYPE_ATTACHMENTS) { NumericSetting attachmentSetting = group.getAttachmentSetting(); sl.add(new SingleChoiceSetting(mContext, new InputMappingIntSetting(attachmentSetting), diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index a1c7a32f95..ea3942cdcb 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -21,6 +21,10 @@ Wii Remote Extension 3 Wii Remote Extension 4 + Wii Remote + General and Options + Motion Simulation + Motion Input Extension Input Binding From a78dca5fb08d7c0f18d3d43d5b3b485549a5a8ce Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 17 Sep 2022 14:16:45 +0200 Subject: [PATCH 21/38] Android: Move Sideways Wii Remote setting one submenu up A slight departure from DolphinQt, but I think it makes sense, because it will make the important Sideways Wii Remote setting easier to find. --- .../features/settings/ui/SettingsFragmentPresenter.java | 6 ++++-- Source/Android/app/src/main/res/values/strings.xml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 3f60937810..f731a3426f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -1127,8 +1127,10 @@ public final class SettingsFragmentPresenter sl.add(new SubmenuSetting(mContext, R.string.wiimote_motion_input, MenuTag.getWiimoteMotionInputMenuTag(wiimoteNumber))); + // TYPE_OTHER is included here instead of in addWiimoteGeneralSubSettings so that touchscreen + // users won't have to dig into a submenu to find the Sideways Wii Remote setting addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), - Collections.singleton(ControlGroup.TYPE_ATTACHMENTS)); + new ArraySet<>(Arrays.asList(ControlGroup.TYPE_ATTACHMENTS, ControlGroup.TYPE_OTHER))); } private void addExtensionTypeSettings(ArrayList sl, int wiimoteNumber, @@ -1141,7 +1143,7 @@ public final class SettingsFragmentPresenter private void addWiimoteGeneralSubSettings(ArrayList sl, int wiimoteNumber) { addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), - new ArraySet<>(Arrays.asList(ControlGroup.TYPE_BUTTONS, ControlGroup.TYPE_OTHER))); + Collections.singleton(ControlGroup.TYPE_BUTTONS)); } private void addWiimoteMotionSimulationSubSettings(ArrayList sl, int wiimoteNumber) diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index ea3942cdcb..4fd30b59fb 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -22,7 +22,7 @@ Wii Remote Extension 4 Wii Remote - General and Options + Buttons Motion Simulation Motion Input Extension From 2113bf5e3afface4acd072f677c620c60bd6edde Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 18 Apr 2022 18:54:58 +0200 Subject: [PATCH 22/38] Android: Implement enabling/disabling control groups All this code for just a single checkbox... Ah well, it has to be done --- .../model/ControlGroupEnabledSetting.java | 50 +++++++++++++++++++ .../model/controlleremu/ControlGroup.java | 10 ++++ .../ui/SettingsFragmentPresenter.java | 7 +++ .../app/src/main/res/values/strings.xml | 1 + Source/Android/jni/Input/ControlGroup.cpp | 21 ++++++++ 5 files changed, 89 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.java new file mode 100644 index 0000000000..95aa080429 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.java @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; +import org.dolphinemu.dolphinemu.features.settings.model.Settings; + +public class ControlGroupEnabledSetting implements AbstractBooleanSetting +{ + private final ControlGroup mControlGroup; + + public ControlGroupEnabledSetting(ControlGroup controlGroup) + { + mControlGroup = controlGroup; + } + + @Override + public boolean getBoolean(Settings settings) + { + return mControlGroup.getEnabled(); + } + + @Override + public void setBoolean(Settings settings, boolean newValue) + { + mControlGroup.setEnabled(newValue); + } + + @Override + public boolean isOverridden(Settings settings) + { + return false; + } + + @Override + public boolean isRuntimeEditable() + { + return true; + } + + @Override + public boolean delete(Settings settings) + { + boolean newValue = mControlGroup.getDefaultEnabledValue() != ControlGroup.DEFAULT_ENABLED_NO; + mControlGroup.setEnabled(newValue); + + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java index 0a7681af6d..1bafd4a1f2 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java @@ -27,6 +27,10 @@ public class ControlGroup public static final int TYPE_IMU_GYROSCOPE = 12; public static final int TYPE_IMU_CURSOR = 13; + public static final int DEFAULT_ENABLED_ALWAYS = 0; + public static final int DEFAULT_ENABLED_YES = 1; + public static final int DEFAULT_ENABLED_NO = 2; + @Keep private final long mPointer; @@ -40,6 +44,12 @@ public class ControlGroup public native int getGroupType(); + public native int getDefaultEnabledValue(); + + public native boolean getEnabled(); + + public native void setEnabled(boolean value); + public native int getControlCount(); public native Control getControl(int i); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index f731a3426f..8ace1df30b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -19,6 +19,7 @@ import org.dolphinemu.dolphinemu.features.input.model.InputMappingBooleanSetting import org.dolphinemu.dolphinemu.features.input.model.InputMappingDoubleSetting; import org.dolphinemu.dolphinemu.features.input.model.InputMappingIntSetting; import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; +import org.dolphinemu.dolphinemu.features.input.model.ControlGroupEnabledSetting; 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.view.InputMappingControlSetting; @@ -1178,6 +1179,12 @@ public final class SettingsFragmentPresenter sl.add(new HeaderSetting(group.getUiName(), "")); + if (group.getDefaultEnabledValue() != ControlGroup.DEFAULT_ENABLED_ALWAYS) + { + sl.add(new SwitchSetting(mContext, new ControlGroupEnabledSetting(group), R.string.enabled, + 0)); + } + int controlCount = group.getControlCount(); for (int j = 0; j < controlCount; j++) { diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 4fd30b59fb..7099a1e33f 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -585,6 +585,7 @@ It can efficiently compress both junk data and encrypted Wii data. Dolphin does not have permission to access one or more configured paths. Would you like to fix this before starting? + Enabled Total Pitch Total Yaw Vertical Offset diff --git a/Source/Android/jni/Input/ControlGroup.cpp b/Source/Android/jni/Input/ControlGroup.cpp index 0b69145b36..045ab1b519 100644 --- a/Source/Android/jni/Input/ControlGroup.cpp +++ b/Source/Android/jni/Input/ControlGroup.cpp @@ -44,6 +44,27 @@ Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_g return static_cast(GetPointer(env, obj)->type); } +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getDefaultEnabledValue( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->default_value); +} + +JNIEXPORT jboolean JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getEnabled( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->enabled); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_setEnabled( + JNIEnv* env, jobject obj, jboolean value) +{ + GetPointer(env, obj)->enabled = value; +} + JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getControlCount( JNIEnv* env, jobject obj) From 2b1dd52750c8c85796be4a6785fb769481ec191c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 14 Aug 2022 11:32:22 +0200 Subject: [PATCH 23/38] Android: Add input device selection --- .../input/model/ControllerInterface.java | 2 + .../features/input/model/MappingCommon.java | 9 ++- .../controlleremu/EmulatedController.java | 4 ++ .../input/model/view/InputDeviceSetting.java | 64 +++++++++++++++++++ .../view/InputMappingControlSetting.java | 5 ++ .../features/input/ui/MotionAlertDialog.java | 2 +- .../settings/model/view/SettingsItem.java | 5 ++ .../model/view/StringSingleChoiceSetting.java | 26 +++++++- .../features/settings/ui/SettingsAdapter.java | 2 + .../ui/SettingsFragmentPresenter.java | 40 +++++++++--- .../ui/viewholder/SettingViewHolder.java | 2 +- .../ui/viewholder/SingleChoiceViewHolder.java | 6 +- .../app/src/main/res/values/strings.xml | 2 + Source/Android/jni/CMakeLists.txt | 1 + .../Android/jni/Input/EmulatedController.cpp | 23 +++++-- Source/Android/jni/Input/EmulatedController.h | 13 ++++ Source/Android/jni/Input/MappingCommon.cpp | 16 ++++- .../ControllerInterface/Android/Android.cpp | 7 ++ 18 files changed, 204 insertions(+), 25 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java create mode 100644 Source/Android/jni/Input/EmulatedController.h diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java index 0d6a078b4e..b2535465e8 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java @@ -95,6 +95,8 @@ public final class ControllerInterface */ public static native void refreshDevices(); + public static native String[] getAllDeviceStrings(); + @Keep private static void registerInputDeviceListener() { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java index cb136a908d..b0dc489fec 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java @@ -2,6 +2,10 @@ package org.dolphinemu.dolphinemu.features.input.model; +import androidx.annotation.NonNull; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; + public final class MappingCommon { private MappingCommon() @@ -15,10 +19,13 @@ public final class MappingCommon * When this is being called, a separate thread must be calling ControllerInterface's * dispatchKeyEvent and dispatchGenericMotionEvent, otherwise no inputs will be registered. * + * @param controller The device to detect inputs from. + * @param allDevices Whether to also detect inputs from devices other than the specified one. * @return The input(s) pressed by the user in the form of an InputCommon expression, * or an empty string if there were no inputs. */ - public static native String detectInput(); + public static native String detectInput(@NonNull EmulatedController controller, + boolean allDevices); public static native void save(); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java index b0fbd0fd92..5a5793f530 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java @@ -21,6 +21,10 @@ public class EmulatedController mPointer = pointer; } + public native String getDefaultDevice(); + + public native void setDefaultDevice(String device); + public native int getGroupCount(); public native ControlGroup getGroup(int index); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java new file mode 100644 index 0000000000..2462e867d3 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.view; + +import android.content.Context; + +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; +import org.dolphinemu.dolphinemu.features.settings.model.Settings; +import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting; + +public class InputDeviceSetting extends StringSingleChoiceSetting +{ + private final EmulatedController mController; + + public InputDeviceSetting(Context context, int titleId, int descriptionId, + EmulatedController controller) + { + super(context, null, titleId, descriptionId, null, null, null); + + mController = controller; + + refreshChoicesAndValues(); + } + + @Override + public String getSelectedChoice(Settings settings) + { + return mController.getDefaultDevice(); + } + + @Override + public String getSelectedValue(Settings settings) + { + return mController.getDefaultDevice(); + } + + @Override + public void setSelectedValue(Settings settings, String newValue) + { + mController.setDefaultDevice(newValue); + } + + @Override + public void refreshChoicesAndValues() + { + String[] devices = ControllerInterface.getAllDeviceStrings(); + + mChoices = devices; + mValues = devices; + } + + @Override + public boolean canClear() + { + return true; + } + + @Override + public void clear(Settings settings) + { + setSelectedValue(settings, ""); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java index 8888e2430b..a858b35099 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java @@ -47,4 +47,9 @@ public final class InputMappingControlSetting extends SettingsItem { return null; } + + public EmulatedController getController() + { + return mController; + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java index f36d9bc478..0a4afc7acf 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java @@ -46,7 +46,7 @@ public final class MotionAlertDialog extends AlertDialog mRunning = true; new Thread(() -> { - String result = MappingCommon.detectInput(); + String result = MappingCommon.detectInput(mSetting.getController(), false); mActivity.runOnUiThread(() -> { if (mRunning) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java index 9c7b89c12f..6f06677be3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java @@ -95,6 +95,11 @@ public abstract class SettingsItem return getSetting() != null; } + public boolean canClear() + { + return hasSetting(); + } + public void clear(Settings settings) { getSetting().delete(settings); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.java index df7ee6f4e9..70463f216b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.java @@ -14,8 +14,8 @@ public class StringSingleChoiceSetting extends SettingsItem { private AbstractStringSetting mSetting; - private String[] mChoices; - private String[] mValues; + protected String[] mChoices; + protected String[] mValues; private MenuTag mMenuTag; public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId, @@ -60,6 +60,19 @@ public class StringSingleChoiceSetting extends SettingsItem return mValues; } + public String getChoiceAt(int index) + { + if (mChoices == null) + return null; + + if (index >= 0 && index < mChoices.length) + { + return mChoices[index]; + } + + return ""; + } + public String getValueAt(int index) { if (mValues == null) @@ -73,6 +86,11 @@ public class StringSingleChoiceSetting extends SettingsItem return ""; } + public String getSelectedChoice(Settings settings) + { + return getChoiceAt(getSelectedValueIndex(settings)); + } + public String getSelectedValue(Settings settings) { return mSetting.getString(settings); @@ -102,6 +120,10 @@ public class StringSingleChoiceSetting extends SettingsItem mSetting.setString(settings, selection); } + public void refreshChoicesAndValues() + { + } + @Override public int getType() { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index 8fe3fe4602..cd2d047ecb 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -253,6 +253,8 @@ public final class SettingsAdapter extends RecyclerView.Adapter sl, int wiimoteNumber) { + EmulatedController wiimote = EmulatedController.getWiimote(wiimoteNumber); + + addControllerMetaSettings(sl, wiimote); + sl.add(new HeaderSetting(mContext, R.string.wiimote, 0)); sl.add(new SubmenuSetting(mContext, R.string.wiimote_general, MenuTag.getWiimoteGeneralMenuTag(wiimoteNumber))); @@ -1130,44 +1138,58 @@ public final class SettingsFragmentPresenter // TYPE_OTHER is included here instead of in addWiimoteGeneralSubSettings so that touchscreen // users won't have to dig into a submenu to find the Sideways Wii Remote setting - addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), + addControllerMappingSettings(sl, wiimote, new ArraySet<>(Arrays.asList(ControlGroup.TYPE_ATTACHMENTS, ControlGroup.TYPE_OTHER))); } private void addExtensionTypeSettings(ArrayList sl, int wiimoteNumber, int extensionType) { - addControllerSettings(sl, + addControllerMappingSettings(sl, EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType), null); } private void addWiimoteGeneralSubSettings(ArrayList sl, int wiimoteNumber) { - addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), + addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber), Collections.singleton(ControlGroup.TYPE_BUTTONS)); } private void addWiimoteMotionSimulationSubSettings(ArrayList sl, int wiimoteNumber) { - addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), + addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber), new ArraySet<>(Arrays.asList(ControlGroup.TYPE_FORCE, ControlGroup.TYPE_TILT, ControlGroup.TYPE_CURSOR, ControlGroup.TYPE_SHAKE))); } private void addWiimoteMotionInputSubSettings(ArrayList sl, int wiimoteNumber) { - addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber), + addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber), new ArraySet<>(Arrays.asList(ControlGroup.TYPE_IMU_ACCELEROMETER, ControlGroup.TYPE_IMU_GYROSCOPE, ControlGroup.TYPE_IMU_CURSOR))); } /** - * @param sl The list to place controller settings into. + * Adds settings and actions that apply to a controller as a whole. + * For instance, the device setting and the Clear action. + * + * @param sl The list to place controller settings into. * @param controller The controller to add settings for. + */ + private void addControllerMetaSettings(ArrayList sl, EmulatedController controller) + { + sl.add(new InputDeviceSetting(mContext, R.string.input_device, 0, controller)); + } + + /** + * Adds mapping settings and other control-specific settings. + * + * @param sl The list to place controller settings into. + * @param controller The controller to add settings for. * @param groupTypeFilter If this is non-null, only groups whose types match this are considered. */ - private void addControllerSettings(ArrayList sl, EmulatedController controller, - Set groupTypeFilter) + private void addControllerMappingSettings(ArrayList sl, + EmulatedController controller, Set groupTypeFilter) { int groupCount = controller.getGroupCount(); for (int i = 0; i < groupCount; i++) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.java index d34e4a1536..18fd466461 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.java @@ -83,7 +83,7 @@ public abstract class SettingViewHolder extends RecyclerView.ViewHolder { SettingsItem item = getItem(); - if (item == null || !item.hasSetting()) + if (item == null || !item.canClear()) return false; if (!item.isEditable()) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.java index db56ebea40..beb15867c5 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.java @@ -64,10 +64,8 @@ public final class SingleChoiceViewHolder extends SettingViewHolder else if (item instanceof StringSingleChoiceSetting) { StringSingleChoiceSetting setting = (StringSingleChoiceSetting) item; - String[] choices = setting.getChoices(); - int valueIndex = setting.getSelectedValueIndex(settings); - if (valueIndex != -1) - mBinding.textSettingDescription.setText(choices[valueIndex]); + String choice = setting.getSelectedChoice(settings); + mBinding.textSettingDescription.setText(choice); } else if (item instanceof SingleChoiceSettingDynamicDescriptions) { diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 7099a1e33f..3cde229c28 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -27,6 +27,8 @@ Motion Input Extension + Device + Input Binding Press or move an input to bind it to %1$s. diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index 96dbb9d014..0b0d50a9ec 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(main SHARED Input/ControlReference.cpp Input/ControlReference.h Input/EmulatedController.cpp + Input/EmulatedController.h Input/InputOverrider.cpp Input/MappingCommon.cpp Input/NumericSetting.cpp diff --git a/Source/Android/jni/Input/EmulatedController.cpp b/Source/Android/jni/Input/EmulatedController.cpp index af6ba3b48a..ba9e454dae 100644 --- a/Source/Android/jni/Input/EmulatedController.cpp +++ b/Source/Android/jni/Input/EmulatedController.cpp @@ -16,7 +16,7 @@ #include "jni/Input/ControlGroup.h" #include "jni/Input/ControlReference.h" -static ControllerEmu::EmulatedController* GetPointer(JNIEnv* env, jobject obj) +ControllerEmu::EmulatedController* EmulatedControllerFromJava(JNIEnv* env, jobject obj) { return reinterpret_cast( env->GetLongField(obj, IDCache::GetEmulatedControllerPointer())); @@ -34,25 +34,40 @@ static jobject EmulatedControllerToJava(JNIEnv* env, ControllerEmu::EmulatedCont extern "C" { +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getDefaultDevice( + JNIEnv* env, jobject obj) +{ + return ToJString(env, EmulatedControllerFromJava(env, obj)->GetDefaultDevice().ToString()); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_setDefaultDevice( + JNIEnv* env, jobject obj, jstring j_device) +{ + return EmulatedControllerFromJava(env, obj)->SetDefaultDevice(GetJString(env, j_device)); +} + JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroupCount( JNIEnv* env, jobject obj) { - return static_cast(GetPointer(env, obj)->groups.size()); + return static_cast(EmulatedControllerFromJava(env, obj)->groups.size()); } JNIEXPORT jobject JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroup( JNIEnv* env, jobject obj, jint controller_index) { - return ControlGroupToJava(env, GetPointer(env, obj)->groups[controller_index].get()); + return ControlGroupToJava(env, + EmulatedControllerFromJava(env, obj)->groups[controller_index].get()); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_updateSingleControlReference( JNIEnv* env, jobject obj, jobject control_reference) { - return GetPointer(env, obj)->UpdateSingleControlReference( + return EmulatedControllerFromJava(env, obj)->UpdateSingleControlReference( g_controller_interface, ControlReferenceFromJava(env, control_reference)); } diff --git a/Source/Android/jni/Input/EmulatedController.h b/Source/Android/jni/Input/EmulatedController.h new file mode 100644 index 0000000000..cfe8674ac6 --- /dev/null +++ b/Source/Android/jni/Input/EmulatedController.h @@ -0,0 +1,13 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace ControllerEmu +{ +class ControlReference; +} + +ControllerEmu::EmulatedController* EmulatedControllerFromJava(JNIEnv* env, jobject obj); diff --git a/Source/Android/jni/Input/MappingCommon.cpp b/Source/Android/jni/Input/MappingCommon.cpp index c02f1c2ae5..4e5d88ccf6 100644 --- a/Source/Android/jni/Input/MappingCommon.cpp +++ b/Source/Android/jni/Input/MappingCommon.cpp @@ -17,6 +17,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/MappingCommon.h" #include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/Input/EmulatedController.h" namespace { @@ -28,10 +29,19 @@ constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5); extern "C" { JNIEXPORT jstring JNICALL -Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_detectInput(JNIEnv* env, jclass) +Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_detectInput( + JNIEnv* env, jclass, jobject j_emulated_controller, jboolean all_devices) { - const std::vector device_strings = g_controller_interface.GetAllDeviceStrings(); - const ciface::Core::DeviceQualifier default_device{}; + ControllerEmu::EmulatedController* emulated_controller = + EmulatedControllerFromJava(env, j_emulated_controller); + + const ciface::Core::DeviceQualifier default_device = emulated_controller->GetDefaultDevice(); + + std::vector device_strings; + if (all_devices) + device_strings = g_controller_interface.GetAllDeviceStrings(); + else + device_strings = {default_device.ToString()}; auto detections = g_controller_interface.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME, diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 811343bf73..d2ccec4bb3 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -1122,4 +1122,11 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_refreshD { g_controller_interface.RefreshDevices(); } + +JNIEXPORT jobjectArray JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getAllDeviceStrings( + JNIEnv* env, jclass) +{ + return VectorToJStringArray(env, g_controller_interface.GetAllDeviceStrings()); +} } From a1cc19f443fb9c39d1341f3aa1c80b1ecfe0ec4d Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 30 Oct 2022 17:20:10 +0100 Subject: [PATCH 24/38] Android: Add "Create mappings for other devices" --- .../features/input/ui/MotionAlertDialog.java | 12 ++++--- .../settings/ui/SettingsActivity.java | 25 ++++++++++++-- .../settings/ui/SettingsActivityView.java | 12 +++++++ .../features/settings/ui/SettingsAdapter.java | 3 +- .../settings/ui/SettingsFragment.java | 12 +++++++ .../ui/SettingsFragmentPresenter.java | 34 +++++++++++++++++++ .../settings/ui/SettingsFragmentView.java | 12 +++++++ .../app/src/main/res/values/strings.xml | 2 ++ 8 files changed, 105 insertions(+), 7 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java index 0a4afc7acf..9872bdd01e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java @@ -22,20 +22,24 @@ public final class MotionAlertDialog extends AlertDialog { private final Activity mActivity; private final InputMappingControlSetting mSetting; + private final boolean mAllDevices; private boolean mRunning = false; /** * Constructor * - * @param activity The current {@link Activity}. - * @param setting The setting to show this dialog for. + * @param activity The current {@link Activity}. + * @param setting The setting to show this dialog for. + * @param allDevices Whether to detect inputs from devices other than the configured one. */ - public MotionAlertDialog(Activity activity, InputMappingControlSetting setting) + public MotionAlertDialog(Activity activity, InputMappingControlSetting setting, + boolean allDevices) { super(activity); mActivity = activity; mSetting = setting; + mAllDevices = allDevices; } @Override @@ -46,7 +50,7 @@ public final class MotionAlertDialog extends AlertDialog mRunning = true; new Thread(() -> { - String result = MappingCommon.detectInput(mSetting.getController(), false); + String result = MappingCommon.detectInput(mSetting.getController(), mAllDevices); mActivity.runOnUiThread(() -> { if (mRunning) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java index 2af17e2d2f..f10a915bcc 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java @@ -41,15 +41,17 @@ public final class SettingsActivity extends AppCompatActivity implements Setting private static final String ARG_GAME_ID = "game_id"; private static final String ARG_REVISION = "revision"; private static final String ARG_IS_WII = "is_wii"; + private static final String KEY_MAPPING_ALL_DEVICES = "all_devices"; private static final String FRAGMENT_TAG = "settings"; + private SettingsActivityPresenter mPresenter; - private AlertDialog dialog; - private CollapsingToolbarLayout mToolbarLayout; private ActivitySettingsBinding mBinding; + private boolean mMappingAllDevices = false; + public static void launch(Context context, MenuTag menuTag, String gameId, int revision, boolean isWii) { @@ -82,6 +84,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting { MainPresenter.skipRescanningLibrary(); } + else + { + mMappingAllDevices = savedInstanceState.getBoolean(KEY_MAPPING_ALL_DEVICES); + } mBinding = ActivitySettingsBinding.inflate(getLayoutInflater()); setContentView(mBinding.getRoot()); @@ -125,7 +131,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting { // Critical: If super method is not called, rotations will be busted. super.onSaveInstanceState(outState); + mPresenter.saveState(outState); + + outState.putBoolean(KEY_MAPPING_ALL_DEVICES, mMappingAllDevices); } @Override @@ -333,6 +342,18 @@ public final class SettingsActivity extends AppCompatActivity implements Setting mToolbarLayout.setTitle(title); } + @Override + public void setMappingAllDevices(boolean allDevices) + { + mMappingAllDevices = allDevices; + } + + @Override + public boolean isMappingAllDevices() + { + return mMappingAllDevices; + } + private void setInsets() { ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbarSettings, (v, windowInsets) -> diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java index c835aa4399..d71d729af8 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java @@ -97,4 +97,16 @@ public interface SettingsActivityView * Accesses the material toolbar layout and changes the title */ void setToolbarTitle(String title); + + /** + * Sets whether the input mapping dialog should detect inputs from all devices, + * not just the device configured for the controller. + */ + void setMappingAllDevices(boolean allDevices); + + /** + * Returns whether the input mapping dialog should detect inputs from all devices, + * not just the device configured for the controller. + */ + boolean isMappingAllDevices(); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index cd2d047ecb..10ca9ddef2 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -312,7 +312,8 @@ public final class SettingsAdapter extends RecyclerView.Adapter diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index c0540cefed..e2199d2922 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -1179,6 +1179,40 @@ public final class SettingsFragmentPresenter private void addControllerMetaSettings(ArrayList sl, EmulatedController controller) { sl.add(new InputDeviceSetting(mContext, R.string.input_device, 0, controller)); + + sl.add(new SwitchSetting(mContext, new AbstractBooleanSetting() + { + @Override + public boolean isOverridden(Settings settings) + { + return false; + } + + @Override + public boolean isRuntimeEditable() + { + return true; + } + + @Override + public boolean delete(Settings settings) + { + mView.setMappingAllDevices(false); + return true; + } + + @Override + public boolean getBoolean(Settings settings) + { + return mView.isMappingAllDevices(); + } + + @Override + public void setBoolean(Settings settings, boolean newValue) + { + mView.setMappingAllDevices(newValue); + } + }, R.string.input_device_all_devices, R.string.input_device_all_devices_description)); } /** diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java index 295ff95abd..280657158f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java @@ -89,4 +89,16 @@ public interface SettingsFragmentView * @param value The current value of the setting. */ boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value); + + /** + * Sets whether the input mapping dialog should detect inputs from all devices, + * not just the device configured for the controller. + */ + void setMappingAllDevices(boolean allDevices); + + /** + * Returns whether the input mapping dialog should detect inputs from all devices, + * not just the device configured for the controller. + */ + boolean isMappingAllDevices(); } diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 3cde229c28..f8c4852027 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -28,6 +28,8 @@ Extension Device + Create Mappings for Other Devices + Detects inputs from all devices, not just the selected device. Input Binding Press or move an input to bind it to %1$s. From ea3200d4ba9378c4dbec647c75b994a0c7aefced Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 30 Oct 2022 19:06:04 +0100 Subject: [PATCH 25/38] Android: Prevent showing mapping dialog with no device selected --- .../dolphinemu/features/settings/ui/SettingsAdapter.java | 9 +++++++++ Source/Android/app/src/main/res/values/strings.xml | 1 + 2 files changed, 10 insertions(+) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index 10ca9ddef2..2cf2883fda 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -312,6 +312,15 @@ public final class SettingsAdapter extends RecyclerView.AdapterInput Binding Press or move an input to bind it to %1$s. + You need to select a device first! Settings From 8b78f73e80dbd7c98b778aa33d0e04d71987ba3c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 14 Aug 2022 13:05:30 +0200 Subject: [PATCH 26/38] Android: Add Default and Clear actions for controllers --- .../controlleremu/EmulatedController.java | 4 ++++ .../ui/SettingsFragmentPresenter.java | 19 +++++++++++++++ .../app/src/main/res/values/strings.xml | 5 ++++ .../Android/jni/Input/EmulatedController.cpp | 24 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java index 5a5793f530..5f6275ffcd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java @@ -31,6 +31,10 @@ public class EmulatedController public native void updateSingleControlReference(ControlReference controlReference); + public native void loadDefaultSettings(); + + public native void clearSettings(); + public static native EmulatedController getGcPad(int controllerIndex); public static native EmulatedController getWiimote(int controllerIndex); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index e2199d2922..8d9a34c252 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -1213,6 +1213,13 @@ public final class SettingsFragmentPresenter mView.setMappingAllDevices(newValue); } }, R.string.input_device_all_devices, R.string.input_device_all_devices_description)); + + sl.add(new RunRunnable(mContext, R.string.input_reset_to_default, + R.string.input_reset_to_default_description, R.string.input_reset_warning, 0, true, + () -> loadDefaultControllerSettings(controller))); + + sl.add(new RunRunnable(mContext, R.string.input_clear, R.string.input_clear_description, + R.string.input_reset_warning, 0, true, () -> clearControllerSettings(controller))); } /** @@ -1280,6 +1287,18 @@ public final class SettingsFragmentPresenter } } + private void loadDefaultControllerSettings(EmulatedController controller) + { + controller.loadDefaultSettings(); + mView.getAdapter().notifyAllSettingsChanged(); + } + + private void clearControllerSettings(EmulatedController controller) + { + controller.clearSettings(); + mView.getAdapter().notifyAllSettingsChanged(); + } + private static int getLogVerbosityEntries() { // Value obtained from LogLevel in Common/Logging/Log.h diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index ff41d8a631..16252e6b84 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -30,6 +30,11 @@ Device Create Mappings for Other Devices Detects inputs from all devices, not just the selected device. + Default + Reset settings for this controller to the default. + Clear + Clear settings for this controller. + Are you sure? Your current controller settings will be deleted. Input Binding Press or move an input to bind it to %1$s. diff --git a/Source/Android/jni/Input/EmulatedController.cpp b/Source/Android/jni/Input/EmulatedController.cpp index ba9e454dae..fb38471738 100644 --- a/Source/Android/jni/Input/EmulatedController.cpp +++ b/Source/Android/jni/Input/EmulatedController.cpp @@ -3,6 +3,7 @@ #include +#include "Common/IniFile.h" #include "Core/HW/GCPad.h" #include "Core/HW/Wiimote.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h" @@ -71,6 +72,29 @@ Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedContro g_controller_interface, ControlReferenceFromJava(env, control_reference)); } +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_loadDefaultSettings( + JNIEnv* env, jobject obj) +{ + ControllerEmu::EmulatedController* controller = EmulatedControllerFromJava(env, obj); + + controller->LoadDefaults(g_controller_interface); + controller->UpdateReferences(g_controller_interface); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_clearSettings( + JNIEnv* env, jobject obj) +{ + ControllerEmu::EmulatedController* controller = EmulatedControllerFromJava(env, obj); + + // Loading an empty IniFile section clears everything. + IniFile::Section section; + + controller->LoadConfig(§ion); + controller->UpdateReferences(g_controller_interface); +} + JNIEXPORT jobject JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGcPad( JNIEnv* env, jclass, jint controller_index) From 7ef229d9082f72795e71db58ed13824bdd3d3e2c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 14 Aug 2022 21:11:00 +0200 Subject: [PATCH 27/38] Android: Show warning when using old pre-defined controller INIs Co-authored-by: Charles Lombardo --- .../features/settings/ui/SettingsActivity.java | 15 ++++++++++++++- .../settings/ui/SettingsActivityView.java | 9 +++++++++ .../features/settings/ui/SettingsFragment.java | 16 ++++++++++++++-- .../settings/ui/SettingsFragmentPresenter.java | 18 ++++++++++++++++++ .../settings/ui/SettingsFragmentView.java | 8 ++++++++ .../src/main/res/layout/activity_settings.xml | 12 ++++++++++++ .../app/src/main/res/values/strings.xml | 1 + 7 files changed, 76 insertions(+), 3 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java index f10a915bcc..d4ec285681 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java @@ -9,6 +9,7 @@ import android.os.Bundle; import android.provider.Settings; import android.view.Menu; import android.view.MenuInflater; +import android.view.View; import android.widget.Toast; import androidx.annotation.NonNull; @@ -339,7 +340,7 @@ public final class SettingsActivity extends AppCompatActivity implements Setting public void setToolbarTitle(String title) { - mToolbarLayout.setTitle(title); + mBinding.toolbarSettingsLayout.setTitle(title); } @Override @@ -354,6 +355,14 @@ public final class SettingsActivity extends AppCompatActivity implements Setting return mMappingAllDevices; } + @Override + public int setOldControllerSettingsWarningVisibility(boolean visible) + { + // We use INVISIBLE instead of GONE to avoid getting a stale height for the return value + mBinding.oldControllerSettingsWarning.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + return visible ? mBinding.oldControllerSettingsWarning.getHeight() : 0; + } + private void setInsets() { ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbarSettings, (v, windowInsets) -> @@ -364,6 +373,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting mBinding.frameContentSettings.setPadding(insets.left, 0, insets.right, 0); + int textPadding = getResources().getDimensionPixelSize(R.dimen.spacing_large); + mBinding.oldControllerSettingsWarning.setPadding(textPadding + insets.left, textPadding, + textPadding + insets.right, textPadding + insets.bottom); + InsetsHelper.applyNavbarWorkaround(insets.bottom, mBinding.workaroundView); ThemeHelper.setNavigationBarColor(this, MaterialColors.getColor(mBinding.appbarSettings, R.attr.colorSurface)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java index d71d729af8..203b14ca4b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java @@ -109,4 +109,13 @@ public interface SettingsActivityView * not just the device configured for the controller. */ boolean isMappingAllDevices(); + + /** + * Shows or hides a warning telling the user that they're using incompatible controller settings. + * The warning is hidden by default. + * + * @param visible Whether the warning should be visible. + * @return The height of the warning view, or 0 if the view is now invisible. + */ + int setOldControllerSettingsWarningVisibility(boolean visible); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java index 7a3f7b6db8..fcc27c4e5d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java @@ -38,6 +38,8 @@ public final class SettingsFragment extends Fragment implements SettingsFragment private SettingsAdapter mAdapter; + private int mOldControllerSettingsWarningHeight = 0; + private static final Map titles = new HashMap<>(); static @@ -260,13 +262,23 @@ public final class SettingsFragment extends Fragment implements SettingsFragment return mActivity.isMappingAllDevices(); } + @Override + public void setOldControllerSettingsWarningVisibility(boolean visible) + { + mOldControllerSettingsWarningHeight = + mActivity.setOldControllerSettingsWarningVisibility(visible); + + // Trigger the insets listener we've registered + mBinding.listSettings.requestApplyInsets(); + } + private void setInsets() { ViewCompat.setOnApplyWindowInsetsListener(mBinding.listSettings, (v, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); - v.setPadding(0, 0, 0, - insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_list)); + int listSpacing = getResources().getDimensionPixelSize(R.dimen.spacing_list); + v.setPadding(0, 0, 0, insets.bottom + listSpacing + mOldControllerSettingsWarningHeight); return windowInsets; }); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 8d9a34c252..6cce1361ec 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -80,6 +80,7 @@ public final class SettingsFragmentPresenter private Settings mSettings; private ArrayList mSettingsList; + private boolean mHasOldControllerSettings = false; private int mSerialPort1Type; private int mControllerNumber; @@ -144,6 +145,7 @@ public final class SettingsFragmentPresenter else { mView.showSettingsList(mSettingsList); + mView.setOldControllerSettingsWarningVisibility(mHasOldControllerSettings); } } @@ -1220,6 +1222,8 @@ public final class SettingsFragmentPresenter sl.add(new RunRunnable(mContext, R.string.input_clear, R.string.input_clear_description, R.string.input_reset_warning, 0, true, () -> clearControllerSettings(controller))); + + updateOldControllerSettingsWarningVisibility(controller); } /** @@ -1232,6 +1236,8 @@ public final class SettingsFragmentPresenter private void addControllerMappingSettings(ArrayList sl, EmulatedController controller, Set groupTypeFilter) { + updateOldControllerSettingsWarningVisibility(controller); + int groupCount = controller.getGroupCount(); for (int i = 0; i < groupCount; i++) { @@ -1287,16 +1293,28 @@ public final class SettingsFragmentPresenter } } + private void updateOldControllerSettingsWarningVisibility(EmulatedController controller) + { + String defaultDevice = controller.getDefaultDevice(); + + mHasOldControllerSettings = defaultDevice.startsWith("Android/") && + defaultDevice.endsWith("/Touchscreen"); + + mView.setOldControllerSettingsWarningVisibility(mHasOldControllerSettings); + } + private void loadDefaultControllerSettings(EmulatedController controller) { controller.loadDefaultSettings(); mView.getAdapter().notifyAllSettingsChanged(); + updateOldControllerSettingsWarningVisibility(controller); } private void clearControllerSettings(EmulatedController controller) { controller.clearSettings(); mView.getAdapter().notifyAllSettingsChanged(); + updateOldControllerSettingsWarningVisibility(controller); } private static int getLogVerbosityEntries() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java index 280657158f..53a3010962 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java @@ -101,4 +101,12 @@ public interface SettingsFragmentView * not just the device configured for the controller. */ boolean isMappingAllDevices(); + + /** + * Shows or hides a warning telling the user that they're using incompatible controller settings. + * The warning is hidden by default. + * + * @param visible Whether the warning should be visible. + */ + void setOldControllerSettingsWarningVisibility(boolean visible); } diff --git a/Source/Android/app/src/main/res/layout/activity_settings.xml b/Source/Android/app/src/main/res/layout/activity_settings.xml index 4b2d96dfb6..533893df12 100644 --- a/Source/Android/app/src/main/res/layout/activity_settings.xml +++ b/Source/Android/app/src/main/res/layout/activity_settings.xml @@ -39,6 +39,18 @@ android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> + + Clear Clear settings for this controller. Are you sure? Your current controller settings will be deleted. + Your controller settings are from an old version of Dolphin and won\'t work in this version. Press \"Default\" to start over with new settings. Input Binding Press or move an input to bind it to %1$s. From 1eeded23df829973065d51c2595bf743910bc07e Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 26 Dec 2022 19:26:58 +0100 Subject: [PATCH 28/38] Android: Add input profile management Co-authored-by: Charles Lombardo --- .../controlleremu/EmulatedController.java | 4 + .../features/input/ui/ProfileAdapter.java | 65 ++++++++ .../features/input/ui/ProfileDialog.kt | 76 +++++++++ .../input/ui/ProfileDialogPresenter.java | 150 ++++++++++++++++++ .../features/input/ui/ProfileViewHolder.java | 76 +++++++++ .../features/settings/ui/MenuTag.java | 15 +- .../settings/ui/SettingsActivity.java | 14 ++ .../settings/ui/SettingsActivityView.java | 16 ++ .../settings/ui/SettingsFragment.java | 14 ++ .../ui/SettingsFragmentPresenter.java | 15 +- .../settings/ui/SettingsFragmentView.java | 11 ++ .../utils/DirectoryInitialization.java | 14 +- .../app/src/main/res/drawable/ic_delete.xml | 9 ++ .../app/src/main/res/drawable/ic_save.xml | 9 ++ .../main/res/layout/dialog_input_profiles.xml | 25 +++ .../src/main/res/layout/list_item_profile.xml | 69 ++++++++ .../app/src/main/res/values/strings.xml | 8 + .../Android/jni/Input/EmulatedController.cpp | 28 ++++ 18 files changed, 612 insertions(+), 6 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.java create mode 100644 Source/Android/app/src/main/res/drawable/ic_delete.xml create mode 100644 Source/Android/app/src/main/res/drawable/ic_save.xml create mode 100644 Source/Android/app/src/main/res/layout/dialog_input_profiles.xml create mode 100644 Source/Android/app/src/main/res/layout/list_item_profile.xml diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java index 5f6275ffcd..47d4dc5c96 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java @@ -35,6 +35,10 @@ public class EmulatedController public native void clearSettings(); + public native void loadProfile(String path); + + public native void saveProfile(String path); + public static native EmulatedController getGcPad(int controllerIndex); public static native EmulatedController getWiimote(int controllerIndex); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.java new file mode 100644 index 0000000000..9908d6f5fd --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.java @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.ui; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import org.dolphinemu.dolphinemu.databinding.ListItemProfileBinding; + +public final class ProfileAdapter extends RecyclerView.Adapter +{ + private final Context mContext; + private final ProfileDialogPresenter mPresenter; + + private final String[] mStockProfileNames; + private final String[] mUserProfileNames; + + public ProfileAdapter(Context context, ProfileDialogPresenter presenter) + { + mContext = context; + mPresenter = presenter; + + mStockProfileNames = presenter.getProfileNames(true); + mUserProfileNames = presenter.getProfileNames(false); + } + + @NonNull @Override + public ProfileViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) + { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + + ListItemProfileBinding binding = ListItemProfileBinding.inflate(inflater, parent, false); + return new ProfileViewHolder(mPresenter, binding); + } + + @Override + public void onBindViewHolder(@NonNull ProfileViewHolder holder, int position) + { + if (position < mStockProfileNames.length) + { + holder.bind(mStockProfileNames[position], true); + return; + } + + position -= mStockProfileNames.length; + + if (position < mUserProfileNames.length) + { + holder.bind(mUserProfileNames[position], false); + return; + } + + holder.bindAsEmpty(mContext); + } + + @Override + public int getItemCount() + { + return mStockProfileNames.length + mUserProfileNames.length + 1; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt new file mode 100644 index 0000000000..2779ee7473 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.ui + +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.google.android.material.divider.MaterialDividerItemDecoration +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.databinding.DialogInputProfilesBinding +import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag + +class ProfileDialog : BottomSheetDialogFragment() { + private var presenter: ProfileDialogPresenter? = null + + private var _binding: DialogInputProfilesBinding? = null + private val binding get() = _binding!! + + override fun onCreate(savedInstanceState: Bundle?) { + val menuTag: MenuTag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requireArguments().getSerializable(KEY_MENU_TAG, MenuTag::class.java) as MenuTag + } else { + requireArguments().getSerializable(KEY_MENU_TAG) as MenuTag + } + + presenter = ProfileDialogPresenter(this, menuTag) + + super.onCreate(savedInstanceState) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = DialogInputProfilesBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + binding.profileList.adapter = ProfileAdapter(context, presenter) + binding.profileList.layoutManager = LinearLayoutManager(context) + val divider = MaterialDividerItemDecoration(requireActivity(), LinearLayoutManager.VERTICAL) + divider.isLastItemDecorated = false + binding.profileList.addItemDecoration(divider) + + // You can't expand a bottom sheet with a controller/remote/other non-touch devices + val behavior: BottomSheetBehavior = BottomSheetBehavior.from(view.parent as View) + if (!resources.getBoolean(R.bool.hasTouch)) { + behavior.state = BottomSheetBehavior.STATE_EXPANDED + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + private const val KEY_MENU_TAG = "menu_tag" + + @JvmStatic + fun create(menuTag: MenuTag): ProfileDialog { + val dialog = ProfileDialog() + val args = Bundle() + args.putSerializable(KEY_MENU_TAG, menuTag) + dialog.arguments = args + return dialog + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.java new file mode 100644 index 0000000000..ae35aec46d --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.java @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.ui; + +import android.content.Context; +import android.view.LayoutInflater; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.google.android.material.textfield.TextInputEditText; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding; +import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; +import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; + +import java.io.File; +import java.text.Collator; +import java.util.Arrays; + +public final class ProfileDialogPresenter +{ + private static final String EXTENSION = ".ini"; + + private final Context mContext; + private final DialogFragment mDialog; + private final MenuTag mMenuTag; + + public ProfileDialogPresenter(DialogFragment dialog, MenuTag menuTag) + { + mContext = dialog.getContext(); + mDialog = dialog; + mMenuTag = menuTag; + } + + public String[] getProfileNames(boolean stock) + { + File[] profiles = new File(getProfileDirectoryPath(stock)).listFiles( + file -> !file.isDirectory() && file.getName().endsWith(EXTENSION)); + + if (profiles == null) + return new String[0]; + + return Arrays.stream(profiles) + .map(file -> file.getName().substring(0, file.getName().length() - EXTENSION.length())) + .sorted(Collator.getInstance()) + .toArray(String[]::new); + } + + public void loadProfile(@NonNull String profileName, boolean stock) + { + new MaterialAlertDialogBuilder(mContext) + .setMessage(mContext.getString(R.string.input_profile_confirm_load, profileName)) + .setPositiveButton(R.string.yes, (dialogInterface, i) -> + { + mMenuTag.getCorrespondingEmulatedController() + .loadProfile(getProfilePath(profileName, stock)); + ((SettingsActivityView) mDialog.requireActivity()).onControllerSettingsChanged(); + mDialog.dismiss(); + }) + .setNegativeButton(R.string.no, null) + .show(); + } + + public void saveProfile(@NonNull String profileName) + { + // If the user is saving over an existing profile, we should show an overwrite warning. + // If the user is creating a new profile, we normally shouldn't show a warning, + // but if they've entered the name of an existing profile, we should shown an overwrite warning. + + String profilePath = getProfilePath(profileName, false); + if (!new File(profilePath).exists()) + { + mMenuTag.getCorrespondingEmulatedController().saveProfile(profilePath); + mDialog.dismiss(); + } + else + { + new MaterialAlertDialogBuilder(mContext) + .setMessage(mContext.getString(R.string.input_profile_confirm_save, profileName)) + .setPositiveButton(R.string.yes, (dialogInterface, i) -> + { + mMenuTag.getCorrespondingEmulatedController().saveProfile(profilePath); + mDialog.dismiss(); + }) + .setNegativeButton(R.string.no, null) + .show(); + } + } + + public void saveProfileAndPromptForName() + { + LayoutInflater inflater = LayoutInflater.from(mContext); + + DialogInputStringBinding binding = DialogInputStringBinding.inflate(inflater); + TextInputEditText input = binding.input; + + new MaterialAlertDialogBuilder(mContext) + .setView(binding.getRoot()) + .setPositiveButton(R.string.ok, (dialogInterface, i) -> + saveProfile(input.getText().toString())) + .setNegativeButton(R.string.cancel, null) + .show(); + } + + public void deleteProfile(@NonNull String profileName) + { + new MaterialAlertDialogBuilder(mContext) + .setMessage(mContext.getString(R.string.input_profile_confirm_delete, profileName)) + .setPositiveButton(R.string.yes, (dialogInterface, i) -> + { + new File(getProfilePath(profileName, false)).delete(); + mDialog.dismiss(); + }) + .setNegativeButton(R.string.no, null) + .show(); + } + + private String getProfileDirectoryName() + { + if (mMenuTag.isGCPadMenu()) + return "GCPad"; + else if (mMenuTag.isWiimoteMenu()) + return "Wiimote"; + else + throw new UnsupportedOperationException(); + } + + private String getProfileDirectoryPath(boolean stock) + { + if (stock) + { + return DirectoryInitialization.getSysDirectory() + "/Profiles/" + getProfileDirectoryName() + + '/'; + } + else + { + return DirectoryInitialization.getUserDirectory() + "/Config/Profiles/" + + getProfileDirectoryName() + '/'; + } + } + + private String getProfilePath(String profileName, boolean stock) + { + return getProfileDirectoryPath(stock) + profileName + EXTENSION; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.java new file mode 100644 index 0000000000..d4d40c0c65 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.java @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.ui; + +import android.content.Context; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.databinding.ListItemProfileBinding; + +public class ProfileViewHolder extends RecyclerView.ViewHolder +{ + private final ProfileDialogPresenter mPresenter; + private final ListItemProfileBinding mBinding; + + private String mProfileName; + private boolean mStock; + + public ProfileViewHolder(@NonNull ProfileDialogPresenter presenter, + @NonNull ListItemProfileBinding binding) + { + super(binding.getRoot()); + + mPresenter = presenter; + mBinding = binding; + + binding.buttonLoad.setOnClickListener(view -> loadProfile()); + binding.buttonSave.setOnClickListener(view -> saveProfile()); + binding.buttonDelete.setOnClickListener(view -> deleteProfile()); + } + + public void bind(String profileName, boolean stock) + { + mProfileName = profileName; + mStock = stock; + + mBinding.textName.setText(profileName); + + mBinding.buttonLoad.setVisibility(View.VISIBLE); + mBinding.buttonSave.setVisibility(stock ? View.GONE : View.VISIBLE); + mBinding.buttonDelete.setVisibility(stock ? View.GONE : View.VISIBLE); + } + + public void bindAsEmpty(Context context) + { + mProfileName = null; + mStock = false; + + mBinding.textName.setText(context.getText(R.string.input_profile_new)); + + mBinding.buttonLoad.setVisibility(View.GONE); + mBinding.buttonSave.setVisibility(View.VISIBLE); + mBinding.buttonDelete.setVisibility(View.GONE); + } + + private void loadProfile() + { + mPresenter.loadProfile(mProfileName, mStock); + } + + private void saveProfile() + { + if (mProfileName == null) + mPresenter.saveProfileAndPromptForName(); + else + mPresenter.saveProfile(mProfileName); + } + + private void deleteProfile() + { + mPresenter.deleteProfile(mProfileName); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java index f97a013074..a0a67dd838 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java @@ -4,6 +4,8 @@ package org.dolphinemu.dolphinemu.features.settings.ui; import androidx.annotation.NonNull; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; + public enum MenuTag { SETTINGS("settings"), @@ -88,6 +90,16 @@ public enum MenuTag return subType; } + public EmulatedController getCorrespondingEmulatedController() + { + if (isGCPadMenu()) + return EmulatedController.getGcPad(getSubType()); + else if (isWiimoteMenu()) + return EmulatedController.getWiimote(getSubType()); + else + throw new UnsupportedOperationException(); + } + public boolean isSerialPort1Menu() { return this == CONFIG_SERIALPORT1; @@ -143,7 +155,8 @@ public enum MenuTag { for (MenuTag menuTag : MenuTag.values()) { - if (menuTag.tag.equals(tag) && menuTag.subType == subtype) return menuTag; + if (menuTag.tag.equals(tag) && menuTag.subType == subtype) + return menuTag; } throw new IllegalArgumentException("You are asking for a menu that is not available or " + diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java index d4ec285681..bce24c1c46 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java @@ -19,6 +19,7 @@ import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; +import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.ViewModelProvider; @@ -44,6 +45,7 @@ public final class SettingsActivity extends AppCompatActivity implements Setting private static final String ARG_IS_WII = "is_wii"; private static final String KEY_MAPPING_ALL_DEVICES = "all_devices"; private static final String FRAGMENT_TAG = "settings"; + private static final String FRAGMENT_DIALOG_TAG = "settings_dialog"; private SettingsActivityPresenter mPresenter; private AlertDialog dialog; @@ -191,6 +193,12 @@ public final class SettingsActivity extends AppCompatActivity implements Setting transaction.commit(); } + @Override + public void showDialogFragment(DialogFragment fragment) + { + fragment.show(getSupportFragmentManager(), FRAGMENT_DIALOG_TAG); + } + private boolean areSystemAnimationsEnabled() { float duration = Settings.Global.getFloat( @@ -314,6 +322,12 @@ public final class SettingsActivity extends AppCompatActivity implements Setting mPresenter.onSettingChanged(); } + @Override + public void onControllerSettingsChanged() + { + getFragment().onControllerSettingsChanged(); + } + @Override public void onMenuTagAction(@NonNull MenuTag menuTag, int value) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java index 203b14ca4b..464d0ea165 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java @@ -5,6 +5,7 @@ package org.dolphinemu.dolphinemu.features.settings.ui; import android.os.Bundle; import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import org.dolphinemu.dolphinemu.features.settings.model.Settings; @@ -21,6 +22,13 @@ public interface SettingsActivityView */ void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack, String gameId); + /** + * Shows a DialogFragment. + * + * Only one can be shown at a time. + */ + void showDialogFragment(DialogFragment fragment); + /** * Called by a contained Fragment to get access to the Setting HashMap * loaded from disk, so that each Fragment doesn't need to perform its own @@ -60,6 +68,14 @@ public interface SettingsActivityView */ void onSettingChanged(); + /** + * Refetches the values of all controller settings. + * + * To be used when loading an input profile or performing some other action that changes all + * controller settings at once. + */ + void onControllerSettingsChanged(); + /** * Called by a containing Fragment to tell the containing Activity that the user wants to open the * MenuTag associated with a setting. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java index fcc27c4e5d..5f3202e63e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java @@ -13,6 +13,7 @@ import androidx.annotation.Nullable; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; +import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -221,6 +222,12 @@ public final class SettingsFragment extends Fragment implements SettingsFragment mActivity.showSettingsFragment(menuKey, null, true, getArguments().getString(ARGUMENT_GAME_ID)); } + @Override + public void showDialogFragment(DialogFragment fragment) + { + mActivity.showDialogFragment(fragment); + } + @Override public void showToastMessage(String message) { @@ -239,6 +246,13 @@ public final class SettingsFragment extends Fragment implements SettingsFragment mActivity.onSettingChanged(); } + @Override + public void onControllerSettingsChanged() + { + mAdapter.notifyAllSettingsChanged(); + mPresenter.updateOldControllerSettingsWarningVisibility(); + } + @Override public void onMenuTagAction(@NonNull MenuTag menuTag, int value) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 6cce1361ec..3d33237439 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -24,6 +24,7 @@ import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedCont import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; 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; import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting; @@ -1223,6 +1224,9 @@ public final class SettingsFragmentPresenter sl.add(new RunRunnable(mContext, R.string.input_clear, R.string.input_clear_description, R.string.input_reset_warning, 0, true, () -> clearControllerSettings(controller))); + sl.add(new RunRunnable(mContext, R.string.input_profiles, 0, 0, 0, true, + () -> mView.showDialogFragment(ProfileDialog.create(mMenuTag)))); + updateOldControllerSettingsWarningVisibility(controller); } @@ -1293,6 +1297,11 @@ public final class SettingsFragmentPresenter } } + public void updateOldControllerSettingsWarningVisibility() + { + updateOldControllerSettingsWarningVisibility(mMenuTag.getCorrespondingEmulatedController()); + } + private void updateOldControllerSettingsWarningVisibility(EmulatedController controller) { String defaultDevice = controller.getDefaultDevice(); @@ -1306,15 +1315,13 @@ public final class SettingsFragmentPresenter private void loadDefaultControllerSettings(EmulatedController controller) { controller.loadDefaultSettings(); - mView.getAdapter().notifyAllSettingsChanged(); - updateOldControllerSettingsWarningVisibility(controller); + mView.onControllerSettingsChanged(); } private void clearControllerSettings(EmulatedController controller) { controller.clearSettings(); - mView.getAdapter().notifyAllSettingsChanged(); - updateOldControllerSettingsWarningVisibility(controller); + mView.onControllerSettingsChanged(); } private static int getLogVerbosityEntries() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java index 53a3010962..809f26f7cc 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java @@ -3,6 +3,7 @@ package org.dolphinemu.dolphinemu.features.settings.ui; import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentActivity; import org.dolphinemu.dolphinemu.features.settings.model.Settings; @@ -55,6 +56,8 @@ public interface SettingsFragmentView */ void loadSubMenu(MenuTag menuKey); + void showDialogFragment(DialogFragment fragment); + /** * Tell the Fragment to tell the containing activity to display a toast message. * @@ -72,6 +75,14 @@ public interface SettingsFragmentView */ void onSettingChanged(); + /** + * Refetches the values of all controller settings. + * + * To be used when loading an input profile or performing some other action that changes all + * controller settings at once. + */ + void onControllerSettingsChanged(); + /** * Have the fragment tell the containing Activity that the user wants to open the MenuTag * associated with a setting. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java index cf85e12740..45f2960bd9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java @@ -45,6 +45,7 @@ public final class DirectoryInitialization new MutableLiveData<>(DirectoryInitializationState.NOT_YET_INITIALIZED); private static volatile boolean areDirectoriesAvailable = false; private static String userPath; + private static String sysPath; private static boolean isUsingLegacyUserDirectory = false; public enum DirectoryInitializationState @@ -153,7 +154,8 @@ public final class DirectoryInitialization } // Let the native code know where the Sys directory is. - SetSysDirectory(sysDirectory.getPath()); + sysPath = sysDirectory.getPath(); + SetSysDirectory(sysPath); } private static void deleteDirectoryRecursively(@NonNull final File file) @@ -204,6 +206,16 @@ public final class DirectoryInitialization return userPath; } + public static String getSysDirectory() + { + if (!areDirectoriesAvailable) + { + throw new IllegalStateException( + "DirectoryInitialization must run before accessing the Sys directory!"); + } + return sysPath; + } + public static File getGameListCache(Context context) { return new File(context.getExternalCacheDir(), "gamelist.cache"); diff --git a/Source/Android/app/src/main/res/drawable/ic_delete.xml b/Source/Android/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 0000000000..c841ba25f4 --- /dev/null +++ b/Source/Android/app/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,9 @@ + + + diff --git a/Source/Android/app/src/main/res/drawable/ic_save.xml b/Source/Android/app/src/main/res/drawable/ic_save.xml new file mode 100644 index 0000000000..0f449672ee --- /dev/null +++ b/Source/Android/app/src/main/res/drawable/ic_save.xml @@ -0,0 +1,9 @@ + + + diff --git a/Source/Android/app/src/main/res/layout/dialog_input_profiles.xml b/Source/Android/app/src/main/res/layout/dialog_input_profiles.xml new file mode 100644 index 0000000000..d6ba18d39b --- /dev/null +++ b/Source/Android/app/src/main/res/layout/dialog_input_profiles.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/Source/Android/app/src/main/res/layout/list_item_profile.xml b/Source/Android/app/src/main/res/layout/list_item_profile.xml new file mode 100644 index 0000000000..343f3d7796 --- /dev/null +++ b/Source/Android/app/src/main/res/layout/list_item_profile.xml @@ -0,0 +1,69 @@ + + + + + +