split controller

This commit is contained in:
german77 2023-05-07 14:19:25 -06:00 committed by Narr the Reg
parent 238e46ec93
commit 896670345e
13 changed files with 1511 additions and 643 deletions

View File

@ -159,6 +159,8 @@ add_library(core STATIC
hid/irs_types.h
hid/motion_input.cpp
hid/motion_input.h
hid/system_controller.cpp
hid/system_controller.h
hle/api_version.h
hle/ipc.h
hle/kernel/board/nintendo/nx/k_memory_layout.cpp

View File

@ -46,26 +46,26 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->Connect(true);
controller->Connect();
} else if (parameters.allow_dual_joycons) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
controller->Connect(true);
controller->Connect();
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
// Assign left joycons to even player indices and right joycons to odd player indices.
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
// a right Joycon for Player 2 in 2 Player Assist mode.
if (index % 2 == 0) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
controller->Connect(true);
controller->Connect();
} else {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
controller->Connect(true);
controller->Connect();
}
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
!Settings::values.use_docked_mode.GetValue()) {
// We should *never* reach here under any normal circumstances.
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->Connect(true);
controller->Connect();
} else {
ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!");
}

View File

@ -95,7 +95,6 @@ void EmulatedController::ReloadFromSettings() {
motion_params[index] = Common::ParamPackage(player.motions[index]);
}
controller.color_values = {};
controller.colors_state.fullkey = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
@ -537,181 +536,18 @@ void EmulatedController::UnloadInput() {
}
}
void EmulatedController::EnableConfiguration() {
is_configuring = true;
tmp_is_connected = is_connected;
tmp_npad_type = npad_type;
void EmulatedController::DisableInput() {
npad_input_enabled = false;
}
void EmulatedController::DisableConfiguration() {
is_configuring = false;
void EmulatedController::EnableInput() {
npad_input_enabled = true;
// 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 = 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(), [&param](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(), [&param](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() {
@ -766,16 +602,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
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) {
@ -857,18 +683,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
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:
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();
@ -881,7 +695,7 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
Connect();
}
}
TriggerOnChange(ControllerTriggerType::Button, true);
TriggerOnChange(ControllerTriggerType::Button);
}
void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
@ -890,7 +704,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
return;
}
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); });
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick); });
std::scoped_lock lock{mutex};
const auto stick_value = TransformToStick(callback);
@ -911,12 +725,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
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),
@ -946,7 +754,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
return;
}
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); });
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger); });
std::scoped_lock lock{mutex};
const auto trigger_value = TransformToTrigger(callback);
@ -960,12 +768,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
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();
@ -992,7 +794,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
if (index >= controller.motion_values.size()) {
return;
}
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); });
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion); });
std::scoped_lock lock{mutex};
auto& raw_status = controller.motion_values[index].raw_status;
auto& emulated = controller.motion_values[index].emulated;
@ -1023,48 +825,44 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= controller.color_values.size()) {
if (index > RightIndex) {
return;
}
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); });
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color); });
std::scoped_lock lock{mutex};
controller.color_values[index] = TransformToColor(callback);
const auto color_values = TransformToColor(callback);
if (is_configuring) {
return;
}
if (controller.color_values[index].body == 0) {
if (color_values.body == 0) {
trigger_guard.Cancel();
return;
}
controller.colors_state.fullkey = {
.body = GetNpadColor(controller.color_values[index].body),
.button = GetNpadColor(controller.color_values[index].buttons),
.body = GetNpadColor(color_values.body),
.button = GetNpadColor(color_values.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),
.body = GetNpadColor(color_values.left_grip),
.button = GetNpadColor(color_values.buttons),
};
controller.colors_state.right = {
.body = GetNpadColor(controller.color_values[index].right_grip),
.button = GetNpadColor(controller.color_values[index].buttons),
.body = GetNpadColor(color_values.right_grip),
.button = GetNpadColor(color_values.buttons),
};
} else {
switch (index) {
case LeftIndex:
controller.colors_state.left = {
.body = GetNpadColor(controller.color_values[index].body),
.button = GetNpadColor(controller.color_values[index].buttons),
.body = GetNpadColor(color_values.body),
.button = GetNpadColor(color_values.buttons),
};
break;
case RightIndex:
controller.colors_state.right = {
.body = GetNpadColor(controller.color_values[index].body),
.button = GetNpadColor(controller.color_values[index].buttons),
.body = GetNpadColor(color_values.body),
.button = GetNpadColor(color_values.buttons),
};
break;
}
@ -1073,21 +871,17 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= controller.battery_values.size()) {
if (index > DualIndex) {
return;
}
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); });
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery); });
std::scoped_lock lock{mutex};
controller.battery_values[index] = TransformToBattery(callback);
if (is_configuring) {
return;
}
const auto battery_values = TransformToBattery(callback);
bool is_charging = false;
bool is_powered = false;
NpadBatteryLevel battery_level = 0;
switch (controller.battery_values[index]) {
switch (battery_values) {
case Common::Input::BatteryLevel::Charging:
is_charging = true;
is_powered = true;
@ -1139,46 +933,33 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
}
void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); });
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor); });
std::scoped_lock lock{mutex};
controller.camera_values = TransformToCamera(callback);
const auto 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;
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(camera_values.format);
controller.camera_state.data = camera_values.data;
}
void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); });
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController); });
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); });
SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc); });
std::scoped_lock lock{mutex};
controller.nfc_values = TransformToNfc(callback);
if (is_configuring) {
return;
}
const auto nfc_values = TransformToNfc(callback);
controller.nfc_state = {
controller.nfc_values.state,
controller.nfc_values.data,
nfc_values.state,
nfc_values.data,
};
}
@ -1409,10 +1190,9 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
npad_type);
}
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
bool EmulatedController::IsControllerFullkey() const {
std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
switch (npad_type) {
case NpadStyleIndex::ProController:
case NpadStyleIndex::GameCube:
case NpadStyleIndex::NES:
@ -1425,10 +1205,9 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
}
}
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
bool EmulatedController::IsControllerSupported() const {
std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
switch (npad_type) {
case NpadStyleIndex::ProController:
return supported_style_tag.fullkey.As<bool>();
case NpadStyleIndex::Handheld:
@ -1456,20 +1235,15 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
}
}
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);
void EmulatedController::Connect() {
if (!IsControllerSupported()) {
LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
return;
}
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected); });
std::scoped_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = true;
return;
}
if (is_connected) {
trigger_guard.Cancel();
@ -1480,12 +1254,8 @@ void EmulatedController::Connect(bool use_temporary_value) {
void EmulatedController::Disconnect() {
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected); });
std::scoped_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = false;
return;
}
if (!is_connected) {
trigger_guard.Cancel();
@ -1494,11 +1264,8 @@ void EmulatedController::Disconnect() {
is_connected = false;
}
bool EmulatedController::IsConnected(bool get_temporary_value) const {
bool EmulatedController::IsConnected() const {
std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_is_connected;
}
return is_connected;
}
@ -1508,27 +1275,14 @@ NpadIdType EmulatedController::GetNpadIdType() const {
}
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
std::scoped_lock lock{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); });
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type); });
std::scoped_lock lock{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;
@ -1568,87 +1322,23 @@ ButtonValues EmulatedController::GetButtonsValues() const {
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;
}
@ -1673,6 +1363,7 @@ const CameraState& EmulatedController::GetCamera() const {
}
RingSensorForce EmulatedController::GetRingSensorForce() const {
std::scoped_lock lock{mutex};
return controller.ring_analog_state;
}
@ -1690,13 +1381,10 @@ NpadColor EmulatedController::GetNpadColor(u32 color) {
};
}
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
void EmulatedController::TriggerOnChange(ControllerTriggerType type) {
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);
}

View File

@ -126,16 +126,8 @@ struct ControllerStatus {
SticksValues stick_values{};
ControllerMotionValues motion_values{};
TriggerValues trigger_values{};
ColorValues color_values{};
BatteryValues battery_values{};
VibrationValues vibration_values{};
CameraValues camera_values{};
RingAnalogValue ring_analog_value{};
NfcValues nfc_values{};
// Data for HID services
HomeButtonState home_button_state{};
CaptureButtonState capture_button_state{};
NpadButtonState npad_button_state{};
DebugPadButton debug_pad_button_state{};
AnalogSticks analog_stick_state{};
@ -167,7 +159,6 @@ enum class ControllerTriggerType {
struct ControllerUpdateCallback {
std::function<void(ControllerTriggerType)> on_change;
bool is_npad_service;
};
class EmulatedController {
@ -210,43 +201,29 @@ public:
/**
* Sets the connected status to true
* @param use_temporary_value If true tmp_npad_type will be used
*/
void Connect(bool use_temporary_value = false);
void Connect();
/// Sets the connected status to false
void Disconnect();
/**
* Is the emulated connected
* @param get_temporary_value If true tmp_is_connected will be returned
* @return true if the controller has the connected status
*/
bool IsConnected(bool get_temporary_value = false) const;
bool IsConnected() const;
/// Removes all callbacks created from input devices
void UnloadInput();
/**
* Sets the emulated controller into configuring mode
* Sets the emulated controller into idle mode
* This prevents the modification of the HID state of the emulated controller by input commands
*/
void EnableConfiguration();
void DisableInput();
/// Returns the emulated controller into normal mode, allowing the modification of the HID state
void DisableConfiguration();
/// Enables Home and Screenshot buttons
void EnableSystemButtons();
/// Disables Home and Screenshot buttons
void DisableSystemButtons();
/// Sets Home and Screenshot buttons to false
void ResetSystemButtons();
/// Returns true if the emulated controller is in configuring mode
bool IsConfiguring() const;
void EnableInput();
/// Reload all input devices
void ReloadInput();
@ -254,75 +231,9 @@ public:
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
/// Returns a vector of mapped devices from the mapped button and stick parameters
std::vector<Common::ParamPackage> GetMappedDevices() const;
// Returns the current mapped button device
Common::ParamPackage GetButtonParam(std::size_t index) const;
// Returns the current mapped stick device
Common::ParamPackage GetStickParam(std::size_t index) const;
// Returns the current mapped motion device
Common::ParamPackage GetMotionParam(std::size_t index) const;
/**
* Updates the current mapped button device
* @param param ParamPackage with controller data to be mapped
*/
void SetButtonParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped stick device
* @param param ParamPackage with controller data to be mapped
*/
void SetStickParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped motion device
* @param param ParamPackage with controller data to be mapped
*/
void SetMotionParam(std::size_t index, Common::ParamPackage param);
/// Auto calibrates the current motion devices
void StartMotionCalibration();
/// Returns the latest button status from the controller with parameters
ButtonValues GetButtonsValues() const;
/// Returns the latest analog stick status from the controller with parameters
SticksValues GetSticksValues() const;
/// Returns the latest trigger status from the controller with parameters
TriggerValues GetTriggersValues() const;
/// Returns the latest motion status from the controller with parameters
ControllerMotionValues GetMotionValues() const;
/// Returns the latest color status from the controller with parameters
ColorValues GetColorsValues() const;
/// Returns the latest battery status from the controller with parameters
BatteryValues GetBatteryValues() const;
/// Returns the latest camera status from the controller with parameters
CameraValues GetCameraValues() const;
/// Returns the latest status of analog input from the ring sensor with parameters
RingAnalogValue GetRingSensorValues() const;
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
/// Returns the latest status of button input for the hid::CaptureButton service
CaptureButtonState GetCaptureButtons() const;
/// Returns the latest status of button input for the hid::Npad service
NpadButtonState GetNpadButtons() const;
@ -432,17 +343,15 @@ private:
void LoadVirtualGamepadParams();
/**
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller style is fullkey
*/
bool IsControllerFullkey(bool use_temporary_value = false) const;
bool IsControllerFullkey() const;
/**
* Checks the current controller type against the supported_style_tag
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller is supported
*/
bool IsControllerSupported(bool use_temporary_value = false) const;
bool IsControllerSupported() const;
/**
* Updates the button status of the controller
@ -517,9 +426,8 @@ private:
/**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
* @param is_service_update indicates if this event should only be sent to HID services
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
void TriggerOnChange(ControllerTriggerType type);
NpadButton GetTurboButtonMask() const;
@ -528,15 +436,10 @@ private:
NpadStyleIndex original_npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
bool is_configuring{false};
bool system_buttons_enabled{true};
bool npad_input_enabled{true};
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
u32 turbo_button_state{0};
// Temporary values to avoid doing changes while the controller is in configuring mode
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
bool tmp_is_connected{false};
ButtonParams button_params;
StickParams stick_params;
ControllerMotionParams motion_params;

View File

@ -6,6 +6,7 @@
#include "core/hid/emulated_controller.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hid/system_controller.h"
namespace Core::HID {
@ -20,6 +21,16 @@ HIDCore::HIDCore()
player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
other{std::make_unique<EmulatedController>(NpadIdType::Other)},
handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
system_player_1{std::make_unique<SystemController>(NpadIdType::Player1)},
system_player_2{std::make_unique<SystemController>(NpadIdType::Player2)},
system_player_3{std::make_unique<SystemController>(NpadIdType::Player3)},
system_player_4{std::make_unique<SystemController>(NpadIdType::Player4)},
system_player_5{std::make_unique<SystemController>(NpadIdType::Player5)},
system_player_6{std::make_unique<SystemController>(NpadIdType::Player6)},
system_player_7{std::make_unique<SystemController>(NpadIdType::Player7)},
system_player_8{std::make_unique<SystemController>(NpadIdType::Player8)},
system_other{std::make_unique<SystemController>(NpadIdType::Other)},
system_handheld{std::make_unique<SystemController>(NpadIdType::Handheld)},
console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
HIDCore::~HIDCore() = default;
@ -81,6 +92,65 @@ const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type
return nullptr;
}
}
SystemController* HIDCore::GetSystemController(NpadIdType npad_id_type) {
switch (npad_id_type) {
case NpadIdType::Player1:
return system_player_1.get();
case NpadIdType::Player2:
return system_player_2.get();
case NpadIdType::Player3:
return system_player_3.get();
case NpadIdType::Player4:
return system_player_4.get();
case NpadIdType::Player5:
return system_player_5.get();
case NpadIdType::Player6:
return system_player_6.get();
case NpadIdType::Player7:
return system_player_7.get();
case NpadIdType::Player8:
return system_player_8.get();
case NpadIdType::Other:
return system_other.get();
case NpadIdType::Handheld:
return system_handheld.get();
case NpadIdType::Invalid:
default:
ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
const SystemController* HIDCore::GetSystemController(NpadIdType npad_id_type) const {
switch (npad_id_type) {
case NpadIdType::Player1:
return system_player_1.get();
case NpadIdType::Player2:
return system_player_2.get();
case NpadIdType::Player3:
return system_player_3.get();
case NpadIdType::Player4:
return system_player_4.get();
case NpadIdType::Player5:
return system_player_5.get();
case NpadIdType::Player6:
return system_player_6.get();
case NpadIdType::Player7:
return system_player_7.get();
case NpadIdType::Player8:
return system_player_8.get();
case NpadIdType::Other:
return system_other.get();
case NpadIdType::Handheld:
return system_handheld.get();
case NpadIdType::Invalid:
default:
ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
EmulatedConsole* HIDCore::GetEmulatedConsole() {
return console.get();
}
@ -107,16 +177,16 @@ const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t inde
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
supported_style_tag.raw = style_tag.raw;
player_1->SetSupportedNpadStyleTag(supported_style_tag);
player_2->SetSupportedNpadStyleTag(supported_style_tag);
player_3->SetSupportedNpadStyleTag(supported_style_tag);
player_4->SetSupportedNpadStyleTag(supported_style_tag);
player_5->SetSupportedNpadStyleTag(supported_style_tag);
player_6->SetSupportedNpadStyleTag(supported_style_tag);
player_7->SetSupportedNpadStyleTag(supported_style_tag);
player_8->SetSupportedNpadStyleTag(supported_style_tag);
other->SetSupportedNpadStyleTag(supported_style_tag);
handheld->SetSupportedNpadStyleTag(supported_style_tag);
system_player_1->SetSupportedNpadStyleTag(supported_style_tag);
system_player_2->SetSupportedNpadStyleTag(supported_style_tag);
system_player_3->SetSupportedNpadStyleTag(supported_style_tag);
system_player_4->SetSupportedNpadStyleTag(supported_style_tag);
system_player_5->SetSupportedNpadStyleTag(supported_style_tag);
system_player_6->SetSupportedNpadStyleTag(supported_style_tag);
system_player_7->SetSupportedNpadStyleTag(supported_style_tag);
system_player_8->SetSupportedNpadStyleTag(supported_style_tag);
system_other->SetSupportedNpadStyleTag(supported_style_tag);
system_handheld->SetSupportedNpadStyleTag(supported_style_tag);
}
NpadStyleTag HIDCore::GetSupportedStyleTag() const {
@ -155,57 +225,57 @@ NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
}
void HIDCore::EnableAllControllerConfiguration() {
player_1->EnableConfiguration();
player_2->EnableConfiguration();
player_3->EnableConfiguration();
player_4->EnableConfiguration();
player_5->EnableConfiguration();
player_6->EnableConfiguration();
player_7->EnableConfiguration();
player_8->EnableConfiguration();
other->EnableConfiguration();
handheld->EnableConfiguration();
system_player_1->EnableConfiguration();
system_player_2->EnableConfiguration();
system_player_3->EnableConfiguration();
system_player_4->EnableConfiguration();
system_player_5->EnableConfiguration();
system_player_6->EnableConfiguration();
system_player_7->EnableConfiguration();
system_player_8->EnableConfiguration();
system_other->EnableConfiguration();
system_handheld->EnableConfiguration();
}
void HIDCore::DisableAllControllerConfiguration() {
player_1->DisableConfiguration();
player_2->DisableConfiguration();
player_3->DisableConfiguration();
player_4->DisableConfiguration();
player_5->DisableConfiguration();
player_6->DisableConfiguration();
player_7->DisableConfiguration();
player_8->DisableConfiguration();
other->DisableConfiguration();
handheld->DisableConfiguration();
system_player_1->DisableConfiguration();
system_player_2->DisableConfiguration();
system_player_3->DisableConfiguration();
system_player_4->DisableConfiguration();
system_player_5->DisableConfiguration();
system_player_6->DisableConfiguration();
system_player_7->DisableConfiguration();
system_player_8->DisableConfiguration();
system_other->DisableConfiguration();
system_handheld->DisableConfiguration();
}
void HIDCore::ReloadInputDevices() {
player_1->ReloadFromSettings();
player_2->ReloadFromSettings();
player_3->ReloadFromSettings();
player_4->ReloadFromSettings();
player_5->ReloadFromSettings();
player_6->ReloadFromSettings();
player_7->ReloadFromSettings();
player_8->ReloadFromSettings();
other->ReloadFromSettings();
handheld->ReloadFromSettings();
system_player_1->ReloadFromSettings();
system_player_2->ReloadFromSettings();
system_player_3->ReloadFromSettings();
system_player_4->ReloadFromSettings();
system_player_5->ReloadFromSettings();
system_player_6->ReloadFromSettings();
system_player_7->ReloadFromSettings();
system_player_8->ReloadFromSettings();
system_other->ReloadFromSettings();
system_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();
system_player_1->UnloadInput();
system_player_2->UnloadInput();
system_player_3->UnloadInput();
system_player_4->UnloadInput();
system_player_5->UnloadInput();
system_player_6->UnloadInput();
system_player_7->UnloadInput();
system_player_8->UnloadInput();
system_other->UnloadInput();
system_handheld->UnloadInput();
console->UnloadInput();
devices->UnloadInput();
}

View File

@ -12,6 +12,7 @@ namespace Core::HID {
class EmulatedConsole;
class EmulatedController;
class EmulatedDevices;
class SystemController;
} // namespace Core::HID
namespace Core::HID {
@ -30,6 +31,12 @@ public:
EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
SystemController* GetSystemController(NpadIdType npad_id_type);
const SystemController* GetSystemController(NpadIdType npad_id_type) const;
SystemController* GetSystemControllerByIndex(std::size_t index);
const SystemController* GetSystemControllerByIndex(std::size_t index) const;
EmulatedConsole* GetEmulatedConsole();
const EmulatedConsole* GetEmulatedConsole() const;
@ -76,6 +83,16 @@ private:
std::unique_ptr<EmulatedController> handheld;
std::unique_ptr<EmulatedConsole> console;
std::unique_ptr<EmulatedDevices> devices;
std::unique_ptr<SystemController> system_player_1;
std::unique_ptr<SystemController> system_player_2;
std::unique_ptr<SystemController> system_player_3;
std::unique_ptr<SystemController> system_player_4;
std::unique_ptr<SystemController> system_player_5;
std::unique_ptr<SystemController> system_player_6;
std::unique_ptr<SystemController> system_player_7;
std::unique_ptr<SystemController> system_player_8;
std::unique_ptr<SystemController> system_other;
std::unique_ptr<SystemController> system_handheld;
NpadStyleTag supported_style_tag{NpadStyleSet::All};
};

View File

@ -0,0 +1,860 @@
// 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/system_controller.h"
#include "core/hid/input_converter.h"
namespace Core::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_TRIGGER_MAX = 0x7fff;
SystemController::SystemController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
SystemController::~SystemController() = default;
NpadStyleIndex SystemController::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 SystemController::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 SystemController::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]);
}
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 SystemController::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];
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);
}
void SystemController::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();
}
}
void SystemController::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();
}
}
void SystemController::EnableConfiguration() {
is_configuring = true;
tmp_is_connected = is_connected;
tmp_npad_type = npad_type;
}
void SystemController::DisableConfiguration() {
is_configuring = false;
// 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 SystemController::EnableSystemButtons() {
std::scoped_lock lock{mutex};
system_buttons_enabled = true;
}
void SystemController::DisableSystemButtons() {
std::scoped_lock lock{mutex};
system_buttons_enabled = false;
controller.home_button_state.raw = 0;
controller.capture_button_state.raw = 0;
}
void SystemController::ResetSystemButtons() {
std::scoped_lock lock{mutex};
controller.home_button_state.home.Assign(false);
controller.capture_button_state.capture.Assign(false);
}
bool SystemController::IsConfiguring() const {
return is_configuring;
}
void SystemController::SaveCurrentConfig() {
const auto player_index = 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 SystemController::RestoreConfig() {
if (!is_configuring) {
return;
}
ReloadFromSettings();
}
std::vector<Common::ParamPackage> SystemController::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", 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(), [&param](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 SystemController::GetButtonParam(std::size_t index) const {
if (index >= button_params.size()) {
return {};
}
return button_params[index];
}
Common::ParamPackage SystemController::GetStickParam(std::size_t index) const {
if (index >= stick_params.size()) {
return {};
}
return stick_params[index];
}
Common::ParamPackage SystemController::GetMotionParam(std::size_t index) const {
if (index >= motion_params.size()) {
return {};
}
return motion_params[index];
}
void SystemController::SetButtonParam(std::size_t index, Common::ParamPackage param) {
if (index >= button_params.size()) {
return;
}
button_params[index] = std::move(param);
ReloadInput();
}
void SystemController::SetStickParam(std::size_t index, Common::ParamPackage param) {
if (index >= stick_params.size()) {
return;
}
stick_params[index] = std::move(param);
ReloadInput();
}
void SystemController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
if (index >= motion_params.size()) {
return;
}
motion_params[index] = std::move(param);
ReloadInput();
}
void SystemController::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.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);
break;
case Settings::NativeButton::B:
controller.npad_button_state.b.Assign(current_status.value);
break;
case Settings::NativeButton::X:
controller.npad_button_state.x.Assign(current_status.value);
break;
case Settings::NativeButton::Y:
controller.npad_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);
break;
case Settings::NativeButton::R:
controller.npad_button_state.r.Assign(current_status.value);
break;
case Settings::NativeButton::ZL:
controller.npad_button_state.zl.Assign(current_status.value);
break;
case Settings::NativeButton::ZR:
controller.npad_button_state.zr.Assign(current_status.value);
break;
case Settings::NativeButton::Plus:
controller.npad_button_state.plus.Assign(current_status.value);
break;
case Settings::NativeButton::Minus:
controller.npad_button_state.minus.Assign(current_status.value);
break;
case Settings::NativeButton::DLeft:
controller.npad_button_state.left.Assign(current_status.value);
break;
case Settings::NativeButton::DUp:
controller.npad_button_state.up.Assign(current_status.value);
break;
case Settings::NativeButton::DRight:
controller.npad_button_state.right.Assign(current_status.value);
break;
case Settings::NativeButton::DDown:
controller.npad_button_state.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:
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 SystemController::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) {
if (!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 SystemController::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 SystemController::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 SystemController::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 SystemController::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 SystemController::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{mutex};
if (is_configuring) {
tmp_is_connected = true;
return;
}
if (is_connected) {
trigger_guard.Cancel();
return;
}
is_connected = true;
}
void SystemController::Disconnect() {
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
std::scoped_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = false;
return;
}
if (!is_connected) {
trigger_guard.Cancel();
return;
}
is_connected = false;
}
bool SystemController::IsConnected(bool get_temporary_value) const {
std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_is_connected;
}
return is_connected;
}
NpadIdType SystemController::GetNpadIdType() const {
std::scoped_lock lock{mutex};
return npad_id_type;
}
NpadStyleIndex SystemController::GetNpadStyleIndex(bool get_temporary_value) const {
std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_npad_type;
}
return npad_type;
}
void SystemController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
std::scoped_lock lock{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",
NpadIdTypeToIndex(npad_id_type));
}
npad_type = npad_type_;
}
ButtonValues SystemController::GetButtonsValues() const {
std::scoped_lock lock{mutex};
return controller.button_values;
}
SticksValues SystemController::GetSticksValues() const {
std::scoped_lock lock{mutex};
return controller.stick_values;
}
TriggerValues SystemController::GetTriggersValues() const {
std::scoped_lock lock{mutex};
return controller.trigger_values;
}
HomeButtonState SystemController::GetHomeButtons() const {
std::scoped_lock lock{mutex};
return controller.home_button_state;
}
CaptureButtonState SystemController::GetCaptureButtons() const {
std::scoped_lock lock{mutex};
return controller.capture_button_state;
}
NpadButtonState SystemController::GetNpadButtons() const {
std::scoped_lock lock{mutex};
return controller.npad_button_state;
}
AnalogSticks SystemController::GetSticks() const {
std::scoped_lock lock{mutex};
return controller.analog_stick_state;
}
NpadGcTriggerState SystemController::GetTriggers() const {
std::scoped_lock lock{mutex};
return controller.gc_trigger_state;
}
void SystemController::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 SystemController::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 SystemController::DeleteCallback(int key) {
std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
}
} // namespace Core::HID

View File

@ -0,0 +1,330 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
#include "core/hid/irs_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;
const std::size_t output_devices_size = 4;
struct ControllerMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
};
using ButtonDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>;
using StickDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
using TriggerValues =
std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
struct AnalogSticks {
AnalogStickState left{};
AnalogStickState right{};
};
enum EmulatedDeviceIndex : u8 {
LeftIndex,
RightIndex,
DualIndex,
AllDevices,
};
struct ControllerStatus {
// Data from input_common
ButtonValues button_values{};
SticksValues stick_values{};
TriggerValues trigger_values{};
// Data for HID services
HomeButtonState home_button_state{};
CaptureButtonState capture_button_state{};
NpadButtonState npad_button_state{};
AnalogSticks analog_stick_state{};
NpadGcTriggerState gc_trigger_state{};
};
enum class ControllerTriggerType {
Button,
Stick,
Trigger,
Connected,
Disconnected,
Type,
All,
};
struct ControllerUpdateCallback {
std::function<void(ControllerTriggerType)> on_change;
bool is_npad_service;
};
class SystemController {
public:
/**
* Contains all input data (buttons, joysticks, vibration, and motion) within this controller.
* @param npad_id_type npad id type for this specific controller
*/
explicit SystemController(NpadIdType npad_id_type_);
~SystemController();
YUZU_NON_COPYABLE(SystemController);
YUZU_NON_MOVEABLE(SystemController);
/// Converts the controller type from settings to npad type
static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type);
/// Converts npad type to the equivalent of controller type from settings
static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type);
/// Gets the NpadIdType for this controller
NpadIdType GetNpadIdType() const;
/// Sets the NpadStyleIndex for this controller
void SetNpadStyleIndex(NpadStyleIndex npad_type_);
/**
* Gets the NpadStyleIndex for this controller
* @param get_temporary_value If true tmp_npad_type will be returned
* @return NpadStyleIndex set on the controller
*/
NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
/**
* Sets the supported controller types. Disconnects the controller if current type is not
* supported
* @param supported_styles bitflag with supported types
*/
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
/**
* Sets the connected status to true
* @param use_temporary_value If true tmp_npad_type will be used
*/
void Connect(bool use_temporary_value = false);
/// Sets the connected status to false
void Disconnect();
/**
* Is the emulated connected
* @param get_temporary_value If true tmp_is_connected will be returned
* @return true if the controller has the connected status
*/
bool IsConnected(bool get_temporary_value = false) const;
/// Removes all callbacks created from input devices
void UnloadInput();
/**
* Sets the emulated controller into configuring mode
* This prevents the modification of the HID state of the emulated controller by input commands
*/
void EnableConfiguration();
/// Returns the emulated controller into normal mode, allowing the modification of the HID state
void DisableConfiguration();
/// Enables Home and Screenshot buttons
void EnableSystemButtons();
/// Disables Home and Screenshot buttons
void DisableSystemButtons();
/// Sets Home and Screenshot buttons to false
void ResetSystemButtons();
/// Returns true if the emulated controller is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
void ReloadInput();
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
/// Returns a vector of mapped devices from the mapped button and stick parameters
std::vector<Common::ParamPackage> GetMappedDevices() const;
// Returns the current mapped button device
Common::ParamPackage GetButtonParam(std::size_t index) const;
// Returns the current mapped stick device
Common::ParamPackage GetStickParam(std::size_t index) const;
// Returns the current mapped motion device
Common::ParamPackage GetMotionParam(std::size_t index) const;
/**
* Updates the current mapped button device
* @param param ParamPackage with controller data to be mapped
*/
void SetButtonParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped stick device
* @param param ParamPackage with controller data to be mapped
*/
void SetStickParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped motion device
* @param param ParamPackage with controller data to be mapped
*/
void SetMotionParam(std::size_t index, Common::ParamPackage param);
/// Returns the latest button status from the controller with parameters
ButtonValues GetButtonsValues() const;
/// Returns the latest analog stick status from the controller with parameters
SticksValues GetSticksValues() const;
/// Returns the latest trigger status from the controller with parameters
TriggerValues GetTriggersValues() const;
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
/// Returns the latest status of button input for the hid::CaptureButton service
CaptureButtonState GetCaptureButtons() const;
/// Returns the latest status of button input for the hid::Npad service
NpadButtonState GetNpadButtons() const;
/// Returns the latest status of stick input from the mouse
AnalogSticks GetSticks() const;
/// Returns the latest status of trigger input from the mouse
NpadGcTriggerState GetTriggers() const;
/**
* Adds a callback to the list of events
* @param update_callback A ConsoleUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(ControllerUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
* @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
private:
/// creates input devices from params
void LoadDevices();
/**
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller style is fullkey
*/
bool IsControllerFullkey(bool use_temporary_value = false) const;
/**
* Checks the current controller type against the supported_style_tag
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller is supported
*/
bool IsControllerSupported(bool use_temporary_value = false) const;
/**
* Updates the button status of the controller
* @param callback A CallbackStatus containing the button status
* @param index Button ID of the to be updated
*/
void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Updates the analog stick status of the controller
* @param callback A CallbackStatus containing the analog stick status
* @param index stick ID of the to be updated
*/
void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Updates the trigger status of the controller
* @param callback A CallbackStatus containing the trigger status
* @param index trigger ID of the to be updated
*/
void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
* @param is_service_update indicates if this event should only be sent to HID services
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
const NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
NpadStyleIndex original_npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
bool is_configuring{false};
bool system_buttons_enabled{true};
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
// Temporary values to avoid doing changes while the controller is in configuring mode
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
bool tmp_is_connected{false};
ButtonParams button_params;
StickParams stick_params;
TriggerParams trigger_params;
ControllerMotionParams motion_params;
RingAnalogParams ring_params;
ButtonDevices button_devices;
StickDevices stick_devices;
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0;
// Stores the current status of all controller input
ControllerStatus controller;
};
} // namespace Core::HID

