yuzu: Add custom ringcon configuration

This commit is contained in:
german77 2022-01-08 23:23:40 -06:00 committed by Narr the Reg
parent b2359f1527
commit d2f9412cf1
19 changed files with 992 additions and 65 deletions

View file

@ -15,6 +15,7 @@ EmulatedDevices::EmulatedDevices() = default;
EmulatedDevices::~EmulatedDevices() = default;
void EmulatedDevices::ReloadFromSettings() {
ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
ReloadInput();
}
@ -66,6 +67,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 +123,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 +165,7 @@ void EmulatedDevices::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
Settings::values.ringcon_analogs = ring_params.Serialize();
}
void EmulatedDevices::RestoreConfig() {
@ -164,6 +175,15 @@ 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()) {
@ -410,6 +430,23 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
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;
@ -425,6 +462,10 @@ MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
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;
@ -450,6 +491,10 @@ AnalogStickState EmulatedDevices::GetMouseWheel() const {
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) {

View file

@ -26,9 +26,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 +41,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 +59,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 +67,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 +120,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 +138,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 +156,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
@ -185,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,11 +224,14 @@ 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;

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
@ -190,6 +191,7 @@ void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_attached);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
@ -217,7 +219,7 @@ void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
if (bus_handle_.internal_index == 0) {
if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
MakeDevice<RingController>(bus_handle_);
devices[device_index.value()].is_device_initializated = true;
devices[device_index.value()].device->ActivateDevice();

View file

@ -12,7 +12,7 @@ namespace Service::HID {
HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
: service_context(service_context_) {
send_command_asyc_event = service_context.CreateEvent("hidbus:SendCommandAsycEvent");
send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
}
HidbusBase::~HidbusBase() = default;
@ -66,7 +66,7 @@ void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
}
Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
return send_command_asyc_event->GetReadableEvent();
return send_command_async_event->GetReadableEvent();
}
} // namespace Service::HID

View file

@ -165,6 +165,7 @@ protected:
bool device_enabled{};
bool polling_mode_enabled{};
JoyPollingMode polling_mode = {};
// TODO(German77): All data accessors need to be replaced with a ring lifo object
JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
ButtonOnlyPollingDataAccessor button_only_data{};
@ -172,7 +173,7 @@ protected:
u8* transfer_memory{nullptr};
bool is_transfer_memory_set{};
Kernel::KEvent* send_command_asyc_event;
Kernel::KEvent* send_command_async_event;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::HID

View file

