diff options
| author | Narr the Reg <juangerman-13@hotmail.com> | 2024-01-04 20:37:43 -0600 |
|---|---|---|
| committer | Narr the Reg <juangerman-13@hotmail.com> | 2024-01-05 11:41:15 -0600 |
| commit | ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch) | |
| tree | 3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/core/hid/emulated_controller.cpp | |
| parent | 92a331af76cba638f01490eeb0045ca4d6d27df7 (diff) | |
hid_core: Move hid to it's own subproject
Diffstat (limited to 'src/core/hid/emulated_controller.cpp')
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 1972 |
1 files changed, 0 insertions, 1972 deletions
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 |
