committed by
Narr the Reg
9 changed files with 2025 additions and 0 deletions
-
8src/core/CMakeLists.txt
-
208src/core/hid/emulated_console.cpp
-
142src/core/hid/emulated_console.h
-
745src/core/hid/emulated_controller.cpp
-
232src/core/hid/emulated_controller.h
-
349src/core/hid/emulated_devices.cpp
-
137src/core/hid/emulated_devices.h
-
144src/core/hid/hid_core.cpp
-
60src/core/hid/hid_core.h
@ -0,0 +1,208 @@ |
|||
// Copyright 2021 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included
|
|||
|
|||
#include <fmt/format.h>
|
|||
|
|||
#include "core/hid/emulated_console.h"
|
|||
#include "core/hid/input_converter.h"
|
|||
|
|||
namespace Core::HID { |
|||
EmulatedConsole::EmulatedConsole() {} |
|||
|
|||
EmulatedConsole::~EmulatedConsole() = default; |
|||
|
|||
void EmulatedConsole::ReloadFromSettings() { |
|||
// Using first motion device from player 1. No need to assign a special config at the moment
|
|||
const auto& player = Settings::values.players.GetValue()[0]; |
|||
motion_params = Common::ParamPackage(player.motions[0]); |
|||
|
|||
ReloadInput(); |
|||
} |
|||
|
|||
void EmulatedConsole::ReloadInput() { |
|||
motion_devices = Input::CreateDevice<Input::InputDevice>(motion_params); |
|||
if (motion_devices) { |
|||
Input::InputCallback motion_callback{ |
|||
[this](Input::CallbackStatus callback) { SetMotion(callback); }}; |
|||
motion_devices->SetCallback(motion_callback); |
|||
} |
|||
|
|||
// TODO: Fix this mess
|
|||
std::size_t index = 0; |
|||
const std::string mouse_device_string = |
|||
fmt::format("engine:mouse,axis_x:10,axis_y:11,button:{}", index); |
|||
touch_devices[index] = Input::CreateDeviceFromString<Input::InputDevice>(mouse_device_string); |
|||
Input::InputCallback trigger_callbackk{ |
|||
[this, index](Input::CallbackStatus callback) { SetTouch(callback, index); }}; |
|||
touch_devices[index]->SetCallback(trigger_callbackk); |
|||
|
|||
index++; |
|||
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; |
|||
for (const auto& config_entry : touch_buttons) { |
|||
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_button_params.Set("touch_id", static_cast<int>(index)); |
|||
LOG_ERROR(Common, "{} ", touch_button_params.Serialize()); |
|||
touch_devices[index] = |
|||
Input::CreateDeviceFromString<Input::InputDevice>(touch_button_params.Serialize()); |
|||
if (!touch_devices[index]) { |
|||
continue; |
|||
} |
|||
|
|||
Input::InputCallback trigger_callback{ |
|||
[this, index](Input::CallbackStatus callback) { SetTouch(callback, index); }}; |
|||
touch_devices[index]->SetCallback(trigger_callback); |
|||
index++; |
|||
} |
|||
} |
|||
|
|||
void EmulatedConsole::UnloadInput() { |
|||
motion_devices.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; |
|||
} |
|||
|
|||
void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { |
|||
motion_params = param; |
|||
ReloadInput(); |
|||
} |
|||
|
|||
void EmulatedConsole::SetMotion(Input::CallbackStatus callback) { |
|||
std::lock_guard 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) { |
|||
TriggerOnChange(ConsoleTriggerType::Motion); |
|||
return; |
|||
} |
|||
|
|||
auto& motion = console.motion_state; |
|||
motion.accel = emulated.GetAcceleration(); |
|||
motion.gyro = emulated.GetGyroscope(); |
|||
motion.rotation = emulated.GetGyroscope(); |
|||
motion.orientation = emulated.GetOrientation(); |
|||
motion.quaternion = emulated.GetQuaternion(); |
|||
motion.is_at_rest = emulated.IsMoving(motion_sensitivity); |
|||
|
|||
TriggerOnChange(ConsoleTriggerType::Motion); |
|||
} |
|||
|
|||
void EmulatedConsole::SetTouch(Input::CallbackStatus callback, [[maybe_unused]] std::size_t index) { |
|||
if (index >= console.touch_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard lock{mutex}; |
|||
|
|||
console.touch_values[index] = TransformToTouch(callback); |
|||
|
|||
if (is_configuring) { |
|||
TriggerOnChange(ConsoleTriggerType::Touch); |
|||
return; |
|||
} |
|||
|
|||
console.touch_state[index] = { |
|||
.position = {console.touch_values[index].x.value, console.touch_values[index].y.value}, |
|||
.id = console.touch_values[index].id, |
|||
.pressed = console.touch_values[index].pressed.value, |
|||
}; |
|||
|
|||
TriggerOnChange(ConsoleTriggerType::Touch); |
|||
} |
|||
|
|||
ConsoleMotionValues EmulatedConsole::GetMotionValues() const { |
|||
return console.motion_values; |
|||
} |
|||
|
|||
TouchValues EmulatedConsole::GetTouchValues() const { |
|||
return console.touch_values; |
|||
} |
|||
|
|||
ConsoleMotion EmulatedConsole::GetMotion() const { |
|||
return console.motion_state; |
|||
} |
|||
|
|||
TouchFingerState EmulatedConsole::GetTouch() const { |
|||
return console.touch_state; |
|||
} |
|||
|
|||
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { |
|||
for (const std::pair<int, ConsoleUpdateCallback> 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::lock_guard lock{mutex}; |
|||
callback_list.insert_or_assign(last_callback_key, update_callback); |
|||
return last_callback_key++; |
|||
} |
|||
|
|||
void EmulatedConsole::DeleteCallback(int key) { |
|||
std::lock_guard lock{mutex}; |
|||
if (!callback_list.contains(key)) { |
|||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); |
|||
return; |
|||
} |
|||
callback_list.erase(key); |
|||
} |
|||
} // namespace Core::HID
|
|||
@ -0,0 +1,142 @@ |
|||
// Copyright 2021 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
#include <mutex> |
|||
#include <unordered_map> |
|||
|
|||
#include "common/input.h" |
|||
#include "common/param_package.h" |
|||
#include "common/point.h" |
|||
#include "common/quaternion.h" |
|||
#include "common/settings.h" |
|||
#include "common/vector_math.h" |
|||
#include "core/hid/hid_types.h" |
|||
#include "core/hid/motion_input.h" |
|||
|
|||
namespace Core::HID { |
|||
|
|||
struct ConsoleMotionInfo { |
|||
Input::MotionStatus raw_status; |
|||
MotionInput emulated{}; |
|||
}; |
|||
|
|||
using ConsoleMotionDevices = std::unique_ptr<Input::InputDevice>; |
|||
using TouchDevices = std::array<std::unique_ptr<Input::InputDevice>, 16>; |
|||
|
|||
using ConsoleMotionParams = Common::ParamPackage; |
|||
using TouchParams = std::array<Common::ParamPackage, 16>; |
|||
|
|||
using ConsoleMotionValues = ConsoleMotionInfo; |
|||
using TouchValues = std::array<Input::TouchStatus, 16>; |
|||
|
|||
struct TouchFinger { |
|||
u64_le last_touch{}; |
|||
Common::Point<float> position{}; |
|||
u32_le id{}; |
|||
bool pressed{}; |
|||
Core::HID::TouchAttribute attribute{}; |
|||
}; |
|||
|
|||
struct ConsoleMotion { |
|||
bool is_at_rest{}; |
|||
Common::Vec3f accel{}; |
|||
Common::Vec3f gyro{}; |
|||
Common::Vec3f rotation{}; |
|||
std::array<Common::Vec3f, 3> orientation{}; |
|||
Common::Quaternion<f32> quaternion{}; |
|||
}; |
|||
|
|||
using TouchFingerState = std::array<TouchFinger, 16>; |
|||
|
|||
struct ConsoleStatus { |
|||
// Data from input_common |
|||
ConsoleMotionValues motion_values{}; |
|||
TouchValues touch_values{}; |
|||
|
|||
// Data for Nintendo devices; |
|||
ConsoleMotion motion_state{}; |
|||
TouchFingerState touch_state{}; |
|||
}; |
|||
|
|||
enum class ConsoleTriggerType { |
|||
Motion, |
|||
Touch, |
|||
All, |
|||
}; |
|||
|
|||
struct ConsoleUpdateCallback { |
|||
std::function<void(ConsoleTriggerType)> on_change; |
|||
}; |
|||
|
|||
class EmulatedConsole { |
|||
public: |
|||
/** |
|||
* TODO: Write description |
|||
* |
|||
* @param npad_id_type |
|||
*/ |
|||
explicit EmulatedConsole(); |
|||
~EmulatedConsole(); |
|||
|
|||
YUZU_NON_COPYABLE(EmulatedConsole); |
|||
YUZU_NON_MOVEABLE(EmulatedConsole); |
|||
|
|||
void ReloadFromSettings(); |
|||
void ReloadInput(); |
|||
void UnloadInput(); |
|||
|
|||
void EnableConfiguration(); |
|||
void DisableConfiguration(); |
|||
bool IsConfiguring() const; |
|||
void SaveCurrentConfig(); |
|||
void RestoreConfig(); |
|||
|
|||
Common::ParamPackage GetMotionParam() const; |
|||
|
|||
void SetMotionParam(Common::ParamPackage param); |
|||
|
|||
ConsoleMotionValues GetMotionValues() const; |
|||
TouchValues GetTouchValues() const; |
|||
|
|||
ConsoleMotion GetMotion() const; |
|||
TouchFingerState GetTouch() const; |
|||
|
|||
int SetCallback(ConsoleUpdateCallback update_callback); |
|||
void DeleteCallback(int key); |
|||
|
|||
private: |
|||
/** |
|||
* Sets the status of a button. Applies toggle properties to the output. |
|||
* |
|||
* @param A CallbackStatus and a button index number |
|||
*/ |
|||
void SetMotion(Input::CallbackStatus callback); |
|||
void SetTouch(Input::CallbackStatus callback, std::size_t index); |
|||
|
|||
/** |
|||
* Triggers a callback that something has changed |
|||
* |
|||
* @param Input type of the 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; |
|||
std::unordered_map<int, ConsoleUpdateCallback> callback_list; |
|||
int last_callback_key = 0; |
|||
ConsoleStatus console; |
|||
}; |
|||
|
|||
} // namespace Core::HID |
|||
@ -0,0 +1,745 @@ |
|||
// Copyright 2021 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included
|
|||
|
|||
#include <fmt/format.h>
|
|||
|
|||
#include "core/hid/emulated_controller.h"
|
|||
#include "core/hid/input_converter.h"
|
|||
|
|||
namespace Core::HID { |
|||
constexpr s32 HID_JOYSTICK_MAX = 0x7fff; |
|||
constexpr s32 HID_TRIGGER_MAX = 0x7fff; |
|||
|
|||
EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {} |
|||
|
|||
EmulatedController::~EmulatedController() = default; |
|||
|
|||
NpadType EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) { |
|||
switch (type) { |
|||
case Settings::ControllerType::ProController: |
|||
return NpadType::ProController; |
|||
case Settings::ControllerType::DualJoyconDetached: |
|||
return NpadType::JoyconDual; |
|||
case Settings::ControllerType::LeftJoycon: |
|||
return NpadType::JoyconLeft; |
|||
case Settings::ControllerType::RightJoycon: |
|||
return NpadType::JoyconRight; |
|||
case Settings::ControllerType::Handheld: |
|||
return NpadType::Handheld; |
|||
case Settings::ControllerType::GameCube: |
|||
return NpadType::GameCube; |
|||
default: |
|||
return NpadType::ProController; |
|||
} |
|||
} |
|||
|
|||
Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadType type) { |
|||
switch (type) { |
|||
case NpadType::ProController: |
|||
return Settings::ControllerType::ProController; |
|||
case NpadType::JoyconDual: |
|||
return Settings::ControllerType::DualJoyconDetached; |
|||
case NpadType::JoyconLeft: |
|||
return Settings::ControllerType::LeftJoycon; |
|||
case NpadType::JoyconRight: |
|||
return Settings::ControllerType::RightJoycon; |
|||
case NpadType::Handheld: |
|||
return Settings::ControllerType::Handheld; |
|||
case NpadType::GameCube: |
|||
return Settings::ControllerType::GameCube; |
|||
default: |
|||
return Settings::ControllerType::ProController; |
|||
} |
|||
} |
|||
|
|||
void EmulatedController::ReloadFromSettings() { |
|||
const auto player_index = 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]); |
|||
} |
|||
ReloadInput(); |
|||
} |
|||
|
|||
void EmulatedController::ReloadInput() { |
|||
const auto player_index = NpadIdTypeToIndex(npad_id_type); |
|||
const auto& player = Settings::values.players.GetValue()[player_index]; |
|||
const auto left_side = button_params[Settings::NativeButton::ZL]; |
|||
const auto right_side = button_params[Settings::NativeButton::ZR]; |
|||
|
|||
std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, |
|||
button_params.begin() + Settings::NativeButton::BUTTON_NS_END, |
|||
button_devices.begin(), Input::CreateDevice<Input::InputDevice>); |
|||
std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, |
|||
stick_params.begin() + Settings::NativeAnalog::STICK_HID_END, |
|||
stick_devices.begin(), Input::CreateDevice<Input::InputDevice>); |
|||
std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, |
|||
motion_params.begin() + Settings::NativeMotion::MOTION_HID_END, |
|||
motion_devices.begin(), Input::CreateDevice<Input::InputDevice>); |
|||
|
|||
trigger_devices[0] = |
|||
Input::CreateDevice<Input::InputDevice>(button_params[Settings::NativeButton::ZL]); |
|||
trigger_devices[1] = |
|||
Input::CreateDevice<Input::InputDevice>(button_params[Settings::NativeButton::ZR]); |
|||
|
|||
controller.colors_state.left = { |
|||
.body = player.body_color_left, |
|||
.button = player.button_color_left, |
|||
}; |
|||
|
|||
controller.colors_state.right = { |
|||
.body = player.body_color_right, |
|||
.button = player.button_color_right, |
|||
}; |
|||
|
|||
controller.colors_state.fullkey = controller.colors_state.left; |
|||
|
|||
battery_devices[0] = Input::CreateDevice<Input::InputDevice>(left_side); |
|||
battery_devices[1] = Input::CreateDevice<Input::InputDevice>(right_side); |
|||
|
|||
for (std::size_t index = 0; index < button_devices.size(); ++index) { |
|||
if (!button_devices[index]) { |
|||
continue; |
|||
} |
|||
Input::InputCallback button_callback{ |
|||
[this, index](Input::CallbackStatus callback) { SetButton(callback, index); }}; |
|||
button_devices[index]->SetCallback(button_callback); |
|||
} |
|||
|
|||
for (std::size_t index = 0; index < stick_devices.size(); ++index) { |
|||
if (!stick_devices[index]) { |
|||
continue; |
|||
} |
|||
Input::InputCallback stick_callback{ |
|||
[this, index](Input::CallbackStatus callback) { SetStick(callback, index); }}; |
|||
stick_devices[index]->SetCallback(stick_callback); |
|||
} |
|||
|
|||
for (std::size_t index = 0; index < trigger_devices.size(); ++index) { |
|||
if (!trigger_devices[index]) { |
|||
continue; |
|||
} |
|||
Input::InputCallback trigger_callback{ |
|||
[this, index](Input::CallbackStatus callback) { SetTrigger(callback, index); }}; |
|||
trigger_devices[index]->SetCallback(trigger_callback); |
|||
} |
|||
|
|||
for (std::size_t index = 0; index < battery_devices.size(); ++index) { |
|||
if (!battery_devices[index]) { |
|||
continue; |
|||
} |
|||
Input::InputCallback battery_callback{ |
|||
[this, index](Input::CallbackStatus callback) { SetBattery(callback, index); }}; |
|||
battery_devices[index]->SetCallback(battery_callback); |
|||
} |
|||
|
|||
for (std::size_t index = 0; index < motion_devices.size(); ++index) { |
|||
if (!motion_devices[index]) { |
|||
continue; |
|||
} |
|||
Input::InputCallback motion_callback{ |
|||
[this, index](Input::CallbackStatus callback) { SetMotion(callback, index); }}; |
|||
motion_devices[index]->SetCallback(motion_callback); |
|||
} |
|||
|
|||
SetNpadType(MapSettingsTypeToNPad(player.controller_type)); |
|||
|
|||
if (player.connected) { |
|||
Connect(); |
|||
} else { |
|||
Disconnect(); |
|||
} |
|||
} |
|||
|
|||
void EmulatedController::UnloadInput() { |
|||
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(); |
|||
} |
|||
} |
|||
|
|||
void EmulatedController::EnableConfiguration() { |
|||
is_configuring = true; |
|||
SaveCurrentConfig(); |
|||
} |
|||
|
|||
void EmulatedController::DisableConfiguration() { |
|||
is_configuring = false; |
|||
} |
|||
|
|||
bool EmulatedController::IsConfiguring() const { |
|||
return is_configuring; |
|||
} |
|||
|
|||
void EmulatedController::SaveCurrentConfig() { |
|||
if (!is_configuring) { |
|||
return; |
|||
} |
|||
|
|||
const auto player_index = NpadIdTypeToIndex(npad_id_type); |
|||
auto& player = Settings::values.players.GetValue()[player_index]; |
|||
|
|||
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(); |
|||
} |
|||
} |
|||
|
|||
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(), [param](const Common::ParamPackage param_) { |
|||
return param.Get("engine", "") == param_.Get("engine", "") && |
|||
param.Get("guid", "") == param_.Get("guid", "") && |
|||
param.Get("port", "") == param_.Get("port", ""); |
|||
}); |
|||
if (devices_it != devices.end()) { |
|||
continue; |
|||
} |
|||
Common::ParamPackage device{}; |
|||
device.Set("engine", param.Get("engine", "")); |
|||
device.Set("guid", param.Get("guid", "")); |
|||
device.Set("port", param.Get("port", "")); |
|||
devices.push_back(device); |
|||
} |
|||
|
|||
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(), [param](const Common::ParamPackage param_) { |
|||
return param.Get("engine", "") == param_.Get("engine", "") && |
|||
param.Get("guid", "") == param_.Get("guid", "") && |
|||
param.Get("port", "") == param_.Get("port", ""); |
|||
}); |
|||
if (devices_it != devices.end()) { |
|||
continue; |
|||
} |
|||
Common::ParamPackage device{}; |
|||
device.Set("engine", param.Get("engine", "")); |
|||
device.Set("guid", param.Get("guid", "")); |
|||
device.Set("port", param.Get("port", "")); |
|||
devices.push_back(device); |
|||
} |
|||
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] = param; |
|||
ReloadInput(); |
|||
} |
|||
|
|||
void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) { |
|||
if (index >= stick_params.size()) { |
|||
return; |
|||
} |
|||
stick_params[index] = param; |
|||
ReloadInput(); |
|||
} |
|||
|
|||
void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) { |
|||
if (index >= motion_params.size()) { |
|||
return; |
|||
} |
|||
motion_params[index] = param; |
|||
ReloadInput(); |
|||
} |
|||
|
|||
void EmulatedController::SetButton(Input::CallbackStatus callback, std::size_t index) { |
|||
if (index >= controller.button_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard lock{mutex}; |
|||
bool value_changed = false; |
|||
const auto new_status = TransformToButton(callback); |
|||
auto& current_status = controller.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) { |
|||
controller.npad_button_state.raw = NpadButton::None; |
|||
controller.debug_pad_button_state.raw = 0; |
|||
TriggerOnChange(ControllerTriggerType::Button); |
|||
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::SL: |
|||
controller.npad_button_state.left_sl.Assign(current_status.value); |
|||
controller.npad_button_state.right_sl.Assign(current_status.value); |
|||
break; |
|||
case Settings::NativeButton::SR: |
|||
controller.npad_button_state.left_sr.Assign(current_status.value); |
|||
controller.npad_button_state.right_sr.Assign(current_status.value); |
|||
break; |
|||
case Settings::NativeButton::Home: |
|||
case Settings::NativeButton::Screenshot: |
|||
break; |
|||
} |
|||
TriggerOnChange(ControllerTriggerType::Button); |
|||
} |
|||
|
|||
void EmulatedController::SetStick(Input::CallbackStatus callback, std::size_t index) { |
|||
if (index >= controller.stick_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard lock{mutex}; |
|||
controller.stick_values[index] = TransformToStick(callback); |
|||
|
|||
if (is_configuring) { |
|||
controller.analog_stick_state.left = {}; |
|||
controller.analog_stick_state.right = {}; |
|||
TriggerOnChange(ControllerTriggerType::Stick); |
|||
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; |
|||
} |
|||
|
|||
TriggerOnChange(ControllerTriggerType::Stick); |
|||
} |
|||
|
|||
void EmulatedController::SetTrigger(Input::CallbackStatus callback, std::size_t index) { |
|||
if (index >= controller.trigger_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard lock{mutex}; |
|||
controller.trigger_values[index] = TransformToTrigger(callback); |
|||
|
|||
if (is_configuring) { |
|||
controller.gc_trigger_state.left = 0; |
|||
controller.gc_trigger_state.right = 0; |
|||
TriggerOnChange(ControllerTriggerType::Trigger); |
|||
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); |
|||
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); |
|||
break; |
|||
} |
|||
|
|||
TriggerOnChange(ControllerTriggerType::Trigger); |
|||
} |
|||
|
|||
void EmulatedController::SetMotion(Input::CallbackStatus callback, std::size_t index) { |
|||
if (index >= controller.motion_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard 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.UpdateRotation(raw_status.delta_timestamp); |
|||
emulated.UpdateOrientation(raw_status.delta_timestamp); |
|||
|
|||
if (is_configuring) { |
|||
TriggerOnChange(ControllerTriggerType::Motion); |
|||
return; |
|||
} |
|||
|
|||
auto& motion = controller.motion_state[index]; |
|||
motion.accel = emulated.GetAcceleration(); |
|||
motion.gyro = emulated.GetGyroscope(); |
|||
motion.rotation = emulated.GetGyroscope(); |
|||
motion.orientation = emulated.GetOrientation(); |
|||
motion.is_at_rest = emulated.IsMoving(motion_sensitivity); |
|||
|
|||
TriggerOnChange(ControllerTriggerType::Motion); |
|||
} |
|||
|
|||
void EmulatedController::SetBattery(Input::CallbackStatus callback, std::size_t index) { |
|||
if (index >= controller.battery_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard lock{mutex}; |
|||
controller.battery_values[index] = TransformToBattery(callback); |
|||
|
|||
if (is_configuring) { |
|||
TriggerOnChange(ControllerTriggerType::Battery); |
|||
return; |
|||
} |
|||
|
|||
bool is_charging = false; |
|||
bool is_powered = false; |
|||
BatteryLevel battery_level = 0; |
|||
switch (controller.battery_values[index]) { |
|||
case Input::BatteryLevel::Charging: |
|||
is_charging = true; |
|||
is_powered = true; |
|||
battery_level = 6; |
|||
break; |
|||
case Input::BatteryLevel::Medium: |
|||
battery_level = 6; |
|||
break; |
|||
case Input::BatteryLevel::Low: |
|||
battery_level = 4; |
|||
break; |
|||
case Input::BatteryLevel::Critical: |
|||
battery_level = 2; |
|||
break; |
|||
case Input::BatteryLevel::Empty: |
|||
battery_level = 0; |
|||
break; |
|||
case Input::BatteryLevel::Full: |
|||
default: |
|||
is_powered = true; |
|||
battery_level = 8; |
|||
break; |
|||
} |
|||
|
|||
switch (index) { |
|||
case 0: |
|||
controller.battery_state.left = { |
|||
.is_powered = is_powered, |
|||
.is_charging = is_charging, |
|||
.battery_level = battery_level, |
|||
}; |
|||
break; |
|||
case 1: |
|||
controller.battery_state.right = { |
|||
.is_powered = is_powered, |
|||
.is_charging = is_charging, |
|||
.battery_level = battery_level, |
|||
}; |
|||
break; |
|||
case 2: |
|||
controller.battery_state.dual = { |
|||
.is_powered = is_powered, |
|||
.is_charging = is_charging, |
|||
.battery_level = battery_level, |
|||
}; |
|||
break; |
|||
} |
|||
TriggerOnChange(ControllerTriggerType::Battery); |
|||
} |
|||
|
|||
bool EmulatedController::SetVibration([[maybe_unused]] std::size_t device_index, |
|||
[[maybe_unused]] VibrationValue vibration) { |
|||
return false; |
|||
} |
|||
|
|||
int EmulatedController::TestVibration(std::size_t device_index) { |
|||
return 1; |
|||
} |
|||
|
|||
void EmulatedController::Connect() { |
|||
std::lock_guard lock{mutex}; |
|||
if (is_connected) { |
|||
LOG_WARNING(Service_HID, "Tried to turn on a connected controller {}", npad_id_type); |
|||
return; |
|||
} |
|||
is_connected = true; |
|||
TriggerOnChange(ControllerTriggerType::Connected); |
|||
} |
|||
|
|||
void EmulatedController::Disconnect() { |
|||
std::lock_guard lock{mutex}; |
|||
if (!is_connected) { |
|||
LOG_WARNING(Service_HID, "Tried to turn off a disconnected controller {}", npad_id_type); |
|||
return; |
|||
} |
|||
is_connected = false; |
|||
TriggerOnChange(ControllerTriggerType::Disconnected); |
|||
} |
|||
|
|||
bool EmulatedController::IsConnected() const { |
|||
return is_connected; |
|||
} |
|||
|
|||
bool EmulatedController::IsVibrationEnabled() const { |
|||
return is_vibration_enabled; |
|||
} |
|||
|
|||
NpadIdType EmulatedController::GetNpadIdType() const { |
|||
return npad_id_type; |
|||
} |
|||
|
|||
NpadType EmulatedController::GetNpadType() const { |
|||
return npad_type; |
|||
} |
|||
|
|||
void EmulatedController::SetNpadType(NpadType npad_type_) { |
|||
std::lock_guard lock{mutex}; |
|||
if (npad_type == npad_type_) { |
|||
return; |
|||
} |
|||
npad_type = npad_type_; |
|||
TriggerOnChange(ControllerTriggerType::Type); |
|||
} |
|||
|
|||
ButtonValues EmulatedController::GetButtonsValues() const { |
|||
return controller.button_values; |
|||
} |
|||
|
|||
SticksValues EmulatedController::GetSticksValues() const { |
|||
return controller.stick_values; |
|||
} |
|||
|
|||
TriggerValues EmulatedController::GetTriggersValues() const { |
|||
return controller.trigger_values; |
|||
} |
|||
|
|||
ControllerMotionValues EmulatedController::GetMotionValues() const { |
|||
return controller.motion_values; |
|||
} |
|||
|
|||
ColorValues EmulatedController::GetColorsValues() const { |
|||
return controller.color_values; |
|||
} |
|||
|
|||
BatteryValues EmulatedController::GetBatteryValues() const { |
|||
return controller.battery_values; |
|||
} |
|||
|
|||
NpadButtonState EmulatedController::GetNpadButtons() const { |
|||
if (is_configuring) { |
|||
return {}; |
|||
} |
|||
return controller.npad_button_state; |
|||
} |
|||
|
|||
DebugPadButton EmulatedController::GetDebugPadButtons() const { |
|||
if (is_configuring) { |
|||
return {}; |
|||
} |
|||
return controller.debug_pad_button_state; |
|||
} |
|||
|
|||
AnalogSticks EmulatedController::GetSticks() const { |
|||
if (is_configuring) { |
|||
return {}; |
|||
} |
|||
return controller.analog_stick_state; |
|||
} |
|||
|
|||
NpadGcTriggerState EmulatedController::GetTriggers() const { |
|||
if (is_configuring) { |
|||
return {}; |
|||
} |
|||
return controller.gc_trigger_state; |
|||
} |
|||
|
|||
MotionState EmulatedController::GetMotions() const { |
|||
return controller.motion_state; |
|||
} |
|||
|
|||
ControllerColors EmulatedController::GetColors() const { |
|||
return controller.colors_state; |
|||
} |
|||
|
|||
BatteryLevelState EmulatedController::GetBattery() const { |
|||
return controller.battery_state; |
|||
} |
|||
|
|||
void EmulatedController::TriggerOnChange(ControllerTriggerType type) { |
|||
for (const std::pair<int, ControllerUpdateCallback> poller_pair : callback_list) { |
|||
const ControllerUpdateCallback& poller = poller_pair.second; |
|||
if (poller.on_change) { |
|||
poller.on_change(type); |
|||
} |
|||
} |
|||
} |
|||
|
|||
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { |
|||
std::lock_guard lock{mutex}; |
|||
callback_list.insert_or_assign(last_callback_key, update_callback); |
|||
return last_callback_key++; |
|||
} |
|||
|
|||
void EmulatedController::DeleteCallback(int key) { |
|||
std::lock_guard lock{mutex}; |
|||
if (!callback_list.contains(key)) { |
|||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); |
|||
return; |
|||
} |
|||
callback_list.erase(key); |
|||
} |
|||
} // namespace Core::HID
|
|||
@ -0,0 +1,232 @@ |
|||
// Copyright 2021 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
#include <mutex> |
|||
#include <unordered_map> |
|||
|
|||
#include "common/input.h" |
|||
#include "common/param_package.h" |
|||
#include "common/point.h" |
|||
#include "common/quaternion.h" |
|||
#include "common/settings.h" |
|||
#include "common/vector_math.h" |
|||
#include "core/hid/hid_types.h" |
|||
#include "core/hid/motion_input.h" |
|||
|
|||
namespace Core::HID { |
|||
|
|||
struct ControllerMotionInfo { |
|||
Input::MotionStatus raw_status; |
|||
MotionInput emulated{}; |
|||
}; |
|||
|
|||
using ButtonDevices = |
|||
std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeButton::NumButtons>; |
|||
using StickDevices = |
|||
std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>; |
|||
using ControllerMotionDevices = |
|||
std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeMotion::NumMotions>; |
|||
using TriggerDevices = |
|||
std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; |
|||
using BatteryDevices = std::array<std::unique_ptr<Input::InputDevice>, 2>; |
|||
|
|||
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 BatteryParams = std::array<Common::ParamPackage, 2>; |
|||
|
|||
using ButtonValues = std::array<Input::ButtonStatus, Settings::NativeButton::NumButtons>; |
|||
using SticksValues = std::array<Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; |
|||
using TriggerValues = std::array<Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>; |
|||
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>; |
|||
using ColorValues = std::array<Input::BodyColorStatus, 3>; |
|||
using BatteryValues = std::array<Input::BatteryStatus, 3>; |
|||
using VibrationValues = std::array<Input::VibrationStatus, 2>; |
|||
|
|||
struct AnalogSticks { |
|||
AnalogStickState left; |
|||
AnalogStickState right; |
|||
}; |
|||
|
|||
struct ControllerColors { |
|||
NpadControllerColor fullkey; |
|||
NpadControllerColor left; |
|||
NpadControllerColor right; |
|||
}; |
|||
|
|||
struct BatteryLevelState { |
|||
NpadPowerInfo dual; |
|||
NpadPowerInfo left; |
|||
NpadPowerInfo right; |
|||
}; |
|||
|
|||
struct ControllerMotion { |
|||
bool is_at_rest; |
|||
Common::Vec3f accel{}; |
|||
Common::Vec3f gyro{}; |
|||
Common::Vec3f rotation{}; |
|||
std::array<Common::Vec3f, 3> orientation{}; |
|||
}; |
|||
|
|||
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{}; |
|||
|
|||
// Data for Nintendo devices |
|||
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{}; |
|||
}; |
|||
enum class ControllerTriggerType { |
|||
Button, |
|||
Stick, |
|||
Trigger, |
|||
Motion, |
|||
Color, |
|||
Battery, |
|||
Vibration, |
|||
Connected, |
|||
Disconnected, |
|||
Type, |
|||
All, |
|||
}; |
|||
|
|||
struct ControllerUpdateCallback { |
|||
std::function<void(ControllerTriggerType)> on_change; |
|||
}; |
|||
|
|||
class EmulatedController { |
|||
public: |
|||
/** |
|||
* TODO: Write description |
|||
* |
|||
* @param npad_id_type |
|||
*/ |
|||
explicit EmulatedController(NpadIdType npad_id_type_); |
|||
~EmulatedController(); |
|||
|
|||
YUZU_NON_COPYABLE(EmulatedController); |
|||
YUZU_NON_MOVEABLE(EmulatedController); |
|||
|
|||
static NpadType MapSettingsTypeToNPad(Settings::ControllerType type); |
|||
static Settings::ControllerType MapNPadToSettingsType(NpadType type); |
|||
|
|||
/// Gets the NpadIdType for this controller. |
|||
NpadIdType GetNpadIdType() const; |
|||
|
|||
/// Sets the NpadType for this controller. |
|||
void SetNpadType(NpadType npad_type_); |
|||
|
|||
/// Gets the NpadType for this controller. |
|||
NpadType GetNpadType() const; |
|||
|
|||
void Connect(); |
|||
void Disconnect(); |
|||
|
|||
bool IsConnected() const; |
|||
bool IsVibrationEnabled() const; |
|||
|
|||
void ReloadFromSettings(); |
|||
void ReloadInput(); |
|||
void UnloadInput(); |
|||
|
|||
void EnableConfiguration(); |
|||
void DisableConfiguration(); |
|||
bool IsConfiguring() const; |
|||
void SaveCurrentConfig(); |
|||
void RestoreConfig(); |
|||
|
|||
std::vector<Common::ParamPackage> GetMappedDevices() const; |
|||
|
|||
Common::ParamPackage GetButtonParam(std::size_t index) const; |
|||
Common::ParamPackage GetStickParam(std::size_t index) const; |
|||
Common::ParamPackage GetMotionParam(std::size_t index) const; |
|||
|
|||
void SetButtonParam(std::size_t index, Common::ParamPackage param); |
|||
void SetStickParam(std::size_t index, Common::ParamPackage param); |
|||
void SetMotionParam(std::size_t index, Common::ParamPackage param); |
|||
|
|||
ButtonValues GetButtonsValues() const; |
|||
SticksValues GetSticksValues() const; |
|||
TriggerValues GetTriggersValues() const; |
|||
ControllerMotionValues GetMotionValues() const; |
|||
ColorValues GetColorsValues() const; |
|||
BatteryValues GetBatteryValues() const; |
|||
|
|||
NpadButtonState GetNpadButtons() const; |
|||
DebugPadButton GetDebugPadButtons() const; |
|||
AnalogSticks GetSticks() const; |
|||
NpadGcTriggerState GetTriggers() const; |
|||
MotionState GetMotions() const; |
|||
ControllerColors GetColors() const; |
|||
BatteryLevelState GetBattery() const; |
|||
|
|||
bool SetVibration(std::size_t device_index, VibrationValue vibration); |
|||
int TestVibration(std::size_t device_index); |
|||
|
|||
int SetCallback(ControllerUpdateCallback update_callback); |
|||
void DeleteCallback(int key); |
|||
|
|||
private: |
|||
/** |
|||
* Sets the status of a button. Applies toggle properties to the output. |
|||
* |
|||
* @param A CallbackStatus and a button index number |
|||
*/ |
|||
void SetButton(Input::CallbackStatus callback, std::size_t index); |
|||
void SetStick(Input::CallbackStatus callback, std::size_t index); |
|||
void SetTrigger(Input::CallbackStatus callback, std::size_t index); |
|||
void SetMotion(Input::CallbackStatus callback, std::size_t index); |
|||
void SetBattery(Input::CallbackStatus callback, std::size_t index); |
|||
|
|||
/** |
|||
* Triggers a callback that something has changed |
|||
* |
|||
* @param Input type of the trigger |
|||
*/ |
|||
void TriggerOnChange(ControllerTriggerType type); |
|||
|
|||
NpadIdType npad_id_type; |
|||
NpadType npad_type{NpadType::None}; |
|||
bool is_connected{false}; |
|||
bool is_configuring{false}; |
|||
bool is_vibration_enabled{true}; |
|||
f32 motion_sensitivity{0.01f}; |
|||
|
|||
ButtonParams button_params; |
|||
StickParams stick_params; |
|||
ControllerMotionParams motion_params; |
|||
TriggerParams trigger_params; |
|||
BatteryParams battery_params; |
|||
|
|||
ButtonDevices button_devices; |
|||
StickDevices stick_devices; |
|||
ControllerMotionDevices motion_devices; |
|||
TriggerDevices trigger_devices; |
|||
BatteryDevices battery_devices; |
|||
// VibrationDevices vibration_devices; |
|||
|
|||
mutable std::mutex mutex; |
|||
std::unordered_map<int, ControllerUpdateCallback> callback_list; |
|||
int last_callback_key = 0; |
|||
ControllerStatus controller; |
|||
}; |
|||
|
|||
} // namespace Core::HID |
|||
@ -0,0 +1,349 @@ |
|||
// Copyright 2021 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included
|
|||
|
|||
#include <fmt/format.h>
|
|||
|
|||
#include "core/hid/emulated_devices.h"
|
|||
#include "core/hid/input_converter.h"
|
|||
|
|||
namespace Core::HID { |
|||
|
|||
EmulatedDevices::EmulatedDevices() {} |
|||
|
|||
EmulatedDevices::~EmulatedDevices() = default; |
|||
|
|||
void EmulatedDevices::ReloadFromSettings() { |
|||
const auto& mouse = Settings::values.mouse_buttons; |
|||
|
|||
for (std::size_t index = 0; index < mouse.size(); ++index) { |
|||
mouse_button_params[index] = Common::ParamPackage(mouse[index]); |
|||
} |
|||
ReloadInput(); |
|||
} |
|||
|
|||
void EmulatedDevices::ReloadInput() { |
|||
std::transform(mouse_button_params.begin() + Settings::NativeMouseButton::MOUSE_HID_BEGIN, |
|||
mouse_button_params.begin() + Settings::NativeMouseButton::MOUSE_HID_END, |
|||
mouse_button_devices.begin(), Input::CreateDevice<Input::InputDevice>); |
|||
|
|||
std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(), |
|||
keyboard_devices.begin(), Input::CreateDeviceFromString<Input::InputDevice>); |
|||
|
|||
std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(), |
|||
keyboard_modifier_devices.begin(), |
|||
Input::CreateDeviceFromString<Input::InputDevice>); |
|||
|
|||
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { |
|||
if (!mouse_button_devices[index]) { |
|||
continue; |
|||
} |
|||
Input::InputCallback button_callback{ |
|||
[this, index](Input::CallbackStatus callback) { SetMouseButton(callback, index); }}; |
|||
mouse_button_devices[index]->SetCallback(button_callback); |
|||
} |
|||
|
|||
for (std::size_t index = 0; index < keyboard_devices.size(); ++index) { |
|||
if (!keyboard_devices[index]) { |
|||
continue; |
|||
} |
|||
Input::InputCallback button_callback{ |
|||
[this, index](Input::CallbackStatus callback) { SetKeyboardButton(callback, index); }}; |
|||
keyboard_devices[index]->SetCallback(button_callback); |
|||
} |
|||
|
|||
for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) { |
|||
if (!keyboard_modifier_devices[index]) { |
|||
continue; |
|||
} |
|||
Input::InputCallback button_callback{[this, index](Input::CallbackStatus callback) { |
|||
SetKeyboardModifier(callback, index); |
|||
}}; |
|||
keyboard_modifier_devices[index]->SetCallback(button_callback); |
|||
} |
|||
} |
|||
|
|||
void EmulatedDevices::UnloadInput() { |
|||
for (auto& button : mouse_button_devices) { |
|||
button.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; |
|||
} |
|||
|
|||
auto& mouse = Settings::values.mouse_buttons; |
|||
|
|||
for (std::size_t index = 0; index < mouse.size(); ++index) { |
|||
mouse[index] = mouse_button_params[index].Serialize(); |
|||
} |
|||
} |
|||
|
|||
void EmulatedDevices::RestoreConfig() { |
|||
if (!is_configuring) { |
|||
return; |
|||
} |
|||
ReloadFromSettings(); |
|||
} |
|||
|
|||
Common::ParamPackage EmulatedDevices::GetMouseButtonParam(std::size_t index) const { |
|||
if (index >= mouse_button_params.size()) { |
|||
return {}; |
|||
} |
|||
return mouse_button_params[index]; |
|||
} |
|||
|
|||
void EmulatedDevices::SetButtonParam(std::size_t index, Common::ParamPackage param) { |
|||
if (index >= mouse_button_params.size()) { |
|||
return; |
|||
} |
|||
mouse_button_params[index] = param; |
|||
ReloadInput(); |
|||
} |
|||
|
|||
void EmulatedDevices::SetKeyboardButton(Input::CallbackStatus callback, std::size_t index) { |
|||
if (index >= device_status.keyboard_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard 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
|
|||
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) { |
|||
TriggerOnChange(DeviceTriggerType::Keyboard); |
|||
return; |
|||
} |
|||
|
|||
// TODO(german77): Do this properly
|
|||
// switch (index) {
|
|||
// case Settings::NativeKeyboard::A:
|
|||
// interface_status.keyboard_state.a.Assign(current_status.value);
|
|||
// break;
|
|||
// ....
|
|||
//}
|
|||
|
|||
TriggerOnChange(DeviceTriggerType::Keyboard); |
|||
} |
|||
|
|||
void EmulatedDevices::SetKeyboardModifier(Input::CallbackStatus callback, std::size_t index) { |
|||
if (index >= device_status.keyboard_moddifier_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard 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) { |
|||
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; |
|||
} |
|||
|
|||
TriggerOnChange(DeviceTriggerType::KeyboardModdifier); |
|||
} |
|||
|
|||
void EmulatedDevices::SetMouseButton(Input::CallbackStatus callback, std::size_t index) { |
|||
if (index >= device_status.mouse_button_values.size()) { |
|||
return; |
|||
} |
|||
std::lock_guard 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) { |
|||
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; |
|||
} |
|||
|
|||
TriggerOnChange(DeviceTriggerType::Mouse); |
|||
} |
|||
|
|||
MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { |
|||
return device_status.mouse_button_values; |
|||
} |
|||
|
|||
KeyboardKey EmulatedDevices::GetKeyboard() const { |
|||
return device_status.keyboard_state; |
|||
} |
|||
|
|||
KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { |
|||
return device_status.keyboard_moddifier_state; |
|||
} |
|||
|
|||
MouseButton EmulatedDevices::GetMouseButtons() const { |
|||
return device_status.mouse_button_state; |
|||
} |
|||
|
|||
MousePosition EmulatedDevices::GetMousePosition() const { |
|||
return device_status.mouse_position_state; |
|||
} |
|||
|
|||
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { |
|||
for (const std::pair<int, InterfaceUpdateCallback> 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::lock_guard lock{mutex}; |
|||
callback_list.insert_or_assign(last_callback_key, update_callback); |
|||
return last_callback_key++; |
|||
} |
|||
|
|||
void EmulatedDevices::DeleteCallback(int key) { |
|||
std::lock_guard lock{mutex}; |
|||
if (!callback_list.contains(key)) { |
|||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); |
|||
return; |
|||
} |
|||
callback_list.erase(key); |
|||
} |
|||
} // namespace Core::HID
|
|||
@ -0,0 +1,137 @@ |
|||
// Copyright 2021 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
#include <mutex> |
|||
#include <unordered_map> |
|||
|
|||
#include "common/input.h" |
|||
#include "common/param_package.h" |
|||
#include "common/settings.h" |
|||
#include "core/hid/hid_types.h" |
|||
#include "core/hid/motion_input.h" |
|||
|
|||
namespace Core::HID { |
|||
|
|||
using KeyboardDevices = |
|||
std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeKeyboard::NumKeyboardKeys>; |
|||
using KeyboardModifierDevices = |
|||
std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeKeyboard::NumKeyboardMods>; |
|||
using MouseButtonDevices = |
|||
std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeMouseButton::NumMouseButtons>; |
|||
|
|||
using MouseButtonParams = |
|||
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; |
|||
|
|||
using KeyboardValues = std::array<Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; |
|||
using KeyboardModifierValues = |
|||
std::array<Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>; |
|||
using MouseButtonValues = |
|||
std::array<Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>; |
|||
|
|||
struct MousePosition { |
|||
s32 x; |
|||
s32 y; |
|||
s32 delta_wheel_x; |
|||
s32 delta_wheel_y; |
|||
}; |
|||
|
|||
struct DeviceStatus { |
|||
// Data from input_common |
|||
KeyboardValues keyboard_values{}; |
|||
KeyboardModifierValues keyboard_moddifier_values{}; |
|||
MouseButtonValues mouse_button_values{}; |
|||
|
|||
// Data for Nintendo devices |
|||
KeyboardKey keyboard_state{}; |
|||
KeyboardModifier keyboard_moddifier_state{}; |
|||
MouseButton mouse_button_state{}; |
|||
MousePosition mouse_position_state{}; |
|||
}; |
|||
|
|||
enum class DeviceTriggerType { |
|||
Keyboard, |
|||
KeyboardModdifier, |
|||
Mouse, |
|||
}; |
|||
|
|||
struct InterfaceUpdateCallback { |
|||
std::function<void(DeviceTriggerType)> on_change; |
|||
}; |
|||
|
|||
class EmulatedDevices { |
|||
public: |
|||
/** |
|||
* TODO: Write description |
|||
* |
|||
* @param npad_id_type |
|||
*/ |
|||
explicit EmulatedDevices(); |
|||
~EmulatedDevices(); |
|||
|
|||
YUZU_NON_COPYABLE(EmulatedDevices); |
|||
YUZU_NON_MOVEABLE(EmulatedDevices); |
|||
|
|||
void ReloadFromSettings(); |
|||
void ReloadInput(); |
|||
void UnloadInput(); |
|||
|
|||
void EnableConfiguration(); |
|||
void DisableConfiguration(); |
|||
bool IsConfiguring() const; |
|||
void SaveCurrentConfig(); |
|||
void RestoreConfig(); |
|||
|
|||
std::vector<Common::ParamPackage> GetMappedDevices() const; |
|||
|
|||
Common::ParamPackage GetMouseButtonParam(std::size_t index) const; |
|||
|
|||
void SetButtonParam(std::size_t index, Common::ParamPackage param); |
|||
|
|||
KeyboardValues GetKeyboardValues() const; |
|||
KeyboardModifierValues GetKeyboardModdifierValues() const; |
|||
MouseButtonValues GetMouseButtonsValues() const; |
|||
|
|||
KeyboardKey GetKeyboard() const; |
|||
KeyboardModifier GetKeyboardModifier() const; |
|||
MouseButton GetMouseButtons() const; |
|||
MousePosition GetMousePosition() const; |
|||
|
|||
int SetCallback(InterfaceUpdateCallback update_callback); |
|||
void DeleteCallback(int key); |
|||
|
|||
private: |
|||
/** |
|||
* Sets the status of a button. Applies toggle properties to the output. |
|||
* |
|||
* @param A CallbackStatus and a button index number |
|||
*/ |
|||
void SetKeyboardButton(Input::CallbackStatus callback, std::size_t index); |
|||
void SetKeyboardModifier(Input::CallbackStatus callback, std::size_t index); |
|||
void SetMouseButton(Input::CallbackStatus callback, std::size_t index); |
|||
|
|||
/** |
|||
* Triggers a callback that something has changed |
|||
* |
|||
* @param Input type of the trigger |
|||
*/ |
|||
void TriggerOnChange(DeviceTriggerType type); |
|||
|
|||
bool is_configuring{false}; |
|||
|
|||
MouseButtonParams mouse_button_params; |
|||
|
|||
KeyboardDevices keyboard_devices; |
|||
KeyboardModifierDevices keyboard_modifier_devices; |
|||
MouseButtonDevices mouse_button_devices; |
|||
|
|||
mutable std::mutex mutex; |
|||
std::unordered_map<int, InterfaceUpdateCallback> callback_list; |
|||
int last_callback_key = 0; |
|||
DeviceStatus device_status; |
|||
}; |
|||
|
|||
} // namespace Core::HID |
|||
@ -0,0 +1,144 @@ |
|||
// Copyright 2021 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "core/hid/hid_core.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: |
|||
UNREACHABLE_MSG("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: |
|||
UNREACHABLE_MSG("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(IndexToNpadIdType(index)); |
|||
} |
|||
|
|||
const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { |
|||
return GetEmulatedController(IndexToNpadIdType(index)); |
|||
} |
|||
|
|||
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { |
|||
supported_style_tag.raw = style_tag.raw; |
|||
} |
|||
|
|||
NpadStyleTag HIDCore::GetSupportedStyleTag() const { |
|||
return supported_style_tag; |
|||
} |
|||
|
|||
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
|
|||
@ -0,0 +1,60 @@ |
|||
// Copyright 2021 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
|
|||
#include "core/hid/emulated_console.h" |
|||
#include "core/hid/emulated_controller.h" |
|||
#include "core/hid/emulated_devices.h" |
|||
|
|||
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; |
|||
|
|||
// Reloads all input devices from settings |
|||
void ReloadInputDevices(); |
|||
|
|||
// Removes all callbacks from input common |
|||
void UnloadInputDevices(); |
|||
|
|||
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; |
|||
}; |
|||
|
|||
} // namespace Core::HID |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue