summaryrefslogtreecommitdiffstats
path: root/src/core/hid
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hid')
-rw-r--r--src/core/hid/emulated_console.cpp37
-rw-r--r--src/core/hid/emulated_console.h6
-rw-r--r--src/core/hid/emulated_controller.cpp625
-rw-r--r--src/core/hid/emulated_controller.h95
-rw-r--r--src/core/hid/emulated_devices.cpp83
-rw-r--r--src/core/hid/emulated_devices.h40
-rw-r--r--src/core/hid/hid_core.cpp9
-rw-r--r--src/core/hid/hid_core.h5
-rw-r--r--src/core/hid/hid_types.h134
-rw-r--r--src/core/hid/input_converter.cpp44
-rw-r--r--src/core/hid/input_converter.h21
-rw-r--r--src/core/hid/input_interpreter.cpp5
-rw-r--r--src/core/hid/input_interpreter.h5
-rw-r--r--src/core/hid/irs_types.h301
-rw-r--r--src/core/hid/motion_input.cpp5
-rw-r--r--src/core/hid/motion_input.h5
16 files changed, 1138 insertions, 282 deletions
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index eef0ff493..aac45907d 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/hid/emulated_console.h"
@@ -28,12 +27,19 @@ void EmulatedConsole::SetTouchParams() {
// We can't use mouse as touch if native mouse is enabled
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
}
- touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
- touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
+
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
touch_params[index++] =
- Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
touch_params[index++] =
- Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
const auto button_index =
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
@@ -132,7 +138,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
}
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
auto& raw_status = console.motion_values.raw_status;
auto& emulated = console.motion_values.emulated;
@@ -151,6 +157,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
emulated.UpdateOrientation(raw_status.delta_timestamp);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
return;
}
@@ -166,6 +173,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
// Find what is this value
motion.verticalization_error = 0.0f;
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
}
@@ -173,11 +181,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
if (index >= console.touch_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
console.touch_values[index] = TransformToTouch(callback);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
return;
}
@@ -189,26 +198,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
.pressed = console.touch_values[index].pressed.value,
};
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
}
ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
+ std::scoped_lock lock{mutex};
return console.motion_values;
}
TouchValues EmulatedConsole::GetTouchValues() const {
+ std::scoped_lock lock{mutex};
return console.touch_values;
}
ConsoleMotion EmulatedConsole::GetMotion() const {
+ std::scoped_lock lock{mutex};
return console.motion_state;
}
TouchFingerState EmulatedConsole::GetTouch() const {
+ std::scoped_lock lock{mutex};
return console.touch_state;
}
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ConsoleUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
@@ -218,13 +233,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
}
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, update_callback);
return last_callback_key++;
}
void EmulatedConsole::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ 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);
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 5eb170823..1c510cd19 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -183,6 +182,7 @@ private:
TouchDevices touch_devices;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, ConsoleUpdateCallback> callback_list;
int last_callback_key = 0;
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 2bee173b3..025f1c78e 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -1,7 +1,7 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/thread.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/input_converter.h"
@@ -85,23 +85,26 @@ void EmulatedController::ReloadFromSettings() {
motion_params[index] = Common::ParamPackage(player.motions[index]);
}
+ controller.colors_state.fullkey = {
+ .body = GetNpadColor(player.body_color_left),
+ .button = GetNpadColor(player.button_color_left),
+ };
controller.colors_state.left = {
- .body = player.body_color_left,
- .button = player.button_color_left,
+ .body = GetNpadColor(player.body_color_left),
+ .button = GetNpadColor(player.button_color_left),
};
-
controller.colors_state.right = {
- .body = player.body_color_right,
- .button = player.button_color_right,
+ .body = GetNpadColor(player.body_color_right),
+ .button = GetNpadColor(player.button_color_right),
};
- controller.colors_state.fullkey = controller.colors_state.left;
-
// 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;
}
if (player.connected) {
@@ -127,10 +130,17 @@ void EmulatedController::LoadDevices() {
battery_params[LeftIndex].Set("battery", true);
battery_params[RightIndex].Set("battery", true);
+ camera_params = Common::ParamPackage{"engine:camera,camera:1"};
+ nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
+
output_params[LeftIndex] = left_joycon;
output_params[RightIndex] = right_joycon;
+ output_params[2] = camera_params;
+ output_params[3] = nfc_params;
output_params[LeftIndex].Set("output", true);
output_params[RightIndex].Set("output", true);
+ output_params[2].Set("output", true);
+ output_params[3].Set("output", true);
LoadTASParams();
@@ -147,6 +157,8 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
+ camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
+ nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params);
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
Common::Input::CreateDevice<Common::Input::OutputDevice>);
@@ -268,6 +280,24 @@ void EmulatedController::ReloadInput() {
motion_devices[index]->ForceUpdate();
}
+ if (camera_devices) {
+ camera_devices->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
+ });
+ camera_devices->ForceUpdate();
+ }
+
+ if (nfc_devices) {
+ if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) {
+ nfc_devices->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
+ });
+ nfc_devices->ForceUpdate();
+ }
+ }
+
// Use a common UUID for TAS
static constexpr Common::UUID TAS_UUID = Common::UUID{
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -323,6 +353,8 @@ void EmulatedController::UnloadInput() {
for (auto& stick : tas_stick_devices) {
stick.reset();
}
+ camera_devices.reset();
+ nfc_devices.reset();
}
void EmulatedController::EnableConfiguration() {
@@ -340,6 +372,7 @@ void EmulatedController::DisableConfiguration() {
Disconnect();
}
SetNpadStyleIndex(tmp_npad_type);
+ original_npad_type = tmp_npad_type;
}
// Apply temporary connected status to the real controller
@@ -353,14 +386,17 @@ void EmulatedController::DisableConfiguration() {
}
void EmulatedController::EnableSystemButtons() {
+ std::scoped_lock lock{mutex};
system_buttons_enabled = true;
}
void EmulatedController::DisableSystemButtons() {
+ std::scoped_lock lock{mutex};
system_buttons_enabled = false;
}
void EmulatedController::ResetSystemButtons() {
+ std::scoped_lock lock{mutex};
controller.home_button_state.home.Assign(false);
controller.capture_button_state.capture.Assign(false);
}
@@ -494,139 +530,151 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
if (index >= controller.button_values.size()) {
return;
}
- {
- std::lock_guard lock{mutex};
- bool value_changed = false;
- const auto new_status = TransformToButton(callback);
- auto& current_status = controller.button_values[index];
+ 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;
- }
+ // 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.uuid = uuid;
+ current_status.toggle = new_status.toggle;
+ 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;
- }
+ // 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;
- }
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
}
+ }
+
+ if (!value_changed) {
+ return;
+ }
- if (!value_changed) {
+ if (is_configuring) {
+ controller.npad_button_state.raw = NpadButton::None;
+ controller.debug_pad_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 (is_configuring) {
- controller.npad_button_state.raw = NpadButton::None;
- controller.debug_pad_button_state.raw = 0;
- TriggerOnChange(ControllerTriggerType::Button, false);
+ if (index == Settings::NativeButton::ZL) {
return;
}
+ }
- switch (index) {
- case Settings::NativeButton::A:
- controller.npad_button_state.a.Assign(current_status.value);
- controller.debug_pad_button_state.a.Assign(current_status.value);
- break;
- case Settings::NativeButton::B:
- controller.npad_button_state.b.Assign(current_status.value);
- controller.debug_pad_button_state.b.Assign(current_status.value);
- break;
- case Settings::NativeButton::X:
- controller.npad_button_state.x.Assign(current_status.value);
- controller.debug_pad_button_state.x.Assign(current_status.value);
- break;
- case Settings::NativeButton::Y:
- controller.npad_button_state.y.Assign(current_status.value);
- controller.debug_pad_button_state.y.Assign(current_status.value);
- break;
- case Settings::NativeButton::LStick:
- controller.npad_button_state.stick_l.Assign(current_status.value);
- break;
- case Settings::NativeButton::RStick:
- controller.npad_button_state.stick_r.Assign(current_status.value);
- break;
- case Settings::NativeButton::L:
- controller.npad_button_state.l.Assign(current_status.value);
- controller.debug_pad_button_state.l.Assign(current_status.value);
- break;
- case Settings::NativeButton::R:
- controller.npad_button_state.r.Assign(current_status.value);
- controller.debug_pad_button_state.r.Assign(current_status.value);
- break;
- case Settings::NativeButton::ZL:
- controller.npad_button_state.zl.Assign(current_status.value);
- controller.debug_pad_button_state.zl.Assign(current_status.value);
- break;
- case Settings::NativeButton::ZR:
- controller.npad_button_state.zr.Assign(current_status.value);
- controller.debug_pad_button_state.zr.Assign(current_status.value);
- break;
- case Settings::NativeButton::Plus:
- controller.npad_button_state.plus.Assign(current_status.value);
- controller.debug_pad_button_state.plus.Assign(current_status.value);
- break;
- case Settings::NativeButton::Minus:
- controller.npad_button_state.minus.Assign(current_status.value);
- controller.debug_pad_button_state.minus.Assign(current_status.value);
- break;
- case Settings::NativeButton::DLeft:
- controller.npad_button_state.left.Assign(current_status.value);
- controller.debug_pad_button_state.d_left.Assign(current_status.value);
- break;
- case Settings::NativeButton::DUp:
- controller.npad_button_state.up.Assign(current_status.value);
- controller.debug_pad_button_state.d_up.Assign(current_status.value);
- break;
- case Settings::NativeButton::DRight:
- controller.npad_button_state.right.Assign(current_status.value);
- controller.debug_pad_button_state.d_right.Assign(current_status.value);
- break;
- case Settings::NativeButton::DDown:
- controller.npad_button_state.down.Assign(current_status.value);
- controller.debug_pad_button_state.d_down.Assign(current_status.value);
- break;
- case Settings::NativeButton::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);
+ switch (index) {
+ case Settings::NativeButton::A:
+ controller.npad_button_state.a.Assign(current_status.value);
+ controller.debug_pad_button_state.a.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::B:
+ controller.npad_button_state.b.Assign(current_status.value);
+ controller.debug_pad_button_state.b.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::X:
+ controller.npad_button_state.x.Assign(current_status.value);
+ controller.debug_pad_button_state.x.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Y:
+ controller.npad_button_state.y.Assign(current_status.value);
+ controller.debug_pad_button_state.y.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::LStick:
+ controller.npad_button_state.stick_l.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::RStick:
+ controller.npad_button_state.stick_r.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::L:
+ controller.npad_button_state.l.Assign(current_status.value);
+ controller.debug_pad_button_state.l.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::R:
+ controller.npad_button_state.r.Assign(current_status.value);
+ controller.debug_pad_button_state.r.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::ZL:
+ controller.npad_button_state.zl.Assign(current_status.value);
+ controller.debug_pad_button_state.zl.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::ZR:
+ controller.npad_button_state.zr.Assign(current_status.value);
+ controller.debug_pad_button_state.zr.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Plus:
+ controller.npad_button_state.plus.Assign(current_status.value);
+ controller.debug_pad_button_state.plus.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Minus:
+ controller.npad_button_state.minus.Assign(current_status.value);
+ controller.debug_pad_button_state.minus.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DLeft:
+ controller.npad_button_state.left.Assign(current_status.value);
+ controller.debug_pad_button_state.d_left.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DUp:
+ controller.npad_button_state.up.Assign(current_status.value);
+ controller.debug_pad_button_state.d_up.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DRight:
+ controller.npad_button_state.right.Assign(current_status.value);
+ controller.debug_pad_button_state.d_right.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DDown:
+ controller.npad_button_state.down.Assign(current_status.value);
+ controller.debug_pad_button_state.d_down.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::SL:
+ controller.npad_button_state.left_sl.Assign(current_status.value);
+ controller.npad_button_state.right_sl.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::SR:
+ controller.npad_button_state.left_sr.Assign(current_status.value);
+ controller.npad_button_state.right_sr.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Home:
+ if (!system_buttons_enabled) {
break;
- case Settings::NativeButton::Screenshot:
- if (!system_buttons_enabled) {
- break;
- }
- controller.capture_button_state.capture.Assign(current_status.value);
+ }
+ 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();
@@ -643,7 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (index >= controller.stick_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_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
@@ -659,6 +707,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (is_configuring) {
controller.analog_stick_state.left = {};
controller.analog_stick_state.right = {};
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Stick, false);
return;
}
@@ -685,6 +734,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
break;
}
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Stick, true);
}
@@ -693,7 +743,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (index >= controller.trigger_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto trigger_value = TransformToTrigger(callback);
// Only read trigger values that have the same uuid or are pressed once
@@ -709,10 +759,16 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (is_configuring) {
controller.gc_trigger_state.left = 0;
controller.gc_trigger_state.right = 0;
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Trigger, false);
return;
}
+ // Only GC controllers have analog triggers
+ if (npad_type != NpadStyleIndex::GameCube) {
+ return;
+ }
+
const auto& trigger = controller.trigger_values[index];
switch (index) {
@@ -727,6 +783,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
break;
}
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Trigger, true);
}
@@ -735,7 +792,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
if (index >= controller.motion_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
auto& raw_status = controller.motion_values[index].raw_status;
auto& emulated = controller.motion_values[index].emulated;
@@ -756,6 +813,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
force_update_motion = raw_status.force_update;
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Motion, false);
return;
}
@@ -767,6 +825,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Motion, true);
}
@@ -775,10 +834,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
if (index >= controller.battery_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
controller.battery_values[index] = TransformToBattery(callback);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Battery, false);
return;
}
@@ -835,9 +895,49 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
};
break;
}
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Battery, true);
}
+void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
+ std::unique_lock lock{mutex};
+ controller.camera_values = TransformToCamera(callback);
+
+ if (is_configuring) {
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::IrSensor, false);
+ 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;
+
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::IrSensor, true);
+}
+
+void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
+ std::unique_lock lock{mutex};
+ controller.nfc_values = TransformToNfc(callback);
+
+ if (is_configuring) {
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Nfc, false);
+ return;
+ }
+
+ controller.nfc_state = {
+ controller.nfc_values.state,
+ controller.nfc_values.data,
+ };
+
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Nfc, true);
+}
+
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
if (device_index >= output_devices.size()) {
return false;
@@ -871,18 +971,100 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
}
bool EmulatedController::TestVibration(std::size_t device_index) {
- static constexpr VibrationValue test_vibration = {
+ if (device_index >= output_devices.size()) {
+ return false;
+ }
+ if (!output_devices[device_index]) {
+ return false;
+ }
+
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ const auto& player = Settings::values.players.GetValue()[player_index];
+
+ if (!player.vibration_enabled) {
+ return false;
+ }
+
+ const Common::Input::VibrationStatus test_vibration = {
.low_amplitude = 0.001f,
- .low_frequency = 160.0f,
+ .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
.high_amplitude = 0.001f,
- .high_frequency = 320.0f,
+ .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
+ .type = Common::Input::VibrationAmplificationType::Test,
+ };
+
+ const Common::Input::VibrationStatus zero_vibration = {
+ .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
+ .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
+ .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
+ .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
+ .type = Common::Input::VibrationAmplificationType::Test,
};
// Send a slight vibration to test for rumble support
- SetVibration(device_index, test_vibration);
+ output_devices[device_index]->SetVibration(test_vibration);
+
+ // Wait for about 15ms to ensure the controller is ready for the stop command
+ std::this_thread::sleep_for(std::chrono::milliseconds(15));
// Stop any vibration and return the result
- return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
+ return output_devices[device_index]->SetVibration(zero_vibration) ==
+ Common::Input::VibrationError::None;
+}
+
+bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
+ LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
+ auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
+ auto& nfc_output_device = output_devices[3];
+
+ const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
+ const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode);
+
+ return virtual_nfc_result == Common::Input::PollingError::None ||
+ mapped_nfc_result == Common::Input::PollingError::None;
+}
+
+bool EmulatedController::SetCameraFormat(
+ Core::IrSensor::ImageTransferProcessorFormat camera_format) {
+ LOG_INFO(Service_HID, "Set camera format {}", camera_format);
+
+ auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
+ auto& camera_output_device = output_devices[2];
+
+ if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
+ camera_format)) == Common::Input::CameraError::None) {
+ return true;
+ }
+
+ // Fallback to Qt camera if native device doesn't have support
+ return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
+ camera_format)) == Common::Input::CameraError::None;
+}
+
+bool EmulatedController::HasNfc() const {
+ const auto& nfc_output_device = output_devices[3];
+
+ switch (npad_type) {
+ case NpadStyleIndex::JoyconRight:
+ case NpadStyleIndex::JoyconDual:
+ case NpadStyleIndex::ProController:
+ break;
+ default:
+ return false;
+ }
+
+ const bool has_virtual_nfc =
+ npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld;
+ const bool is_virtual_nfc_supported =
+ nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported;
+
+ return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);
+}
+
+bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
+ auto& nfc_output_device = output_devices[3];
+
+ return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
}
void EmulatedController::SetLedPattern() {
@@ -907,13 +1089,27 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag 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
+ // 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);
@@ -921,11 +1117,28 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
return;
}
+ // Fallback Dual joycon controllers to Pro controllers
+ if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
+ LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
+ SetNpadStyleIndex(NpadStyleIndex::ProController);
+ Connect();
+ return;
+ }
+
+ // Fallback Pro controllers to Dual joycon
+ if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) {
+ LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
+ SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
+ Connect();
+ return;
+ }
+
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
npad_type);
}
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
+ std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
@@ -941,6 +1154,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
}
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
+ std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
@@ -976,40 +1190,44 @@ void EmulatedController::Connect(bool use_temporary_value) {
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
return;
}
- {
- std::lock_guard lock{mutex};
- if (is_configuring) {
- tmp_is_connected = true;
- TriggerOnChange(ControllerTriggerType::Connected, false);
- return;
- }
- if (is_connected) {
- return;
- }
- is_connected = true;
+ std::unique_lock lock{mutex};
+ if (is_configuring) {
+ tmp_is_connected = true;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Connected, false);
+ return;
+ }
+
+ if (is_connected) {
+ return;
}
+ is_connected = true;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Connected, true);
}
void EmulatedController::Disconnect() {
- {
- std::lock_guard lock{mutex};
- if (is_configuring) {
- tmp_is_connected = false;
- TriggerOnChange(ControllerTriggerType::Disconnected, false);
- return;
- }
+ std::unique_lock lock{mutex};
+ if (is_configuring) {
+ tmp_is_connected = false;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Disconnected, false);
+ return;
+ }
- if (!is_connected) {
- return;
- }
- is_connected = false;
+ if (!is_connected) {
+ return;
}
+ is_connected = false;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Disconnected, true);
}
bool EmulatedController::IsConnected(bool get_temporary_value) const {
+ std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_is_connected;
}
@@ -1023,10 +1241,12 @@ bool EmulatedController::IsVibrationEnabled() const {
}
NpadIdType EmulatedController::GetNpadIdType() const {
+ std::scoped_lock lock{mutex};
return npad_id_type;
}
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
+ std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_npad_type;
}
@@ -1034,27 +1254,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
}
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
- {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
- if (is_configuring) {
- if (tmp_npad_type == npad_type_) {
- return;
- }
- tmp_npad_type = npad_type_;
- TriggerOnChange(ControllerTriggerType::Type, false);
+ if (is_configuring) {
+ if (tmp_npad_type == npad_type_) {
return;
}
+ tmp_npad_type = npad_type_;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Type, false);
+ return;
+ }
- if (npad_type == npad_type_) {
- return;
- }
- if (is_connected) {
- LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
- NpadIdTypeToIndex(npad_id_type));
- }
- npad_type = npad_type_;
+ if (npad_type == npad_type_) {
+ return;
+ }
+ if (is_connected) {
+ LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
+ NpadIdTypeToIndex(npad_id_type));
}
+ npad_type = npad_type_;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Type, true);
}
@@ -1082,30 +1303,42 @@ LedPattern EmulatedController::GetLedPattern() const {
}
ButtonValues EmulatedController::GetButtonsValues() const {
+ std::scoped_lock lock{mutex};
return controller.button_values;
}
SticksValues EmulatedController::GetSticksValues() const {
+ std::scoped_lock lock{mutex};
return controller.stick_values;
}
TriggerValues EmulatedController::GetTriggersValues() const {
+ std::scoped_lock lock{mutex};
return controller.trigger_values;
}
ControllerMotionValues EmulatedController::GetMotionValues() const {
+ std::scoped_lock lock{mutex};
return controller.motion_values;
}
ColorValues EmulatedController::GetColorsValues() const {
+ std::scoped_lock lock{mutex};
return controller.color_values;
}
BatteryValues EmulatedController::GetBatteryValues() const {
+ std::scoped_lock lock{mutex};
return controller.battery_values;
}
+CameraValues EmulatedController::GetCameraValues() const {
+ std::scoped_lock lock{mutex};
+ return controller.camera_values;
+}
+
HomeButtonState EmulatedController::GetHomeButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1113,6 +1346,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
}
CaptureButtonState EmulatedController::GetCaptureButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1120,6 +1354,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
}
NpadButtonState EmulatedController::GetNpadButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1127,6 +1362,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
}
DebugPadButton EmulatedController::GetDebugPadButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1134,20 +1370,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
}
AnalogSticks EmulatedController::GetSticks() const {
+ std::unique_lock lock{mutex};
+
if (is_configuring) {
return {};
}
+
// Some drivers like stick from buttons need constant refreshing
for (auto& device : stick_devices) {
if (!device) {
continue;
}
+ lock.unlock();
device->SoftUpdate();
+ lock.lock();
}
+
return controller.analog_stick_state;
}
NpadGcTriggerState EmulatedController::GetTriggers() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1155,26 +1398,54 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
}
MotionState EmulatedController::GetMotions() const {
+ std::unique_lock lock{mutex};
+
+ // Some drivers like mouse motion need constant refreshing
if (force_update_motion) {
for (auto& device : motion_devices) {
if (!device) {
continue;
}
+ lock.unlock();
device->ForceUpdate();
+ lock.lock();
}
}
+
return controller.motion_state;
}
ControllerColors EmulatedController::GetColors() const {
+ std::scoped_lock lock{mutex};
return controller.colors_state;
}
BatteryLevelState EmulatedController::GetBattery() const {
+ std::scoped_lock lock{mutex};
return controller.battery_state;
}
+const CameraState& EmulatedController::GetCamera() const {
+ std::scoped_lock lock{mutex};
+ return controller.camera_state;
+}
+
+const NfcState& EmulatedController::GetNfc() const {
+ std::scoped_lock lock{mutex};
+ return controller.nfc_state;
+}
+
+NpadColor EmulatedController::GetNpadColor(u32 color) {
+ return {
+ .r = static_cast<u8>((color >> 16) & 0xFF),
+ .g = static_cast<u8>((color >> 8) & 0xFF),
+ .b = static_cast<u8>(color & 0xFF),
+ .a = 0xff,
+ };
+}
+
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ControllerUpdateCallback& poller = poller_pair.second;
if (!is_npad_service_update && poller.is_npad_service) {
@@ -1187,13 +1458,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
}
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedController::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ 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);
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index d8642c5b3..319226bf8 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -16,10 +15,12 @@
#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{};
@@ -35,15 +36,18 @@ using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
using BatteryDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
-using OutputDevices =
- std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
+using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
+using NfcDevices = std::unique_ptr<Common::Input::InputDevice>;
+using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
-using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
+using CameraParams = Common::ParamPackage;
+using NfcParams = Common::ParamPackage;
+using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
@@ -52,6 +56,8 @@ using TriggerValues =
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
+using CameraValues = Common::Input::CameraStatus;
+using NfcValues = Common::Input::NfcStatus;
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
struct AnalogSticks {
@@ -71,6 +77,17 @@ struct BatteryLevelState {
NpadPowerInfo right{};
};
+struct CameraState {
+ Core::IrSensor::ImageTransferProcessorFormat format{};
+ std::vector<u8> data{};
+ std::size_t sample{};
+};
+
+struct NfcState {
+ Common::Input::NfcState state{};
+ std::vector<u8> data{};
+};
+
struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
@@ -97,6 +114,8 @@ struct ControllerStatus {
ColorValues color_values{};
BatteryValues battery_values{};
VibrationValues vibration_values{};
+ CameraValues camera_values{};
+ NfcValues nfc_values{};
// Data for HID serices
HomeButtonState home_button_state{};
@@ -108,6 +127,8 @@ struct ControllerStatus {
NpadGcTriggerState gc_trigger_state{};
ControllerColors colors_state{};
BatteryLevelState battery_state{};
+ CameraState camera_state{};
+ NfcState nfc_state{};
};
enum class ControllerTriggerType {
@@ -118,6 +139,8 @@ enum class ControllerTriggerType {
Color,
Battery,
Vibration,
+ IrSensor,
+ Nfc,
Connected,
Disconnected,
Type,
@@ -270,6 +293,9 @@ public:
/// 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 button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
@@ -297,18 +323,44 @@ public:
/// Returns the latest battery status from the controller
BatteryLevelState GetBattery() const;
+ /// Returns the latest camera status from the controller
+ const CameraState& GetCamera() const;
+
+ /// Returns the latest ntag status from the controller
+ const NfcState& GetNfc() const;
+
/**
* Sends a specific vibration to the output device
- * @return returns true if vibration had no errors
+ * @return true if vibration had no errors
*/
bool SetVibration(std::size_t device_index, VibrationValue vibration);
/**
* Sends a small vibration to the output device
- * @return returns true if SetVibration was successfull
+ * @return true if SetVibration was successfull
*/
bool TestVibration(std::size_t device_index);
+ /**
+ * Sets the desired data to be polled from a controller
+ * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
+ * @return true if SetPollingMode was successfull
+ */
+ bool SetPollingMode(Common::Input::PollingMode polling_mode);
+
+ /**
+ * Sets the desired camera format to be polled from a controller
+ * @param camera_format size of each frame
+ * @return true if SetCameraFormat was successfull
+ */
+ bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
+
+ /// Returns true if the device has nfc support
+ bool HasNfc() const;
+
+ /// Returns true if the nfc tag was written
+ bool WriteNfc(const std::vector<u8>& data);
+
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;
@@ -387,14 +439,34 @@ private:
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
+ * Updates the camera status of the controller
+ * @param callback A CallbackStatus containing the camera status
+ */
+ void SetCamera(const Common::Input::CallbackStatus& callback);
+
+ /**
+ * Updates the nfc status of the controller
+ * @param callback A CallbackStatus containing the nfc status
+ */
+ void SetNfc(const Common::Input::CallbackStatus& callback);
+
+ /**
+ * Converts a color format from bgra to rgba
+ * @param color in bgra format
+ * @return NpadColor in rgba format
+ */
+ NpadColor GetNpadColor(u32 color);
+
+ /**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
* @param is_service_update indicates if this event should only be sent to HID services
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
- NpadIdType npad_id_type;
+ 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};
@@ -411,6 +483,8 @@ private:
ControllerMotionParams motion_params;
TriggerParams trigger_params;
BatteryParams battery_params;
+ CameraParams camera_params;
+ NfcParams nfc_params;
OutputParams output_params;
ButtonDevices button_devices;
@@ -418,6 +492,8 @@ private:
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
BatteryDevices battery_devices;
+ CameraDevices camera_devices;
+ NfcDevices nfc_devices;
OutputDevices output_devices;
// TAS related variables
@@ -427,6 +503,7 @@ private:
StickDevices tas_stick_devices;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0;
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index 708480f2d..8d367b546 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <fmt/format.h>
@@ -15,6 +14,7 @@ EmulatedDevices::EmulatedDevices() = default;
EmulatedDevices::~EmulatedDevices() = default;
void EmulatedDevices::ReloadFromSettings() {
+ ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
ReloadInput();
}
@@ -66,6 +66,8 @@ void EmulatedDevices::ReloadInput() {
key_index++;
}
+ ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
+
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
if (!mouse_button_devices[index]) {
continue;
@@ -120,6 +122,13 @@ void EmulatedDevices::ReloadInput() {
},
});
}
+
+ if (ring_analog_device) {
+ ring_analog_device->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
+ });
+ }
}
void EmulatedDevices::UnloadInput() {
@@ -155,6 +164,7 @@ void EmulatedDevices::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
+ Settings::values.ringcon_analogs = ring_params.Serialize();
}
void EmulatedDevices::RestoreConfig() {
@@ -164,12 +174,21 @@ void EmulatedDevices::RestoreConfig() {
ReloadFromSettings();
}
+Common::ParamPackage EmulatedDevices::GetRingParam() const {
+ return ring_params;
+}
+
+void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
+ ring_params = std::move(param);
+ ReloadInput();
+}
+
void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.keyboard_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_values[index];
@@ -201,6 +220,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
return;
}
@@ -208,6 +228,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
// Index should be converted from NativeKeyboard to KeyboardKeyIndex
UpdateKey(index, current_status.value);
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
}
@@ -227,7 +248,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
if (index >= device_status.keyboard_moddifier_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_moddifier_values[index];
@@ -259,6 +280,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
return;
}
@@ -289,6 +311,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
}
@@ -297,7 +320,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
if (index >= device_status.mouse_button_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.mouse_button_values[index];
@@ -329,6 +352,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -351,6 +375,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
@@ -359,13 +384,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
if (index >= device_status.mouse_analog_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto analog_value = TransformToAnalog(callback);
device_status.mouse_analog_values[index] = analog_value;
if (is_configuring) {
device_status.mouse_position_state = {};
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -379,17 +405,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto touch_value = TransformToTouch(callback);
device_status.mouse_stick_value = touch_value;
if (is_configuring) {
device_status.mouse_position_state = {};
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -397,42 +425,77 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
device_status.mouse_position_state.x = touch_value.x.value;
device_status.mouse_position_state.y = touch_value.y.value;
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
+void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
+ std::lock_guard lock{mutex};
+ const auto force_value = TransformToStick(callback);
+
+ device_status.ring_analog_value = force_value.x;
+
+ if (is_configuring) {
+ device_status.ring_analog_value = {};
+ TriggerOnChange(DeviceTriggerType::RingController);
+ return;
+ }
+
+ device_status.ring_analog_state.force = force_value.x.value;
+
+ TriggerOnChange(DeviceTriggerType::RingController);
+}
+
KeyboardValues EmulatedDevices::GetKeyboardValues() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_values;
}
KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_values;
}
MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_button_values;
}
+RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
+ return device_status.ring_analog_value;
+}
+
KeyboardKey EmulatedDevices::GetKeyboard() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_state;
}
KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_state;
}
MouseButton EmulatedDevices::GetMouseButtons() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_button_state;
}
MousePosition EmulatedDevices::GetMousePosition() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_position_state;
}
AnalogStickState EmulatedDevices::GetMouseWheel() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_wheel_state;
}
+RingSensorForce EmulatedDevices::GetRingSensorForce() const {
+ return device_status.ring_analog_state;
+}
+
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const InterfaceUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
@@ -442,13 +505,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
}
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedDevices::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ 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);
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 790d3b411..4149eeced 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -26,9 +25,11 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice
using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
+using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
using MouseButtonParams =
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
+using RingAnalogParams = Common::ParamPackage;
using KeyboardValues =
std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -39,12 +40,17 @@ using MouseButtonValues =
using MouseAnalogValues =
std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickValue = Common::Input::TouchStatus;
+using RingAnalogValue = Common::Input::AnalogStatus;
struct MousePosition {
f32 x;
f32 y;
};
+struct RingSensorForce {
+ f32 force;
+};
+
struct DeviceStatus {
// Data from input_common
KeyboardValues keyboard_values{};
@@ -52,6 +58,7 @@ struct DeviceStatus {
MouseButtonValues mouse_button_values{};
MouseAnalogValues mouse_analog_values{};
MouseStickValue mouse_stick_value{};
+ RingAnalogValue ring_analog_value{};
// Data for HID serices
KeyboardKey keyboard_state{};
@@ -59,12 +66,14 @@ struct DeviceStatus {
MouseButton mouse_button_state{};
MousePosition mouse_position_state{};
AnalogStickState mouse_wheel_state{};
+ RingSensorForce ring_analog_state{};
};
enum class DeviceTriggerType {
Keyboard,
KeyboardModdifier,
Mouse,
+ RingController,
};
struct InterfaceUpdateCallback {
@@ -110,6 +119,15 @@ public:
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
+ // Returns the current mapped ring device
+ Common::ParamPackage GetRingParam() const;
+
+ /**
+ * Updates the current mapped ring device
+ * @param param ParamPackage with ring sensor data to be mapped
+ */
+ void SetRingParam(Common::ParamPackage param);
+
/// Returns the latest status of button input from the keyboard with parameters
KeyboardValues GetKeyboardValues() const;
@@ -119,6 +137,9 @@ public:
/// Returns the latest status of button input from the mouse with parameters
MouseButtonValues GetMouseButtonsValues() const;
+ /// Returns the latest status of analog input from the ring sensor with parameters
+ RingAnalogValue GetRingSensorValues() const;
+
/// Returns the latest status of button input from the keyboard
KeyboardKey GetKeyboard() const;
@@ -134,6 +155,9 @@ public:
/// Returns the latest mouse wheel change
AnalogStickState GetMouseWheel() const;
+ /// Returns the latest ringcon force sensor value
+ RingSensorForce GetRingSensorForce() const;
+
/**
* Adds a callback to the list of events
* @param update_callback InterfaceUpdateCallback that will be triggered
@@ -186,6 +210,12 @@ private:
void SetMouseStick(const Common::Input::CallbackStatus& callback);
/**
+ * Updates the ring analog sensor status of the ring controller
+ * @param callback A CallbackStatus containing the force status
+ */
+ void SetRingAnalog(const Common::Input::CallbackStatus& callback);
+
+ /**
* Triggers a callback that something has changed on the device status
* @param type Input type of the event to trigger
*/
@@ -193,13 +223,17 @@ private:
bool is_configuring{false};
+ RingAnalogParams ring_params;
+
KeyboardDevices keyboard_devices;
KeyboardModifierDevices keyboard_modifier_devices;
MouseButtonDevices mouse_button_devices;
MouseAnalogDevices mouse_analog_devices;
MouseStickDevice mouse_stick_device;
+ RingAnalogDevice ring_analog_device;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, InterfaceUpdateCallback> callback_list;
int last_callback_key = 0;
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index a1c3bbb57..7d6373414 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/hid/emulated_console.h"
@@ -49,7 +48,7 @@ EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
return handheld.get();
case NpadIdType::Invalid:
default:
- UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
+ ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
@@ -78,7 +77,7 @@ const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type
return handheld.get();
case NpadIdType::Invalid:
default:
- UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
+ ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
index 717f605e7..5fe36551e 100644
--- a/src/core/hid/hid_core.h
+++ b/src/core/hid/hid_core.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 778b328b9..e3b1cfbc6 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -273,6 +272,7 @@ enum class VibrationDeviceType : u32 {
Unknown = 0,
LinearResonantActuator = 1,
GcErm = 2,
+ N64 = 3,
};
// This is nn::hid::VibrationGcErmCommand
@@ -317,27 +317,35 @@ static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"
// This is nn::hid::TouchState
struct TouchState {
- u64 delta_time;
- TouchAttribute attribute;
- u32 finger;
- Common::Point<u32> position;
- u32 diameter_x;
- u32 diameter_y;
- u32 rotation_angle;
+ u64 delta_time{};
+ TouchAttribute attribute{};
+ u32 finger{};
+ Common::Point<u32> position{};
+ u32 diameter_x{};
+ u32 diameter_y{};
+ u32 rotation_angle{};
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
+struct NpadColor {
+ u8 r{};
+ u8 g{};
+ u8 b{};
+ u8 a{};
+};
+static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");
+
// This is nn::hid::NpadControllerColor
struct NpadControllerColor {
- u32 body;
- u32 button;
+ NpadColor body{};
+ NpadColor button{};
};
static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
// This is nn::hid::AnalogStickState
struct AnalogStickState {
- s32 x;
- s32 y;
+ s32 x{};
+ s32 y{};
};
static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");
@@ -355,10 +363,10 @@ static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid s
// This is nn::hid::system::NpadPowerInfo
struct NpadPowerInfo {
- bool is_powered;
- bool is_charging;
+ bool is_powered{};
+ bool is_charging{};
INSERT_PADDING_BYTES(0x6);
- NpadBatteryLevel battery_level;
+ NpadBatteryLevel battery_level{8};
};
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
@@ -475,8 +483,8 @@ static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size"
// This is nn::hid::ConsoleSixAxisSensorHandle
struct ConsoleSixAxisSensorHandle {
- u8 unknown_1;
- u8 unknown_2;
+ u8 unknown_1{};
+ u8 unknown_2{};
INSERT_PADDING_BYTES_NOINIT(2);
};
static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
@@ -484,35 +492,79 @@ static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
// This is nn::hid::SixAxisSensorHandle
struct SixAxisSensorHandle {
- NpadStyleIndex npad_type;
- u8 npad_id;
- DeviceIndex device_index;
+ NpadStyleIndex npad_type{NpadStyleIndex::None};
+ u8 npad_id{};
+ DeviceIndex device_index{DeviceIndex::None};
INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
+// These parameters seem related to how much gyro/accelerometer is used
struct SixAxisSensorFusionParameters {
- f32 parameter1;
- f32 parameter2;
+ f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
+ f32 parameter2{0.4f}; // Default 0.4
};
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
"SixAxisSensorFusionParameters is an invalid size");
+// This is nn::hid::server::SixAxisSensorProperties
+struct SixAxisSensorProperties {
+ union {
+ u8 raw{};
+ BitField<0, 1, u8> is_newly_assigned;
+ BitField<1, 1, u8> is_firmware_update_available;
+ };
+};
+static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");
+
+// This is nn::hid::SixAxisSensorCalibrationParameter
+struct SixAxisSensorCalibrationParameter {
+ std::array<u8, 0x744> unknown_data{};
+};
+static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
+ "SixAxisSensorCalibrationParameter is an invalid size");
+
+// This is nn::hid::SixAxisSensorIcInformation
+struct SixAxisSensorIcInformation {
+ f32 angular_rate{2000.0f}; // dps
+ std::array<f32, 6> unknown_gyro_data1{
+ -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f,
+ }; // dps
+ std::array<f32, 9> unknown_gyro_data2{
+ 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
+ };
+ std::array<f32, 9> unknown_gyro_data3{
+ 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
+ };
+ f32 acceleration_range{8.0f}; // g force
+ std::array<f32, 6> unknown_accel_data1{
+ -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f,
+ }; // g force
+ std::array<f32, 9> unknown_accel_data2{
+ 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
+ };
+ std::array<f32, 9> unknown_accel_data3{
+ 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
+ };
+};
+static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
+ "SixAxisSensorIcInformation is an invalid size");
+
// This is nn::hid::VibrationDeviceHandle
struct VibrationDeviceHandle {
- NpadStyleIndex npad_type;
- u8 npad_id;
- DeviceIndex device_index;
+ NpadStyleIndex npad_type{NpadStyleIndex::None};
+ u8 npad_id{};
+ DeviceIndex device_index{DeviceIndex::None};
INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");
// This is nn::hid::VibrationValue
struct VibrationValue {
- f32 low_amplitude;
- f32 low_frequency;
- f32 high_amplitude;
- f32 high_frequency;
+ f32 low_amplitude{};
+ f32 low_frequency{};
+ f32 high_amplitude{};
+ f32 high_frequency{};
};
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
@@ -561,7 +613,7 @@ static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid
// This is nn::hid::KeyboardKey
struct KeyboardKey {
// This should be a 256 bit flag
- std::array<u8, 32> key;
+ std::array<u8, 32> key{};
};
static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
@@ -590,16 +642,16 @@ static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size"
// This is nn::hid::detail::MouseState
struct MouseState {
- s64 sampling_number;
- s32 x;
- s32 y;
- s32 delta_x;
- s32 delta_y;
+ s64 sampling_number{};
+ s32 x{};
+ s32 y{};
+ s32 delta_x{};
+ s32 delta_y{};
// Axis Order in HW is switched for the wheel
- s32 delta_wheel_y;
- s32 delta_wheel_x;
- MouseButton button;
- MouseAttribute attribute;
+ s32 delta_wheel_y{};
+ s32 delta_wheel_x{};
+ MouseButton button{};
+ MouseAttribute attribute{};
};
static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index cd41607a7..fe9915abe 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -1,7 +1,7 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <algorithm>
#include <random>
#include "common/input.h"
@@ -52,6 +52,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
Common::Input::ButtonStatus status{};
switch (callback.type) {
case Common::Input::InputType::Analog:
+ status.value = TransformToTrigger(callback).pressed.value;
+ status.toggle = callback.analog_status.properties.toggle;
+ break;
case Common::Input::InputType::Trigger:
status.value = TransformToTrigger(callback).pressed.value;
break;
@@ -197,6 +200,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
x = std::clamp(x, 0.0f, 1.0f);
y = std::clamp(y, 0.0f, 1.0f);
+ // Limit id to maximum number of fingers
+ status.id = std::clamp(status.id, 0, 16);
+
if (status.pressed.inverted) {
status.pressed.value = !status.pressed.value;
}
@@ -267,6 +273,34 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
return status;
}
+Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
+ Common::Input::CameraStatus camera{};
+ switch (callback.type) {
+ case Common::Input::InputType::IrSensor:
+ camera = callback.camera_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
+ break;
+ }
+
+ return camera;
+}
+
+Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) {
+ Common::Input::NfcStatus nfc{};
+ switch (callback.type) {
+ case Common::Input::InputType::Nfc:
+ return callback.nfc_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
+ break;
+ }
+
+ return nfc;
+}
+
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
const auto& properties = analog.properties;
float& raw_value = analog.raw_value;
@@ -328,7 +362,7 @@ void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogS
raw_y += properties_y.offset;
// Apply X scale correction from offset
- if (std::abs(properties_x.offset) < 0.5f) {
+ if (std::abs(properties_x.offset) < 0.75f) {
if (raw_x > 0) {
raw_x /= 1 + properties_x.offset;
} else {
@@ -337,7 +371,7 @@ void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogS
}
// Apply Y scale correction from offset
- if (std::abs(properties_y.offset) < 0.5f) {
+ if (std::abs(properties_y.offset) < 0.75f) {
if (raw_y > 0) {
raw_y /= 1 + properties_y.offset;
} else {
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index d24582226..b7eb6e660 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -78,6 +77,22 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
/**
+ * Converts raw input data into a valid camera status.
+ *
+ * @param callback Supported callbacks: Camera.
+ * @return A valid CameraObject object.
+ */
+Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw input data into a valid nfc status.
+ *
+ * @param callback Supported callbacks: Nfc.
+ * @return A valid CameraObject object.
+ */
+Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
+
+/**
* Converts raw analog data into a valid analog value
* @param analog An analog object containing raw data and properties
* @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 2dbda8814..76d6b8ab0 100644
--- a/src/core/hid/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hid/hid_types.h"
diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h
index 70c34d474..8c521b381 100644
--- a/src/core/hid/input_interpreter.h
+++ b/src/core/hid/input_interpreter.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h
new file mode 100644
index 000000000..88c5b016d
--- /dev/null
+++ b/src/core/hid/irs_types.h
@@ -0,0 +1,301 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hid/hid_types.h"
+
+namespace Core::IrSensor {
+
+// This is nn::irsensor::CameraAmbientNoiseLevel
+enum class CameraAmbientNoiseLevel : u32 {
+ Low,
+ Medium,
+ High,
+ Unkown3, // This level can't be reached
+};
+
+// This is nn::irsensor::CameraLightTarget
+enum class CameraLightTarget : u32 {
+ AllLeds,
+ BrightLeds,
+ DimLeds,
+ None,
+};
+
+// This is nn::irsensor::PackedCameraLightTarget
+enum class PackedCameraLightTarget : u8 {
+ AllLeds,
+ BrightLeds,
+ DimLeds,
+ None,
+};
+
+// This is nn::irsensor::AdaptiveClusteringMode
+enum class AdaptiveClusteringMode : u32 {
+ StaticFov,
+ DynamicFov,
+};
+
+// This is nn::irsensor::AdaptiveClusteringTargetDistance
+enum class AdaptiveClusteringTargetDistance : u32 {
+ Near,
+ Middle,
+ Far,
+};
+
+// This is nn::irsensor::ImageTransferProcessorFormat
+enum class ImageTransferProcessorFormat : u32 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+};
+
+// This is nn::irsensor::PackedImageTransferProcessorFormat
+enum class PackedImageTransferProcessorFormat : u8 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+};
+
+// This is nn::irsensor::IrCameraStatus
+enum class IrCameraStatus : u32 {
+ Available,
+ Unsupported,
+ Unconnected,
+};
+
+// This is nn::irsensor::IrCameraInternalStatus
+enum class IrCameraInternalStatus : u32 {
+ Stopped,
+ FirmwareUpdateNeeded,
+ Unkown2,
+ Unkown3,
+ Unkown4,
+ FirmwareVersionRequested,
+ FirmwareVersionIsInvalid,
+ Ready,
+ Setting,
+};
+
+// This is nn::irsensor::detail::StatusManager::IrSensorMode
+enum class IrSensorMode : u64 {
+ None,
+ MomentProcessor,
+ ClusteringProcessor,
+ ImageTransferProcessor,
+ PointingProcessorMarker,
+ TeraPluginProcessor,
+ IrLedProcessor,
+};
+
+// This is nn::irsensor::ImageProcessorStatus
+enum ImageProcessorStatus : u32 {
+ Stopped,
+ Running,
+};
+
+// This is nn::irsensor::HandAnalysisMode
+enum class HandAnalysisMode : u32 {
+ None,
+ Silhouette,
+ Image,
+ SilhoueteAndImage,
+ SilhuetteOnly,
+};
+
+// This is nn::irsensor::IrSensorFunctionLevel
+enum class IrSensorFunctionLevel : u8 {
+ unknown0,
+ unknown1,
+ unknown2,
+ unknown3,
+ unknown4,
+};
+
+// This is nn::irsensor::MomentProcessorPreprocess
+enum class MomentProcessorPreprocess : u32 {
+ Unkown0,
+ Unkown1,
+};
+
+// This is nn::irsensor::PackedMomentProcessorPreprocess
+enum class PackedMomentProcessorPreprocess : u8 {
+ Unkown0,
+ Unkown1,
+};
+
+// This is nn::irsensor::PointingStatus
+enum class PointingStatus : u32 {
+ Unkown0,
+ Unkown1,
+};
+
+struct IrsRect {
+ s16 x;
+ s16 y;
+ s16 width;
+ s16 height;
+};
+
+struct IrsCentroid {
+ f32 x;
+ f32 y;
+};
+
+struct CameraConfig {
+ u64 exposure_time;
+ CameraLightTarget light_target;
+ u32 gain;
+ bool is_negative_used;
+ INSERT_PADDING_BYTES(7);
+};
+static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
+
+struct PackedCameraConfig {
+ u64 exposure_time;
+ PackedCameraLightTarget light_target;
+ u8 gain;
+ bool is_negative_used;
+ INSERT_PADDING_BYTES(5);
+};
+static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
+
+// This is nn::irsensor::IrCameraHandle
+struct IrCameraHandle {
+ u8 npad_id{};
+ Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
+
+// This is nn::irsensor::PackedMcuVersion
+struct PackedMcuVersion {
+ u16 major;
+ u16 minor;
+};
+static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
+
+// This is nn::irsensor::PackedMomentProcessorConfig
+struct PackedMomentProcessorConfig {
+ PackedCameraConfig camera_config;
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ PackedMomentProcessorPreprocess preprocess;
+ u8 preprocess_intensity_threshold;
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
+ "PackedMomentProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedClusteringProcessorConfig
+struct PackedClusteringProcessorConfig {
+ PackedCameraConfig camera_config;
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u8 object_intensity_min;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
+ "PackedClusteringProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedImageTransferProcessorConfig
+struct PackedImageTransferProcessorConfig {
+ PackedCameraConfig camera_config;
+ PackedMcuVersion required_mcu_version;
+ PackedImageTransferProcessorFormat format;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
+ "PackedImageTransferProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedTeraPluginProcessorConfig
+struct PackedTeraPluginProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 mode;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+};
+static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
+ "PackedTeraPluginProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedPointingProcessorConfig
+struct PackedPointingProcessorConfig {
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+};
+static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
+ "PackedPointingProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedFunctionLevel
+struct PackedFunctionLevel {
+ IrSensorFunctionLevel function_level;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
+
+// This is nn::irsensor::PackedImageTransferProcessorExConfig
+struct PackedImageTransferProcessorExConfig {
+ PackedCameraConfig camera_config;
+ PackedMcuVersion required_mcu_version;
+ PackedImageTransferProcessorFormat origin_format;
+ PackedImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(5);
+};
+static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
+ "PackedImageTransferProcessorExConfig is an invalid size");
+
+// This is nn::irsensor::PackedIrLedProcessorConfig
+struct PackedIrLedProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 light_target;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
+ "PackedIrLedProcessorConfig is an invalid size");
+
+// This is nn::irsensor::HandAnalysisConfig
+struct HandAnalysisConfig {
+ HandAnalysisMode mode;
+};
+static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
+
+// This is nn::irsensor::detail::ProcessorState contents are different for each processor
+struct ProcessorState {
+ std::array<u8, 0xE20> processor_raw_data{};
+};
+static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
+
+// This is nn::irsensor::detail::DeviceFormat
+struct DeviceFormat {
+ Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
+ Core::IrSensor::IrCameraInternalStatus camera_internal_status{
+ Core::IrSensor::IrCameraInternalStatus::Ready};
+ Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
+ ProcessorState state{};
+};
+static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
+
+// This is nn::irsensor::ImageTransferProcessorState
+struct ImageTransferProcessorState {
+ u64 sampling_number;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ INSERT_PADDING_BYTES(4);
+};
+static_assert(sizeof(ImageTransferProcessorState) == 0x10,
+ "ImageTransferProcessorState is an invalid size");
+
+} // namespace Core::IrSensor
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp
index 05042fd99..b1f658e62 100644
--- a/src/core/hid/motion_input.cpp
+++ b/src/core/hid/motion_input.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/math_util.h"
#include "core/hid/motion_input.h"
diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h
index bca4520fa..f5fd90db5 100644
--- a/src/core/hid/motion_input.h
+++ b/src/core/hid/motion_input.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once