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 20c3c0868..988ce1426 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 @@ -918,6 +918,7 @@ 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/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 6d310fea6..678bbe419 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 08eaf3283..8e9e637dd 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..01a66dffa 100644 --- a/src/android/app/src/main/jni/input_manager.h +++ b/src/android/app/src/main/jni/input_manager.h @@ -40,6 +40,7 @@ enum ButtonType { N3DS_TRIGGER_R = 774, N3DS_BUTTON_DEBUG = 781, 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 3bf862c6b..811c33cae 100644 --- a/src/citra_qt/citra_qt.cpp +++ b/src/citra_qt/citra_qt.cpp @@ -508,6 +508,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); @@ -892,6 +895,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() { @@ -1594,6 +1599,7 @@ void GMainWindow::ShutdownGame() { UpdateSaveStates(); emulation_running = false; + RefreshTurboPollingState(); game_title.clear(); UpdateWindowTitle(); @@ -2505,6 +2511,7 @@ void GMainWindow::OnStartGame() { UpdateSaveStates(); UpdateStatusButtons(); + UpdateTurboButtonBinding(); } void GMainWindow::OnRestartGame() { @@ -2713,6 +2720,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; @@ -2867,6 +2922,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 a4fa3c9aa..1090999a8 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" @@ -272,6 +274,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(); @@ -385,6 +390,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 f5aac8030..cd43939fc 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 a504dd3aa..5e7fb7f0a 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 4c97dbb89..807e55e56 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 c0f595d12..f62a850d8 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -141,6 +141,7 @@ enum Values { Select, Debug, Gpio14, + Turbo, ZL, ZR, @@ -178,6 +179,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;