diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt index e53354dc9..5b24a1d38 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt @@ -803,8 +803,8 @@ object NativeLibrary { const val DPAD = 780 const val BUTTON_DEBUG = 781 const val BUTTON_GPIO14 = 782 + const val BUTTON_TURBO = 783 const val BUTTON_SWAP = 800 - const val BUTTON_TURBO = 801 } /** diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyUtility.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyUtility.kt index 0a4a1ffa3..991f1cde9 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyUtility.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/hotkeys/HotkeyUtility.kt @@ -20,6 +20,11 @@ class HotkeyUtility( var HotkeyIsPressed = false fun handleHotkey(bindedButton: Int): Boolean { + if (bindedButton == NativeLibrary.ButtonType.BUTTON_TURBO) { + TurboHelper.toggleTurbo(true) + HotkeyIsPressed = true + return true + } if(hotkeyButtons.contains(bindedButton)) { when (bindedButton) { Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen() diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt index 02d10cfe9..887bb36d4 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/Settings.kt @@ -121,6 +121,7 @@ class Settings { const val KEY_BUTTON_SELECT = "button_select" const val KEY_BUTTON_START = "button_start" const val KEY_BUTTON_HOME = "button_home" + const val KEY_BUTTON_TURBO = "button_turbo" const val KEY_BUTTON_UP = "button_up" const val KEY_BUTTON_DOWN = "button_down" const val KEY_BUTTON_LEFT = "button_left" @@ -150,7 +151,8 @@ class Settings { KEY_BUTTON_Y, KEY_BUTTON_SELECT, KEY_BUTTON_START, - KEY_BUTTON_HOME + KEY_BUTTON_HOME, + KEY_BUTTON_TURBO ) val buttonTitles = listOf( R.string.button_a, @@ -159,7 +161,8 @@ class Settings { R.string.button_y, R.string.button_select, R.string.button_start, - R.string.button_home + R.string.button_home, + R.string.button_turbo ) val circlePadKeys = listOf( KEY_CIRCLEPAD_AXIS_VERTICAL, @@ -245,4 +248,4 @@ class Settings { ) } } -} \ No newline at end of file +} diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt index a2ff73f70..764e2d7ce 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt @@ -124,6 +124,7 @@ class InputBindingSetting( Settings.KEY_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_SELECT Settings.KEY_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_START Settings.KEY_BUTTON_HOME -> NativeLibrary.ButtonType.BUTTON_HOME + Settings.KEY_BUTTON_TURBO -> NativeLibrary.ButtonType.BUTTON_TURBO Settings.KEY_BUTTON_UP -> NativeLibrary.ButtonType.DPAD_UP Settings.KEY_BUTTON_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN Settings.KEY_BUTTON_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 1659dc3d6..aad286722 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -66,8 +66,9 @@ static const std::array default_buttons InputManager::N3DS_TRIGGER_L, InputManager::N3DS_TRIGGER_R, InputManager::N3DS_BUTTON_START, InputManager::N3DS_BUTTON_SELECT, InputManager::N3DS_BUTTON_DEBUG, InputManager::N3DS_BUTTON_GPIO14, - InputManager::N3DS_BUTTON_ZL, InputManager::N3DS_BUTTON_ZR, - InputManager::N3DS_BUTTON_HOME, + InputManager::N3DS_BUTTON_TURBO, InputManager::N3DS_BUTTON_ZL, + InputManager::N3DS_BUTTON_ZR, InputManager::N3DS_BUTTON_HOME, + InputManager::N3DS_BUTTON_POWER, }; static const std::array default_analogs{{ diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index 26688a708..e2969dc89 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -40,6 +40,7 @@ button_start= button_select= button_debug= button_gpio14= +button_turbo= button_zl= button_zr= button_home= diff --git a/src/android/app/src/main/jni/input_manager.h b/src/android/app/src/main/jni/input_manager.h index 31d4b756e..ea5213d8f 100644 --- a/src/android/app/src/main/jni/input_manager.h +++ b/src/android/app/src/main/jni/input_manager.h @@ -39,7 +39,8 @@ enum ButtonType { N3DS_TRIGGER_L = 773, N3DS_TRIGGER_R = 774, N3DS_BUTTON_DEBUG = 781, - N3DS_BUTTON_GPIO14 = 782 + N3DS_BUTTON_GPIO14 = 782, + N3DS_BUTTON_TURBO = 783 }; class ButtonList; diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp index ea28f0f2c..4dc86d4f7 100644 --- a/src/citra_qt/citra_qt.cpp +++ b/src/citra_qt/citra_qt.cpp @@ -494,6 +494,9 @@ void GMainWindow::InitializeWidgets() { }); InputCommon::Init(); + turbo_poll_timer.setInterval(10); + connect(&turbo_poll_timer, &QTimer::timeout, this, &GMainWindow::PollTurboButton); + UpdateTurboButtonBinding(true); multiplayer_state = new MultiplayerState(system, this, game_list->GetModel(), ui->action_Leave_Room, ui->action_Show_Room); multiplayer_state->setVisible(false); @@ -878,6 +881,8 @@ void GMainWindow::InitializeHotkeys() { const auto fullscreen_hotkey = hotkey_registry.GetKeySequence(main_window, fullscreen); add_secondary_window_hotkey(action_secondary_fullscreen, fullscreen_hotkey, SLOT(ToggleSecondaryFullscreen())); + + UpdateTurboButtonBinding(true); } void GMainWindow::SetDefaultUIGeometry() { @@ -1565,6 +1570,7 @@ void GMainWindow::ShutdownGame() { UpdateSaveStates(); emulation_running = false; + RefreshTurboPollingState(); game_title.clear(); UpdateWindowTitle(); @@ -2460,6 +2466,7 @@ void GMainWindow::OnStartGame() { UpdateSaveStates(); UpdateStatusButtons(); + UpdateTurboButtonBinding(); } void GMainWindow::OnRestartGame() { @@ -2668,6 +2675,54 @@ void GMainWindow::ReloadTurbo() { UpdateStatusBar(); } +void GMainWindow::UpdateTurboButtonBinding(bool force) { + const auto& binding = + Settings::values.current_input_profile.buttons[Settings::NativeButton::Turbo]; + + if (!force && binding == turbo_button_binding) { + RefreshTurboPollingState(); + return; + } + + turbo_button_binding = binding; + if (!turbo_button_binding.empty()) { + turbo_button_device = Input::CreateDevice(turbo_button_binding); + if (!turbo_button_device) { + LOG_WARNING(Frontend, "Failed to create turbo controller device for binding {}", + turbo_button_binding); + } + } else { + turbo_button_device.reset(); + } + + RefreshTurboPollingState(); +} + +void GMainWindow::RefreshTurboPollingState() { + if (emulation_running && turbo_button_device) { + turbo_button_was_pressed = turbo_button_device->GetStatus(); + if (!turbo_poll_timer.isActive()) { + turbo_poll_timer.start(); + } + } else { + turbo_poll_timer.stop(); + turbo_button_was_pressed = false; + } +} + +void GMainWindow::PollTurboButton() { + if (!emulation_running || !turbo_button_device) { + return; + } + + const bool pressed = turbo_button_device->GetStatus(); + if (pressed && !turbo_button_was_pressed) { + SetTurboEnabled(!IsTurboEnabled()); + } + + turbo_button_was_pressed = pressed; +} + // TODO: This should probably take in something more descriptive than a bool. -OS void GMainWindow::AdjustSpeedLimit(bool increase) { const int SPEED_LIMIT_STEP = 5; @@ -2818,6 +2873,8 @@ void GMainWindow::OnConfigure() { Settings::values.touch_from_button_maps = old_touch_from_button_maps; Settings::LoadProfile(old_input_profile_index); } + + UpdateTurboButtonBinding(true); } void GMainWindow::OnLoadAmiibo() { diff --git a/src/citra_qt/citra_qt.h b/src/citra_qt/citra_qt.h index 746689cb5..fa0d3f898 100644 --- a/src/citra_qt/citra_qt.h +++ b/src/citra_qt/citra_qt.h @@ -6,6 +6,7 @@ #include #include +#include #include #ifdef __unix__ #include @@ -23,6 +24,7 @@ #include "citra_qt/hotkeys.h" #include "citra_qt/user_data_migration.h" #include "core/core.h" +#include "core/frontend/input.h" #include "core/savestate.h" #include "video_core/rasterizer_interface.h" @@ -266,6 +268,9 @@ private slots: bool IsTurboEnabled(); void SetTurboEnabled(bool); void ReloadTurbo(); + void UpdateTurboButtonBinding(bool force = false); + void RefreshTurboPollingState(); + void PollTurboButton(); void AdjustSpeedLimit(bool increase); void UpdateSecondaryWindowVisibility(); void ToggleScreenLayout(); @@ -377,6 +382,10 @@ private: bool auto_paused = false; bool auto_muted = false; QTimer mouse_hide_timer; + QTimer turbo_poll_timer; + std::unique_ptr turbo_button_device; + std::string turbo_button_binding; + bool turbo_button_was_pressed = false; // Movie bool movie_record_on_start = false; diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index cdb86c4c5..9109b82b9 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -29,7 +29,7 @@ QtConfig::~QtConfig() { const std::array QtConfig::default_buttons = { Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, - Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_V, + Qt::Key_O, Qt::Key_P, Qt::Key_unknown, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_V, }; const std::array, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{ diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index 2cc319968..1835ddfed 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -170,8 +170,8 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent) ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect, - ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR, - ui->buttonHome, ui->buttonPower, + ui->buttonDebug, ui->buttonGpio14, ui->buttonTurbo, ui->buttonZL, + ui->buttonZR, ui->buttonHome, ui->buttonPower, }; analog_map_buttons = {{ diff --git a/src/citra_qt/configuration/configure_input.ui b/src/citra_qt/configuration/configure_input.ui index 67cc7688f..dd0e798e9 100644 --- a/src/citra_qt/configuration/configure_input.ui +++ b/src/citra_qt/configuration/configure_input.ui @@ -470,6 +470,24 @@ + + + + + + Toggle Turbo Mode: + + + + + + + + + + + + @@ -1093,6 +1111,7 @@ buttonHome buttonCircleMod buttonDebug + buttonTurbo buttonGpio14 buttonMotionTouch buttonAutoMap diff --git a/src/citra_sdl/config.cpp b/src/citra_sdl/config.cpp index 58ac46e3c..9c5b95975 100644 --- a/src/citra_sdl/config.cpp +++ b/src/citra_sdl/config.cpp @@ -48,9 +48,11 @@ bool SdlConfig::LoadINI(const std::string& default_contents, bool retry) { } static const std::array default_buttons = { - SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, SDL_SCANCODE_G, - SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_M, SDL_SCANCODE_N, - SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, + SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, + SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, + SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_M, SDL_SCANCODE_N, + SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_1, + SDL_SCANCODE_2, SDL_SCANCODE_B, SDL_SCANCODE_V, }; static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs{{ diff --git a/src/citra_sdl/default_ini.h b/src/citra_sdl/default_ini.h index 3fb471449..3a67df278 100644 --- a/src/citra_sdl/default_ini.h +++ b/src/citra_sdl/default_ini.h @@ -40,6 +40,7 @@ button_start= button_select= button_debug= button_gpio14= +button_turbo= button_zl= button_zr= button_home= diff --git a/src/common/settings.h b/src/common/settings.h index 589de0057..a50a522c1 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -133,6 +133,7 @@ enum Values { Select, Debug, Gpio14, + Turbo, ZL, ZR, @@ -170,6 +171,7 @@ static const std::array mapping = {{ "button_select", "button_debug", "button_gpio14", + "button_turbo", "button_zl", "button_zr", "button_home", diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 81b833f34..6f02ab207 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -208,6 +208,7 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) { state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus()); state.debug.Assign(buttons[Debug - BUTTON_HID_BEGIN]->GetStatus()); state.gpio14.Assign(buttons[Gpio14 - BUTTON_HID_BEGIN]->GetStatus()); + state.turbo.Assign(buttons[Turbo - BUTTON_HID_BEGIN]->GetStatus()); // Get current circle pad position and update circle pad direction float circle_pad_x_f, circle_pad_y_f; diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 79713ca69..edf965c6f 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -52,6 +52,7 @@ struct PadState { BitField<11, 1, u32> y; BitField<12, 1, u32> debug; BitField<13, 1, u32> gpio14; + BitField<14, 1, u32> turbo; BitField<28, 1, u32> circle_right; BitField<29, 1, u32> circle_left; diff --git a/src/core/movie.cpp b/src/core/movie.cpp index e280a10a2..ac48827ce 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -59,7 +59,8 @@ struct ControllerState { BitField<11, 1, u16> y; BitField<12, 1, u16> debug; BitField<13, 1, u16> gpio14; - // Bits 14-15 are currently unused + BitField<14, 1, u16> turbo; + // Bit 15 is currently unused }; s16_le circle_pad_x; s16_le circle_pad_y; @@ -261,6 +262,7 @@ void Movie::Play(Service::HID::PadState& pad_state, s16& circle_pad_x, s16& circ pad_state.y.Assign(s.pad_and_circle.y); pad_state.debug.Assign(s.pad_and_circle.debug); pad_state.gpio14.Assign(s.pad_and_circle.gpio14); + pad_state.turbo.Assign(s.pad_and_circle.turbo); circle_pad_x = s.pad_and_circle.circle_pad_x; circle_pad_y = s.pad_and_circle.circle_pad_y; @@ -385,6 +387,7 @@ void Movie::Record(const Service::HID::PadState& pad_state, const s16& circle_pa s.pad_and_circle.y.Assign(static_cast(pad_state.y)); s.pad_and_circle.debug.Assign(static_cast(pad_state.debug)); s.pad_and_circle.gpio14.Assign(static_cast(pad_state.gpio14)); + s.pad_and_circle.turbo.Assign(static_cast(pad_state.turbo)); s.pad_and_circle.circle_pad_x = circle_pad_x; s.pad_and_circle.circle_pad_y = circle_pad_y;