View File

@ -24,7 +24,6 @@ ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
.is_npad_service = true,
};
callback_key = npad_device->SetCallback(engine_callback);
}

View File

@ -6,7 +6,7 @@
#include <QStandardItemModel>
#include <QTimer>
#include "core/hid/emulated_controller.h"
#include "core/hid/system_controller.h"
#include "core/hid/hid_core.h"
#include "ui_configure_hotkeys.h"
@ -42,7 +42,7 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
&ConfigureHotkeys::RestoreDefaults);
connect(ui->button_clear_all, &QPushButton::clicked, this, &ConfigureHotkeys::ClearAll);
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
controller = hid_core.GetSystemController(Core::HID::NpadIdType::Player1);
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });

View File

@ -12,7 +12,7 @@ class ParamPackage;
namespace Core::HID {
class HIDCore;
class EmulatedController;
class SystemController;
enum class NpadButton : u64;
} // namespace Core::HID
@ -60,7 +60,7 @@ private:
void SetPollingResult(Core::HID::NpadButton button, bool cancel);
QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const;
Core::HID::EmulatedController* controller;
Core::HID::SystemController* controller;
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
std::optional<std::function<void(Core::HID::NpadButton, bool)>> input_setter;

View File

@ -12,7 +12,7 @@
#include <QTimer>
#include "common/assert.h"
#include "common/param_package.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/system_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "input_common/drivers/keyboard.h"
@ -297,24 +297,24 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
timeout_timer(std::make_unique<QTimer>()),
poll_timer(std::make_unique<QTimer>()), bottom_row{bottom_row_}, hid_core{hid_core_} {
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
auto* emulated_controller_handheld =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
emulated_controller_p1->SaveCurrentConfig();
emulated_controller_p1->EnableConfiguration();
emulated_controller_handheld->SaveCurrentConfig();
emulated_controller_handheld->EnableConfiguration();
if (emulated_controller_handheld->IsConnected(true)) {
emulated_controller_p1->Disconnect();
emulated_controller = emulated_controller_handheld;
auto* system_controller_p1 =
hid_core.GetSystemController(Core::HID::NpadIdType::Player1);
auto* system_controller_handheld =
hid_core.GetSystemController(Core::HID::NpadIdType::Handheld);
system_controller_p1->SaveCurrentConfig();
system_controller_p1->EnableConfiguration();
system_controller_handheld->SaveCurrentConfig();
system_controller_handheld->EnableConfiguration();
if (system_controller_handheld->IsConnected(true)) {
system_controller_p1->Disconnect();
system_controller = system_controller_handheld;
} else {
emulated_controller = emulated_controller_p1;
system_controller = system_controller_p1;
}
} else {
emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index);
emulated_controller->SaveCurrentConfig();
emulated_controller->EnableConfiguration();
system_controller = hid_core.GetSystemControllerByIndex(player_index);
system_controller->SaveCurrentConfig();
system_controller->EnableConfiguration();
}
ui->setupUi(this);
@ -357,7 +357,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
ui->controllerFrame->SetController(emulated_controller);
ui->controllerFrame->SetController(system_controller->GetNpadIdType());
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
auto* const button = button_map[button_id];
@ -370,7 +370,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
HandleClick(
button, button_id,
[=, this](const Common::ParamPackage& params) {
emulated_controller->SetButtonParam(button_id, params);
system_controller->SetButtonParam(button_id, params);
},
InputCommon::Polling::InputType::Button);
});
@ -379,9 +379,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
connect(button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) {
QMenu context_menu;
Common::ParamPackage param = emulated_controller->GetButtonParam(button_id);
Common::ParamPackage param = system_controller->GetButtonParam(button_id);
context_menu.addAction(tr("Clear"), [&] {
emulated_controller->SetButtonParam(button_id, {});
system_controller->SetButtonParam(button_id, {});
button_map[button_id]->setText(tr("[not set]"));
});
if (param.Has("code") || param.Has("button") || param.Has("hat")) {
@ -389,19 +389,19 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
const bool invert_value = !param.Get("inverted", false);
param.Set("inverted", invert_value);
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
system_controller->SetButtonParam(button_id, param);
});
context_menu.addAction(tr("Toggle button"), [&] {
const bool toggle_value = !param.Get("toggle", false);
param.Set("toggle", toggle_value);
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
system_controller->SetButtonParam(button_id, param);
});
context_menu.addAction(tr("Turbo button"), [&] {
const bool turbo_value = !param.Get("turbo", false);
param.Set("turbo", turbo_value);
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
system_controller->SetButtonParam(button_id, param);
});
}
if (param.Has("axis")) {
@ -409,13 +409,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
const bool toggle_value = !(param.Get("invert", "+") == "-");
param.Set("invert", toggle_value ? "-" : "+");
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
system_controller->SetButtonParam(button_id, param);
});
context_menu.addAction(tr("Invert button"), [&] {
const bool invert_value = !param.Get("inverted", false);
param.Set("inverted", invert_value);
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
system_controller->SetButtonParam(button_id, param);
});
context_menu.addAction(tr("Set threshold"), [&] {
const int button_threshold =
@ -431,13 +431,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
if (button_id == Settings::NativeButton::ZR) {
ui->sliderZRThreshold->setValue(new_threshold);
}
emulated_controller->SetButtonParam(button_id, param);
system_controller->SetButtonParam(button_id, param);
});
context_menu.addAction(tr("Toggle axis"), [&] {
const bool toggle_value = !param.Get("toggle", false);
param.Set("toggle", toggle_value);
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
system_controller->SetButtonParam(button_id, param);
});
}
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
@ -454,7 +454,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
HandleClick(
button, motion_id,
[=, this](const Common::ParamPackage& params) {
emulated_controller->SetMotionParam(motion_id, params);
system_controller->SetMotionParam(motion_id, params);
},
InputCommon::Polling::InputType::Motion);
});
@ -464,9 +464,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
connect(button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) {
QMenu context_menu;
Common::ParamPackage param = emulated_controller->GetMotionParam(motion_id);
Common::ParamPackage param = system_controller->GetMotionParam(motion_id);
context_menu.addAction(tr("Clear"), [&] {
emulated_controller->SetMotionParam(motion_id, {});
system_controller->SetMotionParam(motion_id, {});
motion_map[motion_id]->setText(tr("[not set]"));
});
if (param.Has("motion")) {
@ -477,7 +477,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
this, tr("Set threshold"), tr("Choose a value between 0% and 100%"),
gyro_threshold, 0, 100);
param.Set("threshold", new_threshold / 1000.0f);
emulated_controller->SetMotionParam(motion_id, param);
system_controller->SetMotionParam(motion_id, param);
});
context_menu.addAction(tr("Calibrate sensor"), [&] {
emulated_controller->StartMotionCalibration();
@ -489,21 +489,21 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] {
Common::ParamPackage param =
emulated_controller->GetButtonParam(Settings::NativeButton::ZL);
system_controller->GetButtonParam(Settings::NativeButton::ZL);
if (param.Has("threshold")) {
const auto slider_value = ui->sliderZLThreshold->value();
param.Set("threshold", slider_value / 100.0f);
emulated_controller->SetButtonParam(Settings::NativeButton::ZL, param);
system_controller->SetButtonParam(Settings::NativeButton::ZL, param);
}
});
connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] {
Common::ParamPackage param =
emulated_controller->GetButtonParam(Settings::NativeButton::ZR);
system_controller->GetButtonParam(Settings::NativeButton::ZR);
if (param.Has("threshold")) {
const auto slider_value = ui->sliderZRThreshold->value();
param.Set("threshold", slider_value / 100.0f);
emulated_controller->SetButtonParam(Settings::NativeButton::ZR, param);
system_controller->SetButtonParam(Settings::NativeButton::ZR, param);
}
});
@ -531,7 +531,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
HandleClick(
analog_map_buttons[analog_id][sub_button_id], analog_id,
[=, this](const Common::ParamPackage& params) {
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
Common::ParamPackage param = system_controller->GetStickParam(analog_id);
SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
// Correct axis direction for inverted sticks
if (input_subsystem->IsStickInverted(param)) {
@ -552,7 +552,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
break;
}
}
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
},
InputCommon::Polling::InputType::Stick);
});
@ -562,10 +562,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
connect(analog_button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) {
QMenu context_menu;
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
Common::ParamPackage param = system_controller->GetStickParam(analog_id);
context_menu.addAction(tr("Clear"), [&] {
if (param.Get("engine", "") != "analog_from_button") {
emulated_controller->SetStickParam(analog_id, {});
system_controller->SetStickParam(analog_id, {});
for (auto button : analog_map_buttons[analog_id]) {
button->setText(tr("[not set]"));
}
@ -585,12 +585,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
param.Erase("right");
break;
}
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
});
context_menu.addAction(tr("Center axis"), [&] {
const auto stick_value =
emulated_controller->GetSticksValues()[analog_id];
system_controller->GetSticksValues()[analog_id];
const float offset_x = stick_value.x.properties.offset;
const float offset_y = stick_value.y.properties.offset;
float raw_value_x = stick_value.x.raw_value;
@ -612,20 +612,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
param.Set("offset_x", -raw_value_x + offset_x);
param.Set("offset_y", -raw_value_y + offset_y);
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
});
context_menu.addAction(tr("Invert axis"), [&] {
if (sub_button_id == 2 || sub_button_id == 3) {
const bool invert_value = param.Get("invert_x", "+") == "-";
const std::string invert_str = invert_value ? "+" : "-";
param.Set("invert_x", invert_str);
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
}
if (sub_button_id == 0 || sub_button_id == 1) {
const bool invert_value = param.Get("invert_y", "+") == "-";
const std::string invert_str = invert_value ? "+" : "-";
param.Set("invert_y", invert_str);
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
}
for (int analog_sub_button_id = 0;
analog_sub_button_id < ANALOG_SUB_BUTTONS_NUM;
@ -644,9 +644,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
HandleClick(
analog_map_modifier_button[analog_id], analog_id,
[=, this](const Common::ParamPackage& params) {
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
Common::ParamPackage param = system_controller->GetStickParam(analog_id);
param.Set("modifier", params.Serialize());
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
},
InputCommon::Polling::InputType::Button);
});
@ -657,11 +657,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) {
QMenu context_menu;
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
Common::ParamPackage param = system_controller->GetStickParam(analog_id);
context_menu.addAction(tr("Clear"), [&] {
param.Set("modifier", "");
analog_map_modifier_button[analog_id]->setText(tr("[not set]"));
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
});
context_menu.addAction(tr("Toggle button"), [&] {
Common::ParamPackage modifier_param =
@ -670,7 +670,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
modifier_param.Set("toggle", toggle_value);
param.Set("modifier", modifier_param.Serialize());
analog_map_modifier_button[analog_id]->setText(ButtonToText(modifier_param));
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
});
context_menu.addAction(tr("Invert button"), [&] {
Common::ParamPackage modifier_param =
@ -679,7 +679,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
modifier_param.Set("inverted", invert_value);
param.Set("modifier", modifier_param.Serialize());
analog_map_modifier_button[analog_id]->setText(ButtonToText(modifier_param));
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
});
context_menu.exec(
analog_map_modifier_button[analog_id]->mapToGlobal(menu_location));
@ -687,27 +687,27 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
[=, this] {
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
Common::ParamPackage param = system_controller->GetStickParam(analog_id);
const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
param.Set("range", spinbox_value / 100.0f);
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
});
connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
Common::ParamPackage param = system_controller->GetStickParam(analog_id);
const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
param.Set("deadzone", slider_value / 100.0f);
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
});
connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
Common::ParamPackage param = system_controller->GetStickParam(analog_id);
const auto slider_value = analog_map_modifier_slider[analog_id]->value();
analog_map_modifier_label[analog_id]->setText(
tr("Modifier Range: %1%").arg(slider_value));
param.Set("modifier_scale", slider_value / 100.0f);
emulated_controller->SetStickParam(analog_id, param);
system_controller->SetStickParam(analog_id, param);
});
}
@ -749,28 +749,28 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
auto* emulated_controller_handheld =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
bool is_connected = emulated_controller->IsConnected(true);
auto* system_controller_p1 =
hid_core.GetSystemController(Core::HID::NpadIdType::Player1);
auto* system_controller_handheld =
hid_core.GetSystemController(Core::HID::NpadIdType::Handheld);
bool is_connected = system_controller->IsConnected(true);
emulated_controller_p1->SetNpadStyleIndex(type);
emulated_controller_handheld->SetNpadStyleIndex(type);
system_controller_p1->SetNpadStyleIndex(type);
system_controller_handheld->SetNpadStyleIndex(type);
if (is_connected) {
if (type == Core::HID::NpadStyleIndex::Handheld) {
emulated_controller_p1->Disconnect();
emulated_controller_handheld->Connect(true);
emulated_controller = emulated_controller_handheld;
system_controller_p1->Disconnect();
system_controller_handheld->Connect(true);
system_controller = system_controller_handheld;
} else {
emulated_controller_handheld->Disconnect();
emulated_controller_p1->Connect(true);
emulated_controller = emulated_controller_p1;
system_controller_handheld->Disconnect();
system_controller_p1->Connect(true);
system_controller = system_controller_p1;
}
}
ui->controllerFrame->SetController(emulated_controller);
ui->controllerFrame->SetController(system_controller->GetNpadIdType());
}
emulated_controller->SetNpadStyleIndex(type);
system_controller->SetNpadStyleIndex(type);
});
connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
@ -806,34 +806,33 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
ConfigureInputPlayer::~ConfigureInputPlayer() {
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
auto* emulated_controller_handheld =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
emulated_controller_p1->DisableConfiguration();
emulated_controller_handheld->DisableConfiguration();
auto* system_controller_p1 =
hid_core.GetSystemController(Core::HID::NpadIdType::Player1);
auto* system_controller_handheld =
hid_core.GetSystemController(Core::HID::NpadIdType::Handheld);
system_controller_p1->DisableConfiguration();
system_controller_handheld->DisableConfiguration();
} else {
emulated_controller->DisableConfiguration();
system_controller->DisableConfiguration();
}
}
void ConfigureInputPlayer::ApplyConfiguration() {
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
auto* emulated_controller_handheld =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
emulated_controller_p1->DisableConfiguration();
emulated_controller_p1->SaveCurrentConfig();
emulated_controller_p1->EnableConfiguration();
emulated_controller_handheld->DisableConfiguration();
emulated_controller_handheld->SaveCurrentConfig();
emulated_controller_handheld->EnableConfiguration();
auto* system_controller_p1 = hid_core.GetSystemController(Core::HID::NpadIdType::Player1);
auto* system_controller_handheld =
hid_core.GetSystemController(Core::HID::NpadIdType::Handheld);
system_controller_p1->DisableConfiguration();
system_controller_p1->SaveCurrentConfig();
system_controller_p1->EnableConfiguration();
system_controller_handheld->DisableConfiguration();
system_controller_handheld->SaveCurrentConfig();
system_controller_handheld->EnableConfiguration();
return;
}
emulated_controller->DisableConfiguration();
emulated_controller->SaveCurrentConfig();
emulated_controller->EnableConfiguration();
system_controller->DisableConfiguration();
system_controller->SaveCurrentConfig();
system_controller->EnableConfiguration();
}
void ConfigureInputPlayer::showEvent(QShowEvent* event) {
@ -858,7 +857,7 @@ void ConfigureInputPlayer::RetranslateUI() {
}
void ConfigureInputPlayer::LoadConfiguration() {
emulated_controller->ReloadFromSettings();
system_controller->ReloadFromSettings();
UpdateUI();
UpdateInputDeviceCombobox();
@ -868,17 +867,17 @@ void ConfigureInputPlayer::LoadConfiguration() {
}
const int comboBoxIndex =
GetIndexFromControllerType(emulated_controller->GetNpadStyleIndex(true));
GetIndexFromControllerType(system_controller->GetNpadStyleIndex(true));
ui->comboControllerType->setCurrentIndex(comboBoxIndex);
ui->groupConnectedController->setChecked(emulated_controller->IsConnected(true));
ui->groupConnectedController->setChecked(system_controller->IsConnected(true));
}
void ConfigureInputPlayer::ConnectPlayer(bool connected) {
ui->groupConnectedController->setChecked(connected);
if (connected) {
emulated_controller->Connect(true);
system_controller->Connect(true);
} else {
emulated_controller->Disconnect();
system_controller->Disconnect();
}
}
@ -889,7 +888,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
return;
}
const auto devices = emulated_controller->GetMappedDevices();
const auto devices = system_controller->GetMappedDevices();
UpdateInputDevices();
if (devices.empty()) {
@ -968,7 +967,7 @@ void ConfigureInputPlayer::ClearAll() {
if (button == nullptr) {
continue;
}
emulated_controller->SetButtonParam(button_id, {});
system_controller->SetButtonParam(button_id, {});
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
@ -977,7 +976,7 @@ void ConfigureInputPlayer::ClearAll() {
if (analog_button == nullptr) {
continue;
}
emulated_controller->SetStickParam(analog_id, {});
system_controller->SetStickParam(analog_id, {});
}
}
@ -986,7 +985,7 @@ void ConfigureInputPlayer::ClearAll() {
if (motion_button == nullptr) {
continue;
}
emulated_controller->SetMotionParam(motion_id, {});
system_controller->SetMotionParam(motion_id, {});
}
UpdateUI();
@ -995,31 +994,31 @@ void ConfigureInputPlayer::ClearAll() {
void ConfigureInputPlayer::UpdateUI() {
for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
const Common::ParamPackage param = emulated_controller->GetButtonParam(button);
const Common::ParamPackage param = system_controller->GetButtonParam(button);
button_map[button]->setText(ButtonToText(param));
}
const Common::ParamPackage ZL_param =
emulated_controller->GetButtonParam(Settings::NativeButton::ZL);
system_controller->GetButtonParam(Settings::NativeButton::ZL);
if (ZL_param.Has("threshold")) {
const int button_threshold = static_cast<int>(ZL_param.Get("threshold", 0.5f) * 100.0f);
ui->sliderZLThreshold->setValue(button_threshold);
}
const Common::ParamPackage ZR_param =
emulated_controller->GetButtonParam(Settings::NativeButton::ZR);
system_controller->GetButtonParam(Settings::NativeButton::ZR);
if (ZR_param.Has("threshold")) {
const int button_threshold = static_cast<int>(ZR_param.Get("threshold", 0.5f) * 100.0f);
ui->sliderZRThreshold->setValue(button_threshold);
}
for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
const Common::ParamPackage param = emulated_controller->GetMotionParam(motion_id);
const Common::ParamPackage param = system_controller->GetMotionParam(motion_id);
motion_map[motion_id]->setText(ButtonToText(param));
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
const Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
const Common::ParamPackage param = system_controller->GetStickParam(analog_id);
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
@ -1347,7 +1346,7 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
if (button == nullptr) {
continue;
}
emulated_controller->SetButtonParam(button_id, {});
system_controller->SetButtonParam(button_id, {});
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
@ -1356,7 +1355,7 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
if (analog_button == nullptr) {
continue;
}
emulated_controller->SetStickParam(analog_id, {});
system_controller->SetStickParam(analog_id, {});
}
}
@ -1365,13 +1364,13 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
if (motion_button == nullptr) {
continue;
}
emulated_controller->SetMotionParam(motion_id, {});
system_controller->SetMotionParam(motion_id, {});
}
// Reset keyboard or mouse bindings
if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) {
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
emulated_controller->SetButtonParam(
system_controller->SetButtonParam(
button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
Config::default_buttons[button_id])});
}
@ -1385,11 +1384,11 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
Config::default_stick_mod[analog_id]));
emulated_controller->SetStickParam(analog_id, analog_param);
system_controller->SetStickParam(analog_id, analog_param);
}
for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
emulated_controller->SetMotionParam(
system_controller->SetMotionParam(
motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
Config::default_motions[motion_id])});
}
@ -1409,15 +1408,15 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
for (const auto& button_mapping : button_mappings) {
const std::size_t index = button_mapping.first;
emulated_controller->SetButtonParam(index, button_mapping.second);
system_controller->SetButtonParam(index, button_mapping.second);
}
for (const auto& analog_mapping : analog_mappings) {
const std::size_t index = analog_mapping.first;
emulated_controller->SetStickParam(index, analog_mapping.second);
system_controller->SetStickParam(index, analog_mapping.second);
}
for (const auto& motion_mapping : motion_mappings) {
const std::size_t index = motion_mapping.first;
emulated_controller->SetMotionParam(index, motion_mapping.second);
system_controller->SetMotionParam(index, motion_mapping.second);
}
UpdateUI();
@ -1609,7 +1608,7 @@ void ConfigureInputPlayer::SaveProfile() {
ApplyConfiguration();
// When we're in handheld mode, only the handheld emulated controller bindings are updated
const bool is_handheld = player_index == 0 && emulated_controller->GetNpadIdType() ==
const bool is_handheld = player_index == 0 && system_controller->GetNpadIdType() ==
Core::HID::NpadIdType::Handheld;
const auto profile_player_index = is_handheld ? HANDHELD_INDEX : player_index;

View File

@ -42,7 +42,7 @@ class ConfigureInputPlayer;
namespace Core::HID {
class HIDCore;
class EmulatedController;
class SystemController;
enum class NpadStyleIndex : u8;
} // namespace Core::HID
@ -189,7 +189,7 @@ private:
/// This will be the the setting function when an input is awaiting configuration.
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
Core::HID::EmulatedController* emulated_controller;
Core::HID::SystemController* system_controller;
static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;