@ -2,7 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hid/emulated_controller.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
@ -13,9 +13,7 @@ namespace Service::HID {
RingController::RingController(Core::HID::HIDCore& hid_core_,
KernelHelpers::ServiceContext& service_context_)
: HidbusBase(service_context_) {
// Use the horizontal axis of left stick for emulating input
// There is no point on adding a frontend implementation since Ring Fit Adventure doesn't work
input = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
input = hid_core_.GetEmulatedDevices();
}
RingController::~RingController() = default;
@ -41,6 +39,8 @@ void RingController::OnUpdate() {
return;
}
// TODO: Increment multitasking counters from motion and sensor data
switch (polling_mode) {
case JoyPollingMode::SixAxisSensorEnable: {
enable_sixaxis_data.header.total_entries = 10;
@ -74,9 +74,8 @@ RingController::RingConData RingController::GetSensorValue() const {
.data = 0,
};
const f32 stick_value = static_cast<f32>(input->GetSticks().left.x) / 32767.0f;
ringcon_sensor_value.data = static_cast<s16>(stick_value * range) + idle_value;
const f32 force_value = input->GetRingSensorForce().force * range;
ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
return ringcon_sensor_value;
}
@ -105,6 +104,8 @@ std::vector<u8> RingController::GetReply() const {
return GetReadRepCountReply();
case RingConCommands::ReadTotalPushCount:
return GetReadTotalPushCountReply();
case RingConCommands::ResetRepCount:
return GetResetRepCountReply();
case RingConCommands::SaveCalData:
return GetSaveDataReply();
default:
@ -119,36 +120,9 @@ bool RingController::SetCommand(const std::vector<u8>& data) {
return false;
}
// There must be a better way to do this
const u32 command_id =
u32{data[0]} + (u32{data[1]} << 8) + (u32{data[2]} << 16) + (u32{data[3]} << 24);
static constexpr std::array supported_commands = {
RingConCommands::GetFirmwareVersion,
RingConCommands::ReadId,
RingConCommands::c20105,
RingConCommands::ReadUnkCal,
RingConCommands::ReadFactoryCal,
RingConCommands::ReadUserCal,
RingConCommands::ReadRepCount,
RingConCommands::ReadTotalPushCount,
RingConCommands::SaveCalData,
};
std::memcpy(&command, data.data(), sizeof(RingConCommands));
for (RingConCommands cmd : supported_commands) {
if (command_id == static_cast<u32>(cmd)) {
return ExcecuteCommand(cmd, data);
}
}
LOG_ERROR(Service_HID, "Command not implemented {}", command_id);
command = RingConCommands::Error;
// Signal a reply to avoid softlocking
send_command_asyc_event->GetWritableEvent().Signal();
return false;
}
bool RingController::ExcecuteCommand(RingConCommands cmd, const std::vector<u8>& data) {
switch (cmd) {
switch (command) {
case RingConCommands::GetFirmwareVersion:
case RingConCommands::ReadId:
case RingConCommands::c20105:
@ -158,23 +132,27 @@ bool RingController::ExcecuteCommand(RingConCommands cmd, const std::vector<u8>&
case RingConCommands::ReadRepCount:
case RingConCommands::ReadTotalPushCount:
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
command = cmd;
send_command_asyc_event->GetWritableEvent().Signal();
send_command_async_event->GetWritableEvent().Signal();
return true;
case RingConCommands::ResetRepCount:
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
total_rep_count = 0;
send_command_async_event->GetWritableEvent().Signal();
return true;
case RingConCommands::SaveCalData: {
ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
SaveCalData save_info{};
std::memcpy(&save_info, &data, sizeof(SaveCalData));
std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
user_calibration = save_info.calibration;
command = cmd;
send_command_asyc_event->GetWritableEvent().Signal();
send_command_async_event->GetWritableEvent().Signal();
return true;
}
default:
LOG_ERROR(Service_HID, "Command not implemented {}", cmd);
LOG_ERROR(Service_HID, "Command not implemented {}", command);
command = RingConCommands::Error;
// Signal a reply to avoid softlocking the game
send_command_async_event->GetWritableEvent().Signal();
return false;
}
}
@ -240,27 +218,29 @@ std::vector<u8> RingController::GetReadUserCalReply() const {
}
std::vector<u8> RingController::GetReadRepCountReply() const {
// The values are hardcoded from a real joycon
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {30, 0, 0},
.crc = GetCrcValue({30, 0, 0, 0}),
.data = {total_rep_count, 0, 0},
.crc = GetCrcValue({total_rep_count, 0, 0, 0}),
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadTotalPushCountReply() const {
// The values are hardcoded from a real joycon
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {30, 0, 0},
.crc = GetCrcValue({30, 0, 0, 0}),
.data = {total_push_count, 0, 0},
.crc = GetCrcValue({total_push_count, 0, 0, 0}),
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetResetRepCountReply() const {
return GetReadRepCountReply();
}
std::vector<u8> RingController::GetSaveDataReply() const {
const StatusReply reply{
.status = DataValid::Valid,

View file

@ -10,7 +10,7 @@
#include "core/hle/service/hid/hidbus/hidbus_base.h"
namespace Core::HID {
class EmulatedController;
class EmulatedDevices;
} // namespace Core::HID
namespace Service::HID {
@ -43,6 +43,7 @@ private:
static constexpr s16 idle_deadzone = 120;
static constexpr s16 range = 2500;
// Most missing command names are leftovers from other firmware versions
enum class RingConCommands : u32 {
GetFirmwareVersion = 0x00020000,
ReadId = 0x00020100,
@ -60,10 +61,10 @@ private:
ReadUserCal = 0x00021A04,
ReadRepCount = 0x00023104,
ReadTotalPushCount = 0x00023204,
Unknown9 = 0x04013104,
Unknown10 = 0x04011104,
Unknown11 = 0x04011204,
Unknown12 = 0x04011304,
ResetRepCount = 0x04013104,
Unknown8 = 0x04011104,
Unknown9 = 0x04011204,
Unknown10 = 0x04011304,
SaveCalData = 0x10011A04,
Error = 0xFFFFFFFF,
};
@ -180,9 +181,6 @@ private:
};
static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
// Executes the command requested
bool ExcecuteCommand(RingConCommands cmd, const std::vector<u8>& data);
// Returns RingConData struct with pressure sensor values
RingConData GetSensorValue() const;
@ -204,12 +202,15 @@ private:
// Returns 20 byte reply with user calibration values
std::vector<u8> GetReadUserCalReply() const;
// (STUBBED) Returns 8 byte reply
// Returns 8 byte reply
std::vector<u8> GetReadRepCountReply() const;
// (STUBBED) Returns 8 byte reply
// Returns 8 byte reply
std::vector<u8> GetReadTotalPushCountReply() const;
// Returns 8 byte reply
std::vector<u8> GetResetRepCountReply() const;
// Returns 4 byte save data reply
std::vector<u8> GetSaveDataReply() const;
@ -225,6 +226,12 @@ private:
RingConCommands command{RingConCommands::Error};
// These counters are used in multitasking mode while the switch is sleeping
// Total steps taken
u8 total_rep_count = 0;
// Total times the ring was pushed
u8 total_push_count = 0;
const u8 device_id = 0x20;
const FirmwareVersion version = {
.sub = 0x0,
@ -242,6 +249,6 @@ private:
.zero = {.value = idle_value, .crc = 225},
};
Core::HID::EmulatedController* input;
Core::HID::EmulatedDevices* input;
};
} // namespace Service::HID