diff options
author | Narr the Reg <juangerman-13@hotmail.com> | 2024-01-05 03:37:43 +0100 |
---|---|---|
committer | Narr the Reg <juangerman-13@hotmail.com> | 2024-01-05 18:41:15 +0100 |
commit | ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch) | |
tree | 3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/core/hid | |
parent | Merge pull request #12437 from ameerj/gl-amd-fixes (diff) | |
download | yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.gz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.bz2 yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.lz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.xz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.zst yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.zip |
Diffstat (limited to 'src/core/hid')
-rw-r--r-- | src/core/hid/emulated_console.cpp | 324 | ||||
-rw-r--r-- | src/core/hid/emulated_console.h | 192 | ||||
-rw-r--r-- | src/core/hid/emulated_controller.cpp | 1972 | ||||
-rw-r--r-- | src/core/hid/emulated_controller.h | 619 | ||||
-rw-r--r-- | src/core/hid/emulated_devices.cpp | 483 | ||||
-rw-r--r-- | src/core/hid/emulated_devices.h | 212 | ||||
-rw-r--r-- | src/core/hid/hid_core.cpp | 222 | ||||
-rw-r--r-- | src/core/hid/hid_core.h | 89 | ||||
-rw-r--r-- | src/core/hid/hid_types.h | 736 | ||||
-rw-r--r-- | src/core/hid/input_converter.cpp | 436 | ||||
-rw-r--r-- | src/core/hid/input_converter.h | 119 | ||||
-rw-r--r-- | src/core/hid/input_interpreter.cpp | 64 | ||||
-rw-r--r-- | src/core/hid/input_interpreter.h | 111 | ||||
-rw-r--r-- | src/core/hid/irs_types.h | 301 | ||||
-rw-r--r-- | src/core/hid/motion_input.cpp | 357 | ||||
-rw-r--r-- | src/core/hid/motion_input.h | 119 |
16 files changed, 0 insertions, 6356 deletions
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp deleted file mode 100644 index b4afd930e..000000000 --- a/src/core/hid/emulated_console.cpp +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/settings.h" -#include "core/hid/emulated_console.h" -#include "core/hid/input_converter.h" - -namespace Core::HID { -EmulatedConsole::EmulatedConsole() = default; - -EmulatedConsole::~EmulatedConsole() = default; - -void EmulatedConsole::ReloadFromSettings() { - // Using first motion device from player 1. No need to assign any unique config at the moment - const auto& player = Settings::values.players.GetValue()[0]; - motion_params[0] = Common::ParamPackage(player.motions[0]); - - ReloadInput(); -} - -void EmulatedConsole::SetTouchParams() { - std::size_t index = 0; - - // We can't use mouse as touch if native mouse is enabled - if (!Settings::values.mouse_enabled) { - touch_params[index++] = - Common::ParamPackage{"engine:mouse,axis_x:0,axis_y:1,button:0,port:2"}; - } - - touch_params[index++] = - Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"}; - touch_params[index++] = - Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"}; - - for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) { - Common::ParamPackage touchscreen_param{}; - touchscreen_param.Set("engine", "touch"); - touchscreen_param.Set("axis_x", i * 2); - touchscreen_param.Set("axis_y", (i * 2) + 1); - touchscreen_param.Set("button", i); - touch_params[index++] = std::move(touchscreen_param); - } - - if (Settings::values.touch_from_button_maps.empty()) { - LOG_WARNING(Input, "touch_from_button_maps is unset by frontend config"); - return; - } - - const auto button_index = - static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); - const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons; - - // Map the rest of the fingers from touch from button configuration - for (const auto& config_entry : touch_buttons) { - if (index >= MaxTouchDevices) { - continue; - } - Common::ParamPackage params{config_entry}; - Common::ParamPackage touch_button_params; - const int x = params.Get("x", 0); - const int y = params.Get("y", 0); - params.Erase("x"); - params.Erase("y"); - touch_button_params.Set("engine", "touch_from_button"); - touch_button_params.Set("button", params.Serialize()); - touch_button_params.Set("x", x); - touch_button_params.Set("y", y); - touch_params[index] = std::move(touch_button_params); - index++; - } -} - -void EmulatedConsole::ReloadInput() { - // If you load any device here add the equivalent to the UnloadInput() function - SetTouchParams(); - - motion_params[1] = Common::ParamPackage{"engine:virtual_gamepad,port:8,motion:0"}; - - for (std::size_t index = 0; index < motion_devices.size(); ++index) { - motion_devices[index] = Common::Input::CreateInputDevice(motion_params[index]); - if (!motion_devices[index]) { - continue; - } - motion_devices[index]->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); }, - }); - } - - // Restore motion state - auto& emulated_motion = console.motion_values.emulated; - auto& motion = console.motion_state; - emulated_motion.ResetRotations(); - emulated_motion.ResetQuaternion(); - motion.accel = emulated_motion.GetAcceleration(); - motion.gyro = emulated_motion.GetGyroscope(); - motion.rotation = emulated_motion.GetRotations(); - motion.orientation = emulated_motion.GetOrientation(); - motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity); - - // Unique index for identifying touch device source - std::size_t index = 0; - for (auto& touch_device : touch_devices) { - touch_device = Common::Input::CreateInputDevice(touch_params[index]); - if (!touch_device) { - continue; - } - touch_device->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetTouch(callback, index); - }, - }); - index++; - } -} - -void EmulatedConsole::UnloadInput() { - for (auto& motion : motion_devices) { - motion.reset(); - } - for (auto& touch : touch_devices) { - touch.reset(); - } -} - -void EmulatedConsole::EnableConfiguration() { - is_configuring = true; - SaveCurrentConfig(); -} - -void EmulatedConsole::DisableConfiguration() { - is_configuring = false; -} - -bool EmulatedConsole::IsConfiguring() const { - return is_configuring; -} - -void EmulatedConsole::SaveCurrentConfig() { - if (!is_configuring) { - return; - } -} - -void EmulatedConsole::RestoreConfig() { - if (!is_configuring) { - return; - } - ReloadFromSettings(); -} - -Common::ParamPackage EmulatedConsole::GetMotionParam() const { - return motion_params[0]; -} - -void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { - motion_params[0] = std::move(param); - ReloadInput(); -} - -void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { - std::unique_lock lock{mutex}; - auto& raw_status = console.motion_values.raw_status; - auto& emulated = console.motion_values.emulated; - - raw_status = TransformToMotion(callback); - emulated.SetAcceleration(Common::Vec3f{ - raw_status.accel.x.value, - raw_status.accel.y.value, - raw_status.accel.z.value, - }); - emulated.SetGyroscope(Common::Vec3f{ - raw_status.gyro.x.value, - raw_status.gyro.y.value, - raw_status.gyro.z.value, - }); - emulated.UpdateRotation(raw_status.delta_timestamp); - emulated.UpdateOrientation(raw_status.delta_timestamp); - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(ConsoleTriggerType::Motion); - return; - } - - auto& motion = console.motion_state; - motion.accel = emulated.GetAcceleration(); - motion.gyro = emulated.GetGyroscope(); - motion.rotation = emulated.GetRotations(); - motion.orientation = emulated.GetOrientation(); - motion.quaternion = emulated.GetQuaternion(); - motion.gyro_bias = emulated.GetGyroBias(); - motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); - // Find what is this value - motion.verticalization_error = 0.0f; - - lock.unlock(); - TriggerOnChange(ConsoleTriggerType::Motion); -} - -void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) { - if (index >= MaxTouchDevices) { - return; - } - std::unique_lock lock{mutex}; - - const auto touch_input = TransformToTouch(callback); - auto touch_index = GetIndexFromFingerId(index); - bool is_new_input = false; - - if (!touch_index.has_value() && touch_input.pressed.value) { - touch_index = GetNextFreeIndex(); - is_new_input = true; - } - - // No free entries or invalid state. Ignore input - if (!touch_index.has_value()) { - return; - } - - auto& touch_value = console.touch_values[touch_index.value()]; - - if (is_new_input) { - touch_value.pressed.value = true; - touch_value.id = static_cast<int>(index); - } - - touch_value.x = touch_input.x; - touch_value.y = touch_input.y; - - if (!touch_input.pressed.value) { - touch_value.pressed.value = false; - } - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(ConsoleTriggerType::Touch); - return; - } - - // Touch outside allowed range. Ignore input - if (touch_index.value() >= MaxActiveTouchInputs) { - return; - } - - console.touch_state[touch_index.value()] = { - .position = {touch_value.x.value, touch_value.y.value}, - .id = static_cast<u32>(touch_index.value()), - .pressed = touch_input.pressed.value, - }; - - lock.unlock(); - TriggerOnChange(ConsoleTriggerType::Touch); -} - -ConsoleMotionValues EmulatedConsole::GetMotionValues() const { - std::scoped_lock lock{mutex}; - return console.motion_values; -} - -TouchValues EmulatedConsole::GetTouchValues() const { - std::scoped_lock lock{mutex}; - return console.touch_values; -} - -ConsoleMotion EmulatedConsole::GetMotion() const { - std::scoped_lock lock{mutex}; - return console.motion_state; -} - -TouchFingerState EmulatedConsole::GetTouch() const { - std::scoped_lock lock{mutex}; - return console.touch_state; -} - -std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const { - for (std::size_t index = 0; index < MaxTouchDevices; ++index) { - const auto& finger = console.touch_values[index]; - if (!finger.pressed.value) { - continue; - } - if (finger.id == static_cast<int>(finger_id)) { - return index; - } - } - return std::nullopt; -} - -std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const { - for (std::size_t index = 0; index < MaxTouchDevices; ++index) { - if (!console.touch_values[index].pressed.value) { - return index; - } - } - return std::nullopt; -} - -void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { - std::scoped_lock lock{callback_mutex}; - for (const auto& poller_pair : callback_list) { - const ConsoleUpdateCallback& poller = poller_pair.second; - if (poller.on_change) { - poller.on_change(type); - } - } -} - -int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { - std::scoped_lock lock{callback_mutex}; - callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); - return last_callback_key++; -} - -void EmulatedConsole::DeleteCallback(int key) { - std::scoped_lock lock{callback_mutex}; - const auto& iterator = callback_list.find(key); - if (iterator == callback_list.end()) { - LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); - return; - } - callback_list.erase(iterator); -} -} // namespace Core::HID diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h deleted file mode 100644 index fae15a556..000000000 --- a/src/core/hid/emulated_console.h +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <functional> -#include <memory> -#include <mutex> -#include <optional> -#include <unordered_map> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/input.h" -#include "common/param_package.h" -#include "common/point.h" -#include "common/quaternion.h" -#include "common/vector_math.h" -#include "core/hid/hid_types.h" -#include "core/hid/motion_input.h" - -namespace Core::HID { -static constexpr std::size_t MaxTouchDevices = 32; -static constexpr std::size_t MaxActiveTouchInputs = 16; - -struct ConsoleMotionInfo { - Common::Input::MotionStatus raw_status{}; - MotionInput emulated{}; -}; - -using ConsoleMotionDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 2>; -using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>; - -using ConsoleMotionParams = std::array<Common::ParamPackage, 2>; -using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>; - -using ConsoleMotionValues = ConsoleMotionInfo; -using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; - -// Contains all motion related data that is used on the services -struct ConsoleMotion { - Common::Vec3f accel{}; - Common::Vec3f gyro{}; - Common::Vec3f rotation{}; - std::array<Common::Vec3f, 3> orientation{}; - Common::Quaternion<f32> quaternion{}; - Common::Vec3f gyro_bias{}; - f32 verticalization_error{}; - bool is_at_rest{}; -}; - -using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>; - -struct ConsoleStatus { - // Data from input_common - ConsoleMotionValues motion_values{}; - TouchValues touch_values{}; - - // Data for HID services - ConsoleMotion motion_state{}; - TouchFingerState touch_state{}; -}; - -enum class ConsoleTriggerType { - Motion, - Touch, - All, -}; - -struct ConsoleUpdateCallback { - std::function<void(ConsoleTriggerType)> on_change; -}; - -class EmulatedConsole { -public: - /** - * Contains all input data within the emulated switch console tablet such as touch and motion - */ - explicit EmulatedConsole(); - ~EmulatedConsole(); - - YUZU_NON_COPYABLE(EmulatedConsole); - YUZU_NON_MOVEABLE(EmulatedConsole); - - /// Removes all callbacks created from input devices - void UnloadInput(); - - /** - * Sets the emulated console into configuring mode - * This prevents the modification of the HID state of the emulated console by input commands - */ - void EnableConfiguration(); - - /// Returns the emulated console into normal mode, allowing the modification of the HID state - void DisableConfiguration(); - - /// Returns true if the emulated console is in configuring mode - bool IsConfiguring() const; - - /// Reload all input devices - void ReloadInput(); - - /// Overrides current mapped devices with the stored configuration and reloads all input devices - void ReloadFromSettings(); - - /// Saves the current mapped configuration - void SaveCurrentConfig(); - - /// Reverts any mapped changes made that weren't saved - void RestoreConfig(); - - // Returns the current mapped motion device - Common::ParamPackage GetMotionParam() const; - - /** - * Updates the current mapped motion device - * @param param ParamPackage with controller data to be mapped - */ - void SetMotionParam(Common::ParamPackage param); - - /// Returns the latest status of motion input from the console with parameters - ConsoleMotionValues GetMotionValues() const; - - /// Returns the latest status of touch input from the console with parameters - TouchValues GetTouchValues() const; - - /// Returns the latest status of motion input from the console - ConsoleMotion GetMotion() const; - - /// Returns the latest status of touch input from the console - TouchFingerState GetTouch() const; - - /** - * Adds a callback to the list of events - * @param update_callback A ConsoleUpdateCallback that will be triggered - * @return an unique key corresponding to the callback index in the list - */ - int SetCallback(ConsoleUpdateCallback update_callback); - - /** - * Removes a callback from the list stopping any future events to this object - * @param key Key corresponding to the callback index in the list - */ - void DeleteCallback(int key); - -private: - /// Creates and stores the touch params - void SetTouchParams(); - - /** - * Updates the motion status of the console - * @param callback A CallbackStatus containing gyro and accelerometer data - */ - void SetMotion(const Common::Input::CallbackStatus& callback); - - /** - * Updates the touch status of the console - * @param callback A CallbackStatus containing the touch position - * @param index Finger ID to be updated - */ - void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index); - - std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const; - - std::optional<std::size_t> GetNextFreeIndex() const; - - /** - * Triggers a callback that something has changed on the console status - * @param type Input type of the event to trigger - */ - void TriggerOnChange(ConsoleTriggerType type); - - bool is_configuring{false}; - f32 motion_sensitivity{0.01f}; - - ConsoleMotionParams motion_params; - TouchParams touch_params; - - ConsoleMotionDevices motion_devices; - TouchDevices touch_devices; - - mutable std::mutex mutex; - mutable std::mutex callback_mutex; - std::unordered_map<int, ConsoleUpdateCallback> callback_list; - int last_callback_key = 0; - - // Stores the current status of all console input - ConsoleStatus console; -}; - -} // namespace Core::HID diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp deleted file mode 100644 index a6e681e15..000000000 --- a/src/core/hid/emulated_controller.cpp +++ /dev/null @@ -1,1972 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <common/scope_exit.h> - -#include "common/polyfill_ranges.h" -#include "common/thread.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/input_converter.h" -#include "core/hle/service/hid/hid_util.h" - -namespace Core::HID { -constexpr s32 HID_JOYSTICK_MAX = 0x7fff; -constexpr s32 HID_TRIGGER_MAX = 0x7fff; -constexpr u32 TURBO_BUTTON_DELAY = 4; -// Use a common UUID for TAS and Virtual Gamepad -constexpr Common::UUID TAS_UUID = - Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; -constexpr Common::UUID VIRTUAL_UUID = - Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; - -EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {} - -EmulatedController::~EmulatedController() = default; - -NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) { - switch (type) { - case Settings::ControllerType::ProController: - return NpadStyleIndex::ProController; - case Settings::ControllerType::DualJoyconDetached: - return NpadStyleIndex::JoyconDual; - case Settings::ControllerType::LeftJoycon: - return NpadStyleIndex::JoyconLeft; - case Settings::ControllerType::RightJoycon: - return NpadStyleIndex::JoyconRight; - case Settings::ControllerType::Handheld: - return NpadStyleIndex::Handheld; - case Settings::ControllerType::GameCube: - return NpadStyleIndex::GameCube; - case Settings::ControllerType::Pokeball: - return NpadStyleIndex::Pokeball; - case Settings::ControllerType::NES: - return NpadStyleIndex::NES; - case Settings::ControllerType::SNES: - return NpadStyleIndex::SNES; - case Settings::ControllerType::N64: - return NpadStyleIndex::N64; - case Settings::ControllerType::SegaGenesis: - return NpadStyleIndex::SegaGenesis; - default: - return NpadStyleIndex::ProController; - } -} - -Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) { - switch (type) { - case NpadStyleIndex::ProController: - return Settings::ControllerType::ProController; - case NpadStyleIndex::JoyconDual: - return Settings::ControllerType::DualJoyconDetached; - case NpadStyleIndex::JoyconLeft: - return Settings::ControllerType::LeftJoycon; - case NpadStyleIndex::JoyconRight: - return Settings::ControllerType::RightJoycon; - case NpadStyleIndex::Handheld: - return Settings::ControllerType::Handheld; - case NpadStyleIndex::GameCube: - return Settings::ControllerType::GameCube; - case NpadStyleIndex::Pokeball: - return Settings::ControllerType::Pokeball; - case NpadStyleIndex::NES: - return Settings::ControllerType::NES; - case NpadStyleIndex::SNES: - return Settings::ControllerType::SNES; - case NpadStyleIndex::N64: - return Settings::ControllerType::N64; - case NpadStyleIndex::SegaGenesis: - return Settings::ControllerType::SegaGenesis; - default: - return Settings::ControllerType::ProController; - } -} - -void EmulatedController::ReloadFromSettings() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - - for (std::size_t index = 0; index < player.buttons.size(); ++index) { - button_params[index] = Common::ParamPackage(player.buttons[index]); - } - for (std::size_t index = 0; index < player.analogs.size(); ++index) { - stick_params[index] = Common::ParamPackage(player.analogs[index]); - } - for (std::size_t index = 0; index < player.motions.size(); ++index) { - motion_params[index] = Common::ParamPackage(player.motions[index]); - } - - controller.color_values = {}; - ReloadColorsFromSettings(); - - ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); - - // Other or debug controller should always be a pro controller - if (npad_id_type != NpadIdType::Other) { - SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); - original_npad_type = npad_type; - } else { - SetNpadStyleIndex(NpadStyleIndex::ProController); - original_npad_type = npad_type; - } - - Disconnect(); - if (player.connected) { - Connect(); - } - - ReloadInput(); -} - -void EmulatedController::ReloadColorsFromSettings() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - - // Avoid updating colors if overridden by physical controller - if (controller.color_values[LeftIndex].body != 0 && - controller.color_values[RightIndex].body != 0) { - return; - } - - controller.colors_state.fullkey = { - .body = GetNpadColor(player.body_color_left), - .button = GetNpadColor(player.button_color_left), - }; - controller.colors_state.left = { - .body = GetNpadColor(player.body_color_left), - .button = GetNpadColor(player.button_color_left), - }; - controller.colors_state.right = { - .body = GetNpadColor(player.body_color_right), - .button = GetNpadColor(player.button_color_right), - }; -} - -void EmulatedController::LoadDevices() { - // TODO(german77): Use more buttons to detect the correct device - const auto left_joycon = button_params[Settings::NativeButton::DRight]; - const auto right_joycon = button_params[Settings::NativeButton::A]; - - // Triggers for GC controllers - trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; - trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; - - color_params[LeftIndex] = left_joycon; - color_params[RightIndex] = right_joycon; - color_params[LeftIndex].Set("color", true); - color_params[RightIndex].Set("color", true); - - battery_params[LeftIndex] = left_joycon; - battery_params[RightIndex] = right_joycon; - battery_params[LeftIndex].Set("battery", true); - battery_params[RightIndex].Set("battery", true); - - camera_params[0] = right_joycon; - camera_params[0].Set("camera", true); - nfc_params[1] = right_joycon; - nfc_params[1].Set("nfc", true); - - // Only map virtual devices to the first controller - if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) { - camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; - ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; - nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; - } - - output_params[LeftIndex] = left_joycon; - output_params[RightIndex] = right_joycon; - output_params[2] = camera_params[1]; - output_params[3] = nfc_params[0]; - output_params[LeftIndex].Set("output", true); - output_params[RightIndex].Set("output", true); - output_params[2].Set("output", true); - output_params[3].Set("output", true); - - LoadTASParams(); - LoadVirtualGamepadParams(); - - std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(motion_params, motion_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(trigger_params, trigger_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(battery_params, battery_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(ring_params, ring_analog_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice); - std::ranges::transform(output_params, output_devices.begin(), - Common::Input::CreateOutputDevice); - - // Initialize TAS devices - std::ranges::transform(tas_button_params, tas_button_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(tas_stick_params, tas_stick_devices.begin(), - Common::Input::CreateInputDevice); - - // Initialize virtual gamepad devices - std::ranges::transform(virtual_button_params, virtual_button_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(), - Common::Input::CreateInputDevice); - std::ranges::transform(virtual_motion_params, virtual_motion_devices.begin(), - Common::Input::CreateInputDevice); -} - -void EmulatedController::LoadTASParams() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - Common::ParamPackage common_params{}; - common_params.Set("engine", "tas"); - common_params.Set("port", static_cast<int>(player_index)); - for (auto& param : tas_button_params) { - param = common_params; - } - for (auto& param : tas_stick_params) { - param = common_params; - } - - // TODO(german77): Replace this with an input profile or something better - tas_button_params[Settings::NativeButton::A].Set("button", 0); - tas_button_params[Settings::NativeButton::B].Set("button", 1); - tas_button_params[Settings::NativeButton::X].Set("button", 2); - tas_button_params[Settings::NativeButton::Y].Set("button", 3); - tas_button_params[Settings::NativeButton::LStick].Set("button", 4); - tas_button_params[Settings::NativeButton::RStick].Set("button", 5); - tas_button_params[Settings::NativeButton::L].Set("button", 6); - tas_button_params[Settings::NativeButton::R].Set("button", 7); - tas_button_params[Settings::NativeButton::ZL].Set("button", 8); - tas_button_params[Settings::NativeButton::ZR].Set("button", 9); - tas_button_params[Settings::NativeButton::Plus].Set("button", 10); - tas_button_params[Settings::NativeButton::Minus].Set("button", 11); - tas_button_params[Settings::NativeButton::DLeft].Set("button", 12); - tas_button_params[Settings::NativeButton::DUp].Set("button", 13); - tas_button_params[Settings::NativeButton::DRight].Set("button", 14); - tas_button_params[Settings::NativeButton::DDown].Set("button", 15); - tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16); - tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17); - tas_button_params[Settings::NativeButton::Home].Set("button", 18); - tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); - tas_button_params[Settings::NativeButton::SLRight].Set("button", 20); - tas_button_params[Settings::NativeButton::SRRight].Set("button", 21); - - tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); - tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); - tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); - tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); - - // set to optimal stick to avoid sanitizing the stick and tweaking the coordinates - // making sure they play back in the game as originally written down in the script file - tas_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f); - tas_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f); - tas_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f); - tas_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f); -} - -void EmulatedController::LoadVirtualGamepadParams() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - Common::ParamPackage common_params{}; - common_params.Set("engine", "virtual_gamepad"); - common_params.Set("port", static_cast<int>(player_index)); - for (auto& param : virtual_button_params) { - param = common_params; - } - for (auto& param : virtual_stick_params) { - param = common_params; - } - for (auto& param : virtual_stick_params) { - param = common_params; - } - for (auto& param : virtual_motion_params) { - param = common_params; - } - - // TODO(german77): Replace this with an input profile or something better - virtual_button_params[Settings::NativeButton::A].Set("button", 0); - virtual_button_params[Settings::NativeButton::B].Set("button", 1); - virtual_button_params[Settings::NativeButton::X].Set("button", 2); - virtual_button_params[Settings::NativeButton::Y].Set("button", 3); - virtual_button_params[Settings::NativeButton::LStick].Set("button", 4); - virtual_button_params[Settings::NativeButton::RStick].Set("button", 5); - virtual_button_params[Settings::NativeButton::L].Set("button", 6); - virtual_button_params[Settings::NativeButton::R].Set("button", 7); - virtual_button_params[Settings::NativeButton::ZL].Set("button", 8); - virtual_button_params[Settings::NativeButton::ZR].Set("button", 9); - virtual_button_params[Settings::NativeButton::Plus].Set("button", 10); - virtual_button_params[Settings::NativeButton::Minus].Set("button", 11); - virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12); - virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); - virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); - virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); - virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16); - virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17); - virtual_button_params[Settings::NativeButton::Home].Set("button", 18); - virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); - virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20); - virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21); - - virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); - virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); - virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); - virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); - virtual_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f); - virtual_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f); - virtual_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f); - virtual_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f); - - virtual_motion_params[Settings::NativeMotion::MotionLeft].Set("motion", 0); - virtual_motion_params[Settings::NativeMotion::MotionRight].Set("motion", 0); -} - -void EmulatedController::ReloadInput() { - // If you load any device here add the equivalent to the UnloadInput() function - LoadDevices(); - for (std::size_t index = 0; index < button_devices.size(); ++index) { - if (!button_devices[index]) { - continue; - } - const auto uuid = Common::UUID{button_params[index].Get("guid", "")}; - button_devices[index]->SetCallback({ - .on_change = - [this, index, uuid](const Common::Input::CallbackStatus& callback) { - SetButton(callback, index, uuid); - }, - }); - button_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < stick_devices.size(); ++index) { - if (!stick_devices[index]) { - continue; - } - const auto uuid = Common::UUID{stick_params[index].Get("guid", "")}; - stick_devices[index]->SetCallback({ - .on_change = - [this, index, uuid](const Common::Input::CallbackStatus& callback) { - SetStick(callback, index, uuid); - }, - }); - stick_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < trigger_devices.size(); ++index) { - if (!trigger_devices[index]) { - continue; - } - const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")}; - trigger_devices[index]->SetCallback({ - .on_change = - [this, index, uuid](const Common::Input::CallbackStatus& callback) { - SetTrigger(callback, index, uuid); - }, - }); - trigger_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < battery_devices.size(); ++index) { - if (!battery_devices[index]) { - continue; - } - battery_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetBattery(callback, index); - }, - }); - battery_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < color_devices.size(); ++index) { - if (!color_devices[index]) { - continue; - } - color_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetColors(callback, index); - }, - }); - color_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < motion_devices.size(); ++index) { - if (!motion_devices[index]) { - continue; - } - motion_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetMotion(callback, index); - }, - }); - - // Restore motion state - auto& emulated_motion = controller.motion_values[index].emulated; - auto& motion = controller.motion_state[index]; - emulated_motion.ResetRotations(); - emulated_motion.ResetQuaternion(); - motion.accel = emulated_motion.GetAcceleration(); - motion.gyro = emulated_motion.GetGyroscope(); - motion.rotation = emulated_motion.GetRotations(); - motion.euler = emulated_motion.GetEulerAngles(); - motion.orientation = emulated_motion.GetOrientation(); - motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity); - } - - for (std::size_t index = 0; index < camera_devices.size(); ++index) { - if (!camera_devices[index]) { - continue; - } - camera_devices[index]->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, - }); - camera_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) { - if (!ring_analog_devices[index]) { - continue; - } - ring_analog_devices[index]->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); }, - }); - ring_analog_devices[index]->ForceUpdate(); - } - - for (std::size_t index = 0; index < nfc_devices.size(); ++index) { - if (!nfc_devices[index]) { - continue; - } - nfc_devices[index]->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, - }); - nfc_devices[index]->ForceUpdate(); - } - - // Register TAS devices. No need to force update - for (std::size_t index = 0; index < tas_button_devices.size(); ++index) { - if (!tas_button_devices[index]) { - continue; - } - tas_button_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetButton(callback, index, TAS_UUID); - }, - }); - } - - for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) { - if (!tas_stick_devices[index]) { - continue; - } - tas_stick_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetStick(callback, index, TAS_UUID); - }, - }); - } - - // Register virtual devices. No need to force update - for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) { - if (!virtual_button_devices[index]) { - continue; - } - virtual_button_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetButton(callback, index, VIRTUAL_UUID); - }, - }); - } - - for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) { - if (!virtual_stick_devices[index]) { - continue; - } - virtual_stick_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetStick(callback, index, VIRTUAL_UUID); - }, - }); - } - - for (std::size_t index = 0; index < virtual_motion_devices.size(); ++index) { - if (!virtual_motion_devices[index]) { - continue; - } - virtual_motion_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetMotion(callback, index); - }, - }); - } - turbo_button_state = 0; - is_initalized = true; -} - -void EmulatedController::UnloadInput() { - is_initalized = false; - for (auto& button : button_devices) { - button.reset(); - } - for (auto& stick : stick_devices) { - stick.reset(); - } - for (auto& motion : motion_devices) { - motion.reset(); - } - for (auto& trigger : trigger_devices) { - trigger.reset(); - } - for (auto& battery : battery_devices) { - battery.reset(); - } - for (auto& color : color_devices) { - color.reset(); - } - for (auto& output : output_devices) { - output.reset(); - } - for (auto& button : tas_button_devices) { - button.reset(); - } - for (auto& stick : tas_stick_devices) { - stick.reset(); - } - for (auto& button : virtual_button_devices) { - button.reset(); - } - for (auto& stick : virtual_stick_devices) { - stick.reset(); - } - for (auto& motion : virtual_motion_devices) { - motion.reset(); - } - for (auto& camera : camera_devices) { - camera.reset(); - } - for (auto& ring : ring_analog_devices) { - ring.reset(); - } - for (auto& nfc : nfc_devices) { - nfc.reset(); - } -} - -void EmulatedController::EnableConfiguration() { - std::scoped_lock lock{connect_mutex, npad_mutex}; - is_configuring = true; - tmp_is_connected = is_connected; - tmp_npad_type = npad_type; -} - -void EmulatedController::DisableConfiguration() { - is_configuring = false; - - // Get Joycon colors before turning on the controller - for (const auto& color_device : color_devices) { - color_device->ForceUpdate(); - } - - // Apply temporary npad type to the real controller - if (tmp_npad_type != npad_type) { - if (is_connected) { - Disconnect(); - } - SetNpadStyleIndex(tmp_npad_type); - original_npad_type = tmp_npad_type; - } - - // Apply temporary connected status to the real controller - if (tmp_is_connected != is_connected) { - if (tmp_is_connected) { - Connect(); - return; - } - Disconnect(); - } -} - -void EmulatedController::EnableSystemButtons() { - std::scoped_lock lock{mutex}; - system_buttons_enabled = true; -} - -void EmulatedController::DisableSystemButtons() { - std::scoped_lock lock{mutex}; - system_buttons_enabled = false; - controller.home_button_state.raw = 0; - controller.capture_button_state.raw = 0; -} - -void EmulatedController::ResetSystemButtons() { - std::scoped_lock lock{mutex}; - controller.home_button_state.home.Assign(false); - controller.capture_button_state.capture.Assign(false); -} - -bool EmulatedController::IsConfiguring() const { - return is_configuring; -} - -void EmulatedController::SaveCurrentConfig() { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - auto& player = Settings::values.players.GetValue()[player_index]; - player.connected = is_connected; - player.controller_type = MapNPadToSettingsType(npad_type); - for (std::size_t index = 0; index < player.buttons.size(); ++index) { - player.buttons[index] = button_params[index].Serialize(); - } - for (std::size_t index = 0; index < player.analogs.size(); ++index) { - player.analogs[index] = stick_params[index].Serialize(); - } - for (std::size_t index = 0; index < player.motions.size(); ++index) { - player.motions[index] = motion_params[index].Serialize(); - } - if (npad_id_type == NpadIdType::Player1) { - Settings::values.ringcon_analogs = ring_params[0].Serialize(); - } -} - -void EmulatedController::RestoreConfig() { - if (!is_configuring) { - return; - } - ReloadFromSettings(); -} - -std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices() const { - std::vector<Common::ParamPackage> devices; - for (const auto& param : button_params) { - if (!param.Has("engine")) { - continue; - } - const auto devices_it = std::find_if( - devices.begin(), devices.end(), [¶m](const Common::ParamPackage& param_) { - return param.Get("engine", "") == param_.Get("engine", "") && - param.Get("guid", "") == param_.Get("guid", "") && - param.Get("port", 0) == param_.Get("port", 0) && - param.Get("pad", 0) == param_.Get("pad", 0); - }); - if (devices_it != devices.end()) { - continue; - } - - auto& device = devices.emplace_back(); - device.Set("engine", param.Get("engine", "")); - device.Set("guid", param.Get("guid", "")); - device.Set("port", param.Get("port", 0)); - device.Set("pad", param.Get("pad", 0)); - } - - for (const auto& param : stick_params) { - if (!param.Has("engine")) { - continue; - } - if (param.Get("engine", "") == "analog_from_button") { - continue; - } - const auto devices_it = std::find_if( - devices.begin(), devices.end(), [¶m](const Common::ParamPackage& param_) { - return param.Get("engine", "") == param_.Get("engine", "") && - param.Get("guid", "") == param_.Get("guid", "") && - param.Get("port", 0) == param_.Get("port", 0) && - param.Get("pad", 0) == param_.Get("pad", 0); - }); - if (devices_it != devices.end()) { - continue; - } - - auto& device = devices.emplace_back(); - device.Set("engine", param.Get("engine", "")); - device.Set("guid", param.Get("guid", "")); - device.Set("port", param.Get("port", 0)); - device.Set("pad", param.Get("pad", 0)); - } - return devices; -} - -Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const { - if (index >= button_params.size()) { - return {}; - } - return button_params[index]; -} - -Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const { - if (index >= stick_params.size()) { - return {}; - } - return stick_params[index]; -} - -Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const { - if (index >= motion_params.size()) { - return {}; - } - return motion_params[index]; -} - -void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) { - if (index >= button_params.size()) { - return; - } - button_params[index] = std::move(param); - ReloadInput(); -} - -void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) { - if (index >= stick_params.size()) { - return; - } - stick_params[index] = std::move(param); - ReloadInput(); -} - -void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) { - if (index >= motion_params.size()) { - return; - } - motion_params[index] = std::move(param); - ReloadInput(); -} - -void EmulatedController::StartMotionCalibration() { - for (ControllerMotionInfo& motion : controller.motion_values) { - motion.emulated.Calibrate(); - } -} - -void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid) { - if (index >= controller.button_values.size()) { - return; - } - std::unique_lock lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = controller.button_values[index]; - - // Only read button values that have the same uuid or are pressed once - if (current_status.uuid != uuid) { - if (!new_status.value) { - return; - } - } - - current_status.toggle = new_status.toggle; - current_status.turbo = new_status.turbo; - current_status.uuid = uuid; - - // Update button status with current - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } - - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } - } - - if (!value_changed) { - return; - } - - if (is_configuring) { - controller.npad_button_state.raw = NpadButton::None; - controller.debug_pad_button_state.raw = 0; - controller.home_button_state.raw = 0; - controller.capture_button_state.raw = 0; - lock.unlock(); - TriggerOnChange(ControllerTriggerType::Button, false); - return; - } - - // GC controllers have triggers not buttons - if (npad_type == NpadStyleIndex::GameCube) { - if (index == Settings::NativeButton::ZR) { - return; - } - if (index == Settings::NativeButton::ZL) { - return; - } - } - - switch (index) { - case Settings::NativeButton::A: - controller.npad_button_state.a.Assign(current_status.value); - controller.debug_pad_button_state.a.Assign(current_status.value); - break; - case Settings::NativeButton::B: - controller.npad_button_state.b.Assign(current_status.value); - controller.debug_pad_button_state.b.Assign(current_status.value); - break; - case Settings::NativeButton::X: - controller.npad_button_state.x.Assign(current_status.value); - controller.debug_pad_button_state.x.Assign(current_status.value); - break; - case Settings::NativeButton::Y: - controller.npad_button_state.y.Assign(current_status.value); - controller.debug_pad_button_state.y.Assign(current_status.value); - break; - case Settings::NativeButton::LStick: - controller.npad_button_state.stick_l.Assign(current_status.value); - break; - case Settings::NativeButton::RStick: - controller.npad_button_state.stick_r.Assign(current_status.value); - break; - case Settings::NativeButton::L: - controller.npad_button_state.l.Assign(current_status.value); - controller.debug_pad_button_state.l.Assign(current_status.value); - break; - case Settings::NativeButton::R: - controller.npad_button_state.r.Assign(current_status.value); - controller.debug_pad_button_state.r.Assign(current_status.value); - break; - case Settings::NativeButton::ZL: - controller.npad_button_state.zl.Assign(current_status.value); - controller.debug_pad_button_state.zl.Assign(current_status.value); - break; - case Settings::NativeButton::ZR: - controller.npad_button_state.zr.Assign(current_status.value); - controller.debug_pad_button_state.zr.Assign(current_status.value); - break; - case Settings::NativeButton::Plus: - controller.npad_button_state.plus.Assign(current_status.value); - controller.debug_pad_button_state.plus.Assign(current_status.value); - break; - case Settings::NativeButton::Minus: - controller.npad_button_state.minus.Assign(current_status.value); - controller.debug_pad_button_state.minus.Assign(current_status.value); - break; - case Settings::NativeButton::DLeft: - controller.npad_button_state.left.Assign(current_status.value); - controller.debug_pad_button_state.d_left.Assign(current_status.value); - break; - case Settings::NativeButton::DUp: - controller.npad_button_state.up.Assign(current_status.value); - controller.debug_pad_button_state.d_up.Assign(current_status.value); - break; - case Settings::NativeButton::DRight: - controller.npad_button_state.right.Assign(current_status.value); - controller.debug_pad_button_state.d_right.Assign(current_status.value); - break; - case Settings::NativeButton::DDown: - controller.npad_button_state.down.Assign(current_status.value); - controller.debug_pad_button_state.d_down.Assign(current_status.value); - break; - case Settings::NativeButton::SLLeft: - controller.npad_button_state.left_sl.Assign(current_status.value); - break; - case Settings::NativeButton::SLRight: - controller.npad_button_state.right_sl.Assign(current_status.value); - break; - case Settings::NativeButton::SRLeft: - controller.npad_button_state.left_sr.Assign(current_status.value); - break; - case Settings::NativeButton::SRRight: - controller.npad_button_state.right_sr.Assign(current_status.value); - break; - case Settings::NativeButton::Home: - if (!system_buttons_enabled) { - break; - } - controller.home_button_state.home.Assign(current_status.value); - break; - case Settings::NativeButton::Screenshot: - if (!system_buttons_enabled) { - break; - } - controller.capture_button_state.capture.Assign(current_status.value); - break; - } - - lock.unlock(); - - if (!is_connected) { - if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { - Connect(); - } - if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) { - Connect(); - } - } - TriggerOnChange(ControllerTriggerType::Button, true); -} - -void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid) { - if (index >= controller.stick_values.size()) { - return; - } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); }); - std::scoped_lock lock{mutex}; - const auto stick_value = TransformToStick(callback); - - // Only read stick values that have the same uuid or are over the threshold to avoid flapping - if (controller.stick_values[index].uuid != uuid) { - const bool is_tas = uuid == TAS_UUID; - if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) { - trigger_guard.Cancel(); - return; - } - if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left && - !stick_value.right) { - trigger_guard.Cancel(); - return; - } - } - - controller.stick_values[index] = stick_value; - controller.stick_values[index].uuid = uuid; - - if (is_configuring) { - controller.analog_stick_state.left = {}; - controller.analog_stick_state.right = {}; - return; - } - - const AnalogStickState stick{ - .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX), - .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX), - }; - - switch (index) { - case Settings::NativeAnalog::LStick: - controller.analog_stick_state.left = stick; - controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left); - controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up); - controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right); - controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down); - break; - case Settings::NativeAnalog::RStick: - controller.analog_stick_state.right = stick; - controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left); - controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up); - controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right); - controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); - break; - } -} - -void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback, - std::size_t index, Common::UUID uuid) { - if (index >= controller.trigger_values.size()) { - return; - } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); }); - std::scoped_lock lock{mutex}; - const auto trigger_value = TransformToTrigger(callback); - - // Only read trigger values that have the same uuid or are pressed once - if (controller.trigger_values[index].uuid != uuid) { - if (!trigger_value.pressed.value) { - return; - } - } - - controller.trigger_values[index] = trigger_value; - controller.trigger_values[index].uuid = uuid; - - if (is_configuring) { - controller.gc_trigger_state.left = 0; - controller.gc_trigger_state.right = 0; - return; - } - - // Only GC controllers have analog triggers - if (npad_type != NpadStyleIndex::GameCube) { - trigger_guard.Cancel(); - return; - } - - const auto& trigger = controller.trigger_values[index]; - - switch (index) { - case Settings::NativeTrigger::LTrigger: - controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX); - controller.npad_button_state.zl.Assign(trigger.pressed.value); - break; - case Settings::NativeTrigger::RTrigger: - controller.gc_trigger_state.right = - static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX); - controller.npad_button_state.zr.Assign(trigger.pressed.value); - break; - } -} - -void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= controller.motion_values.size()) { - return; - } - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); }); - std::scoped_lock lock{mutex}; - auto& raw_status = controller.motion_values[index].raw_status; - auto& emulated = controller.motion_values[index].emulated; - - raw_status = TransformToMotion(callback); - emulated.SetAcceleration(Common::Vec3f{ - raw_status.accel.x.value, - raw_status.accel.y.value, - raw_status.accel.z.value, - }); - emulated.SetGyroscope(Common::Vec3f{ - raw_status.gyro.x.value, - raw_status.gyro.y.value, - raw_status.gyro.z.value, - }); - emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold); - emulated.UpdateRotation(raw_status.delta_timestamp); - emulated.UpdateOrientation(raw_status.delta_timestamp); - - auto& motion = controller.motion_state[index]; - motion.accel = emulated.GetAcceleration(); - motion.gyro = emulated.GetGyroscope(); - motion.rotation = emulated.GetRotations(); - motion.euler = emulated.GetEulerAngles(); - motion.orientation = emulated.GetOrientation(); - motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); -} - -void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= controller.color_values.size()) { - return; - } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); }); - std::scoped_lock lock{mutex}; - controller.color_values[index] = TransformToColor(callback); - - if (is_configuring) { - return; - } - - if (controller.color_values[index].body == 0) { - trigger_guard.Cancel(); - return; - } - - controller.colors_state.fullkey = { - .body = GetNpadColor(controller.color_values[index].body), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - if (npad_type == NpadStyleIndex::ProController) { - controller.colors_state.left = { - .body = GetNpadColor(controller.color_values[index].left_grip), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - controller.colors_state.right = { - .body = GetNpadColor(controller.color_values[index].right_grip), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - } else { - switch (index) { - case LeftIndex: - controller.colors_state.left = { - .body = GetNpadColor(controller.color_values[index].body), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - break; - case RightIndex: - controller.colors_state.right = { - .body = GetNpadColor(controller.color_values[index].body), - .button = GetNpadColor(controller.color_values[index].buttons), - }; - break; - } - } -} - -void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= controller.battery_values.size()) { - return; - } - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); }); - std::scoped_lock lock{mutex}; - controller.battery_values[index] = TransformToBattery(callback); - - if (is_configuring) { - return; - } - - bool is_charging = false; - bool is_powered = false; - NpadBatteryLevel battery_level = NpadBatteryLevel::Empty; - switch (controller.battery_values[index]) { - case Common::Input::BatteryLevel::Charging: - is_charging = true; - is_powered = true; - battery_level = NpadBatteryLevel::Full; - break; - case Common::Input::BatteryLevel::Medium: - battery_level = NpadBatteryLevel::High; - break; - case Common::Input::BatteryLevel::Low: - battery_level = NpadBatteryLevel::Low; - break; - case Common::Input::BatteryLevel::Critical: - battery_level = NpadBatteryLevel::Critical; - break; - case Common::Input::BatteryLevel::Empty: - battery_level = NpadBatteryLevel::Empty; - break; - case Common::Input::BatteryLevel::None: - case Common::Input::BatteryLevel::Full: - default: - is_powered = true; - battery_level = NpadBatteryLevel::Full; - break; - } - - switch (index) { - case LeftIndex: - controller.battery_state.left = { - .is_powered = is_powered, - .is_charging = is_charging, - .battery_level = battery_level, - }; - break; - case RightIndex: - controller.battery_state.right = { - .is_powered = is_powered, - .is_charging = is_charging, - .battery_level = battery_level, - }; - break; - case DualIndex: - controller.battery_state.dual = { - .is_powered = is_powered, - .is_charging = is_charging, - .battery_level = battery_level, - }; - break; - } -} - -void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); }); - std::scoped_lock lock{mutex}; - controller.camera_values = TransformToCamera(callback); - - if (is_configuring) { - return; - } - - controller.camera_state.sample++; - controller.camera_state.format = - static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); - controller.camera_state.data = controller.camera_values.data; -} - -void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); }); - std::scoped_lock lock{mutex}; - const auto force_value = TransformToStick(callback); - - controller.ring_analog_value = force_value.x; - - if (is_configuring) { - return; - } - - controller.ring_analog_state.force = force_value.x.value; -} - -void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); }); - std::scoped_lock lock{mutex}; - controller.nfc_values = TransformToNfc(callback); - - if (is_configuring) { - return; - } - - controller.nfc_state = controller.nfc_values; -} - -bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { - if (!is_initalized) { - return false; - } - if (device_index >= output_devices.size()) { - return false; - } - if (!output_devices[device_index]) { - return false; - } - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; - - if (!player.vibration_enabled) { - return false; - } - - // Exponential amplification is too strong at low amplitudes. Switch to a linear - // amplification if strength is set below 0.7f - const Common::Input::VibrationAmplificationType type = - strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential - : Common::Input::VibrationAmplificationType::Linear; - - const Common::Input::VibrationStatus status = { - .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f), - .low_frequency = vibration.low_frequency, - .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f), - .high_frequency = vibration.high_frequency, - .type = type, - }; - return output_devices[device_index]->SetVibration(status) == - Common::Input::DriverResult::Success; -} - -bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - - if (!is_initalized) { - return false; - } - - if (!player.vibration_enabled) { - return false; - } - - if (device_index >= output_devices.size()) { - return false; - } - - if (!output_devices[device_index]) { - return false; - } - - return output_devices[device_index]->IsVibrationEnabled(); -} - -Common::Input::DriverResult EmulatedController::SetPollingMode( - EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { - LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); - - if (!is_initalized) { - return Common::Input::DriverResult::InvalidHandle; - } - - auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; - auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_output_device = output_devices[3]; - - if (device_index == EmulatedDeviceIndex::LeftIndex) { - controller.left_polling_mode = polling_mode; - return left_output_device->SetPollingMode(polling_mode); - } - - if (device_index == EmulatedDeviceIndex::RightIndex) { - controller.right_polling_mode = polling_mode; - const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); - const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); - - // Restore previous state - if (mapped_nfc_result != Common::Input::DriverResult::Success) { - right_output_device->SetPollingMode(Common::Input::PollingMode::Active); - } - - if (virtual_nfc_result == Common::Input::DriverResult::Success) { - return virtual_nfc_result; - } - return mapped_nfc_result; - } - - controller.left_polling_mode = polling_mode; - controller.right_polling_mode = polling_mode; - left_output_device->SetPollingMode(polling_mode); - right_output_device->SetPollingMode(polling_mode); - nfc_output_device->SetPollingMode(polling_mode); - return Common::Input::DriverResult::Success; -} - -Common::Input::PollingMode EmulatedController::GetPollingMode( - EmulatedDeviceIndex device_index) const { - if (device_index == EmulatedDeviceIndex::LeftIndex) { - return controller.left_polling_mode; - } - return controller.right_polling_mode; -} - -bool EmulatedController::SetCameraFormat( - Core::IrSensor::ImageTransferProcessorFormat camera_format) { - LOG_INFO(Service_HID, "Set camera format {}", camera_format); - - if (!is_initalized) { - return false; - } - - auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& camera_output_device = output_devices[2]; - - if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( - camera_format)) == Common::Input::DriverResult::Success) { - return true; - } - - // Fallback to Qt camera if native device doesn't have support - return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( - camera_format)) == Common::Input::DriverResult::Success; -} - -Common::ParamPackage EmulatedController::GetRingParam() const { - return ring_params[0]; -} - -void EmulatedController::SetRingParam(Common::ParamPackage param) { - ring_params[0] = std::move(param); - ReloadInput(); -} - -bool EmulatedController::HasNfc() const { - - if (!is_initalized) { - return false; - } - - const auto& nfc_output_device = output_devices[3]; - - switch (npad_type) { - case NpadStyleIndex::JoyconRight: - case NpadStyleIndex::JoyconDual: - case NpadStyleIndex::ProController: - case NpadStyleIndex::Handheld: - break; - default: - return false; - } - - const bool has_virtual_nfc = - npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld; - const bool is_virtual_nfc_supported = - nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported; - - return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); -} - -bool EmulatedController::AddNfcHandle() { - nfc_handles++; - return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) == - Common::Input::DriverResult::Success; -} - -bool EmulatedController::RemoveNfcHandle() { - nfc_handles--; - if (nfc_handles <= 0) { - return SetPollingMode(EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active) == - Common::Input::DriverResult::Success; - } - return true; -} - -bool EmulatedController::StartNfcPolling() { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - const auto device_result = nfc_output_device->StartNfcPolling(); - const auto virtual_device_result = nfc_virtual_output_device->StartNfcPolling(); - - return device_result == Common::Input::NfcState::Success || - virtual_device_result == Common::Input::NfcState::Success; -} - -bool EmulatedController::StopNfcPolling() { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - const auto device_result = nfc_output_device->StopNfcPolling(); - const auto virtual_device_result = nfc_virtual_output_device->StopNfcPolling(); - - return device_result == Common::Input::NfcState::Success || - virtual_device_result == Common::Input::NfcState::Success; -} - -bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) { - return true; - } - - return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success; -} - -bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, - Common::Input::MifareRequest& out_data) { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) { - return true; - } - - return nfc_virtual_output_device->ReadMifareData(request, out_data) == - Common::Input::NfcState::Success; -} - -bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) { - return true; - } - - return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success; -} - -bool EmulatedController::WriteNfc(const std::vector<u8>& data) { - if (!is_initalized) { - return false; - } - - auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; - auto& nfc_virtual_output_device = output_devices[3]; - - if (nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported) { - return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; - } - - return nfc_virtual_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; -} - -void EmulatedController::SetLedPattern() { - if (!is_initalized) { - return; - } - - for (auto& device : output_devices) { - if (!device) { - continue; - } - - const LedPattern pattern = GetLedPattern(); - const Common::Input::LedStatus status = { - .led_1 = pattern.position1 != 0, - .led_2 = pattern.position2 != 0, - .led_3 = pattern.position3 != 0, - .led_4 = pattern.position4 != 0, - }; - device->SetLED(status); - } -} - -void EmulatedController::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode) { - for (auto& motion : controller.motion_values) { - switch (mode) { - case GyroscopeZeroDriftMode::Loose: - motion_sensitivity = motion.emulated.IsAtRestLoose; - motion.emulated.SetGyroThreshold(motion.emulated.ThresholdLoose); - break; - case GyroscopeZeroDriftMode::Tight: - motion_sensitivity = motion.emulated.IsAtRestThight; - motion.emulated.SetGyroThreshold(motion.emulated.ThresholdThight); - break; - case GyroscopeZeroDriftMode::Standard: - default: - motion_sensitivity = motion.emulated.IsAtRestStandard; - motion.emulated.SetGyroThreshold(motion.emulated.ThresholdStandard); - break; - } - } -} - -void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) { - supported_style_tag = supported_styles; - if (!is_connected) { - return; - } - - // Attempt to reconnect with the original type - if (npad_type != original_npad_type) { - Disconnect(); - const auto current_npad_type = npad_type; - SetNpadStyleIndex(original_npad_type); - if (IsControllerSupported()) { - Connect(); - return; - } - SetNpadStyleIndex(current_npad_type); - Connect(); - } - - if (IsControllerSupported()) { - return; - } - - Disconnect(); - - // Fallback Fullkey controllers to Pro controllers - if (IsControllerFullkey() && supported_style_tag.fullkey) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); - SetNpadStyleIndex(NpadStyleIndex::ProController); - Connect(); - return; - } - - // Fallback Dual joycon controllers to Pro controllers - if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); - SetNpadStyleIndex(NpadStyleIndex::ProController); - Connect(); - return; - } - - // Fallback Pro controllers to Dual joycon - if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { - LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); - SetNpadStyleIndex(NpadStyleIndex::JoyconDual); - Connect(); - return; - } - - LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", - npad_type); -} - -bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { - std::scoped_lock lock{mutex}; - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; - switch (type) { - case NpadStyleIndex::ProController: - case NpadStyleIndex::GameCube: - case NpadStyleIndex::NES: - case NpadStyleIndex::SNES: - case NpadStyleIndex::N64: - case NpadStyleIndex::SegaGenesis: - return true; - default: - return false; - } -} - -bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { - std::scoped_lock lock{mutex}; - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; - switch (type) { - case NpadStyleIndex::ProController: - return supported_style_tag.fullkey.As<bool>(); - case NpadStyleIndex::Handheld: - return supported_style_tag.handheld.As<bool>(); - case NpadStyleIndex::JoyconDual: - return supported_style_tag.joycon_dual.As<bool>(); - case NpadStyleIndex::JoyconLeft: - return supported_style_tag.joycon_left.As<bool>(); - case NpadStyleIndex::JoyconRight: - return supported_style_tag.joycon_right.As<bool>(); - case NpadStyleIndex::GameCube: - return supported_style_tag.gamecube.As<bool>(); - case NpadStyleIndex::Pokeball: - return supported_style_tag.palma.As<bool>(); - case NpadStyleIndex::NES: - return supported_style_tag.lark.As<bool>(); - case NpadStyleIndex::SNES: - return supported_style_tag.lucia.As<bool>(); - case NpadStyleIndex::N64: - return supported_style_tag.lagoon.As<bool>(); - case NpadStyleIndex::SegaGenesis: - return supported_style_tag.lager.As<bool>(); - default: - return false; - } -} - -void EmulatedController::Connect(bool use_temporary_value) { - if (!IsControllerSupported(use_temporary_value)) { - const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; - LOG_ERROR(Service_HID, "Controller type {} is not supported", type); - return; - } - - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); }); - std::scoped_lock lock{connect_mutex, mutex}; - if (is_configuring) { - tmp_is_connected = true; - return; - } - - if (is_connected) { - trigger_guard.Cancel(); - return; - } - is_connected = true; -} - -void EmulatedController::Disconnect() { - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); }); - std::scoped_lock lock{connect_mutex, mutex}; - if (is_configuring) { - tmp_is_connected = false; - return; - } - - if (!is_connected) { - trigger_guard.Cancel(); - return; - } - is_connected = false; -} - -bool EmulatedController::IsConnected(bool get_temporary_value) const { - std::scoped_lock lock{connect_mutex}; - if (get_temporary_value && is_configuring) { - return tmp_is_connected; - } - return is_connected; -} - -NpadIdType EmulatedController::GetNpadIdType() const { - std::scoped_lock lock{mutex}; - return npad_id_type; -} - -NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { - std::scoped_lock lock{npad_mutex}; - if (get_temporary_value && is_configuring) { - return tmp_npad_type; - } - return npad_type; -} - -void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); }); - std::scoped_lock lock{mutex, npad_mutex}; - - if (is_configuring) { - if (tmp_npad_type == npad_type_) { - trigger_guard.Cancel(); - return; - } - tmp_npad_type = npad_type_; - return; - } - - if (npad_type == npad_type_) { - trigger_guard.Cancel(); - return; - } - if (is_connected) { - LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", - Service::HID::NpadIdTypeToIndex(npad_id_type)); - } - npad_type = npad_type_; -} - -LedPattern EmulatedController::GetLedPattern() const { - switch (npad_id_type) { - case NpadIdType::Player1: - return LedPattern{1, 0, 0, 0}; - case NpadIdType::Player2: - return LedPattern{1, 1, 0, 0}; - case NpadIdType::Player3: - return LedPattern{1, 1, 1, 0}; - case NpadIdType::Player4: - return LedPattern{1, 1, 1, 1}; - case NpadIdType::Player5: - return LedPattern{1, 0, 0, 1}; - case NpadIdType::Player6: - return LedPattern{1, 0, 1, 0}; - case NpadIdType::Player7: - return LedPattern{1, 0, 1, 1}; - case NpadIdType::Player8: - return LedPattern{0, 1, 1, 0}; - default: - return LedPattern{0, 0, 0, 0}; - } -} - -ButtonValues EmulatedController::GetButtonsValues() const { - std::scoped_lock lock{mutex}; - return controller.button_values; -} - -SticksValues EmulatedController::GetSticksValues() const { - std::scoped_lock lock{mutex}; - return controller.stick_values; -} - -TriggerValues EmulatedController::GetTriggersValues() const { - std::scoped_lock lock{mutex}; - return controller.trigger_values; -} - -ControllerMotionValues EmulatedController::GetMotionValues() const { - std::scoped_lock lock{mutex}; - return controller.motion_values; -} - -ColorValues EmulatedController::GetColorsValues() const { - std::scoped_lock lock{mutex}; - return controller.color_values; -} - -BatteryValues EmulatedController::GetBatteryValues() const { - std::scoped_lock lock{mutex}; - return controller.battery_values; -} - -CameraValues EmulatedController::GetCameraValues() const { - std::scoped_lock lock{mutex}; - return controller.camera_values; -} - -RingAnalogValue EmulatedController::GetRingSensorValues() const { - return controller.ring_analog_value; -} - -HomeButtonState EmulatedController::GetHomeButtons() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return controller.home_button_state; -} - -CaptureButtonState EmulatedController::GetCaptureButtons() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return controller.capture_button_state; -} - -NpadButtonState EmulatedController::GetNpadButtons() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return {controller.npad_button_state.raw & GetTurboButtonMask()}; -} - -DebugPadButton EmulatedController::GetDebugPadButtons() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return controller.debug_pad_button_state; -} - -AnalogSticks EmulatedController::GetSticks() const { - std::scoped_lock lock{mutex}; - - if (is_configuring) { - return {}; - } - - return controller.analog_stick_state; -} - -NpadGcTriggerState EmulatedController::GetTriggers() const { - std::scoped_lock lock{mutex}; - if (is_configuring) { - return {}; - } - return controller.gc_trigger_state; -} - -MotionState EmulatedController::GetMotions() const { - std::unique_lock lock{mutex}; - return controller.motion_state; -} - -ControllerColors EmulatedController::GetColors() const { - std::scoped_lock lock{mutex}; - return controller.colors_state; -} - -BatteryLevelState EmulatedController::GetBattery() const { - std::scoped_lock lock{mutex}; - return controller.battery_state; -} - -const CameraState& EmulatedController::GetCamera() const { - std::scoped_lock lock{mutex}; - return controller.camera_state; -} - -RingSensorForce EmulatedController::GetRingSensorForce() const { - return controller.ring_analog_state; -} - -const NfcState& EmulatedController::GetNfc() const { - std::scoped_lock lock{mutex}; - return controller.nfc_state; -} - -NpadColor EmulatedController::GetNpadColor(u32 color) { - return { - .r = static_cast<u8>((color >> 16) & 0xFF), - .g = static_cast<u8>((color >> 8) & 0xFF), - .b = static_cast<u8>(color & 0xFF), - .a = 0xff, - }; -} - -void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { - std::scoped_lock lock{callback_mutex}; - for (const auto& poller_pair : callback_list) { - const ControllerUpdateCallback& poller = poller_pair.second; - if (!is_npad_service_update && poller.is_npad_service) { - continue; - } - if (poller.on_change) { - poller.on_change(type); - } - } -} - -int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { - std::scoped_lock lock{callback_mutex}; - callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); - return last_callback_key++; -} - -void EmulatedController::DeleteCallback(int key) { - std::scoped_lock lock{callback_mutex}; - const auto& iterator = callback_list.find(key); - if (iterator == callback_list.end()) { - LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); - return; - } - callback_list.erase(iterator); -} - -void EmulatedController::StatusUpdate() { - turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2); - - // Some drivers like key motion need constant refreshing - for (std::size_t index = 0; index < motion_devices.size(); ++index) { - const auto& raw_status = controller.motion_values[index].raw_status; - auto& device = motion_devices[index]; - if (!raw_status.force_update) { - continue; - } - if (!device) { - continue; - } - device->ForceUpdate(); - } -} - -NpadButton EmulatedController::GetTurboButtonMask() const { - // Apply no mask when disabled - if (turbo_button_state < TURBO_BUTTON_DELAY) { - return {NpadButton::All}; - } - - NpadButtonState button_mask{}; - for (std::size_t index = 0; index < controller.button_values.size(); ++index) { - if (!controller.button_values[index].turbo) { - continue; - } - - switch (index) { - case Settings::NativeButton::A: - button_mask.a.Assign(1); - break; - case Settings::NativeButton::B: - button_mask.b.Assign(1); - break; - case Settings::NativeButton::X: - button_mask.x.Assign(1); - break; - case Settings::NativeButton::Y: - button_mask.y.Assign(1); - break; - case Settings::NativeButton::L: - button_mask.l.Assign(1); - break; - case Settings::NativeButton::R: - button_mask.r.Assign(1); - break; - case Settings::NativeButton::ZL: - button_mask.zl.Assign(1); - break; - case Settings::NativeButton::ZR: - button_mask.zr.Assign(1); - break; - case Settings::NativeButton::DLeft: - button_mask.left.Assign(1); - break; - case Settings::NativeButton::DUp: - button_mask.up.Assign(1); - break; - case Settings::NativeButton::DRight: - button_mask.right.Assign(1); - break; - case Settings::NativeButton::DDown: - button_mask.down.Assign(1); - break; - case Settings::NativeButton::SLLeft: - button_mask.left_sl.Assign(1); - break; - case Settings::NativeButton::SLRight: - button_mask.right_sl.Assign(1); - break; - case Settings::NativeButton::SRLeft: - button_mask.left_sr.Assign(1); - break; - case Settings::NativeButton::SRRight: - button_mask.right_sr.Assign(1); - break; - default: - break; - } - } - - return static_cast<NpadButton>(~button_mask.raw); -} - -} // namespace Core::HID diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h deleted file mode 100644 index d6e20ab66..000000000 --- a/src/core/hid/emulated_controller.h +++ /dev/null @@ -1,619 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <functional> -#include <memory> -#include <mutex> -#include <unordered_map> -#include <vector> - -#include "common/common_types.h" -#include "common/input.h" -#include "common/param_package.h" -#include "common/settings.h" -#include "common/vector_math.h" -#include "core/hid/hid_types.h" -#include "core/hid/irs_types.h" -#include "core/hid/motion_input.h" - -namespace Core::HID { -const std::size_t max_emulated_controllers = 2; -const std::size_t output_devices_size = 4; -struct ControllerMotionInfo { - Common::Input::MotionStatus raw_status{}; - MotionInput emulated{}; -}; - -using ButtonDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>; -using StickDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>; -using ControllerMotionDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; -using TriggerDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; -using ColorDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using BatteryDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using CameraDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using RingAnalogDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using NfcDevices = - std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; - -using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; -using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; -using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; -using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; -using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using OutputParams = std::array<Common::ParamPackage, output_devices_size>; - -using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; -using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; -using TriggerValues = - std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>; -using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>; -using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; -using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; -using CameraValues = Common::Input::CameraStatus; -using RingAnalogValue = Common::Input::AnalogStatus; -using NfcValues = Common::Input::NfcStatus; -using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; - -struct AnalogSticks { - AnalogStickState left{}; - AnalogStickState right{}; -}; - -struct ControllerColors { - NpadControllerColor fullkey{}; - NpadControllerColor left{}; - NpadControllerColor right{}; -}; - -struct BatteryLevelState { - NpadPowerInfo dual{}; - NpadPowerInfo left{}; - NpadPowerInfo right{}; -}; - -struct CameraState { - Core::IrSensor::ImageTransferProcessorFormat format{}; - std::vector<u8> data{}; - std::size_t sample{}; -}; - -struct RingSensorForce { - f32 force; -}; - -using NfcState = Common::Input::NfcStatus; - -struct ControllerMotion { - Common::Vec3f accel{}; - Common::Vec3f gyro{}; - Common::Vec3f rotation{}; - Common::Vec3f euler{}; - std::array<Common::Vec3f, 3> orientation{}; - bool is_at_rest{}; -}; - -enum EmulatedDeviceIndex : u8 { - LeftIndex, - RightIndex, - DualIndex, - AllDevices, -}; - -using MotionState = std::array<ControllerMotion, 2>; - -struct ControllerStatus { - // Data from input_common - ButtonValues button_values{}; - SticksValues stick_values{}; - ControllerMotionValues motion_values{}; - TriggerValues trigger_values{}; - ColorValues color_values{}; - BatteryValues battery_values{}; - VibrationValues vibration_values{}; - CameraValues camera_values{}; - RingAnalogValue ring_analog_value{}; - NfcValues nfc_values{}; - - // Data for HID services - HomeButtonState home_button_state{}; - CaptureButtonState capture_button_state{}; - NpadButtonState npad_button_state{}; - DebugPadButton debug_pad_button_state{}; - AnalogSticks analog_stick_state{}; - MotionState motion_state{}; - NpadGcTriggerState gc_trigger_state{}; - ControllerColors colors_state{}; - BatteryLevelState battery_state{}; - CameraState camera_state{}; - RingSensorForce ring_analog_state{}; - NfcState nfc_state{}; - Common::Input::PollingMode left_polling_mode{}; - Common::Input::PollingMode right_polling_mode{}; -}; - -enum class ControllerTriggerType { - Button, - Stick, - Trigger, - Motion, - Color, - Battery, - Vibration, - IrSensor, - RingController, - Nfc, - Connected, - Disconnected, - Type, - All, -}; - -struct ControllerUpdateCallback { - std::function<void(ControllerTriggerType)> on_change; - bool is_npad_service; -}; - -class EmulatedController { -public: - /** - * Contains all input data (buttons, joysticks, vibration, and motion) within this controller. - * @param npad_id_type npad id type for this specific controller - */ - explicit EmulatedController(NpadIdType npad_id_type_); - ~EmulatedController(); - - YUZU_NON_COPYABLE(EmulatedController); - YUZU_NON_MOVEABLE(EmulatedController); - - /// Converts the controller type from settings to npad type - static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type); - - /// Converts npad type to the equivalent of controller type from settings - static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type); - - /// Gets the NpadIdType for this controller - NpadIdType GetNpadIdType() const; - - /// Sets the NpadStyleIndex for this controller - void SetNpadStyleIndex(NpadStyleIndex npad_type_); - - /** - * Gets the NpadStyleIndex for this controller - * @param get_temporary_value If true tmp_npad_type will be returned - * @return NpadStyleIndex set on the controller - */ - NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const; - - /** - * Sets the supported controller types. Disconnects the controller if current type is not - * supported - * @param supported_styles bitflag with supported types - */ - void SetSupportedNpadStyleTag(NpadStyleTag supported_styles); - - /** - * Sets the connected status to true - * @param use_temporary_value If true tmp_npad_type will be used - */ - void Connect(bool use_temporary_value = false); - - /// Sets the connected status to false - void Disconnect(); - - /** - * Is the emulated connected - * @param get_temporary_value If true tmp_is_connected will be returned - * @return true if the controller has the connected status - */ - bool IsConnected(bool get_temporary_value = false) const; - - /// Removes all callbacks created from input devices - void UnloadInput(); - - /** - * Sets the emulated controller into configuring mode - * This prevents the modification of the HID state of the emulated controller by input commands - */ - void EnableConfiguration(); - - /// Returns the emulated controller into normal mode, allowing the modification of the HID state - void DisableConfiguration(); - - /// Enables Home and Screenshot buttons - void EnableSystemButtons(); - - /// Disables Home and Screenshot buttons - void DisableSystemButtons(); - - /// Sets Home and Screenshot buttons to false - void ResetSystemButtons(); - - /// Returns true if the emulated controller is in configuring mode - bool IsConfiguring() const; - - /// Reload all input devices - void ReloadInput(); - - /// Overrides current mapped devices with the stored configuration and reloads all input devices - void ReloadFromSettings(); - - /// Updates current colors with the ones stored in the configuration - void ReloadColorsFromSettings(); - - /// Saves the current mapped configuration - void SaveCurrentConfig(); - - /// Reverts any mapped changes made that weren't saved - void RestoreConfig(); - - /// Returns a vector of mapped devices from the mapped button and stick parameters - std::vector<Common::ParamPackage> GetMappedDevices() const; - - // Returns the current mapped button device - Common::ParamPackage GetButtonParam(std::size_t index) const; - - // Returns the current mapped stick device - Common::ParamPackage GetStickParam(std::size_t index) const; - - // Returns the current mapped motion device - Common::ParamPackage GetMotionParam(std::size_t index) const; - - /** - * Updates the current mapped button device - * @param param ParamPackage with controller data to be mapped - */ - void SetButtonParam(std::size_t index, Common::ParamPackage param); - - /** - * Updates the current mapped stick device - * @param param ParamPackage with controller data to be mapped - */ - void SetStickParam(std::size_t index, Common::ParamPackage param); - - /** - * Updates the current mapped motion device - * @param param ParamPackage with controller data to be mapped - */ - void SetMotionParam(std::size_t index, Common::ParamPackage param); - - /// Auto calibrates the current motion devices - void StartMotionCalibration(); - - /// Returns the latest button status from the controller with parameters - ButtonValues GetButtonsValues() const; - - /// Returns the latest analog stick status from the controller with parameters - SticksValues GetSticksValues() const; - - /// Returns the latest trigger status from the controller with parameters - TriggerValues GetTriggersValues() const; - - /// Returns the latest motion status from the controller with parameters - ControllerMotionValues GetMotionValues() const; - - /// Returns the latest color status from the controller with parameters - ColorValues GetColorsValues() const; - - /// Returns the latest battery status from the controller with parameters - BatteryValues GetBatteryValues() const; - - /// Returns the latest camera status from the controller with parameters - CameraValues GetCameraValues() const; - - /// Returns the latest status of analog input from the ring sensor with parameters - RingAnalogValue GetRingSensorValues() const; - - /// Returns the latest status of button input for the hid::HomeButton service - HomeButtonState GetHomeButtons() const; - - /// Returns the latest status of button input for the hid::CaptureButton service - CaptureButtonState GetCaptureButtons() const; - - /// Returns the latest status of button input for the hid::Npad service - NpadButtonState GetNpadButtons() const; - - /// Returns the latest status of button input for the debug pad service - DebugPadButton GetDebugPadButtons() const; - - /// Returns the latest status of stick input from the mouse - AnalogSticks GetSticks() const; - - /// Returns the latest status of trigger input from the mouse - NpadGcTriggerState GetTriggers() const; - - /// Returns the latest status of motion input from the mouse - MotionState GetMotions() const; - - /// Returns the latest color value from the controller - ControllerColors GetColors() const; - - /// Returns the latest battery status from the controller - BatteryLevelState GetBattery() const; - - /// Returns the latest camera status from the controller - const CameraState& GetCamera() const; - - /// Returns the latest ringcon force sensor value - RingSensorForce GetRingSensorForce() const; - - /// Returns the latest ntag status from the controller - const NfcState& GetNfc() const; - - /** - * Sends a specific vibration to the output device - * @return true if vibration had no errors - */ - bool SetVibration(std::size_t device_index, VibrationValue vibration); - - /** - * Sends a small vibration to the output device - * @return true if SetVibration was successful - */ - bool IsVibrationEnabled(std::size_t device_index); - - /** - * Sets the desired data to be polled from a controller - * @param device_index index of the controller to set the polling mode - * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. - * @return driver result from this command - */ - Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index, - Common::Input::PollingMode polling_mode); - /** - * Get the current polling mode from a controller - * @param device_index index of the controller to set the polling mode - * @return current polling mode - */ - Common::Input::PollingMode GetPollingMode(EmulatedDeviceIndex device_index) const; - - /** - * Sets the desired camera format to be polled from a controller - * @param camera_format size of each frame - * @return true if SetCameraFormat was successful - */ - bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); - - // Returns the current mapped ring device - Common::ParamPackage GetRingParam() const; - - /** - * Updates the current mapped ring device - * @param param ParamPackage with ring sensor data to be mapped - */ - void SetRingParam(Common::ParamPackage param); - - /// Returns true if the device has nfc support - bool HasNfc() const; - - /// Sets the joycon in nfc mode and increments the handle count - bool AddNfcHandle(); - - /// Decrements the handle count if zero sets the joycon in active mode - bool RemoveNfcHandle(); - - /// Start searching for nfc tags - bool StartNfcPolling(); - - /// Stop searching for nfc tags - bool StopNfcPolling(); - - /// Returns true if the nfc tag was readable - bool ReadAmiiboData(std::vector<u8>& data); - - /// Returns true if the nfc tag was written - bool WriteNfc(const std::vector<u8>& data); - - /// Returns true if the nfc tag was readable - bool ReadMifareData(const Common::Input::MifareRequest& request, - Common::Input::MifareRequest& out_data); - - /// Returns true if the nfc tag was written - bool WriteMifareData(const Common::Input::MifareRequest& request); - - /// Returns the led pattern corresponding to this emulated controller - LedPattern GetLedPattern() const; - - /// Asks the output device to change the player led pattern - void SetLedPattern(); - - /// Changes sensitivity of the motion sensor - void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode); - - /** - * Adds a callback to the list of events - * @param update_callback A ConsoleUpdateCallback that will be triggered - * @return an unique key corresponding to the callback index in the list - */ - int SetCallback(ControllerUpdateCallback update_callback); - - /** - * Removes a callback from the list stopping any future events to this object - * @param key Key corresponding to the callback index in the list - */ - void DeleteCallback(int key); - - /// Swaps the state of the turbo buttons and updates motion input - void StatusUpdate(); - -private: - /// creates input devices from params - void LoadDevices(); - - /// Set the params for TAS devices - void LoadTASParams(); - - /// Set the params for virtual pad devices - void LoadVirtualGamepadParams(); - - /** - * @param use_temporary_value If true tmp_npad_type will be used - * @return true if the controller style is fullkey - */ - bool IsControllerFullkey(bool use_temporary_value = false) const; - - /** - * Checks the current controller type against the supported_style_tag - * @param use_temporary_value If true tmp_npad_type will be used - * @return true if the controller is supported - */ - bool IsControllerSupported(bool use_temporary_value = false) const; - - /** - * Updates the button status of the controller - * @param callback A CallbackStatus containing the button status - * @param index Button ID of the to be updated - */ - void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid); - - /** - * Updates the analog stick status of the controller - * @param callback A CallbackStatus containing the analog stick status - * @param index stick ID of the to be updated - */ - void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid); - - /** - * Updates the trigger status of the controller - * @param callback A CallbackStatus containing the trigger status - * @param index trigger ID of the to be updated - */ - void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index, - Common::UUID uuid); - - /** - * Updates the motion status of the controller - * @param callback A CallbackStatus containing gyro and accelerometer data - * @param index motion ID of the to be updated - */ - void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the color status of the controller - * @param callback A CallbackStatus containing the color status - * @param index color ID of the to be updated - */ - void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the battery status of the controller - * @param callback A CallbackStatus containing the battery status - * @param index battery ID of the to be updated - */ - void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the camera status of the controller - * @param callback A CallbackStatus containing the camera status - */ - void SetCamera(const Common::Input::CallbackStatus& callback); - - /** - * Updates the ring analog sensor status of the ring controller - * @param callback A CallbackStatus containing the force status - */ - void SetRingAnalog(const Common::Input::CallbackStatus& callback); - - /** - * Updates the nfc status of the controller - * @param callback A CallbackStatus containing the nfc status - */ - void SetNfc(const Common::Input::CallbackStatus& callback); - - /** - * Converts a color format from bgra to rgba - * @param color in bgra format - * @return NpadColor in rgba format - */ - NpadColor GetNpadColor(u32 color); - - /** - * Triggers a callback that something has changed on the controller status - * @param type Input type of the event to trigger - * @param is_service_update indicates if this event should only be sent to HID services - */ - void TriggerOnChange(ControllerTriggerType type, bool is_service_update); - - NpadButton GetTurboButtonMask() const; - - const NpadIdType npad_id_type; - NpadStyleIndex npad_type{NpadStyleIndex::None}; - NpadStyleIndex original_npad_type{NpadStyleIndex::None}; - NpadStyleTag supported_style_tag{NpadStyleSet::All}; - bool is_connected{false}; - bool is_configuring{false}; - bool is_initalized{false}; - bool system_buttons_enabled{true}; - f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; - u32 turbo_button_state{0}; - std::size_t nfc_handles{0}; - - // Temporary values to avoid doing changes while the controller is in configuring mode - NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; - bool tmp_is_connected{false}; - - ButtonParams button_params; - StickParams stick_params; - ControllerMotionParams motion_params; - TriggerParams trigger_params; - BatteryParams battery_params; - ColorParams color_params; - CameraParams camera_params; - RingAnalogParams ring_params; - NfcParams nfc_params; - OutputParams output_params; - - ButtonDevices button_devices; - StickDevices stick_devices; - ControllerMotionDevices motion_devices; - TriggerDevices trigger_devices; - BatteryDevices battery_devices; - ColorDevices color_devices; - CameraDevices camera_devices; - RingAnalogDevices ring_analog_devices; - NfcDevices nfc_devices; - OutputDevices output_devices; - - // TAS related variables - ButtonParams tas_button_params; - StickParams tas_stick_params; - ButtonDevices tas_button_devices; - StickDevices tas_stick_devices; - - // Virtual gamepad related variables - ButtonParams virtual_button_params; - StickParams virtual_stick_params; - ControllerMotionParams virtual_motion_params; - ButtonDevices virtual_button_devices; - StickDevices virtual_stick_devices; - ControllerMotionDevices virtual_motion_devices; - - mutable std::mutex mutex; - mutable std::mutex callback_mutex; - mutable std::mutex npad_mutex; - mutable std::mutex connect_mutex; - std::unordered_map<int, ControllerUpdateCallback> callback_list; - int last_callback_key = 0; - - // Stores the current status of all controller input - ControllerStatus controller; -}; - -} // namespace Core::HID diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp deleted file mode 100644 index 8e165dded..000000000 --- a/src/core/hid/emulated_devices.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <fmt/format.h> - -#include "core/hid/emulated_devices.h" -#include "core/hid/input_converter.h" - -namespace Core::HID { - -EmulatedDevices::EmulatedDevices() = default; - -EmulatedDevices::~EmulatedDevices() = default; - -void EmulatedDevices::ReloadFromSettings() { - ReloadInput(); -} - -void EmulatedDevices::ReloadInput() { - // If you load any device here add the equivalent to the UnloadInput() function - - // Native Mouse is mapped on port 1, pad 0 - const Common::ParamPackage mouse_params{"engine:mouse,port:1,pad:0"}; - - // Keyboard keys is mapped on port 1, pad 0 for normal keys, pad 1 for moddifier keys - const Common::ParamPackage keyboard_params{"engine:keyboard,port:1"}; - - std::size_t key_index = 0; - for (auto& mouse_device : mouse_button_devices) { - Common::ParamPackage mouse_button_params = mouse_params; - mouse_button_params.Set("button", static_cast<int>(key_index)); - mouse_device = Common::Input::CreateInputDevice(mouse_button_params); - key_index++; - } - - Common::ParamPackage mouse_position_params = mouse_params; - mouse_position_params.Set("axis_x", 0); - mouse_position_params.Set("axis_y", 1); - mouse_position_params.Set("deadzone", 0.0f); - mouse_position_params.Set("range", 1.0f); - mouse_position_params.Set("threshold", 0.0f); - mouse_stick_device = Common::Input::CreateInputDevice(mouse_position_params); - - // First two axis are reserved for mouse position - key_index = 2; - for (auto& mouse_device : mouse_wheel_devices) { - Common::ParamPackage mouse_wheel_params = mouse_params; - mouse_wheel_params.Set("axis", static_cast<int>(key_index)); - mouse_device = Common::Input::CreateInputDevice(mouse_wheel_params); - key_index++; - } - - key_index = 0; - for (auto& keyboard_device : keyboard_devices) { - Common::ParamPackage keyboard_key_params = keyboard_params; - keyboard_key_params.Set("button", static_cast<int>(key_index)); - keyboard_key_params.Set("pad", 0); - keyboard_device = Common::Input::CreateInputDevice(keyboard_key_params); - key_index++; - } - - key_index = 0; - for (auto& keyboard_device : keyboard_modifier_devices) { - Common::ParamPackage keyboard_moddifier_params = keyboard_params; - keyboard_moddifier_params.Set("button", static_cast<int>(key_index)); - keyboard_moddifier_params.Set("pad", 1); - keyboard_device = Common::Input::CreateInputDevice(keyboard_moddifier_params); - key_index++; - } - - for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { - if (!mouse_button_devices[index]) { - continue; - } - mouse_button_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetMouseButton(callback, index); - }, - }); - } - - for (std::size_t index = 0; index < mouse_wheel_devices.size(); ++index) { - if (!mouse_wheel_devices[index]) { - continue; - } - mouse_wheel_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetMouseWheel(callback, index); - }, - }); - } - - if (mouse_stick_device) { - mouse_stick_device->SetCallback({ - .on_change = - [this](const Common::Input::CallbackStatus& callback) { - SetMousePosition(callback); - }, - }); - } - - for (std::size_t index = 0; index < keyboard_devices.size(); ++index) { - if (!keyboard_devices[index]) { - continue; - } - keyboard_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetKeyboardButton(callback, index); - }, - }); - } - - for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) { - if (!keyboard_modifier_devices[index]) { - continue; - } - keyboard_modifier_devices[index]->SetCallback({ - .on_change = - [this, index](const Common::Input::CallbackStatus& callback) { - SetKeyboardModifier(callback, index); - }, - }); - } -} - -void EmulatedDevices::UnloadInput() { - for (auto& button : mouse_button_devices) { - button.reset(); - } - for (auto& analog : mouse_wheel_devices) { - analog.reset(); - } - mouse_stick_device.reset(); - for (auto& button : keyboard_devices) { - button.reset(); - } - for (auto& button : keyboard_modifier_devices) { - button.reset(); - } -} - -void EmulatedDevices::EnableConfiguration() { - is_configuring = true; - SaveCurrentConfig(); -} - -void EmulatedDevices::DisableConfiguration() { - is_configuring = false; -} - -bool EmulatedDevices::IsConfiguring() const { - return is_configuring; -} - -void EmulatedDevices::SaveCurrentConfig() { - if (!is_configuring) { - return; - } -} - -void EmulatedDevices::RestoreConfig() { - if (!is_configuring) { - return; - } - ReloadFromSettings(); -} - -void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.keyboard_values.size()) { - return; - } - std::unique_lock lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = device_status.keyboard_values[index]; - current_status.toggle = new_status.toggle; - - // Update button status with current status - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } - - // Unlock button, ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } - } - - if (!value_changed) { - return; - } - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Keyboard); - return; - } - - // Index should be converted from NativeKeyboard to KeyboardKeyIndex - UpdateKey(index, current_status.value); - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Keyboard); -} - -void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) { - constexpr std::size_t KEYS_PER_BYTE = 8; - auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE]; - const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE)); - if (status) { - entry = entry | mask; - } else { - entry = static_cast<u8>(entry & ~mask); - } -} - -void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.keyboard_moddifier_values.size()) { - return; - } - std::unique_lock lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = device_status.keyboard_moddifier_values[index]; - current_status.toggle = new_status.toggle; - - // Update button status with current - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } - - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } - } - - if (!value_changed) { - return; - } - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(DeviceTriggerType::KeyboardModdifier); - return; - } - - switch (index) { - case Settings::NativeKeyboard::LeftControl: - case Settings::NativeKeyboard::RightControl: - device_status.keyboard_moddifier_state.control.Assign(current_status.value); - break; - case Settings::NativeKeyboard::LeftShift: - case Settings::NativeKeyboard::RightShift: - device_status.keyboard_moddifier_state.shift.Assign(current_status.value); - break; - case Settings::NativeKeyboard::LeftAlt: - device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value); - break; - case Settings::NativeKeyboard::RightAlt: - device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value); - break; - case Settings::NativeKeyboard::CapsLock: - device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value); - break; - case Settings::NativeKeyboard::ScrollLock: - device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value); - break; - case Settings::NativeKeyboard::NumLock: - device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value); - break; - } - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::KeyboardModdifier); -} - -void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.mouse_button_values.size()) { - return; - } - std::unique_lock lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = device_status.mouse_button_values[index]; - current_status.toggle = new_status.toggle; - - // Update button status with current - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } - - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } - } - - if (!value_changed) { - return; - } - - if (is_configuring) { - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); - return; - } - - switch (index) { - case Settings::NativeMouseButton::Left: - device_status.mouse_button_state.left.Assign(current_status.value); - break; - case Settings::NativeMouseButton::Right: - device_status.mouse_button_state.right.Assign(current_status.value); - break; - case Settings::NativeMouseButton::Middle: - device_status.mouse_button_state.middle.Assign(current_status.value); - break; - case Settings::NativeMouseButton::Forward: - device_status.mouse_button_state.forward.Assign(current_status.value); - break; - case Settings::NativeMouseButton::Back: - device_status.mouse_button_state.back.Assign(current_status.value); - break; - } - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); -} - -void EmulatedDevices::SetMouseWheel(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.mouse_wheel_values.size()) { - return; - } - std::unique_lock lock{mutex}; - const auto analog_value = TransformToAnalog(callback); - - device_status.mouse_wheel_values[index] = analog_value; - - if (is_configuring) { - device_status.mouse_wheel_state = {}; - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); - return; - } - - switch (index) { - case Settings::NativeMouseWheel::X: - device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value); - break; - case Settings::NativeMouseWheel::Y: - device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value); - break; - } - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); -} - -void EmulatedDevices::SetMousePosition(const Common::Input::CallbackStatus& callback) { - std::unique_lock lock{mutex}; - const auto touch_value = TransformToTouch(callback); - - device_status.mouse_stick_value = touch_value; - - if (is_configuring) { - device_status.mouse_position_state = {}; - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); - return; - } - - device_status.mouse_position_state.x = touch_value.x.value; - device_status.mouse_position_state.y = touch_value.y.value; - - lock.unlock(); - TriggerOnChange(DeviceTriggerType::Mouse); -} - -KeyboardValues EmulatedDevices::GetKeyboardValues() const { - std::scoped_lock lock{mutex}; - return device_status.keyboard_values; -} - -KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { - std::scoped_lock lock{mutex}; - return device_status.keyboard_moddifier_values; -} - -MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { - std::scoped_lock lock{mutex}; - return device_status.mouse_button_values; -} - -KeyboardKey EmulatedDevices::GetKeyboard() const { - std::scoped_lock lock{mutex}; - return device_status.keyboard_state; -} - -KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { - std::scoped_lock lock{mutex}; - return device_status.keyboard_moddifier_state; -} - -MouseButton EmulatedDevices::GetMouseButtons() const { - std::scoped_lock lock{mutex}; - return device_status.mouse_button_state; -} - -MousePosition EmulatedDevices::GetMousePosition() const { - std::scoped_lock lock{mutex}; - return device_status.mouse_position_state; -} - -AnalogStickState EmulatedDevices::GetMouseWheel() const { - std::scoped_lock lock{mutex}; - return device_status.mouse_wheel_state; -} - -void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { - std::scoped_lock lock{callback_mutex}; - for (const auto& poller_pair : callback_list) { - const InterfaceUpdateCallback& poller = poller_pair.second; - if (poller.on_change) { - poller.on_change(type); - } - } -} - -int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { - std::scoped_lock lock{callback_mutex}; - callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); - return last_callback_key++; -} - -void EmulatedDevices::DeleteCallback(int key) { - std::scoped_lock lock{callback_mutex}; - const auto& iterator = callback_list.find(key); - if (iterator == callback_list.end()) { - LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); - return; - } - callback_list.erase(iterator); -} -} // namespace Core::HID diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h deleted file mode 100644 index 5eab693e4..000000000 --- a/src/core/hid/emulated_devices.h +++ /dev/null @@ -1,212 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> -#include <functional> -#include <memory> -#include <mutex> -#include <unordered_map> -#include <vector> - -#include "common/common_types.h" -#include "common/input.h" -#include "common/param_package.h" -#include "common/settings.h" -#include "core/hid/hid_types.h" - -namespace Core::HID { -using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, - Settings::NativeKeyboard::NumKeyboardKeys>; -using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, - Settings::NativeKeyboard::NumKeyboardMods>; -using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, - Settings::NativeMouseButton::NumMouseButtons>; -using MouseWheelDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, - Settings::NativeMouseWheel::NumMouseWheels>; -using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; - -using MouseButtonParams = - std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; - -using KeyboardValues = - std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; -using KeyboardModifierValues = - std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>; -using MouseButtonValues = - std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>; -using MouseWheelValues = - std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; -using MouseStickValue = Common::Input::TouchStatus; - -struct MousePosition { - f32 x; - f32 y; -}; - -struct DeviceStatus { - // Data from input_common - KeyboardValues keyboard_values{}; - KeyboardModifierValues keyboard_moddifier_values{}; - MouseButtonValues mouse_button_values{}; - MouseWheelValues mouse_wheel_values{}; - MouseStickValue mouse_stick_value{}; - - // Data for HID services - KeyboardKey keyboard_state{}; - KeyboardModifier keyboard_moddifier_state{}; - MouseButton mouse_button_state{}; - MousePosition mouse_position_state{}; - AnalogStickState mouse_wheel_state{}; -}; - -enum class DeviceTriggerType { - Keyboard, - KeyboardModdifier, - Mouse, - RingController, -}; - -struct InterfaceUpdateCallback { - std::function<void(DeviceTriggerType)> on_change; -}; - -class EmulatedDevices { -public: - /** - * Contains all input data related to external devices that aren't necessarily a controller - * This includes devices such as the keyboard or mouse - */ - explicit EmulatedDevices(); - ~EmulatedDevices(); - - YUZU_NON_COPYABLE(EmulatedDevices); - YUZU_NON_MOVEABLE(EmulatedDevices); - - /// Removes all callbacks created from input devices - void UnloadInput(); - - /** - * Sets the emulated devices into configuring mode - * This prevents the modification of the HID state of the emulated devices by input commands - */ - void EnableConfiguration(); - - /// Returns the emulated devices into normal mode, allowing the modification of the HID state - void DisableConfiguration(); - - /// Returns true if the emulated device is in configuring mode - bool IsConfiguring() const; - - /// Reload all input devices - void ReloadInput(); - - /// Overrides current mapped devices with the stored configuration and reloads all input devices - void ReloadFromSettings(); - - /// Saves the current mapped configuration - void SaveCurrentConfig(); - - /// Reverts any mapped changes made that weren't saved - void RestoreConfig(); - - /// Returns the latest status of button input from the keyboard with parameters - KeyboardValues GetKeyboardValues() const; - - /// Returns the latest status of button input from the keyboard modifiers with parameters - KeyboardModifierValues GetKeyboardModdifierValues() const; - - /// Returns the latest status of button input from the mouse with parameters - MouseButtonValues GetMouseButtonsValues() const; - - /// Returns the latest status of button input from the keyboard - KeyboardKey GetKeyboard() const; - - /// Returns the latest status of button input from the keyboard modifiers - KeyboardModifier GetKeyboardModifier() const; - - /// Returns the latest status of button input from the mouse - MouseButton GetMouseButtons() const; - - /// Returns the latest mouse coordinates - MousePosition GetMousePosition() const; - - /// Returns the latest mouse wheel change - AnalogStickState GetMouseWheel() const; - - /** - * Adds a callback to the list of events - * @param update_callback InterfaceUpdateCallback that will be triggered - * @return an unique key corresponding to the callback index in the list - */ - int SetCallback(InterfaceUpdateCallback update_callback); - - /** - * Removes a callback from the list stopping any future events to this object - * @param key Key corresponding to the callback index in the list - */ - void DeleteCallback(int key); - -private: - /// Helps assigning a value to keyboard_state - void UpdateKey(std::size_t key_index, bool status); - - /** - * Updates the touch status of the keyboard device - * @param callback A CallbackStatus containing the key status - * @param index key ID to be updated - */ - void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the keyboard status of the keyboard device - * @param callback A CallbackStatus containing the modifier key status - * @param index modifier key ID to be updated - */ - void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the mouse button status of the mouse device - * @param callback A CallbackStatus containing the button status - * @param index Button ID to be updated - */ - void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the mouse wheel status of the mouse device - * @param callback A CallbackStatus containing the wheel status - * @param index wheel ID to be updated - */ - void SetMouseWheel(const Common::Input::CallbackStatus& callback, std::size_t index); - - /** - * Updates the mouse position status of the mouse device - * @param callback A CallbackStatus containing the position status - */ - void SetMousePosition(const Common::Input::CallbackStatus& callback); - - /** - * Triggers a callback that something has changed on the device status - * @param type Input type of the event to trigger - */ - void TriggerOnChange(DeviceTriggerType type); - - bool is_configuring{false}; - - KeyboardDevices keyboard_devices; - KeyboardModifierDevices keyboard_modifier_devices; - MouseButtonDevices mouse_button_devices; - MouseWheelDevices mouse_wheel_devices; - MouseStickDevice mouse_stick_device; - - mutable std::mutex mutex; - mutable std::mutex callback_mutex; - std::unordered_map<int, InterfaceUpdateCallback> callback_list; - int last_callback_key = 0; - - // Stores the current status of all external device input - DeviceStatus device_status; -}; - -} // namespace Core::HID diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp deleted file mode 100644 index 2cf25a870..000000000 --- a/src/core/hid/hid_core.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "core/hid/emulated_console.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/emulated_devices.h" -#include "core/hid/hid_core.h" -#include "core/hle/service/hid/hid_util.h" - -namespace Core::HID { - -HIDCore::HIDCore() - : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)}, - player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)}, - player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)}, - player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)}, - player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)}, - player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)}, - player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)}, - player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)}, - other{std::make_unique<EmulatedController>(NpadIdType::Other)}, - handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)}, - console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {} - -HIDCore::~HIDCore() = default; - -EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) { - switch (npad_id_type) { - case NpadIdType::Player1: - return player_1.get(); - case NpadIdType::Player2: - return player_2.get(); - case NpadIdType::Player3: - return player_3.get(); - case NpadIdType::Player4: - return player_4.get(); - case NpadIdType::Player5: - return player_5.get(); - case NpadIdType::Player6: - return player_6.get(); - case NpadIdType::Player7: - return player_7.get(); - case NpadIdType::Player8: - return player_8.get(); - case NpadIdType::Other: - return other.get(); - case NpadIdType::Handheld: - return handheld.get(); - case NpadIdType::Invalid: - default: - ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type); - return nullptr; - } -} - -const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const { - switch (npad_id_type) { - case NpadIdType::Player1: - return player_1.get(); - case NpadIdType::Player2: - return player_2.get(); - case NpadIdType::Player3: - return player_3.get(); - case NpadIdType::Player4: - return player_4.get(); - case NpadIdType::Player5: - return player_5.get(); - case NpadIdType::Player6: - return player_6.get(); - case NpadIdType::Player7: - return player_7.get(); - case NpadIdType::Player8: - return player_8.get(); - case NpadIdType::Other: - return other.get(); - case NpadIdType::Handheld: - return handheld.get(); - case NpadIdType::Invalid: - default: - ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type); - return nullptr; - } -} -EmulatedConsole* HIDCore::GetEmulatedConsole() { - return console.get(); -} - -const EmulatedConsole* HIDCore::GetEmulatedConsole() const { - return console.get(); -} - -EmulatedDevices* HIDCore::GetEmulatedDevices() { - return devices.get(); -} - -const EmulatedDevices* HIDCore::GetEmulatedDevices() const { - return devices.get(); -} - -EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { - return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); -} - -const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { - return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); -} - -void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { - supported_style_tag.raw = style_tag.raw; - player_1->SetSupportedNpadStyleTag(supported_style_tag); - player_2->SetSupportedNpadStyleTag(supported_style_tag); - player_3->SetSupportedNpadStyleTag(supported_style_tag); - player_4->SetSupportedNpadStyleTag(supported_style_tag); - player_5->SetSupportedNpadStyleTag(supported_style_tag); - player_6->SetSupportedNpadStyleTag(supported_style_tag); - player_7->SetSupportedNpadStyleTag(supported_style_tag); - player_8->SetSupportedNpadStyleTag(supported_style_tag); - other->SetSupportedNpadStyleTag(supported_style_tag); - handheld->SetSupportedNpadStyleTag(supported_style_tag); -} - -NpadStyleTag HIDCore::GetSupportedStyleTag() const { - return supported_style_tag; -} - -s8 HIDCore::GetPlayerCount() const { - s8 active_players = 0; - for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) { - const auto* const controller = GetEmulatedControllerByIndex(player_index); - if (controller->IsConnected()) { - active_players++; - } - } - return active_players; -} - -NpadIdType HIDCore::GetFirstNpadId() const { - for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) { - const auto* const controller = GetEmulatedControllerByIndex(player_index); - if (controller->IsConnected()) { - return controller->GetNpadIdType(); - } - } - return NpadIdType::Player1; -} - -NpadIdType HIDCore::GetFirstDisconnectedNpadId() const { - for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) { - const auto* const controller = GetEmulatedControllerByIndex(player_index); - if (!controller->IsConnected()) { - return controller->GetNpadIdType(); - } - } - return NpadIdType::Player1; -} - -void HIDCore::SetLastActiveController(NpadIdType npad_id) { - last_active_controller = npad_id; -} - -NpadIdType HIDCore::GetLastActiveController() const { - return last_active_controller; -} - -void HIDCore::EnableAllControllerConfiguration() { - player_1->EnableConfiguration(); - player_2->EnableConfiguration(); - player_3->EnableConfiguration(); - player_4->EnableConfiguration(); - player_5->EnableConfiguration(); - player_6->EnableConfiguration(); - player_7->EnableConfiguration(); - player_8->EnableConfiguration(); - other->EnableConfiguration(); - handheld->EnableConfiguration(); -} - -void HIDCore::DisableAllControllerConfiguration() { - player_1->DisableConfiguration(); - player_2->DisableConfiguration(); - player_3->DisableConfiguration(); - player_4->DisableConfiguration(); - player_5->DisableConfiguration(); - player_6->DisableConfiguration(); - player_7->DisableConfiguration(); - player_8->DisableConfiguration(); - other->DisableConfiguration(); - handheld->DisableConfiguration(); -} - -void HIDCore::ReloadInputDevices() { - player_1->ReloadFromSettings(); - player_2->ReloadFromSettings(); - player_3->ReloadFromSettings(); - player_4->ReloadFromSettings(); - player_5->ReloadFromSettings(); - player_6->ReloadFromSettings(); - player_7->ReloadFromSettings(); - player_8->ReloadFromSettings(); - other->ReloadFromSettings(); - handheld->ReloadFromSettings(); - console->ReloadFromSettings(); - devices->ReloadFromSettings(); -} - -void HIDCore::UnloadInputDevices() { - player_1->UnloadInput(); - player_2->UnloadInput(); - player_3->UnloadInput(); - player_4->UnloadInput(); - player_5->UnloadInput(); - player_6->UnloadInput(); - player_7->UnloadInput(); - player_8->UnloadInput(); - other->UnloadInput(); - handheld->UnloadInput(); - console->UnloadInput(); - devices->UnloadInput(); -} - -} // namespace Core::HID diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h deleted file mode 100644 index 80abab18b..000000000 --- a/src/core/hid/hid_core.h +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> - -#include "common/common_funcs.h" -#include "core/hid/hid_types.h" - -namespace Core::HID { -class EmulatedConsole; -class EmulatedController; -class EmulatedDevices; -} // namespace Core::HID - -namespace Core::HID { - -class HIDCore { -public: - explicit HIDCore(); - ~HIDCore(); - - YUZU_NON_COPYABLE(HIDCore); - YUZU_NON_MOVEABLE(HIDCore); - - EmulatedController* GetEmulatedController(NpadIdType npad_id_type); - const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const; - - EmulatedController* GetEmulatedControllerByIndex(std::size_t index); - const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const; - - EmulatedConsole* GetEmulatedConsole(); - const EmulatedConsole* GetEmulatedConsole() const; - - EmulatedDevices* GetEmulatedDevices(); - const EmulatedDevices* GetEmulatedDevices() const; - - void SetSupportedStyleTag(NpadStyleTag style_tag); - NpadStyleTag GetSupportedStyleTag() const; - - /// Counts the connected players from P1-P8 - s8 GetPlayerCount() const; - - /// Returns the first connected npad id - NpadIdType GetFirstNpadId() const; - - /// Returns the first disconnected npad id - NpadIdType GetFirstDisconnectedNpadId() const; - - /// Sets the npad id of the last active controller - void SetLastActiveController(NpadIdType npad_id); - - /// Returns the npad id of the last controller that pushed a button - NpadIdType GetLastActiveController() const; - - /// Sets all emulated controllers into configuring mode. - void EnableAllControllerConfiguration(); - - /// Sets all emulated controllers into normal mode. - void DisableAllControllerConfiguration(); - - /// Reloads all input devices from settings - void ReloadInputDevices(); - - /// Removes all callbacks from input common - void UnloadInputDevices(); - - /// Number of emulated controllers - static constexpr std::size_t available_controllers{10}; - -private: - std::unique_ptr<EmulatedController> player_1; - std::unique_ptr<EmulatedController> player_2; - std::unique_ptr<EmulatedController> player_3; - std::unique_ptr<EmulatedController> player_4; - std::unique_ptr<EmulatedController> player_5; - std::unique_ptr<EmulatedController> player_6; - std::unique_ptr<EmulatedController> player_7; - std::unique_ptr<EmulatedController> player_8; - std::unique_ptr<EmulatedController> other; - std::unique_ptr<EmulatedController> handheld; - std::unique_ptr<EmulatedConsole> console; - std::unique_ptr<EmulatedDevices> devices; - NpadStyleTag supported_style_tag{NpadStyleSet::All}; - NpadIdType last_active_controller{NpadIdType::Handheld}; -}; - -} // namespace Core::HID diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h deleted file mode 100644 index a81ed6af0..000000000 --- a/src/core/hid/hid_types.h +++ /dev/null @@ -1,736 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/point.h" -#include "common/uuid.h" -#include "common/vector_math.h" - -namespace Core::HID { - -enum class DeviceIndex : u8 { - Left = 0, - Right = 1, - None = 2, - MaxDeviceIndex = 3, -}; - -// This is nn::hid::NpadButton -enum class NpadButton : u64 { - None = 0, - A = 1U << 0, - B = 1U << 1, - X = 1U << 2, - Y = 1U << 3, - StickL = 1U << 4, - StickR = 1U << 5, - L = 1U << 6, - R = 1U << 7, - ZL = 1U << 8, - ZR = 1U << 9, - Plus = 1U << 10, - Minus = 1U << 11, - - Left = 1U << 12, - Up = 1U << 13, - Right = 1U << 14, - Down = 1U << 15, - - StickLLeft = 1U << 16, - StickLUp = 1U << 17, - StickLRight = 1U << 18, - StickLDown = 1U << 19, - - StickRLeft = 1U << 20, - StickRUp = 1U << 21, - StickRRight = 1U << 22, - StickRDown = 1U << 23, - - LeftSL = 1U << 24, - LeftSR = 1U << 25, - - RightSL = 1U << 26, - RightSR = 1U << 27, - - Palma = 1U << 28, - Verification = 1U << 29, - HandheldLeftB = 1U << 30, - LagonCLeft = 1U << 31, - LagonCUp = 1ULL << 32, - LagonCRight = 1ULL << 33, - LagonCDown = 1ULL << 34, - - All = 0xFFFFFFFFFFFFFFFFULL, -}; -DECLARE_ENUM_FLAG_OPERATORS(NpadButton); - -enum class KeyboardKeyIndex : u32 { - A = 4, - B = 5, - C = 6, - D = 7, - E = 8, - F = 9, - G = 10, - H = 11, - I = 12, - J = 13, - K = 14, - L = 15, - M = 16, - N = 17, - O = 18, - P = 19, - Q = 20, - R = 21, - S = 22, - T = 23, - U = 24, - V = 25, - W = 26, - X = 27, - Y = 28, - Z = 29, - D1 = 30, - D2 = 31, - D3 = 32, - D4 = 33, - D5 = 34, - D6 = 35, - D7 = 36, - D8 = 37, - D9 = 38, - D0 = 39, - Return = 40, - Escape = 41, - Backspace = 42, - Tab = 43, - Space = 44, - Minus = 45, - Plus = 46, - OpenBracket = 47, - CloseBracket = 48, - Pipe = 49, - Tilde = 50, - Semicolon = 51, - Quote = 52, - Backquote = 53, - Comma = 54, - Period = 55, - Slash = 56, - CapsLock = 57, - F1 = 58, - F2 = 59, - F3 = 60, - F4 = 61, - F5 = 62, - F6 = 63, - F7 = 64, - F8 = 65, - F9 = 66, - F10 = 67, - F11 = 68, - F12 = 69, - PrintScreen = 70, - ScrollLock = 71, - Pause = 72, - Insert = 73, - Home = 74, - PageUp = 75, - Delete = 76, - End = 77, - PageDown = 78, - RightArrow = 79, - LeftArrow = 80, - DownArrow = 81, - UpArrow = 82, - NumLock = 83, - NumPadDivide = 84, - NumPadMultiply = 85, - NumPadSubtract = 86, - NumPadAdd = 87, - NumPadEnter = 88, - NumPad1 = 89, - NumPad2 = 90, - NumPad3 = 91, - NumPad4 = 92, - NumPad5 = 93, - NumPad6 = 94, - NumPad7 = 95, - NumPad8 = 96, - NumPad9 = 97, - NumPad0 = 98, - NumPadDot = 99, - Backslash = 100, - Application = 101, - Power = 102, - NumPadEquals = 103, - F13 = 104, - F14 = 105, - F15 = 106, - F16 = 107, - F17 = 108, - F18 = 109, - F19 = 110, - F20 = 111, - F21 = 112, - F22 = 113, - F23 = 114, - F24 = 115, - NumPadComma = 133, - Ro = 135, - KatakanaHiragana = 136, - Yen = 137, - Henkan = 138, - Muhenkan = 139, - NumPadCommaPc98 = 140, - HangulEnglish = 144, - Hanja = 145, - Katakana = 146, - Hiragana = 147, - ZenkakuHankaku = 148, - LeftControl = 224, - LeftShift = 225, - LeftAlt = 226, - LeftGui = 227, - RightControl = 228, - RightShift = 229, - RightAlt = 230, - RightGui = 231, -}; - -// This is nn::hid::NpadIdType -enum class NpadIdType : u32 { - Player1 = 0x0, - Player2 = 0x1, - Player3 = 0x2, - Player4 = 0x3, - Player5 = 0x4, - Player6 = 0x5, - Player7 = 0x6, - Player8 = 0x7, - Other = 0x10, - Handheld = 0x20, - - Invalid = 0xFFFFFFFF, -}; - -enum class NpadInterfaceType : u8 { - Bluetooth = 1, - Rail = 2, - Usb = 3, - Embedded = 4, -}; - -// This is nn::hid::NpadStyleIndex -enum class NpadStyleIndex : u8 { - None = 0, - ProController = 3, - Handheld = 4, - HandheldNES = 4, - JoyconDual = 5, - JoyconLeft = 6, - JoyconRight = 7, - GameCube = 8, - Pokeball = 9, - NES = 10, - SNES = 12, - N64 = 13, - SegaGenesis = 14, - SystemExt = 32, - System = 33, - MaxNpadType = 34, -}; - -// This is nn::hid::NpadStyleSet -enum class NpadStyleSet : u32 { - None = 0, - Fullkey = 1U << 0, - Handheld = 1U << 1, - JoyDual = 1U << 2, - JoyLeft = 1U << 3, - JoyRight = 1U << 4, - Gc = 1U << 5, - Palma = 1U << 6, - Lark = 1U << 7, - HandheldLark = 1U << 8, - Lucia = 1U << 9, - Lagoon = 1U << 10, - Lager = 1U << 11, - SystemExt = 1U << 29, - System = 1U << 30, - - All = 0xFFFFFFFFU, -}; -static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); -DECLARE_ENUM_FLAG_OPERATORS(NpadStyleSet) - -// This is nn::hid::VibrationDevicePosition -enum class VibrationDevicePosition : u32 { - None = 0, - Left = 1, - Right = 2, -}; - -// This is nn::hid::VibrationDeviceType -enum class VibrationDeviceType : u32 { - Unknown = 0, - LinearResonantActuator = 1, - GcErm = 2, - N64 = 3, -}; - -// This is nn::hid::VibrationGcErmCommand -enum class VibrationGcErmCommand : u64 { - Stop = 0, - Start = 1, - StopHard = 2, -}; - -// This is nn::hid::GyroscopeZeroDriftMode -enum class GyroscopeZeroDriftMode : u32 { - Loose = 0, - Standard = 1, - Tight = 2, -}; - -// This is nn::settings::system::TouchScreenMode -enum class TouchScreenMode : u32 { - Stylus = 0, - Standard = 1, -}; - -// This is nn::hid::TouchScreenModeForNx -enum class TouchScreenModeForNx : u8 { - UseSystemSetting, - Finger, - Heat2, -}; - -// This is nn::hid::system::NpadBatteryLevel -enum class NpadBatteryLevel : u32 { - Empty, - Critical, - Low, - High, - Full, -}; - -// This is nn::hid::NpadStyleTag -struct NpadStyleTag { - union { - NpadStyleSet raw{}; - - BitField<0, 1, u32> fullkey; - BitField<1, 1, u32> handheld; - BitField<2, 1, u32> joycon_dual; - BitField<3, 1, u32> joycon_left; - BitField<4, 1, u32> joycon_right; - BitField<5, 1, u32> gamecube; - BitField<6, 1, u32> palma; - BitField<7, 1, u32> lark; - BitField<8, 1, u32> handheld_lark; - BitField<9, 1, u32> lucia; - BitField<10, 1, u32> lagoon; - BitField<11, 1, u32> lager; - BitField<29, 1, u32> system_ext; - BitField<30, 1, u32> system; - }; -}; -static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size"); - -// This is nn::hid::TouchAttribute -struct TouchAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> start_touch; - BitField<1, 1, u32> end_touch; - }; -}; -static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); - -// This is nn::hid::TouchState -struct TouchState { - u64 delta_time{}; - TouchAttribute attribute{}; - u32 finger{}; - Common::Point<u32> position{}; - u32 diameter_x{}; - u32 diameter_y{}; - u32 rotation_angle{}; -}; -static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); - -struct TouchFinger { - u64 last_touch{}; - Common::Point<float> position{}; - u32 id{}; - TouchAttribute attribute{}; - bool pressed{}; -}; - -// This is nn::hid::TouchScreenConfigurationForNx -struct TouchScreenConfigurationForNx { - TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; - INSERT_PADDING_BYTES(0xF); -}; -static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10, - "TouchScreenConfigurationForNx is an invalid size"); - -struct NpadColor { - u8 r{}; - u8 g{}; - u8 b{}; - u8 a{}; -}; -static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size"); - -// This is nn::hid::NpadControllerColor -struct NpadControllerColor { - NpadColor body{}; - NpadColor button{}; -}; -static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); - -// This is nn::hid::AnalogStickState -struct AnalogStickState { - s32 x{}; - s32 y{}; -}; -static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size"); - -// This is nn::hid::server::NpadGcTriggerState -struct NpadGcTriggerState { - s64 sampling_number{}; - s32 left{}; - s32 right{}; -}; -static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); - -// This is nn::hid::system::NpadPowerInfo -struct NpadPowerInfo { - bool is_powered{}; - bool is_charging{}; - INSERT_PADDING_BYTES(0x6); - NpadBatteryLevel battery_level{NpadBatteryLevel::Full}; -}; -static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); - -struct LedPattern { - explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { - position1.Assign(light1); - position2.Assign(light2); - position3.Assign(light3); - position4.Assign(light4); - } - union { - u64 raw{}; - BitField<0, 1, u64> position1; - BitField<1, 1, u64> position2; - BitField<2, 1, u64> position3; - BitField<3, 1, u64> position4; - }; -}; - -struct HomeButtonState { - union { - u64 raw{}; - - // Buttons - BitField<0, 1, u64> home; - }; -}; -static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size."); - -struct CaptureButtonState { - union { - u64 raw{}; - - // Buttons - BitField<0, 1, u64> capture; - }; -}; -static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size."); - -struct NpadButtonState { - union { - NpadButton raw{}; - - // Buttons - BitField<0, 1, u64> a; - BitField<1, 1, u64> b; - BitField<2, 1, u64> x; - BitField<3, 1, u64> y; - BitField<4, 1, u64> stick_l; - BitField<5, 1, u64> stick_r; - BitField<6, 1, u64> l; - BitField<7, 1, u64> r; - BitField<8, 1, u64> zl; - BitField<9, 1, u64> zr; - BitField<10, 1, u64> plus; - BitField<11, 1, u64> minus; - - // D-Pad - BitField<12, 1, u64> left; - BitField<13, 1, u64> up; - BitField<14, 1, u64> right; - BitField<15, 1, u64> down; - - // Left JoyStick - BitField<16, 1, u64> stick_l_left; - BitField<17, 1, u64> stick_l_up; - BitField<18, 1, u64> stick_l_right; - BitField<19, 1, u64> stick_l_down; - - // Right JoyStick - BitField<20, 1, u64> stick_r_left; - BitField<21, 1, u64> stick_r_up; - BitField<22, 1, u64> stick_r_right; - BitField<23, 1, u64> stick_r_down; - - BitField<24, 1, u64> left_sl; - BitField<25, 1, u64> left_sr; - - BitField<26, 1, u64> right_sl; - BitField<27, 1, u64> right_sr; - - BitField<28, 1, u64> palma; - BitField<29, 1, u64> verification; - BitField<30, 1, u64> handheld_left_b; - BitField<31, 1, u64> lagon_c_left; - BitField<32, 1, u64> lagon_c_up; - BitField<33, 1, u64> lagon_c_right; - BitField<34, 1, u64> lagon_c_down; - }; -}; -static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size."); - -// This is nn::hid::DebugPadButton -struct DebugPadButton { - union { - u32 raw{}; - BitField<0, 1, u32> a; - BitField<1, 1, u32> b; - BitField<2, 1, u32> x; - BitField<3, 1, u32> y; - BitField<4, 1, u32> l; - BitField<5, 1, u32> r; - BitField<6, 1, u32> zl; - BitField<7, 1, u32> zr; - BitField<8, 1, u32> plus; - BitField<9, 1, u32> minus; - BitField<10, 1, u32> d_left; - BitField<11, 1, u32> d_up; - BitField<12, 1, u32> d_right; - BitField<13, 1, u32> d_down; - }; -}; -static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size"); - -// This is nn::hid::ConsoleSixAxisSensorHandle -struct ConsoleSixAxisSensorHandle { - u8 unknown_1{}; - u8 unknown_2{}; - INSERT_PADDING_BYTES_NOINIT(2); -}; -static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4, - "ConsoleSixAxisSensorHandle is an invalid size"); - -// This is nn::hid::SixAxisSensorHandle -struct SixAxisSensorHandle { - NpadStyleIndex npad_type{NpadStyleIndex::None}; - u8 npad_id{}; - DeviceIndex device_index{DeviceIndex::None}; - INSERT_PADDING_BYTES_NOINIT(1); -}; -static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); - -// These parameters seem related to how much gyro/accelerometer is used -struct SixAxisSensorFusionParameters { - f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03 - f32 parameter2{0.4f}; // Default 0.4 -}; -static_assert(sizeof(SixAxisSensorFusionParameters) == 8, - "SixAxisSensorFusionParameters is an invalid size"); - -// This is nn::hid::server::SixAxisSensorProperties -struct SixAxisSensorProperties { - union { - u8 raw{}; - BitField<0, 1, u8> is_newly_assigned; - BitField<1, 1, u8> is_firmware_update_available; - }; -}; -static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size"); - -// This is nn::hid::SixAxisSensorCalibrationParameter -struct SixAxisSensorCalibrationParameter { - std::array<u8, 0x744> unknown_data{}; -}; -static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744, - "SixAxisSensorCalibrationParameter is an invalid size"); - -// This is nn::hid::SixAxisSensorIcInformation -struct SixAxisSensorIcInformation { - f32 angular_rate{2000.0f}; // dps - std::array<f32, 6> unknown_gyro_data1{ - -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f, - }; // dps - std::array<f32, 9> unknown_gyro_data2{ - 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, - }; - std::array<f32, 9> unknown_gyro_data3{ - 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, - }; - f32 acceleration_range{8.0f}; // g force - std::array<f32, 6> unknown_accel_data1{ - -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f, - }; // g force - std::array<f32, 9> unknown_accel_data2{ - 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, - }; - std::array<f32, 9> unknown_accel_data3{ - 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, - }; -}; -static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, - "SixAxisSensorIcInformation is an invalid size"); - -// This is nn::hid::SixAxisSensorAttribute -struct SixAxisSensorAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> is_connected; - BitField<1, 1, u32> is_interpolated; - }; -}; -static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); - -// This is nn::hid::SixAxisSensorState -struct SixAxisSensorState { - s64 delta_time{}; - s64 sampling_number{}; - Common::Vec3f accel{}; - Common::Vec3f gyro{}; - Common::Vec3f rotation{}; - std::array<Common::Vec3f, 3> orientation{}; - SixAxisSensorAttribute attribute{}; - INSERT_PADDING_BYTES(4); // Reserved -}; -static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); - -// This is nn::hid::VibrationDeviceHandle -struct VibrationDeviceHandle { - NpadStyleIndex npad_type{NpadStyleIndex::None}; - u8 npad_id{}; - DeviceIndex device_index{DeviceIndex::None}; - INSERT_PADDING_BYTES_NOINIT(1); -}; -static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size"); - -// This is nn::hid::VibrationValue -struct VibrationValue { - f32 low_amplitude{}; - f32 low_frequency{}; - f32 high_amplitude{}; - f32 high_frequency{}; -}; -static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); - -constexpr VibrationValue DEFAULT_VIBRATION_VALUE{ - .low_amplitude = 0.0f, - .low_frequency = 160.0f, - .high_amplitude = 0.0f, - .high_frequency = 320.0f, -}; - -// This is nn::hid::VibrationDeviceInfo -struct VibrationDeviceInfo { - VibrationDeviceType type{}; - VibrationDevicePosition position{}; -}; -static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); - -// This is nn::hid::KeyboardModifier -struct KeyboardModifier { - union { - u32 raw{}; - BitField<0, 1, u32> control; - BitField<1, 1, u32> shift; - BitField<2, 1, u32> left_alt; - BitField<3, 1, u32> right_alt; - BitField<4, 1, u32> gui; - BitField<8, 1, u32> caps_lock; - BitField<9, 1, u32> scroll_lock; - BitField<10, 1, u32> num_lock; - BitField<11, 1, u32> katakana; - BitField<12, 1, u32> hiragana; - }; -}; - -static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size"); - -// This is nn::hid::KeyboardAttribute -struct KeyboardAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> is_connected; - }; -}; -static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size"); - -// This is nn::hid::KeyboardKey -struct KeyboardKey { - // This should be a 256 bit flag - std::array<u8, 32> key{}; -}; -static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size"); - -// This is nn::hid::MouseButton -struct MouseButton { - union { - u32_le raw{}; - BitField<0, 1, u32> left; - BitField<1, 1, u32> right; - BitField<2, 1, u32> middle; - BitField<3, 1, u32> forward; - BitField<4, 1, u32> back; - }; -}; -static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size"); - -// This is nn::hid::MouseAttribute -struct MouseAttribute { - union { - u32 raw{}; - BitField<0, 1, u32> transferable; - BitField<1, 1, u32> is_connected; - }; -}; -static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size"); - -// This is nn::hid::detail::MouseState -struct MouseState { - s64 sampling_number{}; - s32 x{}; - s32 y{}; - s32 delta_x{}; - s32 delta_y{}; - // Axis Order in HW is switched for the wheel - s32 delta_wheel_y{}; - s32 delta_wheel_x{}; - MouseButton button{}; - MouseAttribute attribute{}; -}; -static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); - -struct UniquePadId { - u64 id; -}; -static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); - -} // namespace Core::HID diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp deleted file mode 100644 index a05716fd8..000000000 --- a/src/core/hid/input_converter.cpp +++ /dev/null @@ -1,436 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <random> - -#include "common/input.h" -#include "core/hid/input_converter.h" - -namespace Core::HID { - -Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) { - Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None}; - switch (callback.type) { - case Common::Input::InputType::Analog: - case Common::Input::InputType::Trigger: { - const auto value = TransformToTrigger(callback).analog.value; - battery = Common::Input::BatteryLevel::Empty; - if (value > 0.2f) { - battery = Common::Input::BatteryLevel::Critical; - } - if (value > 0.4f) { - battery = Common::Input::BatteryLevel::Low; - } - if (value > 0.6f) { - battery = Common::Input::BatteryLevel::Medium; - } - if (value > 0.8f) { - battery = Common::Input::BatteryLevel::Full; - } - if (value >= 0.95f) { - battery = Common::Input::BatteryLevel::Charging; - } - break; - } - case Common::Input::InputType::Button: - battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging - : Common::Input::BatteryLevel::Critical; - break; - case Common::Input::InputType::Battery: - battery = callback.battery_status; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type); - break; - } - - return battery; -} - -Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) { - Common::Input::ButtonStatus status{}; - switch (callback.type) { - case Common::Input::InputType::Analog: - status.value = TransformToTrigger(callback).pressed.value; - status.toggle = callback.analog_status.properties.toggle; - status.inverted = callback.analog_status.properties.inverted_button; - break; - case Common::Input::InputType::Trigger: - status.value = TransformToTrigger(callback).pressed.value; - break; - case Common::Input::InputType::Button: - status = callback.button_status; - break; - case Common::Input::InputType::Motion: - status.value = std::abs(callback.motion_status.gyro.x.raw_value) > 1.0f; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type); - break; - } - - if (status.inverted) { - status.value = !status.value; - } - - return status; -} - -Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) { - Common::Input::MotionStatus status{}; - switch (callback.type) { - case Common::Input::InputType::Button: { - Common::Input::AnalogProperties properties{ - .deadzone = 0.0f, - .range = 1.0f, - .offset = 0.0f, - }; - status.delta_timestamp = 1000; - status.force_update = true; - status.accel.x = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - status.accel.y = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - status.accel.z = { - .value = 0.0f, - .raw_value = -1.0f, - .properties = properties, - }; - status.gyro.x = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - status.gyro.y = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - status.gyro.z = { - .value = 0.0f, - .raw_value = 0.0f, - .properties = properties, - }; - if (TransformToButton(callback).value) { - std::random_device device; - std::mt19937 gen(device()); - std::uniform_int_distribution<s16> distribution(-5000, 5000); - status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; - } - break; - } - case Common::Input::InputType::Motion: - status = callback.motion_status; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type); - break; - } - SanitizeAnalog(status.accel.x, false); - SanitizeAnalog(status.accel.y, false); - SanitizeAnalog(status.accel.z, false); - SanitizeAnalog(status.gyro.x, false); - SanitizeAnalog(status.gyro.y, false); - SanitizeAnalog(status.gyro.z, false); - - return status; -} - -Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) { - Common::Input::StickStatus status{}; - - switch (callback.type) { - case Common::Input::InputType::Stick: - status = callback.stick_status; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type); - break; - } - - SanitizeStick(status.x, status.y, true); - const auto& properties_x = status.x.properties; - const auto& properties_y = status.y.properties; - const float x = status.x.value; - const float y = status.y.value; - - // Set directional buttons - status.right = x > properties_x.threshold; - status.left = x < -properties_x.threshold; - status.up = y > properties_y.threshold; - status.down = y < -properties_y.threshold; - - return status; -} - -Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) { - Common::Input::TouchStatus status{}; - - switch (callback.type) { - case Common::Input::InputType::Touch: - status = callback.touch_status; - break; - case Common::Input::InputType::Stick: - status.x = callback.stick_status.x; - status.y = callback.stick_status.y; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type); - break; - } - - SanitizeAnalog(status.x, true); - SanitizeAnalog(status.y, true); - float& x = status.x.value; - float& y = status.y.value; - - // Adjust if value is inverted - x = status.x.properties.inverted ? 1.0f + x : x; - y = status.y.properties.inverted ? 1.0f + y : y; - - // clamp value - x = std::clamp(x, 0.0f, 1.0f); - y = std::clamp(y, 0.0f, 1.0f); - - if (status.pressed.inverted) { - status.pressed.value = !status.pressed.value; - } - - return status; -} - -Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) { - Common::Input::TriggerStatus status{}; - float& raw_value = status.analog.raw_value; - bool calculate_button_value = true; - - switch (callback.type) { - case Common::Input::InputType::Analog: - status.analog.properties = callback.analog_status.properties; - raw_value = callback.analog_status.raw_value; - break; - case Common::Input::InputType::Button: - status.analog.properties.range = 1.0f; - status.analog.properties.inverted = callback.button_status.inverted; - raw_value = callback.button_status.value ? 1.0f : 0.0f; - break; - case Common::Input::InputType::Trigger: - status = callback.trigger_status; - calculate_button_value = false; - break; - case Common::Input::InputType::Motion: - status.analog.properties.range = 1.0f; - raw_value = callback.motion_status.accel.x.raw_value; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type); - break; - } - - SanitizeAnalog(status.analog, true); - const auto& properties = status.analog.properties; - float& value = status.analog.value; - - // Set button status - if (calculate_button_value) { - status.pressed.value = value > properties.threshold; - } - - // Adjust if value is inverted - value = properties.inverted ? 1.0f + value : value; - - // clamp value - value = std::clamp(value, 0.0f, 1.0f); - - return status; -} - -Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) { - Common::Input::AnalogStatus status{}; - - switch (callback.type) { - case Common::Input::InputType::Analog: - status.properties = callback.analog_status.properties; - status.raw_value = callback.analog_status.raw_value; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type); - break; - } - - SanitizeAnalog(status, false); - - // Adjust if value is inverted - status.value = status.properties.inverted ? -status.value : status.value; - - return status; -} - -Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) { - Common::Input::CameraStatus camera{}; - switch (callback.type) { - case Common::Input::InputType::IrSensor: - camera = { - .format = callback.camera_status, - .data = callback.raw_data, - }; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type); - break; - } - - return camera; -} - -Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) { - Common::Input::NfcStatus nfc{}; - switch (callback.type) { - case Common::Input::InputType::Nfc: - return callback.nfc_status; - default: - LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); - break; - } - - return nfc; -} - -Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) { - switch (callback.type) { - case Common::Input::InputType::Color: - return callback.color_status; - break; - default: - LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type); - return {}; - break; - } -} - -void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { - const auto& properties = analog.properties; - float& raw_value = analog.raw_value; - float& value = analog.value; - - if (!std::isnormal(raw_value)) { - raw_value = 0; - } - - // Apply center offset - raw_value -= properties.offset; - - // Set initial values to be formatted - value = raw_value; - - // Calculate vector size - const float r = std::abs(value); - - // Return zero if value is smaller than the deadzone - if (r <= properties.deadzone || properties.deadzone == 1.0f) { - analog.value = 0; - return; - } - - // Adjust range of value - const float deadzone_factor = - 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone); - value = value * deadzone_factor / properties.range; - - // Invert direction if needed - if (properties.inverted) { - value = -value; - } - - // Clamp value - if (clamp_value) { - value = std::clamp(value, -1.0f, 1.0f); - } -} - -void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y, - bool clamp_value) { - const auto& properties_x = analog_x.properties; - const auto& properties_y = analog_y.properties; - float& raw_x = analog_x.raw_value; - float& raw_y = analog_y.raw_value; - float& x = analog_x.value; - float& y = analog_y.value; - - if (!std::isnormal(raw_x)) { - raw_x = 0; - } - if (!std::isnormal(raw_y)) { - raw_y = 0; - } - - // Apply center offset - raw_x += properties_x.offset; - raw_y += properties_y.offset; - - // Apply X scale correction from offset - if (std::abs(properties_x.offset) < 0.75f) { - if (raw_x > 0) { - raw_x /= 1 + properties_x.offset; - } else { - raw_x /= 1 - properties_x.offset; - } - } - - // Apply Y scale correction from offset - if (std::abs(properties_y.offset) < 0.75f) { - if (raw_y > 0) { - raw_y /= 1 + properties_y.offset; - } else { - raw_y /= 1 - properties_y.offset; - } - } - - // Invert direction if needed - raw_x = properties_x.inverted ? -raw_x : raw_x; - raw_y = properties_y.inverted ? -raw_y : raw_y; - - // Set initial values to be formatted - x = raw_x; - y = raw_y; - - // Calculate vector size - float r = x * x + y * y; - r = std::sqrt(r); - - // TODO(German77): Use deadzone and range of both axis - - // Return zero if values are smaller than the deadzone - if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) { - x = 0; - y = 0; - return; - } - - // Adjust range of joystick - const float deadzone_factor = - 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone); - x = x * deadzone_factor / properties_x.range; - y = y * deadzone_factor / properties_x.range; - r = r * deadzone_factor / properties_x.range; - - // Normalize joystick - if (clamp_value && r > 1.0f) { - x /= r; - y /= r; - } -} - -} // namespace Core::HID diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h deleted file mode 100644 index c51c03e57..000000000 --- a/src/core/hid/input_converter.h +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace Common::Input { -struct CallbackStatus; -enum class BatteryLevel : u32; -using BatteryStatus = BatteryLevel; -struct AnalogStatus; -struct ButtonStatus; -struct MotionStatus; -struct StickStatus; -struct TouchStatus; -struct TriggerStatus; -}; // namespace Common::Input - -namespace Core::HID { - -/** - * Converts raw input data into a valid battery status. - * - * @param callback Supported callbacks: Analog, Battery, Trigger. - * @return A valid BatteryStatus object. - */ -Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid button status. Applies invert properties to the output. - * - * @param callback Supported callbacks: Analog, Button, Trigger. - * @return A valid TouchStatus object. - */ -Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid motion status. - * - * @param callback Supported callbacks: Motion. - * @return A valid TouchStatus object. - */ -Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert - * properties to the output. - * - * @param callback Supported callbacks: Stick. - * @return A valid StickStatus object. - */ -Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid touch status. - * - * @param callback Supported callbacks: Touch. - * @return A valid TouchStatus object. - */ -Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and - * invert properties to the output. Button status uses the threshold property if necessary. - * - * @param callback Supported callbacks: Analog, Button, Trigger. - * @return A valid TriggerStatus object. - */ -Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid analog status. Applies offset, deadzone, range and - * invert properties to the output. - * - * @param callback Supported callbacks: Analog. - * @return A valid AnalogStatus object. - */ -Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid camera status. - * - * @param callback Supported callbacks: Camera. - * @return A valid CameraObject object. - */ -Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid nfc status. - * - * @param callback Supported callbacks: Nfc. - * @return A valid data tag vector. - */ -Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw input data into a valid color status. - * - * @param callback Supported callbacks: Color. - * @return A valid Color object. - */ -Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback); - -/** - * Converts raw analog data into a valid analog value - * @param analog An analog object containing raw data and properties - * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. - */ -void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value); - -/** - * Converts raw stick data into a valid stick value - * @param analog_x raw analog data and properties for the x-axis - * @param analog_y raw analog data and properties for the y-axis - * @param clamp_value bool that determines if the value needs to be clamped into the unit circle. - */ -void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y, - bool clamp_value); - -} // namespace Core::HID diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp deleted file mode 100644 index 072f38a68..000000000 --- a/src/core/hid/input_interpreter.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/hid/hid_types.h" -#include "core/hid/input_interpreter.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid_server.h" -#include "core/hle/service/hid/resource_manager.h" -#include "core/hle/service/sm/sm.h" - -InputInterpreter::InputInterpreter(Core::System& system) - : npad{system.ServiceManager() - .GetService<Service::HID::IHidServer>("hid") - ->GetResourceManager() - ->GetNpad()} { - ResetButtonStates(); -} - -InputInterpreter::~InputInterpreter() = default; - -void InputInterpreter::PollInput() { - if (npad == nullptr) { - return; - } - const auto button_state = npad->GetAndResetPressState(); - - previous_index = current_index; - current_index = (current_index + 1) % button_states.size(); - - button_states[current_index] = button_state; -} - -void InputInterpreter::ResetButtonStates() { - previous_index = 0; - current_index = 0; - - button_states[0] = Core::HID::NpadButton::All; - - for (std::size_t i = 1; i < button_states.size(); ++i) { - button_states[i] = Core::HID::NpadButton::None; - } -} - -bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const { - return True(button_states[current_index] & button); -} - -bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const { - const bool current_press = True(button_states[current_index] & button); - const bool previous_press = True(button_states[previous_index] & button); - - return current_press && !previous_press; -} - -bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const { - Core::HID::NpadButton held_buttons{button_states[0]}; - - for (std::size_t i = 1; i < button_states.size(); ++i) { - held_buttons &= button_states[i]; - } - - return True(held_buttons & button); -} diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h deleted file mode 100644 index 3569aac93..000000000 --- a/src/core/hid/input_interpreter.h +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "common/common_types.h" - -namespace Core { -class System; -} - -namespace Core::HID { -enum class NpadButton : u64; -} - -namespace Service::HID { -class NPad; -} - -/** - * The InputInterpreter class interfaces with HID to retrieve button press states. - * Input is intended to be polled every 50ms so that a button is considered to be - * held down after 400ms has elapsed since the initial button press and subsequent - * repeated presses occur every 50ms. - */ -class InputInterpreter { -public: - explicit InputInterpreter(Core::System& system); - virtual ~InputInterpreter(); - - /// Gets a button state from HID and inserts it into the array of button states. - void PollInput(); - - /// Resets all the button states to their defaults. - void ResetButtonStates(); - - /** - * Checks whether the button is pressed. - * - * @param button The button to check. - * - * @returns True when the button is pressed. - */ - [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const; - - /** - * Checks whether any of the buttons in the parameter list is pressed. - * - * @tparam HIDButton The buttons to check. - * - * @returns True when at least one of the buttons is pressed. - */ - template <Core::HID::NpadButton... T> - [[nodiscard]] bool IsAnyButtonPressed() { - return (IsButtonPressed(T) || ...); - } - - /** - * The specified button is considered to be pressed once - * if it is currently pressed and not pressed previously. - * - * @param button The button to check. - * - * @returns True when the button is pressed once. - */ - [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const; - - /** - * Checks whether any of the buttons in the parameter list is pressed once. - * - * @tparam T The buttons to check. - * - * @returns True when at least one of the buttons is pressed once. - */ - template <Core::HID::NpadButton... T> - [[nodiscard]] bool IsAnyButtonPressedOnce() const { - return (IsButtonPressedOnce(T) || ...); - } - - /** - * The specified button is considered to be held down if it is pressed in all 9 button states. - * - * @param button The button to check. - * - * @returns True when the button is held down. - */ - [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const; - - /** - * Checks whether any of the buttons in the parameter list is held down. - * - * @tparam T The buttons to check. - * - * @returns True when at least one of the buttons is held down. - */ - template <Core::HID::NpadButton... T> - [[nodiscard]] bool IsAnyButtonHeld() const { - return (IsButtonHeld(T) || ...); - } - -private: - std::shared_ptr<Service::HID::NPad> npad; - - /// Stores 9 consecutive button states polled from HID. - std::array<Core::HID::NpadButton, 9> button_states{}; - - std::size_t previous_index{}; - std::size_t current_index{}; -}; diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h deleted file mode 100644 index 0d1bfe53f..000000000 --- a/src/core/hid/irs_types.h +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hid/hid_types.h" - -namespace Core::IrSensor { - -// This is nn::irsensor::CameraAmbientNoiseLevel -enum class CameraAmbientNoiseLevel : u32 { - Low, - Medium, - High, - Unknown3, // This level can't be reached -}; - -// This is nn::irsensor::CameraLightTarget -enum class CameraLightTarget : u32 { - AllLeds, - BrightLeds, - DimLeds, - None, -}; - -// This is nn::irsensor::PackedCameraLightTarget -enum class PackedCameraLightTarget : u8 { - AllLeds, - BrightLeds, - DimLeds, - None, -}; - -// This is nn::irsensor::AdaptiveClusteringMode -enum class AdaptiveClusteringMode : u32 { - StaticFov, - DynamicFov, -}; - -// This is nn::irsensor::AdaptiveClusteringTargetDistance -enum class AdaptiveClusteringTargetDistance : u32 { - Near, - Middle, - Far, -}; - -// This is nn::irsensor::ImageTransferProcessorFormat -enum class ImageTransferProcessorFormat : u32 { - Size320x240, - Size160x120, - Size80x60, - Size40x30, - Size20x15, -}; - -// This is nn::irsensor::PackedImageTransferProcessorFormat -enum class PackedImageTransferProcessorFormat : u8 { - Size320x240, - Size160x120, - Size80x60, - Size40x30, - Size20x15, -}; - -// This is nn::irsensor::IrCameraStatus -enum class IrCameraStatus : u32 { - Available, - Unsupported, - Unconnected, -}; - -// This is nn::irsensor::IrCameraInternalStatus -enum class IrCameraInternalStatus : u32 { - Stopped, - FirmwareUpdateNeeded, - Unknown2, - Unknown3, - Unknown4, - FirmwareVersionRequested, - FirmwareVersionIsInvalid, - Ready, - Setting, -}; - -// This is nn::irsensor::detail::StatusManager::IrSensorMode -enum class IrSensorMode : u64 { - None, - MomentProcessor, - ClusteringProcessor, - ImageTransferProcessor, - PointingProcessorMarker, - TeraPluginProcessor, - IrLedProcessor, -}; - -// This is nn::irsensor::ImageProcessorStatus -enum ImageProcessorStatus : u32 { - Stopped, - Running, -}; - -// This is nn::irsensor::HandAnalysisMode -enum class HandAnalysisMode : u32 { - None, - Silhouette, - Image, - SilhoueteAndImage, - SilhuetteOnly, -}; - -// This is nn::irsensor::IrSensorFunctionLevel -enum class IrSensorFunctionLevel : u8 { - unknown0, - unknown1, - unknown2, - unknown3, - unknown4, -}; - -// This is nn::irsensor::MomentProcessorPreprocess -enum class MomentProcessorPreprocess : u32 { - Unknown0, - Unknown1, -}; - -// This is nn::irsensor::PackedMomentProcessorPreprocess -enum class PackedMomentProcessorPreprocess : u8 { - Unknown0, - Unknown1, -}; - -// This is nn::irsensor::PointingStatus -enum class PointingStatus : u32 { - Unknown0, - Unknown1, -}; - -struct IrsRect { - s16 x; - s16 y; - s16 width; - s16 height; -}; - -struct IrsCentroid { - f32 x; - f32 y; -}; - -struct CameraConfig { - u64 exposure_time; - CameraLightTarget light_target; - u32 gain; - bool is_negative_used; - INSERT_PADDING_BYTES(7); -}; -static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size"); - -struct PackedCameraConfig { - u64 exposure_time; - PackedCameraLightTarget light_target; - u8 gain; - bool is_negative_used; - INSERT_PADDING_BYTES(5); -}; -static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size"); - -// This is nn::irsensor::IrCameraHandle -struct IrCameraHandle { - u8 npad_id{}; - Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); - -// This is nn::irsensor::PackedMcuVersion -struct PackedMcuVersion { - u16 major; - u16 minor; -}; -static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); - -// This is nn::irsensor::PackedMomentProcessorConfig -struct PackedMomentProcessorConfig { - PackedCameraConfig camera_config; - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; - PackedMomentProcessorPreprocess preprocess; - u8 preprocess_intensity_threshold; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, - "PackedMomentProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedClusteringProcessorConfig -struct PackedClusteringProcessorConfig { - PackedCameraConfig camera_config; - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; - u32 pixel_count_min; - u32 pixel_count_max; - u8 object_intensity_min; - bool is_external_light_filter_enabled; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28, - "PackedClusteringProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedImageTransferProcessorConfig -struct PackedImageTransferProcessorConfig { - PackedCameraConfig camera_config; - PackedMcuVersion required_mcu_version; - PackedImageTransferProcessorFormat format; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, - "PackedImageTransferProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedTeraPluginProcessorConfig -struct PackedTeraPluginProcessorConfig { - PackedMcuVersion required_mcu_version; - u8 mode; - u8 unknown_1; - u8 unknown_2; - u8 unknown_3; -}; -static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, - "PackedTeraPluginProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedPointingProcessorConfig -struct PackedPointingProcessorConfig { - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; -}; -static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, - "PackedPointingProcessorConfig is an invalid size"); - -// This is nn::irsensor::PackedFunctionLevel -struct PackedFunctionLevel { - IrSensorFunctionLevel function_level; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); - -// This is nn::irsensor::PackedImageTransferProcessorExConfig -struct PackedImageTransferProcessorExConfig { - PackedCameraConfig camera_config; - PackedMcuVersion required_mcu_version; - PackedImageTransferProcessorFormat origin_format; - PackedImageTransferProcessorFormat trimming_format; - u16 trimming_start_x; - u16 trimming_start_y; - bool is_external_light_filter_enabled; - INSERT_PADDING_BYTES(5); -}; -static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, - "PackedImageTransferProcessorExConfig is an invalid size"); - -// This is nn::irsensor::PackedIrLedProcessorConfig -struct PackedIrLedProcessorConfig { - PackedMcuVersion required_mcu_version; - u8 light_target; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, - "PackedIrLedProcessorConfig is an invalid size"); - -// This is nn::irsensor::HandAnalysisConfig -struct HandAnalysisConfig { - HandAnalysisMode mode; -}; -static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size"); - -// This is nn::irsensor::detail::ProcessorState contents are different for each processor -struct ProcessorState { - std::array<u8, 0xE20> processor_raw_data{}; -}; -static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size"); - -// This is nn::irsensor::detail::DeviceFormat -struct DeviceFormat { - Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected}; - Core::IrSensor::IrCameraInternalStatus camera_internal_status{ - Core::IrSensor::IrCameraInternalStatus::Ready}; - Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None}; - ProcessorState state{}; -}; -static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size"); - -// This is nn::irsensor::ImageTransferProcessorState -struct ImageTransferProcessorState { - u64 sampling_number; - Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; - INSERT_PADDING_BYTES(4); -}; -static_assert(sizeof(ImageTransferProcessorState) == 0x10, - "ImageTransferProcessorState is an invalid size"); - -} // namespace Core::IrSensor diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp deleted file mode 100644 index f56f2ae1d..000000000 --- a/src/core/hid/motion_input.cpp +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <cmath> - -#include "common/math_util.h" -#include "core/hid/motion_input.h" - -namespace Core::HID { - -MotionInput::MotionInput() { - // Initialize PID constants with default values - SetPID(0.3f, 0.005f, 0.0f); - SetGyroThreshold(ThresholdStandard); - ResetQuaternion(); - ResetRotations(); -} - -void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) { - kp = new_kp; - ki = new_ki; - kd = new_kd; -} - -void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { - accel = acceleration; - - accel.x = std::clamp(accel.x, -AccelMaxValue, AccelMaxValue); - accel.y = std::clamp(accel.y, -AccelMaxValue, AccelMaxValue); - accel.z = std::clamp(accel.z, -AccelMaxValue, AccelMaxValue); -} - -void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) { - gyro = gyroscope - gyro_bias; - - gyro.x = std::clamp(gyro.x, -GyroMaxValue, GyroMaxValue); - gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue); - gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue); - - // Auto adjust gyro_bias to minimize drift - if (!IsMoving(IsAtRestRelaxed)) { - gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f); - } - - // Adjust drift when calibration mode is enabled - if (calibration_mode) { - gyro_bias = (gyro_bias * 0.99f) + (gyroscope * 0.01f); - StopCalibration(); - } - - if (gyro.Length() < gyro_threshold * user_gyro_threshold) { - gyro = {}; - } else { - only_accelerometer = false; - } -} - -void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) { - quat = quaternion; -} - -void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) { - const float cr = std::cos(euler_angles.x * 0.5f); - const float sr = std::sin(euler_angles.x * 0.5f); - const float cp = std::cos(euler_angles.y * 0.5f); - const float sp = std::sin(euler_angles.y * 0.5f); - const float cy = std::cos(euler_angles.z * 0.5f); - const float sy = std::sin(euler_angles.z * 0.5f); - - quat.w = cr * cp * cy + sr * sp * sy; - quat.xyz.x = sr * cp * cy - cr * sp * sy; - quat.xyz.y = cr * sp * cy + sr * cp * sy; - quat.xyz.z = cr * cp * sy - sr * sp * cy; -} - -void MotionInput::SetGyroBias(const Common::Vec3f& bias) { - gyro_bias = bias; -} - -void MotionInput::SetGyroThreshold(f32 threshold) { - gyro_threshold = threshold; -} - -void MotionInput::SetUserGyroThreshold(f32 threshold) { - user_gyro_threshold = threshold / ThresholdStandard; -} - -void MotionInput::EnableReset(bool reset) { - reset_enabled = reset; -} - -void MotionInput::ResetRotations() { - rotations = {}; -} - -void MotionInput::ResetQuaternion() { - quat = {{0.0f, 0.0f, -1.0f}, 0.0f}; -} - -bool MotionInput::IsMoving(f32 sensitivity) const { - return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f; -} - -bool MotionInput::IsCalibrated(f32 sensitivity) const { - return real_error.Length() < sensitivity; -} - -void MotionInput::UpdateRotation(u64 elapsed_time) { - const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f; - if (sample_period > 0.1f) { - return; - } - rotations += gyro * sample_period; -} - -void MotionInput::Calibrate() { - calibration_mode = true; - calibration_counter = 0; -} - -void MotionInput::StopCalibration() { - if (calibration_counter++ > CalibrationSamples) { - calibration_mode = false; - ResetQuaternion(); - ResetRotations(); - } -} - -// Based on Madgwick's implementation of Mayhony's AHRS algorithm. -// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs -void MotionInput::UpdateOrientation(u64 elapsed_time) { - if (!IsCalibrated(0.1f)) { - ResetOrientation(); - } - // Short name local variable for readability - f32 q1 = quat.w; - f32 q2 = quat.xyz[0]; - f32 q3 = quat.xyz[1]; - f32 q4 = quat.xyz[2]; - const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f; - - // Ignore invalid elapsed time - if (sample_period > 0.1f) { - return; - } - - const auto normal_accel = accel.Normalized(); - auto rad_gyro = gyro * Common::PI * 2; - const f32 swap = rad_gyro.x; - rad_gyro.x = rad_gyro.y; - rad_gyro.y = -swap; - rad_gyro.z = -rad_gyro.z; - - // Clear gyro values if there is no gyro present - if (only_accelerometer) { - rad_gyro.x = 0; - rad_gyro.y = 0; - rad_gyro.z = 0; - } - - // Ignore drift correction if acceleration is not reliable - if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) { - const f32 ax = -normal_accel.x; - const f32 ay = normal_accel.y; - const f32 az = -normal_accel.z; - - // Estimated direction of gravity - const f32 vx = 2.0f * (q2 * q4 - q1 * q3); - const f32 vy = 2.0f * (q1 * q2 + q3 * q4); - const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; - - // Error is cross product between estimated direction and measured direction of gravity - const Common::Vec3f new_real_error = { - az * vx - ax * vz, - ay * vz - az * vy, - ax * vy - ay * vx, - }; - - derivative_error = new_real_error - real_error; - real_error = new_real_error; - - // Prevent integral windup - if (ki != 0.0f && !IsCalibrated(0.05f)) { - integral_error += real_error; - } else { - integral_error = {}; - } - - // Apply feedback terms - if (!only_accelerometer) { - rad_gyro += kp * real_error; - rad_gyro += ki * integral_error; - rad_gyro += kd * derivative_error; - } else { - // Give more weight to accelerometer values to compensate for the lack of gyro - rad_gyro += 35.0f * kp * real_error; - rad_gyro += 10.0f * ki * integral_error; - rad_gyro += 10.0f * kd * derivative_error; - - // Emulate gyro values for games that need them - gyro.x = -rad_gyro.y; - gyro.y = rad_gyro.x; - gyro.z = -rad_gyro.z; - UpdateRotation(elapsed_time); - } - } - - const f32 gx = rad_gyro.y; - const f32 gy = rad_gyro.x; - const f32 gz = rad_gyro.z; - - // Integrate rate of change of quaternion - const f32 pa = q2; - const f32 pb = q3; - const f32 pc = q4; - q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period); - q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period); - q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); - q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); - - quat.w = q1; - quat.xyz[0] = q2; - quat.xyz[1] = q3; - quat.xyz[2] = q4; - quat = quat.Normalized(); -} - -std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const { - const Common::Quaternion<float> quad{ - .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w}, - .w = -quat.xyz[2], - }; - const std::array<float, 16> matrix4x4 = quad.ToMatrix(); - - return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]), - Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]), - Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])}; -} - -Common::Vec3f MotionInput::GetAcceleration() const { - return accel; -} - -Common::Vec3f MotionInput::GetGyroscope() const { - return gyro; -} - -Common::Vec3f MotionInput::GetGyroBias() const { - return gyro_bias; -} - -Common::Quaternion<f32> MotionInput::GetQuaternion() const { - return quat; -} - -Common::Vec3f MotionInput::GetRotations() const { - return rotations; -} - -Common::Vec3f MotionInput::GetEulerAngles() const { - // roll (x-axis rotation) - const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z); - const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y); - - // pitch (y-axis rotation) - const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z)); - const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z)); - - // yaw (z-axis rotation) - const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y); - const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z); - - return { - std::atan2(sinr_cosp, cosr_cosp), - 2 * std::atan2(sinp, cosp) - Common::PI / 2, - std::atan2(siny_cosp, cosy_cosp), - }; -} - -void MotionInput::ResetOrientation() { - if (!reset_enabled || only_accelerometer) { - return; - } - if (!IsMoving(IsAtRestRelaxed) && accel.z <= -0.9f) { - ++reset_counter; - if (reset_counter > 900) { - quat.w = 0; - quat.xyz[0] = 0; - quat.xyz[1] = 0; - quat.xyz[2] = -1; - SetOrientationFromAccelerometer(); - integral_error = {}; - reset_counter = 0; - } - } else { - reset_counter = 0; - } -} - -void MotionInput::SetOrientationFromAccelerometer() { - int iterations = 0; - const f32 sample_period = 0.015f; - - const auto normal_accel = accel.Normalized(); - - while (!IsCalibrated(0.01f) && ++iterations < 100) { - // Short name local variable for readability - f32 q1 = quat.w; - f32 q2 = quat.xyz[0]; - f32 q3 = quat.xyz[1]; - f32 q4 = quat.xyz[2]; - - Common::Vec3f rad_gyro; - const f32 ax = -normal_accel.x; - const f32 ay = normal_accel.y; - const f32 az = -normal_accel.z; - - // Estimated direction of gravity - const f32 vx = 2.0f * (q2 * q4 - q1 * q3); - const f32 vy = 2.0f * (q1 * q2 + q3 * q4); - const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; - - // Error is cross product between estimated direction and measured direction of gravity - const Common::Vec3f new_real_error = { - az * vx - ax * vz, - ay * vz - az * vy, - ax * vy - ay * vx, - }; - - derivative_error = new_real_error - real_error; - real_error = new_real_error; - - rad_gyro += 10.0f * kp * real_error; - rad_gyro += 5.0f * ki * integral_error; - rad_gyro += 10.0f * kd * derivative_error; - - const f32 gx = rad_gyro.y; - const f32 gy = rad_gyro.x; - const f32 gz = rad_gyro.z; - - // Integrate rate of change of quaternion - const f32 pa = q2; - const f32 pb = q3; - const f32 pc = q4; - q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period); - q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period); - q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); - q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); - - quat.w = q1; - quat.xyz[0] = q2; - quat.xyz[1] = q3; - quat.xyz[2] = q4; - quat = quat.Normalized(); - } -} -} // namespace Core::HID diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h deleted file mode 100644 index 11678983d..000000000 --- a/src/core/hid/motion_input.h +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "common/quaternion.h" -#include "common/vector_math.h" - -namespace Core::HID { - -class MotionInput { -public: - static constexpr float ThresholdLoose = 0.01f; - static constexpr float ThresholdStandard = 0.007f; - static constexpr float ThresholdThight = 0.002f; - - static constexpr float IsAtRestRelaxed = 0.05f; - static constexpr float IsAtRestLoose = 0.02f; - static constexpr float IsAtRestStandard = 0.01f; - static constexpr float IsAtRestThight = 0.005f; - - static constexpr float GyroMaxValue = 5.0f; - static constexpr float AccelMaxValue = 7.0f; - - static constexpr std::size_t CalibrationSamples = 300; - - explicit MotionInput(); - - MotionInput(const MotionInput&) = default; - MotionInput& operator=(const MotionInput&) = default; - - MotionInput(MotionInput&&) = default; - MotionInput& operator=(MotionInput&&) = default; - - void SetPID(f32 new_kp, f32 new_ki, f32 new_kd); - void SetAcceleration(const Common::Vec3f& acceleration); - void SetGyroscope(const Common::Vec3f& gyroscope); - void SetQuaternion(const Common::Quaternion<f32>& quaternion); - void SetEulerAngles(const Common::Vec3f& euler_angles); - void SetGyroBias(const Common::Vec3f& bias); - void SetGyroThreshold(f32 threshold); - - /// Applies a modifier on top of the normal gyro threshold - void SetUserGyroThreshold(f32 threshold); - - void EnableReset(bool reset); - void ResetRotations(); - void ResetQuaternion(); - - void UpdateRotation(u64 elapsed_time); - void UpdateOrientation(u64 elapsed_time); - - void Calibrate(); - - [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const; - [[nodiscard]] Common::Vec3f GetAcceleration() const; - [[nodiscard]] Common::Vec3f GetGyroscope() const; - [[nodiscard]] Common::Vec3f GetGyroBias() const; - [[nodiscard]] Common::Vec3f GetRotations() const; - [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; - [[nodiscard]] Common::Vec3f GetEulerAngles() const; - - [[nodiscard]] bool IsMoving(f32 sensitivity) const; - [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; - -private: - void StopCalibration(); - void ResetOrientation(); - void SetOrientationFromAccelerometer(); - - // PID constants - f32 kp; - f32 ki; - f32 kd; - - // PID errors - Common::Vec3f real_error; - Common::Vec3f integral_error; - Common::Vec3f derivative_error; - - // Quaternion containing the device orientation - Common::Quaternion<f32> quat; - - // Number of full rotations in each axis - Common::Vec3f rotations; - - // Acceleration vector measurement in G force - Common::Vec3f accel; - - // Gyroscope vector measurement in radians/s. - Common::Vec3f gyro; - - // Vector to be subtracted from gyro measurements - Common::Vec3f gyro_bias; - - // Minimum gyro amplitude to detect if the device is moving - f32 gyro_threshold = 0.0f; - - // Multiplies gyro_threshold by this value - f32 user_gyro_threshold = 0.0f; - - // Number of invalid sequential data - u32 reset_counter = 0; - - // If the provided data is invalid the device will be autocalibrated - bool reset_enabled = true; - - // Use accelerometer values to calculate position - bool only_accelerometer = true; - - // When enabled it will aggressively adjust for gyro drift - bool calibration_mode = false; - - // Used to auto disable calibration mode - std::size_t calibration_counter = 0; -}; - -} // namespace Core::HID |