diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 8fa5a157d..124dd901e 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -45,17 +45,21 @@ static const std::array defaults = { SDL_SCANCODE_L, // indirectly mapped keys - SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT -}; + SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT}; void Config::ReadValues() { // Controls for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { Settings::values.input_mappings[Settings::NativeInput::All[i]] = - Settings::InputDeviceMapping(sdl2_config->Get("Controls", Settings::NativeInput::Mapping[i], std::to_string(defaults[i]))); + Settings::InputDeviceMapping(sdl2_config->Get( + "Controls", Settings::NativeInput::Mapping[i], std::to_string(defaults[i]))); } - Settings::values.pad_circle_modifier = Settings::InputDeviceMapping(sdl2_config->Get("Controls", "pad_circle_modifier", "")); - Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.4); + Settings::values.pad_circle_modifier = + Settings::InputDeviceMapping(sdl2_config->Get("Controls", "pad_circle_modifier", "")); + Settings::values.pad_circle_modifier_scale = + (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.4); + Settings::values.pad_circle_deadzone = + (float)sdl2_config->GetReal("Controls", "pad_circle_deadzone", 0.4); // Core Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 7796982b7..ce2a75752 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -23,17 +23,22 @@ const std::array Config::defaults = Qt::Key_K, Qt::Key_J, Qt::Key_L, // indirectly mapped keys - Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right -}; + Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right}; void Config::ReadValues() { qt_config->beginGroup("Controls"); for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { Settings::values.input_mappings[Settings::NativeInput::All[i]] = - Settings::InputDeviceMapping(qt_config->value(Settings::NativeInput::Mapping[i], defaults[i]).toString().toStdString()); + Settings::InputDeviceMapping( + qt_config->value(Settings::NativeInput::Mapping[i], defaults[i]) + .toString() + .toStdString()); } - Settings::values.pad_circle_modifier = Settings::InputDeviceMapping(qt_config->value("pad_circle_modifier", 0).toString().toStdString()); - Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.4).toFloat(); + Settings::values.pad_circle_modifier = Settings::InputDeviceMapping( + qt_config->value("pad_circle_modifier", 0).toString().toStdString()); + Settings::values.pad_circle_modifier_scale = + qt_config->value("pad_circle_modifier_scale", 0.4).toFloat(); + Settings::values.pad_circle_deadzone = qt_config->value("pad_circle_deadzone", 0.3f).toFloat(); qt_config->endGroup(); qt_config->beginGroup("Core"); @@ -136,11 +141,16 @@ void Config::ReadValues() { void Config::SaveValues() { qt_config->beginGroup("Controls"); for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), - QString::fromStdString(Settings::values.input_mappings[Settings::NativeInput::All[i]].ToString())); + qt_config->setValue( + QString::fromStdString(Settings::NativeInput::Mapping[i]), + QString::fromStdString( + Settings::values.input_mappings[Settings::NativeInput::All[i]].ToString())); } - qt_config->setValue("pad_circle_modifier", QString::fromStdString(Settings::values.pad_circle_modifier.ToString())); - qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale); + qt_config->setValue("pad_circle_modifier", + QString::fromStdString(Settings::values.pad_circle_modifier.ToString())); + qt_config->setValue("pad_circle_modifier_scale", + (double)Settings::values.pad_circle_modifier_scale); + qt_config->setValue("pad_circle_deadzone", (double)Settings::values.pad_circle_deadzone); qt_config->endGroup(); qt_config->beginGroup("Core"); diff --git a/src/core/settings.h b/src/core/settings.h index 87dc1e918..3980aa52c 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -46,37 +46,29 @@ enum Values { CRIGHT, // indirectly mapped keys - CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, + CIRCLE_UP, + CIRCLE_DOWN, + CIRCLE_LEFT, + CIRCLE_RIGHT, NUM_INPUTS }; -static const std::array Mapping = { { - // directly mapped keys - "pad_a", "pad_b", "pad_x", "pad_y", - "pad_l", "pad_r", "pad_zl", "pad_zr", - "pad_start", "pad_select", "pad_home", - "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", - "pad_cup", "pad_cdown", "pad_cleft", "pad_cright", +static const std::array Mapping = { + {// directly mapped keys + "pad_a", "pad_b", "pad_x", "pad_y", "pad_l", "pad_r", "pad_zl", "pad_zr", "pad_start", + "pad_select", "pad_home", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", "pad_cup", + "pad_cdown", "pad_cleft", "pad_cright", - // indirectly mapped keys - "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right" -} }; -static const std::array All = { { - A, B, X, Y, - L, R, ZL, ZR, - START, SELECT, HOME, - DUP, DDOWN, DLEFT, DRIGHT, - CUP, CDOWN, CLEFT, CRIGHT, - CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT -} }; + // indirectly mapped keys + "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right"}}; +static const std::array All = { + {A, B, X, Y, L, R, ZL, ZR, + START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, CUP, + CDOWN, CLEFT, CRIGHT, CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT}}; } -enum class DeviceFramework { - SDL -}; -enum class Device { - Keyboard, Gamepad -}; +enum class DeviceFramework { SDL }; +enum class Device { Keyboard, Gamepad }; struct InputDeviceMapping { DeviceFramework framework = DeviceFramework::SDL; int number = 0; @@ -105,7 +97,8 @@ struct InputDeviceMapping { } bool operator==(const InputDeviceMapping& rhs) const { - return std::tie(device, framework, number) == std::tie(rhs.device, rhs.framework, rhs.number); + return std::tie(device, framework, number) == + std::tie(rhs.device, rhs.framework, rhs.number); } bool operator==(const std::string& rhs) const { return ToString() == rhs; @@ -114,9 +107,7 @@ struct InputDeviceMapping { return ToString() < rhs.ToString(); } - void InitKey() { - - } + void InitKey() {} std::string ToString() const { std::string result; @@ -133,7 +124,7 @@ struct InputDeviceMapping { result += "Gamepad"; result += "/"; - result += key; + result += std::to_string(key); return result; } }; @@ -146,8 +137,7 @@ struct Values { std::array input_mappings; InputDeviceMapping pad_circle_modifier; float pad_circle_modifier_scale; - float circle_pad_deadzone = 30; - float c_stick_deadzone = 30; + float pad_circle_deadzone; // Core bool use_cpu_jit; diff --git a/src/input_core/devices/device.h b/src/input_core/devices/device.h index 2aedff55a..7821c20ce 100644 --- a/src/input_core/devices/device.h +++ b/src/input_core/devices/device.h @@ -20,13 +20,12 @@ public: * @param keymap: vector of PadStates for device to listen for * @return true if successful */ - virtual bool InitDevice( - int number, Settings::InputDeviceMapping device_mapping) = 0; + virtual bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) = 0; /** * Process inputs that were pressed since last frame */ - virtual std::map ProcessInput() = 0; + virtual std::map ProcessInput() = 0; /** * Close connection to device @@ -44,6 +43,7 @@ public: * Clears info from last frame. */ virtual void Clear() = 0; + protected: Settings::InputDeviceMapping input_device_mapping; }; diff --git a/src/input_core/devices/keyboard.cpp b/src/input_core/devices/keyboard.cpp index ef98bb48b..bf6eeee16 100644 --- a/src/input_core/devices/keyboard.cpp +++ b/src/input_core/devices/keyboard.cpp @@ -11,8 +11,7 @@ Keyboard::Keyboard() {} Keyboard::~Keyboard() {} -bool Keyboard::InitDevice( - int number, Settings::InputDeviceMapping device_mapping) { +bool Keyboard::InitDevice(int number, Settings::InputDeviceMapping device_mapping) { // Check if keyboard is mapped for circle up or left. if so, set modifier to -1 /*for (const auto& entry : key_mapping) { @@ -48,32 +47,6 @@ std::map Keyboard::ProcessInput() { input_device_mapping.key = key.first.key; button_status.emplace(input_device_mapping, key.second ? 1.0 : 0.0); } - //for (const auto& entry : key_mapping) { - // if (entry.first == "") - // continue; - // int keycode = std::stoi(entry.first); - // KeyboardKey proxy = KeyboardKey(keycode, ""); - - // // if key is pressed when prev state is unpressed, or if key pressed and is a circle pad - // // direction - // if ((keysPressedCopy[proxy] == true && keys_pressed_last[keycode] == false) || - // (keysPressedCopy[proxy] == true && circle_pad_directions.count(keycode))) { - // for (const auto& key : entry.second) { - // if (circle_pad_directions.count(keycode)) { // If is analog key press - // float modifier = - // (circlePadModPressed) ? Settings::values.pad_circle_modifier_scale : 1; - // KeyMap::PressKey(key, circle_pad_directions[keycode] * modifier); - // } else // Is digital key press - // KeyMap::PressKey(key, 1.0); - // } - // keys_pressed_last[keycode] = true; - // } else if (keysPressedCopy[proxy] == false && keys_pressed_last[keycode] == true) { - // for (const auto& key : entry.second) { - // KeyMap::ReleaseKey(key); - // } - // keys_pressed_last[keycode] = false; - // } - //} return button_status; } diff --git a/src/input_core/devices/keyboard.h b/src/input_core/devices/keyboard.h index 2a59f95b2..01a2ae1d7 100644 --- a/src/input_core/devices/keyboard.h +++ b/src/input_core/devices/keyboard.h @@ -31,8 +31,7 @@ class Keyboard : public IDevice { public: Keyboard(); ~Keyboard(); - bool InitDevice( - int number, Settings::InputDeviceMapping device_mapping) override; + bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) override; std::map ProcessInput() override; bool CloseDevice() override; void KeyPressed(KeyboardKey key); @@ -45,6 +44,6 @@ private: std::map keys_pressed_last; std::mutex m; ///< Keys pressed from frontend is on a separate thread. std::map circle_pad_directions; ///< Inverts the strength of key press if it is a - ///circle pad direction that needs it. + /// circle pad direction that needs it. KeyboardKey circle_pad_modifier; }; diff --git a/src/input_core/devices/sdl_gamepad.cpp b/src/input_core/devices/sdl_gamepad.cpp index 2e84b6df4..f7e31bd90 100644 --- a/src/input_core/devices/sdl_gamepad.cpp +++ b/src/input_core/devices/sdl_gamepad.cpp @@ -23,8 +23,7 @@ SDLGamepad::~SDLGamepad() { CloseDevice(); } -bool SDLGamepad::InitDevice( - int number, Settings::InputDeviceMapping device_mapping) { +bool SDLGamepad::InitDevice(int number, Settings::InputDeviceMapping device_mapping) { if (!SDLGamepad::SDLInitialized && SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) { LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_GAMECONTROLLER) failed"); return false; @@ -56,26 +55,24 @@ std::map SDLGamepad::ProcessInput() { for (int i = 0; i < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX; i++) { SDL_GameControllerButton button = static_cast(i); Uint8 pressed = SDL_GameControllerGetButton(gamepad, button); - input_device_mapping.key = static_cast(gamepadinput_to_sdlname_mapping2[SDL_GameControllerGetStringForButton(button)]); + input_device_mapping.key = static_cast( + gamepadinput_to_sdlname_mapping2[SDL_GameControllerGetStringForButton(button)]); button_status.emplace(input_device_mapping, pressed); } for (int i = 0; i < SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_MAX; i++) { SDL_GameControllerAxis axis = static_cast(i); float strength = fmaxf(-1, (float)SDL_GameControllerGetAxis(gamepad, axis) / 32767.0); - input_device_mapping.key = static_cast(gamepadinput_to_sdlname_mapping2[SDL_GameControllerGetStringForAxis(axis)]); - if (strength < 0) - input_device_mapping.key += 1; //minus axis value is always one greater - - button_status.emplace(input_device_mapping, strength); -//for (const auto& padstate : entry.second) { -//// TODO: calculate deadzone by radial field rather than axial field. (sqrt(x^2 + -//// y^2) > deadzone) -//// dont process if in deadzone. Replace later with settings for deadzone. -//if (abs(value) < 0.2 * 32767.0) -//KeyMap::ReleaseKey(padstate); -//else -//KeyMap::PressKey(padstate, (float)value / 32767.0); -//} + input_device_mapping.key = static_cast( + gamepadinput_to_sdlname_mapping2[SDL_GameControllerGetStringForAxis(axis)]); + if (strength < 0) { + button_status.emplace(input_device_mapping, 0); + input_device_mapping.key += 1; // minus axis value is always one greater + button_status.emplace(input_device_mapping, abs(strength)); + } else { + button_status.emplace(input_device_mapping, abs(strength)); + input_device_mapping.key += 1; // minus axis value is always one greater + button_status.emplace(input_device_mapping, 0); + } } return button_status; } diff --git a/src/input_core/devices/sdl_gamepad.h b/src/input_core/devices/sdl_gamepad.h index a136e2793..17f21a1e5 100644 --- a/src/input_core/devices/sdl_gamepad.h +++ b/src/input_core/devices/sdl_gamepad.h @@ -13,8 +13,7 @@ public: SDLGamepad(int number_, _SDL_GameController* gamepad_); ~SDLGamepad(); - bool InitDevice( - int number, Settings::InputDeviceMapping device_mapping) override; + bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) override; std::map ProcessInput() override; bool CloseDevice() override; Settings::InputDeviceMapping GetInput() override; @@ -30,7 +29,6 @@ public: LeftShoulder, RightShoulder, Start, - Guide, Back, DPadUp, DpadDown, @@ -81,31 +79,30 @@ private: {GamepadInputs::RightXMinus, "rightx"}, }; std::map gamepadinput_to_sdlname_mapping2 = { - { "a", GamepadInputs::ButtonA }, - { "b", GamepadInputs::ButtonB }, - { "x", GamepadInputs::ButtonX }, - { "y", GamepadInputs::ButtonY }, - { "leftshoulder", GamepadInputs::LeftShoulder }, - { "rightshoulder", GamepadInputs::RightShoulder }, - { "start", GamepadInputs::Start }, - { "back", GamepadInputs::Back }, - { "dpup", GamepadInputs::DPadUp }, - { "dpdown", GamepadInputs::DpadDown }, - { "dpleft", GamepadInputs::DpadLeft }, - { "dpright", GamepadInputs::DpadRight }, - { "leftstick", GamepadInputs::L3 }, - { "rightstick", GamepadInputs::R3 }, - { "lefttrigger", GamepadInputs::LeftTrigger }, - { "righttrigger", GamepadInputs::RightTrigger }, - { "lefty", GamepadInputs::LeftYPlus }, - { "lefty2", GamepadInputs::LeftYMinus }, - { "leftx", GamepadInputs::LeftXPlus }, - { "leftx2", GamepadInputs::LeftXMinus }, - { "righty", GamepadInputs::RightYPlus }, - { "righty2", GamepadInputs::RightYMinus }, - { "rightx", GamepadInputs::RightXPlus }, - { "rightx2", GamepadInputs::RightXMinus } - }; + {"a", GamepadInputs::ButtonA}, + {"b", GamepadInputs::ButtonB}, + {"x", GamepadInputs::ButtonX}, + {"y", GamepadInputs::ButtonY}, + {"leftshoulder", GamepadInputs::LeftShoulder}, + {"rightshoulder", GamepadInputs::RightShoulder}, + {"start", GamepadInputs::Start}, + {"back", GamepadInputs::Back}, + {"dpup", GamepadInputs::DPadUp}, + {"dpdown", GamepadInputs::DpadDown}, + {"dpleft", GamepadInputs::DpadLeft}, + {"dpright", GamepadInputs::DpadRight}, + {"leftstick", GamepadInputs::L3}, + {"rightstick", GamepadInputs::R3}, + {"lefttrigger", GamepadInputs::LeftTrigger}, + {"righttrigger", GamepadInputs::RightTrigger}, + {"lefty", GamepadInputs::LeftYPlus}, + {"lefty2", GamepadInputs::LeftYMinus}, + {"leftx", GamepadInputs::LeftXPlus}, + {"leftx2", GamepadInputs::LeftXMinus}, + {"righty", GamepadInputs::RightYPlus}, + {"righty2", GamepadInputs::RightYMinus}, + {"rightx", GamepadInputs::RightXPlus}, + {"rightx2", GamepadInputs::RightXMinus}}; static bool SDLInitialized; std::map keys_pressed; ///< Map of keys that were pressed on previous iteration diff --git a/src/input_core/input_core.cpp b/src/input_core/input_core.cpp index afb4d57a2..99447c0bd 100644 --- a/src/input_core/input_core.cpp +++ b/src/input_core/input_core.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include "core/core_timing.h" @@ -13,106 +12,152 @@ #include "input_core/devices/sdl_gamepad.h" #include "input_core/input_core.h" -namespace InputCore { constexpr u64 frame_ticks = 268123480ull / 60; -static int tick_event; -static Service::HID::PadState pad_state; -static std::tuple circle_pad = {0, 0}; -static std::shared_ptr main_keyboard; ///< Keyboard is always active for Citra -static std::vector> - devices; ///< Devices that are handling input for the game -static std::map> key_mappings; -std::map keys_pressed; ///< keys that were pressed on previous frame. -static std::mutex pad_state_mutex; -static std::mutex touch_mutex; -static u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) -static u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) -static bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false +int InputCore::tick_event; +Service::HID::PadState InputCore::pad_state; +std::tuple InputCore::circle_pad; +std::shared_ptr InputCore::main_keyboard; ///< Keyboard is always active for Citra +std::vector> + InputCore::devices; ///< Devices that are handling input for the game +std::map> InputCore::key_mappings; +std::map + InputCore::keys_pressed; ///< keys that were pressed on previous frame. +std::mutex InputCore::pad_state_mutex; +std::mutex InputCore::touch_mutex; +u16 InputCore::touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) +u16 InputCore::touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) +bool InputCore::touch_pressed; ///< True if touchpad area is currently pressed, otherwise false +const float default_deadzone = 0.5; -static void UpdateEmulatorInputs(std::map inputs) { - for (auto& inputMapping : inputs) { //Loop through all buttons from input device - float strength = inputMapping.second; - auto emulator_inputs = key_mappings[inputMapping.first]; - for (auto& emulator_input : emulator_inputs) { - if (std::find(std::begin(KeyMap::analog_inputs), std::end(KeyMap::analog_inputs), emulator_input) == - std::end(KeyMap::analog_inputs)) { // digital button - if (strength < 0.5 && keys_pressed[emulator_input] == true) { - pad_state.hex &= ~emulator_input.hex; - keys_pressed[emulator_input] = false; - } - else if (strength >= 0.5 && keys_pressed[emulator_input] == false) { - pad_state.hex |= emulator_input.hex; - keys_pressed[emulator_input] = true; - } - } - else { // analog stick - if (emulator_input == Service::HID::PAD_CIRCLE_UP || emulator_input == Service::HID::PAD_CIRCLE_DOWN) { - std::get<1>(circle_pad) = KeyMap::MAX_CIRCLEPAD_POS * strength * -1; - } - else if (emulator_input == Service::HID::PAD_CIRCLE_LEFT || - emulator_input == Service::HID::PAD_CIRCLE_RIGHT) { - std::get<0>(circle_pad) = KeyMap::MAX_CIRCLEPAD_POS * strength; - } - } - } - } +void InputCore::Init() { + ParseSettings(); + tick_event = CoreTiming::RegisterEvent("InputCore::tick_event", InputTickCallback); + CoreTiming::ScheduleEvent(frame_ticks, tick_event); } -static void InputTickCallback(u64, int cycles_late) { +void InputCore::Shutdown() { + CoreTiming::UnscheduleEvent(tick_event, 0); + devices.clear(); +} +void InputCore::InputTickCallback(u64, int cycles_late) { + std::vector> inputs; for (auto& device : devices) { - auto inputs = device->ProcessInput(); - UpdateEmulatorInputs(inputs); + inputs.push_back(device->ProcessInput()); } + UpdateEmulatorInputs(inputs); Service::HID::Update(); // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, tick_event); } +void InputCore::UpdateEmulatorInputs( + std::vector> inputs) { + std::lock_guard lock(pad_state_mutex); + // Apply deadzone + float leftx = 0, lefty = 0; + float circle_pad_modifier = 1.0; + auto circle_pad_modifier_mapping = Settings::values.pad_circle_modifier; + for (auto& input_device : inputs) { + for (auto& button_states : input_device) { // Loop through all buttons from input device + float strength = button_states.second; + auto emulator_inputs = key_mappings[button_states.first]; + for (auto& emulator_input : emulator_inputs) { + if (emulator_input == Service::HID::PAD_CIRCLE_UP && abs(strength) > 0) { + lefty = -strength; + } else if (emulator_input == Service::HID::PAD_CIRCLE_DOWN && abs(strength) > 0) { + lefty = strength; + } else if (emulator_input == Service::HID::PAD_CIRCLE_LEFT && abs(strength) > 0) { + leftx = -strength; + } else if (emulator_input == Service::HID::PAD_CIRCLE_RIGHT && abs(strength) > 0) { + leftx = strength; + } + } + if (button_states.first == circle_pad_modifier_mapping) + circle_pad_modifier = (button_states.second > default_deadzone) + ? Settings::values.pad_circle_modifier_scale + : 1.0; + } + } + float deadzone = Settings::values.pad_circle_deadzone; + std::tuple left_stick = ApplyDeadzone(leftx, lefty, deadzone); -Service::HID::PadState GetPadState() { + for (auto& input_device : inputs) { + for (auto& button_states : input_device) { // Loop through all buttons from input device + float strength = button_states.second; + auto emulator_inputs = key_mappings[button_states.first]; + for (auto& emulator_input : emulator_inputs) { + if (std::find(std::begin(KeyMap::analog_inputs), std::end(KeyMap::analog_inputs), + emulator_input) == + std::end(KeyMap::analog_inputs)) { // digital button + if (abs(strength) < default_deadzone && keys_pressed[emulator_input] == true) { + pad_state.hex &= ~emulator_input.hex; + keys_pressed[emulator_input] = false; + } else if (abs(strength) >= default_deadzone && + keys_pressed[emulator_input] == false) { + pad_state.hex |= emulator_input.hex; + keys_pressed[emulator_input] = true; + } + } else { // analog stick + if (emulator_input == Service::HID::PAD_CIRCLE_UP || + emulator_input == Service::HID::PAD_CIRCLE_DOWN) { + std::get<1>(circle_pad) = std::get<1>(left_stick) * + KeyMap::MAX_CIRCLEPAD_POS * -1 * + circle_pad_modifier; + } else if (emulator_input == Service::HID::PAD_CIRCLE_LEFT || + emulator_input == Service::HID::PAD_CIRCLE_RIGHT) { + std::get<0>(circle_pad) = std::get<0>(left_stick) * + KeyMap::MAX_CIRCLEPAD_POS * circle_pad_modifier; + } + } + } + } + } +} + +Service::HID::PadState InputCore::GetPadState() { std::lock_guard lock(pad_state_mutex); return pad_state; } -void SetPadState(const Service::HID::PadState& state) { +void InputCore::SetPadState(const Service::HID::PadState& state) { std::lock_guard lock(pad_state_mutex); pad_state.hex = state.hex; } -std::tuple GetCirclePad() { +std::tuple InputCore::GetCirclePad() { return circle_pad; } -void SetCirclePad(std::tuple pad) { +void InputCore::SetCirclePad(std::tuple pad) { circle_pad = pad; } -std::shared_ptr GetKeyboard() { +std::shared_ptr InputCore::GetKeyboard() { if (main_keyboard == nullptr) main_keyboard = std::make_shared(); return main_keyboard; } -std::tuple GetTouchState() { +std::tuple InputCore::GetTouchState() { std::lock_guard lock(touch_mutex); return std::make_tuple(touch_x, touch_y, touch_pressed); } -void SetTouchState(std::tuple value) { +void InputCore::SetTouchState(std::tuple value) { std::lock_guard lock(touch_mutex); std::tie(touch_x, touch_y, touch_pressed) = value; } /// Helper method to check if device was already initialized -bool CheckIfMappingExists(const std::vector& uniqueMapping, - Settings::InputDeviceMapping mappingToCheck) { +bool InputCore::CheckIfMappingExists(const std::vector& uniqueMapping, + Settings::InputDeviceMapping mappingToCheck) { return std::any_of(uniqueMapping.begin(), uniqueMapping.end(), [mappingToCheck](const auto& mapping) { return mapping == mappingToCheck; }); } /// Get Unique input mappings from settings -static std::vector GatherUniqueMappings() { +std::vector InputCore::GatherUniqueMappings() { std::vector uniqueMappings; for (const auto& mapping : Settings::values.input_mappings) { @@ -120,11 +165,14 @@ static std::vector GatherUniqueMappings() { uniqueMappings.push_back(mapping); } } + if (!CheckIfMappingExists(uniqueMappings, Settings::values.pad_circle_modifier)) { + uniqueMappings.push_back(Settings::values.pad_circle_modifier); + } return uniqueMappings; } /// Builds map of input keys to 3ds buttons for unique device -static void BuildKeyMapping() { +void InputCore::BuildKeyMapping() { key_mappings.clear(); for (size_t i = 0; i < Settings::values.input_mappings.size(); i++) { auto val = KeyMap::mapping_targets[i]; @@ -135,7 +183,7 @@ static void BuildKeyMapping() { } /// Generate a device for each unique mapping -static void GenerateUniqueDevices() { +void InputCore::GenerateUniqueDevices() { auto uniqueMappings = GatherUniqueMappings(); devices.clear(); std::shared_ptr input; @@ -159,29 +207,49 @@ static void GenerateUniqueDevices() { input->InitDevice(mapping.number, mapping); } + if (main_keyboard == nullptr) { + main_keyboard = std::make_shared(); + devices.push_back(main_keyboard); + } } /// Read settings to initialize devices -void ParseSettings() { +void InputCore::ParseSettings() { GenerateUniqueDevices(); } -void ReloadSettings() { +std::tuple InputCore::ApplyDeadzone(float x, float y, float dead_zone) { + float magnitude = sqrt((x * x) + (y * y)); + if (magnitude < dead_zone) { + x = 0; + y = 0; + } else { + float normalized_x = x / magnitude; + float normalized_y = y / magnitude; + x = normalized_x * ((magnitude - dead_zone) / (1 - dead_zone)); + y = normalized_y * ((magnitude - dead_zone) / (1 - dead_zone)); + } + return std::tuple(x, y); +} + +void InputCore::ReloadSettings() { if (devices.empty()) return; + std::lock_guard lock(pad_state_mutex); devices.clear(); ParseSettings(); } /// Returns all available input devices. Used for key binding in GUI -std::vector> GetAllDevices() { +std::vector> InputCore::GetAllDevices() { auto all_devices = SDLGamepad::GetAllDevices(); all_devices.push_back(InputCore::GetKeyboard()); return all_devices; } -Settings::InputDeviceMapping DetectInput(int max_time, std::function update_GUI) { +Settings::InputDeviceMapping InputCore::DetectInput(int max_time, + std::function update_GUI) { auto devices = GetAllDevices(); for (auto& device : devices) { device->Clear(); @@ -204,15 +272,3 @@ Settings::InputDeviceMapping DetectInput(int max_time, std::function }; return input_device; } - -void Init() { - ParseSettings(); - tick_event = CoreTiming::RegisterEvent("InputCore::tick_event", InputTickCallback); - CoreTiming::ScheduleEvent(frame_ticks, tick_event); -} - -void Shutdown() { - CoreTiming::UnscheduleEvent(tick_event, 0); - devices.clear(); -} -} diff --git a/src/input_core/input_core.h b/src/input_core/input_core.h index 8d3330c9f..c2dc7aea9 100644 --- a/src/input_core/input_core.h +++ b/src/input_core/input_core.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "core/hle/service/hid/hid.h" @@ -14,66 +15,101 @@ class Keyboard; -namespace InputCore { +static class InputCore { +public: + static void Init(); + static void Shutdown(); -void Init(); -void Shutdown(); + /** + * Threadsafe getter to the current PadState + * @return Service::HID::PadState instance + */ + static Service::HID::PadState GetPadState(); -/** - * Threadsafe getter to the current PadState - * @return Service::HID::PadState instance - */ -Service::HID::PadState GetPadState(); + /** + * Threadsafe setter for the current PadState + * @param state New PadState to overwrite current PadState. + */ + static void SetPadState(const Service::HID::PadState& state); -/** - * Threadsafe setter for the current PadState - * @param state New PadState to overwrite current PadState. - */ -void SetPadState(const Service::HID::PadState& state); + /** + * Getter for current CirclePad + * @return std::tuple CirclePad state + */ + static std::tuple GetCirclePad(); -/** - * Getter for current CirclePad - * @return std::tuple CirclePad state - */ -std::tuple GetCirclePad(); + /** + * Setter for current CirclePad + * @param circle New CirclePad state + */ + static void SetCirclePad(std::tuple circle); -/** - * Setter for current CirclePad - * @param circle New CirclePad state - */ -void SetCirclePad(std::tuple circle); + /** + * Getter for Citra's main keyboard input handler + * @return std::shared_ptr Device Keyboard instance + */ + static std::shared_ptr GetKeyboard(); -/** - * Getter for Citra's main keyboard input handler - * @return std::shared_ptr Device Keyboard instance - */ -std::shared_ptr GetKeyboard(); + /** + * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). + * Threadsafe. + * @note This should be called by the core emu thread to get a state set by the window thread. + * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and + * `pressed` is true if the touch screen is currently being pressed + */ + static std::tuple GetTouchState(); -/** - * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). - * Threadsafe. - * @note This should be called by the core emu thread to get a state set by the window thread. - * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and - * `pressed` is true if the touch screen is currently being pressed - */ -std::tuple GetTouchState(); + /** + * Threadsafe setter for the current touch screen state. + * @param value New Touch State + */ + static void SetTouchState(std::tuple value); -/** - * Threadsafe setter for the current touch screen state. - * @param value New Touch State - */ -void SetTouchState(std::tuple value); + /** + * Reload input key mapping settings during game-play + */ + static void ReloadSettings(); -/** - * Reload input key mapping settings during game-play - */ -void ReloadSettings(); + static std::vector> GetAllDevices(); -/** - * Loops through all devices and detects the first device that produces an input - * @param max_time: maximum amount of time to wait until input detected, in milliseconds. - * @param update_GUI: function to run in while loop to process any gui events. - * @return Settings::InputDeviceMapping of input device - */ -Settings::InputDeviceMapping DetectInput(int max_time, std::function update_GUI); -} + /** + * Loops through all devices and detects the first device that produces an input + * @param max_time: maximum amount of time to wait until input detected, in milliseconds. + * @param update_GUI: function to run in while loop to process any gui events. + * @return Settings::InputDeviceMapping of input device + */ + static Settings::InputDeviceMapping DetectInput(int max_time, + std::function update_GUI); + +private: + static int tick_event; + static Service::HID::PadState pad_state; + static std::tuple circle_pad; + static std::shared_ptr main_keyboard; ///< Keyboard is always active for Citra + static std::vector> + devices; ///< Devices that are handling input for the game + static std::map> key_mappings; + static std::map + keys_pressed; ///< keys that were pressed on previous frame. + static std::mutex pad_state_mutex; + static std::mutex touch_mutex; + static u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) + static u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) + static bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false + + static void UpdateEmulatorInputs( + std::vector> inputs); + static void InputTickCallback(u64, int cycles_late); + + static bool CheckIfMappingExists(const std::vector& uniqueMapping, + Settings::InputDeviceMapping mappingToCheck); + + static std::vector GatherUniqueMappings(); + + static void BuildKeyMapping(); + + static void GenerateUniqueDevices(); + + static void ParseSettings(); + static std::tuple ApplyDeadzone(float x, float y, float dead_zone); +}; diff --git a/src/input_core/key_map.cpp b/src/input_core/key_map.cpp index b95ade60a..796a60a00 100644 --- a/src/input_core/key_map.cpp +++ b/src/input_core/key_map.cpp @@ -31,42 +31,4 @@ const std::array mapp const std::array analog_inputs = { Service::HID::PAD_CIRCLE_UP, Service::HID::PAD_CIRCLE_DOWN, Service::HID::PAD_CIRCLE_LEFT, Service::HID::PAD_CIRCLE_RIGHT}; - -void PressKey(const Service::HID::PadState target, const float strength) { - auto pad_state = InputCore::GetPadState(); - // If is digital keytarget - if (std::find(std::begin(analog_inputs), std::end(analog_inputs), target) == - std::end(analog_inputs)) { - pad_state.hex |= target.hex; - InputCore::SetPadState(pad_state); - } else { // it is analog input - auto circle_pad = InputCore::GetCirclePad(); - if (target == Service::HID::PAD_CIRCLE_UP || target == Service::HID::PAD_CIRCLE_DOWN) { - std::get<1>(circle_pad) = MAX_CIRCLEPAD_POS * strength * -1; - } else if (target == Service::HID::PAD_CIRCLE_LEFT || - target == Service::HID::PAD_CIRCLE_RIGHT) { - std::get<0>(circle_pad) = MAX_CIRCLEPAD_POS * strength; - } - InputCore::SetCirclePad(circle_pad); - } -} - -void ReleaseKey(const Service::HID::PadState target) { - auto pad_state = InputCore::GetPadState(); - // If is digital keytarget - if (std::find(std::begin(analog_inputs), std::end(analog_inputs), target) == - std::end(analog_inputs)) { - pad_state.hex &= ~target.hex; - InputCore::SetPadState(pad_state); - } else { // it is analog input - auto circle_pad = InputCore::GetCirclePad(); - if (target == Service::HID::PAD_CIRCLE_UP || target == Service::HID::PAD_CIRCLE_DOWN) { - std::get<1>(circle_pad) = 0; - } else if (target == Service::HID::PAD_CIRCLE_LEFT || - target == Service::HID::PAD_CIRCLE_RIGHT) { - std::get<0>(circle_pad) = 0; - } - InputCore::SetCirclePad(circle_pad); - } -} } diff --git a/src/input_core/key_map.h b/src/input_core/key_map.h index be70f995a..0ab733918 100644 --- a/src/input_core/key_map.h +++ b/src/input_core/key_map.h @@ -15,10 +15,4 @@ namespace KeyMap { extern const std::array mapping_targets; extern const std::array analog_inputs; constexpr int MAX_CIRCLEPAD_POS = 0x9C; /// Max value for a circle pad position - -/// Handles the pressing of a key and modifies InputCore state -void PressKey(Service::HID::PadState target, float strength); - -/// Handles the releasing of a key and modifies InputCore state -void ReleaseKey(Service::HID::PadState target); }