This commit is contained in:
Andy Klimczak 2025-12-09 09:12:02 +00:00 committed by GitHub
commit 61d87cacdf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 121 additions and 14 deletions

View File

@ -803,8 +803,8 @@ object NativeLibrary {
const val DPAD = 780 const val DPAD = 780
const val BUTTON_DEBUG = 781 const val BUTTON_DEBUG = 781
const val BUTTON_GPIO14 = 782 const val BUTTON_GPIO14 = 782
const val BUTTON_TURBO = 783
const val BUTTON_SWAP = 800 const val BUTTON_SWAP = 800
const val BUTTON_TURBO = 801
} }
/** /**

View File

@ -20,6 +20,11 @@ class HotkeyUtility(
var HotkeyIsPressed = false var HotkeyIsPressed = false
fun handleHotkey(bindedButton: Int): Boolean { fun handleHotkey(bindedButton: Int): Boolean {
if (bindedButton == NativeLibrary.ButtonType.BUTTON_TURBO) {
TurboHelper.toggleTurbo(true)
HotkeyIsPressed = true
return true
}
if(hotkeyButtons.contains(bindedButton)) { if(hotkeyButtons.contains(bindedButton)) {
when (bindedButton) { when (bindedButton) {
Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen() Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen()

View File

@ -121,6 +121,7 @@ class Settings {
const val KEY_BUTTON_SELECT = "button_select" const val KEY_BUTTON_SELECT = "button_select"
const val KEY_BUTTON_START = "button_start" const val KEY_BUTTON_START = "button_start"
const val KEY_BUTTON_HOME = "button_home" 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_UP = "button_up"
const val KEY_BUTTON_DOWN = "button_down" const val KEY_BUTTON_DOWN = "button_down"
const val KEY_BUTTON_LEFT = "button_left" const val KEY_BUTTON_LEFT = "button_left"
@ -150,7 +151,8 @@ class Settings {
KEY_BUTTON_Y, KEY_BUTTON_Y,
KEY_BUTTON_SELECT, KEY_BUTTON_SELECT,
KEY_BUTTON_START, KEY_BUTTON_START,
KEY_BUTTON_HOME KEY_BUTTON_HOME,
KEY_BUTTON_TURBO
) )
val buttonTitles = listOf( val buttonTitles = listOf(
R.string.button_a, R.string.button_a,
@ -159,7 +161,8 @@ class Settings {
R.string.button_y, R.string.button_y,
R.string.button_select, R.string.button_select,
R.string.button_start, R.string.button_start,
R.string.button_home R.string.button_home,
R.string.button_turbo
) )
val circlePadKeys = listOf( val circlePadKeys = listOf(
KEY_CIRCLEPAD_AXIS_VERTICAL, KEY_CIRCLEPAD_AXIS_VERTICAL,
@ -245,4 +248,4 @@ class Settings {
) )
} }
} }
} }

View File

@ -124,6 +124,7 @@ class InputBindingSetting(
Settings.KEY_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_SELECT Settings.KEY_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_SELECT
Settings.KEY_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_START Settings.KEY_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_START
Settings.KEY_BUTTON_HOME -> NativeLibrary.ButtonType.BUTTON_HOME 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_UP -> NativeLibrary.ButtonType.DPAD_UP
Settings.KEY_BUTTON_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN Settings.KEY_BUTTON_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
Settings.KEY_BUTTON_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT Settings.KEY_BUTTON_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT

View File

@ -66,8 +66,9 @@ static const std::array<int, Settings::NativeButton::NumButtons> default_buttons
InputManager::N3DS_TRIGGER_L, InputManager::N3DS_TRIGGER_R, InputManager::N3DS_TRIGGER_L, InputManager::N3DS_TRIGGER_R,
InputManager::N3DS_BUTTON_START, InputManager::N3DS_BUTTON_SELECT, InputManager::N3DS_BUTTON_START, InputManager::N3DS_BUTTON_SELECT,
InputManager::N3DS_BUTTON_DEBUG, InputManager::N3DS_BUTTON_GPIO14, InputManager::N3DS_BUTTON_DEBUG, InputManager::N3DS_BUTTON_GPIO14,
InputManager::N3DS_BUTTON_ZL, InputManager::N3DS_BUTTON_ZR, InputManager::N3DS_BUTTON_TURBO, InputManager::N3DS_BUTTON_ZL,
InputManager::N3DS_BUTTON_HOME, InputManager::N3DS_BUTTON_ZR, InputManager::N3DS_BUTTON_HOME,
InputManager::N3DS_BUTTON_POWER,
}; };
static const std::array<int, Settings::NativeAnalog::NumAnalogs> default_analogs{{ static const std::array<int, Settings::NativeAnalog::NumAnalogs> default_analogs{{

View File

@ -40,6 +40,7 @@ button_start=
button_select= button_select=
button_debug= button_debug=
button_gpio14= button_gpio14=
button_turbo=
button_zl= button_zl=
button_zr= button_zr=
button_home= button_home=

View File

@ -39,7 +39,8 @@ enum ButtonType {
N3DS_TRIGGER_L = 773, N3DS_TRIGGER_L = 773,
N3DS_TRIGGER_R = 774, N3DS_TRIGGER_R = 774,
N3DS_BUTTON_DEBUG = 781, N3DS_BUTTON_DEBUG = 781,
N3DS_BUTTON_GPIO14 = 782 N3DS_BUTTON_GPIO14 = 782,
N3DS_BUTTON_TURBO = 783
}; };
class ButtonList; class ButtonList;

View File

@ -494,6 +494,9 @@ void GMainWindow::InitializeWidgets() {
}); });
InputCommon::Init(); 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(), multiplayer_state = new MultiplayerState(system, this, game_list->GetModel(),
ui->action_Leave_Room, ui->action_Show_Room); ui->action_Leave_Room, ui->action_Show_Room);
multiplayer_state->setVisible(false); multiplayer_state->setVisible(false);
@ -878,6 +881,8 @@ void GMainWindow::InitializeHotkeys() {
const auto fullscreen_hotkey = hotkey_registry.GetKeySequence(main_window, fullscreen); const auto fullscreen_hotkey = hotkey_registry.GetKeySequence(main_window, fullscreen);
add_secondary_window_hotkey(action_secondary_fullscreen, fullscreen_hotkey, add_secondary_window_hotkey(action_secondary_fullscreen, fullscreen_hotkey,
SLOT(ToggleSecondaryFullscreen())); SLOT(ToggleSecondaryFullscreen()));
UpdateTurboButtonBinding(true);
} }
void GMainWindow::SetDefaultUIGeometry() { void GMainWindow::SetDefaultUIGeometry() {
@ -1565,6 +1570,7 @@ void GMainWindow::ShutdownGame() {
UpdateSaveStates(); UpdateSaveStates();
emulation_running = false; emulation_running = false;
RefreshTurboPollingState();
game_title.clear(); game_title.clear();
UpdateWindowTitle(); UpdateWindowTitle();
@ -2460,6 +2466,7 @@ void GMainWindow::OnStartGame() {
UpdateSaveStates(); UpdateSaveStates();
UpdateStatusButtons(); UpdateStatusButtons();
UpdateTurboButtonBinding();
} }
void GMainWindow::OnRestartGame() { void GMainWindow::OnRestartGame() {
@ -2668,6 +2675,54 @@ void GMainWindow::ReloadTurbo() {
UpdateStatusBar(); 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<Input::ButtonDevice>(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 // TODO: This should probably take in something more descriptive than a bool. -OS
void GMainWindow::AdjustSpeedLimit(bool increase) { void GMainWindow::AdjustSpeedLimit(bool increase) {
const int SPEED_LIMIT_STEP = 5; 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::values.touch_from_button_maps = old_touch_from_button_maps;
Settings::LoadProfile(old_input_profile_index); Settings::LoadProfile(old_input_profile_index);
} }
UpdateTurboButtonBinding(true);
} }
void GMainWindow::OnLoadAmiibo() { void GMainWindow::OnLoadAmiibo() {

View File

@ -6,6 +6,7 @@
#include <array> #include <array>
#include <memory> #include <memory>
#include <string>
#include <vector> #include <vector>
#ifdef __unix__ #ifdef __unix__
#include <QDBusObjectPath> #include <QDBusObjectPath>
@ -23,6 +24,7 @@
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "citra_qt/user_data_migration.h" #include "citra_qt/user_data_migration.h"
#include "core/core.h" #include "core/core.h"
#include "core/frontend/input.h"
#include "core/savestate.h" #include "core/savestate.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
@ -266,6 +268,9 @@ private slots:
bool IsTurboEnabled(); bool IsTurboEnabled();
void SetTurboEnabled(bool); void SetTurboEnabled(bool);
void ReloadTurbo(); void ReloadTurbo();
void UpdateTurboButtonBinding(bool force = false);
void RefreshTurboPollingState();
void PollTurboButton();
void AdjustSpeedLimit(bool increase); void AdjustSpeedLimit(bool increase);
void UpdateSecondaryWindowVisibility(); void UpdateSecondaryWindowVisibility();
void ToggleScreenLayout(); void ToggleScreenLayout();
@ -377,6 +382,10 @@ private:
bool auto_paused = false; bool auto_paused = false;
bool auto_muted = false; bool auto_muted = false;
QTimer mouse_hide_timer; QTimer mouse_hide_timer;
QTimer turbo_poll_timer;
std::unique_ptr<Input::ButtonDevice> turbo_button_device;
std::string turbo_button_binding;
bool turbo_button_was_pressed = false;
// Movie // Movie
bool movie_record_on_start = false; bool movie_record_on_start = false;

View File

@ -29,7 +29,7 @@ QtConfig::~QtConfig() {
const std::array<int, Settings::NativeButton::NumButtons> QtConfig::default_buttons = { const std::array<int, Settings::NativeButton::NumButtons> QtConfig::default_buttons = {
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, 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_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<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{

View File

@ -170,8 +170,8 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight,
ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect, ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect,
ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR, ui->buttonDebug, ui->buttonGpio14, ui->buttonTurbo, ui->buttonZL,
ui->buttonHome, ui->buttonPower, ui->buttonZR, ui->buttonHome, ui->buttonPower,
}; };
analog_map_buttons = {{ analog_map_buttons = {{

View File

@ -470,6 +470,24 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout_32">
<item>
<widget class="QLabel" name="label_40">
<property name="text">
<string>Toggle Turbo Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonTurbo">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -1093,6 +1111,7 @@
<tabstop>buttonHome</tabstop> <tabstop>buttonHome</tabstop>
<tabstop>buttonCircleMod</tabstop> <tabstop>buttonCircleMod</tabstop>
<tabstop>buttonDebug</tabstop> <tabstop>buttonDebug</tabstop>
<tabstop>buttonTurbo</tabstop>
<tabstop>buttonGpio14</tabstop> <tabstop>buttonGpio14</tabstop>
<tabstop>buttonMotionTouch</tabstop> <tabstop>buttonMotionTouch</tabstop>
<tabstop>buttonAutoMap</tabstop> <tabstop>buttonAutoMap</tabstop>

View File

@ -48,9 +48,11 @@ bool SdlConfig::LoadINI(const std::string& default_contents, bool retry) {
} }
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = { static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = {
SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X,
SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H,
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, 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<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{

View File

@ -40,6 +40,7 @@ button_start=
button_select= button_select=
button_debug= button_debug=
button_gpio14= button_gpio14=
button_turbo=
button_zl= button_zl=
button_zr= button_zr=
button_home= button_home=

View File

@ -133,6 +133,7 @@ enum Values {
Select, Select,
Debug, Debug,
Gpio14, Gpio14,
Turbo,
ZL, ZL,
ZR, ZR,
@ -170,6 +171,7 @@ static const std::array<const char*, NumButtons> mapping = {{
"button_select", "button_select",
"button_debug", "button_debug",
"button_gpio14", "button_gpio14",
"button_turbo",
"button_zl", "button_zl",
"button_zr", "button_zr",
"button_home", "button_home",

View File

@ -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.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus());
state.debug.Assign(buttons[Debug - BUTTON_HID_BEGIN]->GetStatus()); state.debug.Assign(buttons[Debug - BUTTON_HID_BEGIN]->GetStatus());
state.gpio14.Assign(buttons[Gpio14 - 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 // Get current circle pad position and update circle pad direction
float circle_pad_x_f, circle_pad_y_f; float circle_pad_x_f, circle_pad_y_f;

View File

@ -52,6 +52,7 @@ struct PadState {
BitField<11, 1, u32> y; BitField<11, 1, u32> y;
BitField<12, 1, u32> debug; BitField<12, 1, u32> debug;
BitField<13, 1, u32> gpio14; BitField<13, 1, u32> gpio14;
BitField<14, 1, u32> turbo;
BitField<28, 1, u32> circle_right; BitField<28, 1, u32> circle_right;
BitField<29, 1, u32> circle_left; BitField<29, 1, u32> circle_left;

View File

@ -59,7 +59,8 @@ struct ControllerState {
BitField<11, 1, u16> y; BitField<11, 1, u16> y;
BitField<12, 1, u16> debug; BitField<12, 1, u16> debug;
BitField<13, 1, u16> gpio14; 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_x;
s16_le circle_pad_y; 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.y.Assign(s.pad_and_circle.y);
pad_state.debug.Assign(s.pad_and_circle.debug); pad_state.debug.Assign(s.pad_and_circle.debug);
pad_state.gpio14.Assign(s.pad_and_circle.gpio14); 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_x = s.pad_and_circle.circle_pad_x;
circle_pad_y = s.pad_and_circle.circle_pad_y; 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<u16>(pad_state.y)); s.pad_and_circle.y.Assign(static_cast<u16>(pad_state.y));
s.pad_and_circle.debug.Assign(static_cast<u16>(pad_state.debug)); s.pad_and_circle.debug.Assign(static_cast<u16>(pad_state.debug));
s.pad_and_circle.gpio14.Assign(static_cast<u16>(pad_state.gpio14)); s.pad_and_circle.gpio14.Assign(static_cast<u16>(pad_state.gpio14));
s.pad_and_circle.turbo.Assign(static_cast<u16>(pad_state.turbo));
s.pad_and_circle.circle_pad_x = circle_pad_x; s.pad_and_circle.circle_pad_x = circle_pad_x;
s.pad_and_circle.circle_pad_y = circle_pad_y; s.pad_and_circle.circle_pad_y = circle_pad_y;