early-access version 2177
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applet_controller.h"
|
||||
@@ -25,7 +27,7 @@ namespace Service::AM::Applets {
|
||||
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
|
||||
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
|
||||
std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
|
||||
HID::Controller_NPad::NpadStyleSet npad_style_set;
|
||||
Core::HID::NpadStyleTag npad_style_set;
|
||||
npad_style_set.raw = private_arg.style_set;
|
||||
|
||||
return {
|
||||
@@ -243,19 +245,11 @@ void Controller::Execute() {
|
||||
void Controller::ConfigurationComplete() {
|
||||
ControllerSupportResultInfo result_info{};
|
||||
|
||||
const auto& players = Settings::values.players.GetValue();
|
||||
|
||||
// If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
|
||||
// Otherwise, only count connected players from P1-P8.
|
||||
result_info.player_count =
|
||||
is_single_mode
|
||||
? 1
|
||||
: static_cast<s8>(std::count_if(players.begin(), players.end() - 2,
|
||||
[](const auto& player) { return player.connected; }));
|
||||
result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount();
|
||||
|
||||
result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance(
|
||||
players.begin(), std::find_if(players.begin(), players.end(),
|
||||
[](const auto& player) { return player.connected; })));
|
||||
result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
|
||||
|
||||
result_info.result = 0;
|
||||
|
||||
|
@@ -231,7 +231,7 @@ void AppletManager::SetDefaultAppletFrontendSet() {
|
||||
void AppletManager::SetDefaultAppletsIfMissing() {
|
||||
if (frontend.controller == nullptr) {
|
||||
frontend.controller =
|
||||
std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
|
||||
std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
|
||||
}
|
||||
|
||||
if (frontend.error == nullptr) {
|
||||
|
@@ -3,14 +3,19 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hle/service/hid/controllers/console_sixaxis.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
|
||||
|
||||
Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_)
|
||||
: ControllerBase{system_} {}
|
||||
: ControllerBase{system_} {
|
||||
console = system.HIDCore().GetEmulatedConsole();
|
||||
}
|
||||
|
||||
Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
|
||||
|
||||
void Controller_ConsoleSixAxis::OnInit() {}
|
||||
@@ -38,25 +43,21 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
|
||||
// Try to read sixaxis sensor states
|
||||
MotionDevice motion_device{};
|
||||
const auto& device = motions[0];
|
||||
if (device) {
|
||||
std::tie(motion_device.accel, motion_device.gyro, motion_device.rotation,
|
||||
motion_device.orientation, motion_device.quaternion) = device->GetStatus();
|
||||
console_six_axis.is_seven_six_axis_sensor_at_rest = motion_device.gyro.Length2() < 0.0001f;
|
||||
}
|
||||
const auto motion_status = console->GetMotion();
|
||||
|
||||
cur_entry.accel = motion_device.accel;
|
||||
console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
|
||||
cur_entry.accel = motion_status.accel;
|
||||
// Zero gyro values as they just mess up with the camera
|
||||
// Note: Probably a correct sensivity setting must be set
|
||||
cur_entry.gyro = {};
|
||||
cur_entry.quaternion = {
|
||||
{
|
||||
motion_device.quaternion.xyz.y,
|
||||
motion_device.quaternion.xyz.x,
|
||||
-motion_device.quaternion.w,
|
||||
motion_status.quaternion.xyz.y,
|
||||
motion_status.quaternion.xyz.x,
|
||||
-motion_status.quaternion.w,
|
||||
},
|
||||
-motion_device.quaternion.xyz.z,
|
||||
-motion_status.quaternion.xyz.z,
|
||||
};
|
||||
|
||||
console_six_axis.sampling_number++;
|
||||
@@ -70,13 +71,6 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
|
||||
std::memcpy(transfer_memory, &seven_six_axis, sizeof(seven_six_axis));
|
||||
}
|
||||
|
||||
void Controller_ConsoleSixAxis::OnLoadInputDevices() {
|
||||
const auto player = Settings::values.players.GetValue()[0];
|
||||
std::transform(player.motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
|
||||
player.motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions.begin(),
|
||||
Input::CreateDevice<Input::MotionDevice>);
|
||||
}
|
||||
|
||||
void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
|
||||
is_transfer_memory_set = true;
|
||||
transfer_memory = t_mem;
|
||||
|
@@ -5,10 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/quaternion.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
|
||||
namespace Service::HID {
|
||||
@@ -26,9 +26,6 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
// Called on InitializeSevenSixAxisSensor
|
||||
void SetTransferMemoryPointer(u8* t_mem);
|
||||
|
||||
@@ -38,8 +35,8 @@ public:
|
||||
private:
|
||||
struct SevenSixAxisState {
|
||||
INSERT_PADDING_WORDS(4); // unused
|
||||
s64_le sampling_number{};
|
||||
s64_le sampling_number2{};
|
||||
s64 sampling_number{};
|
||||
s64 sampling_number2{};
|
||||
u64 unknown{};
|
||||
Common::Vec3f accel{};
|
||||
Common::Vec3f gyro{};
|
||||
@@ -47,14 +44,24 @@ private:
|
||||
};
|
||||
static_assert(sizeof(SevenSixAxisState) == 0x50, "SevenSixAxisState is an invalid size");
|
||||
|
||||
struct CommonHeader {
|
||||
s64 timestamp;
|
||||
s64 total_entry_count;
|
||||
s64 last_entry_index;
|
||||
s64 entry_count;
|
||||
};
|
||||
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
|
||||
|
||||
// TODO(german77): SevenSixAxisMemory doesn't follow the standard lifo. Investigate
|
||||
struct SevenSixAxisMemory {
|
||||
CommonHeader header{};
|
||||
std::array<SevenSixAxisState, 0x21> sevensixaxis_states{};
|
||||
};
|
||||
static_assert(sizeof(SevenSixAxisMemory) == 0xA70, "SevenSixAxisMemory is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
|
||||
struct ConsoleSharedMemory {
|
||||
u64_le sampling_number{};
|
||||
u64 sampling_number{};
|
||||
bool is_seven_six_axis_sensor_at_rest{};
|
||||
f32 verticalization_error{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
@@ -69,9 +76,7 @@ private:
|
||||
Common::Quaternion<f32> quaternion;
|
||||
};
|
||||
|
||||
using MotionArray =
|
||||
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>;
|
||||
MotionArray motions;
|
||||
Core::HID::EmulatedConsole* console;
|
||||
u8* transfer_memory = nullptr;
|
||||
bool is_transfer_memory_set = false;
|
||||
ConsoleSharedMemory console_six_axis{};
|
||||
|
@@ -11,7 +11,7 @@ ControllerBase::~ControllerBase() = default;
|
||||
|
||||
void ControllerBase::ActivateController() {
|
||||
if (is_activated) {
|
||||
OnRelease();
|
||||
return;
|
||||
}
|
||||
is_activated = true;
|
||||
OnInit();
|
||||
|
@@ -35,9 +35,6 @@ public:
|
||||
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||
std::size_t size) {}
|
||||
|
||||
// Called when input devices should be loaded
|
||||
virtual void OnLoadInputDevices() = 0;
|
||||
|
||||
void ActivateController();
|
||||
|
||||
void DeactivateController();
|
||||
@@ -47,14 +44,6 @@ public:
|
||||
protected:
|
||||
bool is_activated{false};
|
||||
|
||||
struct CommonHeader {
|
||||
s64_le timestamp;
|
||||
s64_le total_entry_count;
|
||||
s64_le last_entry_index;
|
||||
s64_le entry_count;
|
||||
};
|
||||
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
@@ -5,16 +5,20 @@
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/debug_pad.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
|
||||
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
|
||||
[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
|
||||
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
|
||||
Controller_DebugPad::Controller_DebugPad(Core::System& system_) : ControllerBase{system_} {
|
||||
controller = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Other);
|
||||
}
|
||||
|
||||
Controller_DebugPad::Controller_DebugPad(Core::System& system_) : ControllerBase{system_} {}
|
||||
Controller_DebugPad::~Controller_DebugPad() = default;
|
||||
|
||||
void Controller_DebugPad::OnInit() {}
|
||||
@@ -23,63 +27,29 @@ void Controller_DebugPad::OnRelease() {}
|
||||
|
||||
void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||
std::size_t size) {
|
||||
shared_memory.header.timestamp = core_timing.GetCPUTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory.header.entry_count = 0;
|
||||
shared_memory.header.last_entry_index = 0;
|
||||
debug_pad_lifo.buffer_count = 0;
|
||||
debug_pad_lifo.buffer_tail = 0;
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
|
||||
return;
|
||||
}
|
||||
shared_memory.header.entry_count = 16;
|
||||
|
||||
const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
|
||||
shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
|
||||
auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state;
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
|
||||
if (Settings::values.debug_pad_enabled) {
|
||||
cur_entry.attribute.connected.Assign(1);
|
||||
auto& pad = cur_entry.pad_state;
|
||||
next_state.attribute.connected.Assign(1);
|
||||
|
||||
using namespace Settings::NativeButton;
|
||||
pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
|
||||
const auto& button_state = controller->GetDebugPadButtons();
|
||||
const auto& stick_state = controller->GetSticks();
|
||||
|
||||
const auto [stick_l_x_f, stick_l_y_f] =
|
||||
analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
|
||||
const auto [stick_r_x_f, stick_r_y_f] =
|
||||
analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
|
||||
cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
|
||||
cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
|
||||
cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
|
||||
cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
|
||||
next_state.pad_state = button_state;
|
||||
next_state.l_stick = stick_state.left;
|
||||
next_state.r_stick = stick_state.right;
|
||||
}
|
||||
|
||||
std::memcpy(data, &shared_memory, sizeof(SharedMemory));
|
||||
debug_pad_lifo.WriteNextEntry(next_state);
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
|
||||
}
|
||||
|
||||
void Controller_DebugPad::OnLoadInputDevices() {
|
||||
std::transform(Settings::values.debug_pad_buttons.begin(),
|
||||
Settings::values.debug_pad_buttons.begin() +
|
||||
Settings::NativeButton::NUM_BUTTONS_HID,
|
||||
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
std::transform(Settings::values.debug_pad_analogs.begin(),
|
||||
Settings::values.debug_pad_analogs.end(), analogs.begin(),
|
||||
Input::CreateDevice<Input::AnalogDevice>);
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
@@ -10,8 +10,14 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedController;
|
||||
struct DebugPadButton;
|
||||
struct AnalogStickState;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_DebugPad final : public ControllerBase {
|
||||
@@ -28,66 +34,31 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
struct AnalogStick {
|
||||
s32_le x;
|
||||
s32_le y;
|
||||
};
|
||||
static_assert(sizeof(AnalogStick) == 0x8);
|
||||
|
||||
struct PadState {
|
||||
// This is nn::hid::DebugPadAttribute
|
||||
struct DebugPadAttribute {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32> a;
|
||||
BitField<1, 1, u32> b;
|
||||
BitField<2, 1, u32> x;
|
||||
BitField<3, 1, u32> y;
|
||||
BitField<4, 1, u32> l;
|
||||
BitField<5, 1, u32> r;
|
||||
BitField<6, 1, u32> zl;
|
||||
BitField<7, 1, u32> zr;
|
||||
BitField<8, 1, u32> plus;
|
||||
BitField<9, 1, u32> minus;
|
||||
BitField<10, 1, u32> d_left;
|
||||
BitField<11, 1, u32> d_up;
|
||||
BitField<12, 1, u32> d_right;
|
||||
BitField<13, 1, u32> d_down;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
|
||||
|
||||
struct Attributes {
|
||||
union {
|
||||
u32_le raw{};
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> connected;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
|
||||
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
|
||||
|
||||
struct PadStates {
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
Attributes attribute;
|
||||
PadState pad_state;
|
||||
AnalogStick r_stick;
|
||||
AnalogStick l_stick;
|
||||
// This is nn::hid::DebugPadState
|
||||
struct DebugPadState {
|
||||
s64 sampling_number;
|
||||
DebugPadAttribute attribute;
|
||||
Core::HID::DebugPadButton pad_state;
|
||||
Core::HID::AnalogStickState r_stick;
|
||||
Core::HID::AnalogStickState l_stick;
|
||||
};
|
||||
static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state");
|
||||
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
|
||||
|
||||
struct SharedMemory {
|
||||
CommonHeader header;
|
||||
std::array<PadStates, 17> pad_states;
|
||||
INSERT_PADDING_BYTES(0x138);
|
||||
};
|
||||
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
|
||||
SharedMemory shared_memory{};
|
||||
// This is nn::hid::detail::DebugPadLifo
|
||||
Lifo<DebugPadState> debug_pad_lifo{};
|
||||
static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
|
||||
DebugPadState next_state{};
|
||||
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
|
||||
buttons;
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
|
||||
analogs;
|
||||
Core::HID::EmulatedController* controller;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
@@ -5,8 +5,10 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/gesture.h"
|
||||
|
||||
namespace Service::HID {
|
||||
@@ -23,16 +25,14 @@ constexpr f32 Square(s32 num) {
|
||||
return static_cast<f32>(num * num);
|
||||
}
|
||||
|
||||
Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {}
|
||||
Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {
|
||||
console = system.HIDCore().GetEmulatedConsole();
|
||||
}
|
||||
Controller_Gesture::~Controller_Gesture() = default;
|
||||
|
||||
void Controller_Gesture::OnInit() {
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
||||
mouse_finger_id[id] = MAX_POINTS;
|
||||
keyboard_finger_id[id] = MAX_POINTS;
|
||||
udp_finger_id[id] = MAX_POINTS;
|
||||
}
|
||||
shared_memory.header.entry_count = 0;
|
||||
gesture_lifo.buffer_count = 0;
|
||||
gesture_lifo.buffer_tail = 0;
|
||||
force_update = true;
|
||||
}
|
||||
|
||||
@@ -40,50 +40,38 @@ void Controller_Gesture::OnRelease() {}
|
||||
|
||||
void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||
std::size_t size) {
|
||||
shared_memory.header.timestamp = core_timing.GetCPUTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory.header.entry_count = 0;
|
||||
shared_memory.header.last_entry_index = 0;
|
||||
gesture_lifo.buffer_count = 0;
|
||||
gesture_lifo.buffer_tail = 0;
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
|
||||
return;
|
||||
}
|
||||
|
||||
ReadTouchInput();
|
||||
|
||||
GestureProperties gesture = GetGestureProperties();
|
||||
f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) /
|
||||
(1000 * 1000 * 1000);
|
||||
f32 time_difference =
|
||||
static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
|
||||
|
||||
// Only update if necesary
|
||||
if (!ShouldUpdateGesture(gesture, time_difference)) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_update_timestamp = shared_memory.header.timestamp;
|
||||
last_update_timestamp = gesture_lifo.timestamp;
|
||||
UpdateGestureSharedMemory(data, size, gesture, time_difference);
|
||||
}
|
||||
|
||||
void Controller_Gesture::ReadTouchInput() {
|
||||
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
|
||||
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
|
||||
udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
|
||||
}
|
||||
|
||||
if (Settings::values.use_touch_from_button) {
|
||||
const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
keyboard_finger_id[id] =
|
||||
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
|
||||
}
|
||||
const auto touch_status = console->GetTouch();
|
||||
for (std::size_t id = 0; id < fingers.size(); ++id) {
|
||||
fingers[id] = touch_status[id];
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
|
||||
f32 time_difference) {
|
||||
const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
if (force_update) {
|
||||
force_update = false;
|
||||
return true;
|
||||
@@ -97,7 +85,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
|
||||
}
|
||||
|
||||
// Update on press and hold event after 0.5 seconds
|
||||
if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 &&
|
||||
if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
|
||||
time_difference > press_delay) {
|
||||
return enable_press_and_tap;
|
||||
}
|
||||
@@ -108,27 +96,19 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
|
||||
void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
|
||||
GestureProperties& gesture,
|
||||
f32 time_difference) {
|
||||
TouchType type = TouchType::Idle;
|
||||
Attribute attributes{};
|
||||
GestureType type = GestureType::Idle;
|
||||
GestureAttribute attributes{};
|
||||
|
||||
const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
|
||||
shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
|
||||
auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
|
||||
const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
|
||||
|
||||
if (shared_memory.header.entry_count < 16) {
|
||||
shared_memory.header.entry_count++;
|
||||
}
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
|
||||
// Reset values to default
|
||||
cur_entry.delta = {};
|
||||
cur_entry.vel_x = 0;
|
||||
cur_entry.vel_y = 0;
|
||||
cur_entry.direction = Direction::None;
|
||||
cur_entry.rotation_angle = 0;
|
||||
cur_entry.scale = 0;
|
||||
// Reset next state to default
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
next_state.delta = {};
|
||||
next_state.vel_x = 0;
|
||||
next_state.vel_y = 0;
|
||||
next_state.direction = GestureDirection::None;
|
||||
next_state.rotation_angle = 0;
|
||||
next_state.scale = 0;
|
||||
|
||||
if (gesture.active_points > 0) {
|
||||
if (last_gesture.active_points == 0) {
|
||||
@@ -141,46 +121,47 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
|
||||
}
|
||||
|
||||
// Apply attributes
|
||||
cur_entry.detection_count = gesture.detection_count;
|
||||
cur_entry.type = type;
|
||||
cur_entry.attributes = attributes;
|
||||
cur_entry.pos = gesture.mid_point;
|
||||
cur_entry.point_count = static_cast<s32>(gesture.active_points);
|
||||
cur_entry.points = gesture.points;
|
||||
next_state.detection_count = gesture.detection_count;
|
||||
next_state.type = type;
|
||||
next_state.attributes = attributes;
|
||||
next_state.pos = gesture.mid_point;
|
||||
next_state.point_count = static_cast<s32>(gesture.active_points);
|
||||
next_state.points = gesture.points;
|
||||
last_gesture = gesture;
|
||||
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
|
||||
gesture_lifo.WriteNextEntry(next_state);
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
|
||||
}
|
||||
|
||||
void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type,
|
||||
Attribute& attributes) {
|
||||
void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
|
||||
GestureAttribute& attributes) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
gesture.detection_count++;
|
||||
type = TouchType::Touch;
|
||||
type = GestureType::Touch;
|
||||
|
||||
// New touch after cancel is not considered new
|
||||
if (last_entry.type != TouchType::Cancel) {
|
||||
if (last_entry.type != GestureType::Cancel) {
|
||||
attributes.is_new_touch.Assign(1);
|
||||
enable_press_and_tap = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type,
|
||||
void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
|
||||
f32 time_difference) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
// Promote to pan type if touch moved
|
||||
for (size_t id = 0; id < MAX_POINTS; id++) {
|
||||
if (gesture.points[id] != last_gesture.points[id]) {
|
||||
type = TouchType::Pan;
|
||||
type = GestureType::Pan;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Number of fingers changed cancel the last event and clear data
|
||||
if (gesture.active_points != last_gesture.active_points) {
|
||||
type = TouchType::Cancel;
|
||||
type = GestureType::Cancel;
|
||||
enable_press_and_tap = false;
|
||||
gesture.active_points = 0;
|
||||
gesture.mid_point = {};
|
||||
@@ -189,41 +170,41 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch
|
||||
}
|
||||
|
||||
// Calculate extra parameters of panning
|
||||
if (type == TouchType::Pan) {
|
||||
if (type == GestureType::Pan) {
|
||||
UpdatePanEvent(gesture, last_gesture, type, time_difference);
|
||||
return;
|
||||
}
|
||||
|
||||
// Promote to press type
|
||||
if (last_entry.type == TouchType::Touch) {
|
||||
type = TouchType::Press;
|
||||
if (last_entry.type == GestureType::Touch) {
|
||||
type = GestureType::Press;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::EndGesture(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, TouchType& type,
|
||||
Attribute& attributes, f32 time_difference) {
|
||||
GestureProperties& last_gesture_props, GestureType& type,
|
||||
GestureAttribute& attributes, f32 time_difference) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
if (last_gesture_props.active_points != 0) {
|
||||
switch (last_entry.type) {
|
||||
case TouchType::Touch:
|
||||
case GestureType::Touch:
|
||||
if (enable_press_and_tap) {
|
||||
SetTapEvent(gesture, last_gesture_props, type, attributes);
|
||||
return;
|
||||
}
|
||||
type = TouchType::Cancel;
|
||||
type = GestureType::Cancel;
|
||||
force_update = true;
|
||||
break;
|
||||
case TouchType::Press:
|
||||
case TouchType::Tap:
|
||||
case TouchType::Swipe:
|
||||
case TouchType::Pinch:
|
||||
case TouchType::Rotate:
|
||||
type = TouchType::Complete;
|
||||
case GestureType::Press:
|
||||
case GestureType::Tap:
|
||||
case GestureType::Swipe:
|
||||
case GestureType::Pinch:
|
||||
case GestureType::Rotate:
|
||||
type = GestureType::Complete;
|
||||
force_update = true;
|
||||
break;
|
||||
case TouchType::Pan:
|
||||
case GestureType::Pan:
|
||||
EndPanEvent(gesture, last_gesture_props, type, time_difference);
|
||||
break;
|
||||
default:
|
||||
@@ -231,15 +212,15 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) {
|
||||
if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
|
||||
gesture.detection_count++;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, TouchType& type,
|
||||
Attribute& attributes) {
|
||||
type = TouchType::Tap;
|
||||
GestureProperties& last_gesture_props, GestureType& type,
|
||||
GestureAttribute& attributes) {
|
||||
type = GestureType::Tap;
|
||||
gesture = last_gesture_props;
|
||||
force_update = true;
|
||||
f32 tap_time_difference =
|
||||
@@ -251,44 +232,42 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
|
||||
}
|
||||
|
||||
void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, TouchType& type,
|
||||
GestureProperties& last_gesture_props, GestureType& type,
|
||||
f32 time_difference) {
|
||||
auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
cur_entry.delta = gesture.mid_point - last_entry.pos;
|
||||
cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference;
|
||||
cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference;
|
||||
next_state.delta = gesture.mid_point - last_entry.pos;
|
||||
next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
|
||||
next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
|
||||
last_pan_time_difference = time_difference;
|
||||
|
||||
// Promote to pinch type
|
||||
if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
|
||||
pinch_threshold) {
|
||||
type = TouchType::Pinch;
|
||||
cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance;
|
||||
type = GestureType::Pinch;
|
||||
next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
|
||||
}
|
||||
|
||||
const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
|
||||
(1 + (gesture.angle * last_gesture_props.angle)));
|
||||
// Promote to rotate type
|
||||
if (std::abs(angle_between_two_lines) > angle_threshold) {
|
||||
type = TouchType::Rotate;
|
||||
cur_entry.scale = 0;
|
||||
cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
|
||||
type = GestureType::Rotate;
|
||||
next_state.scale = 0;
|
||||
next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, TouchType& type,
|
||||
GestureProperties& last_gesture_props, GestureType& type,
|
||||
f32 time_difference) {
|
||||
auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
cur_entry.vel_x =
|
||||
next_state.vel_x =
|
||||
static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
|
||||
cur_entry.vel_y =
|
||||
next_state.vel_y =
|
||||
static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
|
||||
const f32 curr_vel =
|
||||
std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y));
|
||||
std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
|
||||
|
||||
// Set swipe event with parameters
|
||||
if (curr_vel > swipe_threshold) {
|
||||
@@ -297,105 +276,50 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
|
||||
}
|
||||
|
||||
// End panning without swipe
|
||||
type = TouchType::Complete;
|
||||
cur_entry.vel_x = 0;
|
||||
cur_entry.vel_y = 0;
|
||||
type = GestureType::Complete;
|
||||
next_state.vel_x = 0;
|
||||
next_state.vel_y = 0;
|
||||
force_update = true;
|
||||
}
|
||||
|
||||
void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, TouchType& type) {
|
||||
auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
|
||||
GestureProperties& last_gesture_props, GestureType& type) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
type = TouchType::Swipe;
|
||||
type = GestureType::Swipe;
|
||||
gesture = last_gesture_props;
|
||||
force_update = true;
|
||||
cur_entry.delta = last_entry.delta;
|
||||
next_state.delta = last_entry.delta;
|
||||
|
||||
if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) {
|
||||
if (cur_entry.delta.x > 0) {
|
||||
cur_entry.direction = Direction::Right;
|
||||
if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
|
||||
if (next_state.delta.x > 0) {
|
||||
next_state.direction = GestureDirection::Right;
|
||||
return;
|
||||
}
|
||||
cur_entry.direction = Direction::Left;
|
||||
next_state.direction = GestureDirection::Left;
|
||||
return;
|
||||
}
|
||||
if (cur_entry.delta.y > 0) {
|
||||
cur_entry.direction = Direction::Down;
|
||||
if (next_state.delta.y > 0) {
|
||||
next_state.direction = GestureDirection::Down;
|
||||
return;
|
||||
}
|
||||
cur_entry.direction = Direction::Up;
|
||||
}
|
||||
|
||||
void Controller_Gesture::OnLoadInputDevices() {
|
||||
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
|
||||
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
|
||||
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
|
||||
}
|
||||
|
||||
std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
|
||||
// Dont assign any touch input to a point if disabled
|
||||
if (!Settings::values.touchscreen.enabled) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::size_t first_free_id = 0;
|
||||
while (first_free_id < MAX_POINTS) {
|
||||
if (!fingers[first_free_id].pressed) {
|
||||
return first_free_id;
|
||||
} else {
|
||||
first_free_id++;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() {
|
||||
return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
|
||||
next_state.direction = GestureDirection::Up;
|
||||
}
|
||||
|
||||
const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
|
||||
return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
|
||||
}
|
||||
|
||||
std::size_t Controller_Gesture::UpdateTouchInputEvent(
|
||||
const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
|
||||
const auto& [x, y, pressed] = touch_input;
|
||||
if (finger_id > MAX_POINTS) {
|
||||
LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
|
||||
return MAX_POINTS;
|
||||
}
|
||||
if (pressed) {
|
||||
if (finger_id == MAX_POINTS) {
|
||||
const auto first_free_id = GetUnusedFingerID();
|
||||
if (!first_free_id) {
|
||||
// Invalid finger id do nothing
|
||||
return MAX_POINTS;
|
||||
}
|
||||
finger_id = first_free_id.value();
|
||||
fingers[finger_id].pressed = true;
|
||||
}
|
||||
fingers[finger_id].pos = {x, y};
|
||||
return finger_id;
|
||||
}
|
||||
|
||||
if (finger_id != MAX_POINTS) {
|
||||
fingers[finger_id].pressed = false;
|
||||
}
|
||||
|
||||
return MAX_POINTS;
|
||||
return gesture_lifo.ReadCurrentEntry().state;
|
||||
}
|
||||
|
||||
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
|
||||
GestureProperties gesture;
|
||||
std::array<Finger, MAX_POINTS> active_fingers;
|
||||
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
|
||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
||||
[](const auto& finger) { return finger.pressed; });
|
||||
gesture.active_points =
|
||||
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
||||
|
||||
for (size_t id = 0; id < gesture.active_points; ++id) {
|
||||
const auto& [active_x, active_y] = active_fingers[id].pos;
|
||||
const auto& [active_x, active_y] = active_fingers[id].position;
|
||||
gesture.points[id] = {
|
||||
.x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
|
||||
.y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
|
||||
|
@@ -8,8 +8,9 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/point.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Gesture final : public ControllerBase {
|
||||
@@ -26,14 +27,12 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_FINGERS = 16;
|
||||
static constexpr size_t MAX_POINTS = 4;
|
||||
|
||||
enum class TouchType : u32 {
|
||||
// This is nn::hid::GestureType
|
||||
enum class GestureType : u32 {
|
||||
Idle, // Nothing touching the screen
|
||||
Complete, // Set at the end of a touch event
|
||||
Cancel, // Set when the number of fingers change
|
||||
@@ -46,7 +45,8 @@ private:
|
||||
Rotate, // All points rotating from the midpoint
|
||||
};
|
||||
|
||||
enum class Direction : u32 {
|
||||
// This is nn::hid::GestureDirection
|
||||
enum class GestureDirection : u32 {
|
||||
None,
|
||||
Left,
|
||||
Up,
|
||||
@@ -54,51 +54,41 @@ private:
|
||||
Down,
|
||||
};
|
||||
|
||||
struct Attribute {
|
||||
// This is nn::hid::GestureAttribute
|
||||
struct GestureAttribute {
|
||||
union {
|
||||
u32_le raw{};
|
||||
u32 raw{};
|
||||
|
||||
BitField<4, 1, u32> is_new_touch;
|
||||
BitField<8, 1, u32> is_double_tap;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size");
|
||||
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::GestureState
|
||||
struct GestureState {
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
s64_le detection_count;
|
||||
TouchType type;
|
||||
Direction direction;
|
||||
Common::Point<s32_le> pos;
|
||||
Common::Point<s32_le> delta;
|
||||
s64 sampling_number;
|
||||
s64 detection_count;
|
||||
GestureType type;
|
||||
GestureDirection direction;
|
||||
Common::Point<s32> pos;
|
||||
Common::Point<s32> delta;
|
||||
f32 vel_x;
|
||||
f32 vel_y;
|
||||
Attribute attributes;
|
||||
GestureAttribute attributes;
|
||||
f32 scale;
|
||||
f32 rotation_angle;
|
||||
s32_le point_count;
|
||||
std::array<Common::Point<s32_le>, 4> points;
|
||||
};
|
||||
static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
|
||||
|
||||
struct SharedMemory {
|
||||
CommonHeader header;
|
||||
std::array<GestureState, 17> gesture_states;
|
||||
};
|
||||
static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
|
||||
|
||||
struct Finger {
|
||||
Common::Point<f32> pos{};
|
||||
bool pressed{};
|
||||
s32 point_count;
|
||||
std::array<Common::Point<s32>, 4> points;
|
||||
};
|
||||
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
|
||||
|
||||
struct GestureProperties {
|
||||
std::array<Common::Point<s32_le>, MAX_POINTS> points{};
|
||||
std::array<Common::Point<s32>, MAX_POINTS> points{};
|
||||
std::size_t active_points{};
|
||||
Common::Point<s32_le> mid_point{};
|
||||
s64_le detection_count{};
|
||||
u64_le delta_time{};
|
||||
Common::Point<s32> mid_point{};
|
||||
s64 detection_count{};
|
||||
u64 delta_time{};
|
||||
f32 average_distance{};
|
||||
f32 angle{};
|
||||
};
|
||||
@@ -114,61 +104,48 @@ private:
|
||||
f32 time_difference);
|
||||
|
||||
// Initializes new gesture
|
||||
void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes);
|
||||
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
|
||||
|
||||
// Updates existing gesture state
|
||||
void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference);
|
||||
void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
|
||||
|
||||
// Terminates exiting gesture
|
||||
void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
TouchType& type, Attribute& attributes, f32 time_difference);
|
||||
GestureType& type, GestureAttribute& attributes, f32 time_difference);
|
||||
|
||||
// Set current event to a tap event
|
||||
void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
TouchType& type, Attribute& attributes);
|
||||
GestureType& type, GestureAttribute& attributes);
|
||||
|
||||
// Calculates and set the extra parameters related to a pan event
|
||||
void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
TouchType& type, f32 time_difference);
|
||||
GestureType& type, f32 time_difference);
|
||||
|
||||
// Terminates the pan event
|
||||
void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
TouchType& type, f32 time_difference);
|
||||
GestureType& type, f32 time_difference);
|
||||
|
||||
// Set current event to a swipe event
|
||||
void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
TouchType& type);
|
||||
|
||||
// Returns an unused finger id, if there is no fingers available std::nullopt is returned.
|
||||
[[nodiscard]] std::optional<size_t> GetUnusedFingerID() const;
|
||||
GestureType& type);
|
||||
|
||||
// Retrieves the last gesture entry, as indicated by shared memory indices.
|
||||
[[nodiscard]] GestureState& GetLastGestureEntry();
|
||||
[[nodiscard]] const GestureState& GetLastGestureEntry() const;
|
||||
|
||||
/**
|
||||
* If the touch is new it tries to assign a new finger id, if there is no fingers available no
|
||||
* changes will be made. Updates the coordinates if the finger id it's already set. If the touch
|
||||
* ends delays the output by one frame to set the end_touch flag before finally freeing the
|
||||
* finger id
|
||||
*/
|
||||
size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
|
||||
size_t finger_id);
|
||||
|
||||
// Returns the average distance, angle and middle point of the active fingers
|
||||
GestureProperties GetGestureProperties();
|
||||
|
||||
SharedMemory shared_memory{};
|
||||
std::unique_ptr<Input::TouchDevice> touch_mouse_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_udp_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_btn_device;
|
||||
std::array<size_t, MAX_FINGERS> mouse_finger_id{};
|
||||
std::array<size_t, MAX_FINGERS> keyboard_finger_id{};
|
||||
std::array<size_t, MAX_FINGERS> udp_finger_id{};
|
||||
std::array<Finger, MAX_POINTS> fingers{};
|
||||
// This is nn::hid::detail::GestureLifo
|
||||
Lifo<GestureState> gesture_lifo{};
|
||||
static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
|
||||
GestureState next_state{};
|
||||
|
||||
Core::HID::EmulatedConsole* console;
|
||||
|
||||
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
|
||||
GestureProperties last_gesture{};
|
||||
s64_le last_update_timestamp{};
|
||||
s64_le last_tap_timestamp{};
|
||||
s64 last_update_timestamp{};
|
||||
s64 last_tap_timestamp{};
|
||||
f32 last_pan_time_difference{};
|
||||
bool force_update{false};
|
||||
bool enable_press_and_tap{false};
|
||||
|
@@ -5,14 +5,19 @@
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/emulated_devices.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/keyboard.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
|
||||
constexpr u8 KEYS_PER_BYTE = 8;
|
||||
|
||||
Controller_Keyboard::Controller_Keyboard(Core::System& system_) : ControllerBase{system_} {}
|
||||
Controller_Keyboard::Controller_Keyboard(Core::System& system_) : ControllerBase{system_} {
|
||||
emulated_devices = system.HIDCore().GetEmulatedDevices();
|
||||
}
|
||||
|
||||
Controller_Keyboard::~Controller_Keyboard() = default;
|
||||
|
||||
void Controller_Keyboard::OnInit() {}
|
||||
@@ -21,51 +26,26 @@ void Controller_Keyboard::OnRelease() {}
|
||||
|
||||
void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||
std::size_t size) {
|
||||
shared_memory.header.timestamp = core_timing.GetCPUTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory.header.entry_count = 0;
|
||||
shared_memory.header.last_entry_index = 0;
|
||||
keyboard_lifo.buffer_count = 0;
|
||||
keyboard_lifo.buffer_tail = 0;
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
|
||||
return;
|
||||
}
|
||||
shared_memory.header.entry_count = 16;
|
||||
|
||||
const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
|
||||
shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
|
||||
auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
|
||||
const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state;
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
|
||||
cur_entry.key.fill(0);
|
||||
if (Settings::values.keyboard_enabled) {
|
||||
for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
|
||||
auto& entry = cur_entry.key[i / KEYS_PER_BYTE];
|
||||
entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)));
|
||||
}
|
||||
const auto& keyboard_state = emulated_devices->GetKeyboard();
|
||||
const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
|
||||
|
||||
using namespace Settings::NativeKeyboard;
|
||||
|
||||
// TODO: Assign the correct key to all modifiers
|
||||
cur_entry.modifier.control.Assign(keyboard_mods[LeftControl]->GetStatus());
|
||||
cur_entry.modifier.shift.Assign(keyboard_mods[LeftShift]->GetStatus());
|
||||
cur_entry.modifier.left_alt.Assign(keyboard_mods[LeftAlt]->GetStatus());
|
||||
cur_entry.modifier.right_alt.Assign(keyboard_mods[RightAlt]->GetStatus());
|
||||
cur_entry.modifier.gui.Assign(0);
|
||||
cur_entry.modifier.caps_lock.Assign(keyboard_mods[CapsLock]->GetStatus());
|
||||
cur_entry.modifier.scroll_lock.Assign(keyboard_mods[ScrollLock]->GetStatus());
|
||||
cur_entry.modifier.num_lock.Assign(keyboard_mods[NumLock]->GetStatus());
|
||||
cur_entry.modifier.katakana.Assign(0);
|
||||
cur_entry.modifier.hiragana.Assign(0);
|
||||
next_state.key = keyboard_state;
|
||||
next_state.modifier = keyboard_modifier_state;
|
||||
}
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
|
||||
|
||||
keyboard_lifo.WriteNextEntry(next_state);
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
|
||||
}
|
||||
|
||||
void Controller_Keyboard::OnLoadInputDevices() {
|
||||
std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
|
||||
keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
|
||||
keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
@@ -10,8 +10,14 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedDevices;
|
||||
struct KeyboardModifier;
|
||||
struct KeyboardKey;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Keyboard final : public ControllerBase {
|
||||
@@ -28,47 +34,20 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
struct Modifiers {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32> control;
|
||||
BitField<1, 1, u32> shift;
|
||||
BitField<2, 1, u32> left_alt;
|
||||
BitField<3, 1, u32> right_alt;
|
||||
BitField<4, 1, u32> gui;
|
||||
BitField<8, 1, u32> caps_lock;
|
||||
BitField<9, 1, u32> scroll_lock;
|
||||
BitField<10, 1, u32> num_lock;
|
||||
BitField<11, 1, u32> katakana;
|
||||
BitField<12, 1, u32> hiragana;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Modifiers) == 0x4, "Modifiers is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::KeyboardState
|
||||
struct KeyboardState {
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
|
||||
Modifiers modifier;
|
||||
std::array<u8, 32> key;
|
||||
s64 sampling_number;
|
||||
Core::HID::KeyboardModifier modifier;
|
||||
Core::HID::KeyboardKey key;
|
||||
};
|
||||
static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
|
||||
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
|
||||
|
||||
struct SharedMemory {
|
||||
CommonHeader header;
|
||||
std::array<KeyboardState, 17> pad_states;
|
||||
INSERT_PADDING_BYTES(0x28);
|
||||
};
|
||||
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
|
||||
SharedMemory shared_memory{};
|
||||
// This is nn::hid::detail::KeyboardLifo
|
||||
Lifo<KeyboardState> keyboard_lifo{};
|
||||
static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
|
||||
KeyboardState next_state{};
|
||||
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
|
||||
keyboard_keys;
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
|
||||
keyboard_mods;
|
||||
Core::HID::EmulatedDevices* emulated_devices;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
@@ -4,14 +4,20 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hid/emulated_devices.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/mouse.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
|
||||
|
||||
Controller_Mouse::Controller_Mouse(Core::System& system_) : ControllerBase{system_} {}
|
||||
Controller_Mouse::Controller_Mouse(Core::System& system_) : ControllerBase{system_} {
|
||||
emulated_devices = system.HIDCore().GetEmulatedDevices();
|
||||
}
|
||||
|
||||
Controller_Mouse::~Controller_Mouse() = default;
|
||||
|
||||
void Controller_Mouse::OnInit() {}
|
||||
@@ -19,50 +25,33 @@ void Controller_Mouse::OnRelease() {}
|
||||
|
||||
void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||
std::size_t size) {
|
||||
shared_memory.header.timestamp = core_timing.GetCPUTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory.header.entry_count = 0;
|
||||
shared_memory.header.last_entry_index = 0;
|
||||
mouse_lifo.buffer_count = 0;
|
||||
mouse_lifo.buffer_tail = 0;
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
|
||||
return;
|
||||
}
|
||||
shared_memory.header.entry_count = 16;
|
||||
|
||||
auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
|
||||
shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
|
||||
auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
|
||||
const auto& last_entry = mouse_lifo.ReadCurrentEntry().state;
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
|
||||
cur_entry.attribute.raw = 0;
|
||||
next_state.attribute.raw = 0;
|
||||
if (Settings::values.mouse_enabled) {
|
||||
const auto [px, py, sx, sy] = mouse_device->GetStatus();
|
||||
const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
|
||||
const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
|
||||
cur_entry.x = x;
|
||||
cur_entry.y = y;
|
||||
cur_entry.delta_x = x - last_entry.x;
|
||||
cur_entry.delta_y = y - last_entry.y;
|
||||
cur_entry.mouse_wheel_x = sx;
|
||||
cur_entry.mouse_wheel_y = sy;
|
||||
cur_entry.attribute.is_connected.Assign(1);
|
||||
const auto& mouse_button_state = emulated_devices->GetMouseButtons();
|
||||
const auto& mouse_position_state = emulated_devices->GetMousePosition();
|
||||
next_state.attribute.is_connected.Assign(1);
|
||||
next_state.x = mouse_position_state.x;
|
||||
next_state.y = mouse_position_state.y;
|
||||
next_state.delta_x = next_state.x - last_entry.x;
|
||||
next_state.delta_y = next_state.y - last_entry.y;
|
||||
next_state.delta_wheel_x = mouse_position_state.delta_wheel_x;
|
||||
next_state.delta_wheel_y = mouse_position_state.delta_wheel_y;
|
||||
|
||||
using namespace Settings::NativeMouseButton;
|
||||
cur_entry.button.left.Assign(mouse_button_devices[Left]->GetStatus());
|
||||
cur_entry.button.right.Assign(mouse_button_devices[Right]->GetStatus());
|
||||
cur_entry.button.middle.Assign(mouse_button_devices[Middle]->GetStatus());
|
||||
cur_entry.button.forward.Assign(mouse_button_devices[Forward]->GetStatus());
|
||||
cur_entry.button.back.Assign(mouse_button_devices[Back]->GetStatus());
|
||||
next_state.button = mouse_button_state;
|
||||
}
|
||||
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
|
||||
mouse_lifo.WriteNextEntry(next_state);
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
|
||||
}
|
||||
|
||||
void Controller_Mouse::OnLoadInputDevices() {
|
||||
mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
|
||||
std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
|
||||
mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
@@ -9,8 +9,13 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedDevices;
|
||||
struct MouseState;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Mouse final : public ControllerBase {
|
||||
@@ -27,53 +32,12 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
struct Buttons {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32> left;
|
||||
BitField<1, 1, u32> right;
|
||||
BitField<2, 1, u32> middle;
|
||||
BitField<3, 1, u32> forward;
|
||||
BitField<4, 1, u32> back;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Buttons) == 0x4, "Buttons is an invalid size");
|
||||
// This is nn::hid::detail::MouseLifo
|
||||
Lifo<Core::HID::MouseState> mouse_lifo{};
|
||||
static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
|
||||
Core::HID::MouseState next_state{};
|
||||
|
||||
struct Attributes {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32> transferable;
|
||||
BitField<1, 1, u32> is_connected;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
|
||||
|
||||
struct MouseState {
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
s32_le x;
|
||||
s32_le y;
|
||||
s32_le delta_x;
|
||||
s32_le delta_y;
|
||||
s32_le mouse_wheel_x;
|
||||
s32_le mouse_wheel_y;
|
||||
Buttons button;
|
||||
Attributes attribute;
|
||||
};
|
||||
static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
|
||||
|
||||
struct SharedMemory {
|
||||
CommonHeader header;
|
||||
std::array<MouseState, 17> mouse_states;
|
||||
};
|
||||
SharedMemory shared_memory{};
|
||||
|
||||
std::unique_ptr<Input::MouseDevice> mouse_device;
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
|
||||
mouse_button_devices;
|
||||
Core::HID::EmulatedDevices* emulated_devices;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -12,8 +12,14 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/quaternion.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedController;
|
||||
enum class ControllerTriggerType;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
@@ -48,31 +54,6 @@ public:
|
||||
void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||
std::size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
enum class NPadControllerType {
|
||||
None,
|
||||
ProController,
|
||||
Handheld,
|
||||
JoyDual,
|
||||
JoyLeft,
|
||||
JoyRight,
|
||||
GameCube,
|
||||
Pokeball,
|
||||
};
|
||||
|
||||
enum class NpadType : u8 {
|
||||
ProController = 3,
|
||||
Handheld = 4,
|
||||
JoyconDual = 5,
|
||||
JoyconLeft = 6,
|
||||
JoyconRight = 7,
|
||||
GameCube = 8,
|
||||
Pokeball = 9,
|
||||
MaxNpadType = 10,
|
||||
};
|
||||
|
||||
enum class DeviceIndex : u8 {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
@@ -80,28 +61,33 @@ public:
|
||||
MaxDeviceIndex = 3,
|
||||
};
|
||||
|
||||
// This is nn::hid::GyroscopeZeroDriftMode
|
||||
enum class GyroscopeZeroDriftMode : u32 {
|
||||
Loose = 0,
|
||||
Standard = 1,
|
||||
Tight = 2,
|
||||
};
|
||||
|
||||
enum class NpadHoldType : u64 {
|
||||
// This is nn::hid::NpadJoyHoldType
|
||||
enum class NpadJoyHoldType : u64 {
|
||||
Vertical = 0,
|
||||
Horizontal = 1,
|
||||
};
|
||||
|
||||
enum class NpadAssignments : u32 {
|
||||
// This is nn::hid::NpadJoyAssignmentMode
|
||||
enum class NpadJoyAssignmentMode : u32 {
|
||||
Dual = 0,
|
||||
Single = 1,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadHandheldActivationMode
|
||||
enum class NpadHandheldActivationMode : u64 {
|
||||
Dual = 0,
|
||||
Single = 1,
|
||||
None = 2,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadCommunicationMode
|
||||
enum class NpadCommunicationMode : u64 {
|
||||
Mode_5ms = 0,
|
||||
Mode_10ms = 1,
|
||||
@@ -110,33 +96,14 @@ public:
|
||||
};
|
||||
|
||||
struct DeviceHandle {
|
||||
NpadType npad_type;
|
||||
Core::HID::NpadType npad_type;
|
||||
u8 npad_id;
|
||||
DeviceIndex device_index;
|
||||
INSERT_PADDING_BYTES_NOINIT(1);
|
||||
};
|
||||
static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
|
||||
|
||||
struct NpadStyleSet {
|
||||
union {
|
||||
u32_le raw{};
|
||||
|
||||
BitField<0, 1, u32> fullkey;
|
||||
BitField<1, 1, u32> handheld;
|
||||
BitField<2, 1, u32> joycon_dual;
|
||||
BitField<3, 1, u32> joycon_left;
|
||||
BitField<4, 1, u32> joycon_right;
|
||||
BitField<5, 1, u32> gamecube;
|
||||
BitField<6, 1, u32> palma;
|
||||
BitField<7, 1, u32> lark;
|
||||
BitField<8, 1, u32> handheld_lark;
|
||||
BitField<9, 1, u32> lucia;
|
||||
BitField<29, 1, u32> system_ext;
|
||||
BitField<30, 1, u32> system;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
|
||||
|
||||
// This is nn::hid::VibrationValue
|
||||
struct VibrationValue {
|
||||
f32 amp_low;
|
||||
f32 freq_low;
|
||||
@@ -152,31 +119,15 @@ public:
|
||||
.freq_high = 320.0f,
|
||||
};
|
||||
|
||||
struct LedPattern {
|
||||
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
|
||||
position1.Assign(light1);
|
||||
position2.Assign(light2);
|
||||
position3.Assign(light3);
|
||||
position4.Assign(light4);
|
||||
}
|
||||
union {
|
||||
u64 raw{};
|
||||
BitField<0, 1, u64> position1;
|
||||
BitField<1, 1, u64> position2;
|
||||
BitField<2, 1, u64> position3;
|
||||
BitField<3, 1, u64> position4;
|
||||
};
|
||||
};
|
||||
|
||||
void SetSupportedStyleSet(NpadStyleSet style_set);
|
||||
NpadStyleSet GetSupportedStyleSet() const;
|
||||
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
|
||||
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
|
||||
|
||||
void SetSupportedNpadIdTypes(u8* data, std::size_t length);
|
||||
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
|
||||
std::size_t GetSupportedNpadIdTypesSize() const;
|
||||
|
||||
void SetHoldType(NpadHoldType joy_hold_type);
|
||||
NpadHoldType GetHoldType() const;
|
||||
void SetHoldType(NpadJoyHoldType joy_hold_type);
|
||||
NpadJoyHoldType GetHoldType() const;
|
||||
|
||||
void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
|
||||
NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
|
||||
@@ -184,7 +135,7 @@ public:
|
||||
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
|
||||
NpadCommunicationMode GetNpadCommunicationMode() const;
|
||||
|
||||
void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
|
||||
void SetNpadMode(u32 npad_id, NpadJoyAssignmentMode assignment_mode);
|
||||
|
||||
bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
|
||||
const VibrationValue& vibration_value);
|
||||
@@ -209,9 +160,9 @@ public:
|
||||
void SignalStyleSetChangedEvent(u32 npad_id) const;
|
||||
|
||||
// Adds a new controller at an index.
|
||||
void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
|
||||
void AddNewControllerAt(Core::HID::NpadType controller, std::size_t npad_index);
|
||||
// Adds a new controller at an index with connection status.
|
||||
void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
|
||||
void UpdateControllerAt(Core::HID::NpadType controller, std::size_t npad_index, bool connected);
|
||||
|
||||
void DisconnectNpad(u32 npad_id);
|
||||
void DisconnectNpadAtIndex(std::size_t index);
|
||||
@@ -223,7 +174,7 @@ public:
|
||||
void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2);
|
||||
std::pair<f32, f32> GetSixAxisFusionParameters();
|
||||
void ResetSixAxisFusionParameters();
|
||||
LedPattern GetLedPattern(u32 npad_id);
|
||||
Core::HID::LedPattern GetLedPattern(u32 npad_id);
|
||||
bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
|
||||
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
|
||||
void SetAnalogStickUseCenterClamp(bool use_center_clamp);
|
||||
@@ -241,105 +192,39 @@ public:
|
||||
// Specifically for cheat engine and other features.
|
||||
u32 GetAndResetPressState();
|
||||
|
||||
static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
|
||||
static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
|
||||
static std::size_t NPadIdToIndex(u32 npad_id);
|
||||
static u32 IndexToNPad(std::size_t index);
|
||||
static bool IsNpadIdValid(u32 npad_id);
|
||||
static bool IsDeviceHandleValid(const DeviceHandle& device_handle);
|
||||
|
||||
private:
|
||||
struct CommonHeader {
|
||||
s64_le timestamp;
|
||||
s64_le total_entry_count;
|
||||
s64_le last_entry_index;
|
||||
s64_le entry_count;
|
||||
};
|
||||
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
|
||||
|
||||
enum class ColorAttributes : u32_le {
|
||||
// This is nn::hid::detail::ColorAttribute
|
||||
enum class ColorAttribute : u32 {
|
||||
Ok = 0,
|
||||
ReadError = 1,
|
||||
NoController = 2,
|
||||
};
|
||||
static_assert(sizeof(ColorAttributes) == 4, "ColorAttributes is an invalid size");
|
||||
static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
|
||||
|
||||
struct ControllerColor {
|
||||
u32_le body;
|
||||
u32_le button;
|
||||
// This is nn::hid::detail::NpadFullKeyColorState
|
||||
struct NpadFullKeyColorState {
|
||||
ColorAttribute attribute;
|
||||
Core::HID::NpadControllerColor fullkey;
|
||||
};
|
||||
static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
|
||||
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
|
||||
|
||||
struct FullKeyColor {
|
||||
ColorAttributes attribute;
|
||||
ControllerColor fullkey;
|
||||
// This is nn::hid::detail::NpadJoyColorState
|
||||
struct NpadJoyColorState {
|
||||
ColorAttribute attribute;
|
||||
Core::HID::NpadControllerColor left;
|
||||
Core::HID::NpadControllerColor right;
|
||||
};
|
||||
static_assert(sizeof(FullKeyColor) == 0xC, "FullKeyColor is an invalid size");
|
||||
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
|
||||
|
||||
struct JoyconColor {
|
||||
ColorAttributes attribute;
|
||||
ControllerColor left;
|
||||
ControllerColor right;
|
||||
};
|
||||
static_assert(sizeof(JoyconColor) == 0x14, "JoyconColor is an invalid size");
|
||||
|
||||
struct ControllerPadState {
|
||||
// This is nn::hid::NpadAttribute
|
||||
struct NpadAttribute {
|
||||
union {
|
||||
u64_le raw{};
|
||||
// Button states
|
||||
BitField<0, 1, u64> a;
|
||||
BitField<1, 1, u64> b;
|
||||
BitField<2, 1, u64> x;
|
||||
BitField<3, 1, u64> y;
|
||||
BitField<4, 1, u64> l_stick;
|
||||
BitField<5, 1, u64> r_stick;
|
||||
BitField<6, 1, u64> l;
|
||||
BitField<7, 1, u64> r;
|
||||
BitField<8, 1, u64> zl;
|
||||
BitField<9, 1, u64> zr;
|
||||
BitField<10, 1, u64> plus;
|
||||
BitField<11, 1, u64> minus;
|
||||
|
||||
// D-Pad
|
||||
BitField<12, 1, u64> d_left;
|
||||
BitField<13, 1, u64> d_up;
|
||||
BitField<14, 1, u64> d_right;
|
||||
BitField<15, 1, u64> d_down;
|
||||
|
||||
// Left JoyStick
|
||||
BitField<16, 1, u64> l_stick_left;
|
||||
BitField<17, 1, u64> l_stick_up;
|
||||
BitField<18, 1, u64> l_stick_right;
|
||||
BitField<19, 1, u64> l_stick_down;
|
||||
|
||||
// Right JoyStick
|
||||
BitField<20, 1, u64> r_stick_left;
|
||||
BitField<21, 1, u64> r_stick_up;
|
||||
BitField<22, 1, u64> r_stick_right;
|
||||
BitField<23, 1, u64> r_stick_down;
|
||||
|
||||
// Not always active?
|
||||
BitField<24, 1, u64> left_sl;
|
||||
BitField<25, 1, u64> left_sr;
|
||||
|
||||
BitField<26, 1, u64> right_sl;
|
||||
BitField<27, 1, u64> right_sr;
|
||||
|
||||
BitField<28, 1, u64> palma;
|
||||
BitField<30, 1, u64> handheld_left_b;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
|
||||
|
||||
struct AnalogPosition {
|
||||
s32_le x;
|
||||
s32_le y;
|
||||
};
|
||||
static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
|
||||
|
||||
struct ConnectionState {
|
||||
union {
|
||||
u32_le raw{};
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> is_connected;
|
||||
BitField<1, 1, u32> is_wired;
|
||||
BitField<2, 1, u32> is_left_connected;
|
||||
@@ -348,79 +233,60 @@ private:
|
||||
BitField<5, 1, u32> is_right_wired;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
|
||||
static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
|
||||
|
||||
struct ControllerPad {
|
||||
ControllerPadState pad_states;
|
||||
AnalogPosition l_stick;
|
||||
AnalogPosition r_stick;
|
||||
// This is nn::hid::NpadFullKeyState
|
||||
// This is nn::hid::NpadHandheldState
|
||||
// This is nn::hid::NpadJoyDualState
|
||||
// This is nn::hid::NpadJoyLeftState
|
||||
// This is nn::hid::NpadJoyRightState
|
||||
// This is nn::hid::NpadPalmaState
|
||||
// This is nn::hid::NpadSystemExtState
|
||||
struct NPadGenericState {
|
||||
s64_le sampling_number;
|
||||
Core::HID::NpadButtonState npad_buttons;
|
||||
Core::HID::AnalogStickState l_stick;
|
||||
Core::HID::AnalogStickState r_stick;
|
||||
NpadAttribute connection_status;
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
};
|
||||
static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
|
||||
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
|
||||
|
||||
struct GenericStates {
|
||||
s64_le timestamp;
|
||||
s64_le timestamp2;
|
||||
ControllerPad pad;
|
||||
ConnectionState connection_status;
|
||||
};
|
||||
static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
|
||||
|
||||
struct NPadGeneric {
|
||||
CommonHeader common;
|
||||
std::array<GenericStates, 17> npad;
|
||||
};
|
||||
static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
|
||||
|
||||
struct SixAxisAttributes {
|
||||
// This is nn::hid::SixAxisSensorAttribute
|
||||
struct SixAxisSensorAttribute {
|
||||
union {
|
||||
u32_le raw{};
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> is_connected;
|
||||
BitField<1, 1, u32> is_interpolated;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(SixAxisAttributes) == 4, "SixAxisAttributes is an invalid size");
|
||||
static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
|
||||
|
||||
struct SixAxisStates {
|
||||
s64_le timestamp{};
|
||||
INSERT_PADDING_WORDS(2);
|
||||
s64_le timestamp2{};
|
||||
// This is nn::hid::SixAxisSensorState
|
||||
struct SixAxisSensorState {
|
||||
s64 delta_time{};
|
||||
s64 sampling_number{};
|
||||
Common::Vec3f accel{};
|
||||
Common::Vec3f gyro{};
|
||||
Common::Vec3f rotation{};
|
||||
std::array<Common::Vec3f, 3> orientation{};
|
||||
SixAxisAttributes attribute;
|
||||
SixAxisSensorAttribute attribute;
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
};
|
||||
static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
|
||||
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
|
||||
|
||||
struct SixAxisGeneric {
|
||||
CommonHeader common{};
|
||||
std::array<SixAxisStates, 17> sixaxis{};
|
||||
// This is nn::hid::server::NpadGcTriggerState
|
||||
struct NpadGcTriggerState {
|
||||
s64 sampling_number{};
|
||||
s32 l_analog{};
|
||||
s32 r_analog{};
|
||||
};
|
||||
static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
|
||||
|
||||
struct TriggerState {
|
||||
s64_le timestamp{};
|
||||
s64_le timestamp2{};
|
||||
s32_le l_analog{};
|
||||
s32_le r_analog{};
|
||||
};
|
||||
static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size");
|
||||
|
||||
struct TriggerGeneric {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
s64_le timestamp;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
s64_le total_entry_count;
|
||||
s64_le last_entry_index;
|
||||
s64_le entry_count;
|
||||
std::array<TriggerState, 17> trigger{};
|
||||
};
|
||||
static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
|
||||
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
|
||||
|
||||
// This is nn::hid::NpadSystemProperties
|
||||
struct NPadSystemProperties {
|
||||
union {
|
||||
s64_le raw{};
|
||||
s64 raw{};
|
||||
BitField<0, 1, s64> is_charging_joy_dual;
|
||||
BitField<1, 1, s64> is_charging_joy_left;
|
||||
BitField<2, 1, s64> is_charging_joy_right;
|
||||
@@ -438,17 +304,20 @@ private:
|
||||
};
|
||||
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
|
||||
|
||||
struct NPadButtonProperties {
|
||||
// This is nn::hid::NpadSystemButtonProperties
|
||||
struct NpadSystemButtonProperties {
|
||||
union {
|
||||
s32_le raw{};
|
||||
s32 raw{};
|
||||
BitField<0, 1, s32> is_home_button_protection_enabled;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NPadButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
|
||||
static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
|
||||
"NPadButtonProperties is an invalid size");
|
||||
|
||||
struct NPadDevice {
|
||||
// This is nn::hid::system::DeviceType
|
||||
struct DeviceType {
|
||||
union {
|
||||
u32_le raw{};
|
||||
u32 raw{};
|
||||
BitField<0, 1, s32> fullkey;
|
||||
BitField<1, 1, s32> debug_pad;
|
||||
BitField<2, 1, s32> handheld_left;
|
||||
@@ -465,26 +334,49 @@ private:
|
||||
BitField<13, 1, s32> handheld_lark_nes_left;
|
||||
BitField<14, 1, s32> handheld_lark_nes_right;
|
||||
BitField<15, 1, s32> lucia;
|
||||
BitField<16, 1, s32> lagon;
|
||||
BitField<17, 1, s32> lager;
|
||||
BitField<31, 1, s32> system;
|
||||
};
|
||||
};
|
||||
|
||||
struct MotionDevice {
|
||||
Common::Vec3f accel;
|
||||
Common::Vec3f gyro;
|
||||
Common::Vec3f rotation;
|
||||
std::array<Common::Vec3f, 3> orientation;
|
||||
Common::Quaternion<f32> quaternion;
|
||||
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
|
||||
struct NfcXcdDeviceHandleStateImpl {
|
||||
u64 handle;
|
||||
bool is_available;
|
||||
bool is_activated;
|
||||
INSERT_PADDING_BYTES(0x6); // Reserved
|
||||
u64 sampling_number;
|
||||
};
|
||||
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
|
||||
"NfcXcdDeviceHandleStateImpl is an invalid size");
|
||||
|
||||
struct NfcXcdHandle {
|
||||
INSERT_PADDING_BYTES(0x60);
|
||||
// nn::hid::detail::NfcXcdDeviceHandleStateImplAtomicStorage
|
||||
struct NfcXcdDeviceHandleStateImplAtomicStorage {
|
||||
u64 sampling_number;
|
||||
NfcXcdDeviceHandleStateImpl nfc_xcd_device_handle_state;
|
||||
};
|
||||
static_assert(sizeof(NfcXcdDeviceHandleStateImplAtomicStorage) == 0x20,
|
||||
"NfcXcdDeviceHandleStateImplAtomicStorage is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NfcXcdDeviceHandleState
|
||||
struct NfcXcdDeviceHandleState {
|
||||
// TODO(german77): Make this struct a ring lifo object
|
||||
INSERT_PADDING_BYTES(0x8); // Unused
|
||||
s64 total_buffer_count = max_buffer_size;
|
||||
s64 buffer_tail{};
|
||||
s64 buffer_count{};
|
||||
std::array<NfcXcdDeviceHandleStateImplAtomicStorage, 2> nfc_xcd_device_handle_storage;
|
||||
};
|
||||
static_assert(sizeof(NfcXcdDeviceHandleState) == 0x60,
|
||||
"NfcXcdDeviceHandleState is an invalid size");
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiAttributesSet
|
||||
struct AppletFooterUiAttributes {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiType
|
||||
enum class AppletFooterUiType : u8 {
|
||||
None = 0,
|
||||
HandheldNone = 1,
|
||||
@@ -510,95 +402,137 @@ private:
|
||||
Lagon = 21,
|
||||
};
|
||||
|
||||
struct NPadEntry {
|
||||
NpadStyleSet style_set;
|
||||
NpadAssignments assignment_mode;
|
||||
FullKeyColor fullkey_color;
|
||||
JoyconColor joycon_color;
|
||||
struct AppletFooterUi {
|
||||
AppletFooterUiAttributes attributes;
|
||||
AppletFooterUiType type;
|
||||
INSERT_PADDING_BYTES(0x5B); // Reserved
|
||||
};
|
||||
static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
|
||||
|
||||
NPadGeneric fullkey_states;
|
||||
NPadGeneric handheld_states;
|
||||
NPadGeneric joy_dual_states;
|
||||
NPadGeneric joy_left_states;
|
||||
NPadGeneric joy_right_states;
|
||||
NPadGeneric palma_states;
|
||||
NPadGeneric system_ext_states;
|
||||
SixAxisGeneric sixaxis_fullkey;
|
||||
SixAxisGeneric sixaxis_handheld;
|
||||
SixAxisGeneric sixaxis_dual_left;
|
||||
SixAxisGeneric sixaxis_dual_right;
|
||||
SixAxisGeneric sixaxis_left;
|
||||
SixAxisGeneric sixaxis_right;
|
||||
NPadDevice device_type;
|
||||
INSERT_PADDING_BYTES(0x4); // reserved
|
||||
// This is nn::hid::NpadLarkType
|
||||
enum class NpadLarkType : u32 {
|
||||
Invalid,
|
||||
H1,
|
||||
H2,
|
||||
NL,
|
||||
NR,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLuciaType
|
||||
enum class NpadLuciaType : u32 {
|
||||
Invalid,
|
||||
J,
|
||||
E,
|
||||
U,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLagonType
|
||||
enum class NpadLagonType : u32 {
|
||||
Invalid,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLagerType
|
||||
enum class NpadLagerType : u32 {
|
||||
Invalid,
|
||||
J,
|
||||
E,
|
||||
U,
|
||||
};
|
||||
|
||||
// This is nn::hid::detail::NpadInternalState
|
||||
struct NpadInternalState {
|
||||
Core::HID::NpadStyleTag style_set;
|
||||
NpadJoyAssignmentMode assignment_mode;
|
||||
NpadFullKeyColorState fullkey_color;
|
||||
NpadJoyColorState joycon_color;
|
||||
Lifo<NPadGenericState> fullkey_lifo;
|
||||
Lifo<NPadGenericState> handheld_lifo;
|
||||
Lifo<NPadGenericState> joy_dual_lifo;
|
||||
Lifo<NPadGenericState> joy_left_lifo;
|
||||
Lifo<NPadGenericState> joy_right_lifo;
|
||||
Lifo<NPadGenericState> palma_lifo;
|
||||
Lifo<NPadGenericState> system_ext_lifo;
|
||||
Lifo<SixAxisSensorState> sixaxis_fullkey_lifo;
|
||||
Lifo<SixAxisSensorState> sixaxis_handheld_lifo;
|
||||
Lifo<SixAxisSensorState> sixaxis_dual_left_lifo;
|
||||
Lifo<SixAxisSensorState> sixaxis_dual_right_lifo;
|
||||
Lifo<SixAxisSensorState> sixaxis_left_lifo;
|
||||
Lifo<SixAxisSensorState> sixaxis_right_lifo;
|
||||
DeviceType device_type;
|
||||
INSERT_PADDING_BYTES(0x4); // Reserved
|
||||
NPadSystemProperties system_properties;
|
||||
NPadButtonProperties button_properties;
|
||||
u32 battery_level_dual;
|
||||
u32 battery_level_left;
|
||||
u32 battery_level_right;
|
||||
AppletFooterUiAttributes footer_attributes;
|
||||
AppletFooterUiType footer_type;
|
||||
// nfc_states needs to be checked switchbrew does not match with HW
|
||||
NfcXcdHandle nfc_states;
|
||||
INSERT_PADDING_BYTES(0x8); // Mutex
|
||||
TriggerGeneric gc_trigger_states;
|
||||
INSERT_PADDING_BYTES(0xc1f);
|
||||
NpadSystemButtonProperties button_properties;
|
||||
Core::HID::BatteryLevel battery_level_dual;
|
||||
Core::HID::BatteryLevel battery_level_left;
|
||||
Core::HID::BatteryLevel battery_level_right;
|
||||
union {
|
||||
NfcXcdDeviceHandleState nfc_xcd_device_handle;
|
||||
AppletFooterUi applet_footer;
|
||||
};
|
||||
INSERT_PADDING_BYTES(0x20); // Unknown
|
||||
Lifo<NpadGcTriggerState> gc_trigger_lifo;
|
||||
NpadLarkType lark_type_l_and_main;
|
||||
NpadLarkType lark_type_r;
|
||||
NpadLuciaType lucia_type;
|
||||
NpadLagonType lagon_type;
|
||||
NpadLagerType lager_type;
|
||||
INSERT_PADDING_BYTES(
|
||||
0x4); // FW 13.x Investigate there is some sort of bitflag related to joycons
|
||||
INSERT_PADDING_BYTES(0xc08); // Unknown
|
||||
};
|
||||
static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
|
||||
static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
|
||||
|
||||
struct ControllerHolder {
|
||||
NPadControllerType type;
|
||||
bool is_connected;
|
||||
struct VibrationData {
|
||||
bool device_mounted{};
|
||||
VibrationValue latest_vibration_value{};
|
||||
std::chrono::steady_clock::time_point last_vibration_timepoint{};
|
||||
};
|
||||
|
||||
struct ControllerData {
|
||||
Core::HID::EmulatedController* device;
|
||||
Kernel::KEvent* styleset_changed_event{};
|
||||
NpadInternalState shared_memory_entry{};
|
||||
|
||||
std::array<VibrationData, 2> vibration{};
|
||||
bool unintended_home_button_input_protection{};
|
||||
bool is_connected{};
|
||||
Core::HID::NpadType npad_type{Core::HID::NpadType::None};
|
||||
|
||||
// Current pad state
|
||||
NPadGenericState npad_pad_state{};
|
||||
NPadGenericState npad_libnx_state{};
|
||||
NpadGcTriggerState npad_trigger_state{};
|
||||
SixAxisSensorState sixaxis_fullkey_state{};
|
||||
SixAxisSensorState sixaxis_handheld_state{};
|
||||
SixAxisSensorState sixaxis_dual_left_state{};
|
||||
SixAxisSensorState sixaxis_dual_right_state{};
|
||||
SixAxisSensorState sixaxis_left_lifo_state{};
|
||||
SixAxisSensorState sixaxis_right_lifo_state{};
|
||||
int callback_key;
|
||||
};
|
||||
|
||||
void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
|
||||
void InitNewlyAddedController(std::size_t controller_idx);
|
||||
bool IsControllerSupported(NPadControllerType controller) const;
|
||||
bool IsControllerSupported(Core::HID::NpadType controller) const;
|
||||
void RequestPadStateUpdate(u32 npad_id);
|
||||
void WriteEmptyEntry(NpadInternalState& npad);
|
||||
|
||||
std::atomic<u32> press_state{};
|
||||
|
||||
NpadStyleSet style{};
|
||||
std::array<NPadEntry, 10> shared_memory_entries{};
|
||||
using ButtonArray = std::array<
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
|
||||
10>;
|
||||
using StickArray = std::array<
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
|
||||
10>;
|
||||
using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
|
||||
Settings::NativeVibration::NUM_VIBRATIONS_HID>,
|
||||
10>;
|
||||
using MotionArray = std::array<
|
||||
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
|
||||
10>;
|
||||
|
||||
std::array<ControllerData, 10> controller_data{};
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
std::mutex mutex;
|
||||
ButtonArray buttons;
|
||||
StickArray sticks;
|
||||
VibrationArray vibrations;
|
||||
MotionArray motions;
|
||||
std::vector<u32> supported_npad_id_types{};
|
||||
NpadHoldType hold_type{NpadHoldType::Vertical};
|
||||
NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical};
|
||||
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
|
||||
NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
|
||||
// Each controller should have their own styleset changed event
|
||||
std::array<Kernel::KEvent*, 10> styleset_changed_events{};
|
||||
std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10>
|
||||
last_vibration_timepoints{};
|
||||
std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
|
||||
bool permit_vibration_session_enabled{false};
|
||||
std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
|
||||
std::array<ControllerHolder, 10> connected_controllers{};
|
||||
std::array<bool, 10> unintended_home_button_input_protection{};
|
||||
bool analog_stick_use_center_clamp{};
|
||||
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
|
||||
bool sixaxis_sensors_enabled{true};
|
||||
f32 sixaxis_fusion_parameter1{};
|
||||
f32 sixaxis_fusion_parameter2{};
|
||||
bool sixaxis_at_rest{true};
|
||||
std::array<ControllerPad, 10> npad_pad_states{};
|
||||
std::array<TriggerState, 10> npad_trigger_states{};
|
||||
bool is_in_lr_assignment_mode{false};
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
@@ -31,10 +31,9 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
|
||||
std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
|
||||
}
|
||||
|
||||
void Controller_Stubbed::OnLoadInputDevices() {}
|
||||
|
||||
void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
|
||||
common_offset = off;
|
||||
smart_update = true;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
@@ -22,12 +22,17 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
void SetCommonHeaderOffset(std::size_t off);
|
||||
|
||||
private:
|
||||
struct CommonHeader {
|
||||
s64 timestamp;
|
||||
s64 total_entry_count;
|
||||
s64 last_entry_index;
|
||||
s64 entry_count;
|
||||
};
|
||||
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
|
||||
|
||||
bool smart_update{};
|
||||
std::size_t common_offset{};
|
||||
};
|
||||
|
@@ -7,72 +7,79 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/touchscreen.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
|
||||
|
||||
Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {}
|
||||
Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {
|
||||
console = system.HIDCore().GetEmulatedConsole();
|
||||
}
|
||||
|
||||
Controller_Touchscreen::~Controller_Touchscreen() = default;
|
||||
|
||||
void Controller_Touchscreen::OnInit() {
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
||||
mouse_finger_id[id] = MAX_FINGERS;
|
||||
keyboard_finger_id[id] = MAX_FINGERS;
|
||||
udp_finger_id[id] = MAX_FINGERS;
|
||||
}
|
||||
}
|
||||
void Controller_Touchscreen::OnInit() {}
|
||||
|
||||
void Controller_Touchscreen::OnRelease() {}
|
||||
|
||||
void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||
std::size_t size) {
|
||||
shared_memory.header.timestamp = core_timing.GetCPUTicks();
|
||||
shared_memory.header.total_entry_count = 17;
|
||||
touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory.header.entry_count = 0;
|
||||
shared_memory.header.last_entry_index = 0;
|
||||
touch_screen_lifo.buffer_count = 0;
|
||||
touch_screen_lifo.buffer_tail = 0;
|
||||
std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo));
|
||||
return;
|
||||
}
|
||||
shared_memory.header.entry_count = 16;
|
||||
|
||||
const auto& last_entry =
|
||||
shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
|
||||
shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
|
||||
auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
|
||||
const auto touch_status = console->GetTouch();
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
|
||||
const auto& current_touch = touch_status[id];
|
||||
auto& finger = fingers[id];
|
||||
finger.position = current_touch.position;
|
||||
finger.id = current_touch.id;
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
if (finger.attribute.start_touch) {
|
||||
finger.attribute.raw = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
|
||||
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
|
||||
udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
|
||||
}
|
||||
if (finger.attribute.end_touch) {
|
||||
finger.attribute.raw = 0;
|
||||
finger.pressed = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Settings::values.use_touch_from_button) {
|
||||
const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
keyboard_finger_id[id] =
|
||||
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
|
||||
if (!finger.pressed && current_touch.pressed) {
|
||||
finger.attribute.start_touch.Assign(1);
|
||||
finger.pressed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (finger.pressed && !current_touch.pressed) {
|
||||
finger.attribute.raw = 0;
|
||||
finger.attribute.end_touch.Assign(1);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<Finger, 16> active_fingers;
|
||||
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
|
||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
||||
[](const auto& finger) { return finger.pressed; });
|
||||
const auto active_fingers_count =
|
||||
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
||||
|
||||
const u64 tick = core_timing.GetCPUTicks();
|
||||
cur_entry.entry_count = static_cast<s32_le>(active_fingers_count);
|
||||
const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state;
|
||||
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
next_state.entry_count = static_cast<s32>(active_fingers_count);
|
||||
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
||||
auto& touch_entry = cur_entry.states[id];
|
||||
auto& touch_entry = next_state.states[id];
|
||||
if (id < active_fingers_count) {
|
||||
const auto& [active_x, active_y] = active_fingers[id].position;
|
||||
touch_entry.position = {
|
||||
@@ -97,66 +104,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
||||
touch_entry.finger = 0;
|
||||
}
|
||||
}
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
|
||||
}
|
||||
|
||||
void Controller_Touchscreen::OnLoadInputDevices() {
|
||||
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
|
||||
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
|
||||
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
|
||||
}
|
||||
|
||||
std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
|
||||
// Dont assign any touch input to a finger if disabled
|
||||
if (!Settings::values.touchscreen.enabled) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::size_t first_free_id = 0;
|
||||
while (first_free_id < MAX_FINGERS) {
|
||||
if (!fingers[first_free_id].pressed) {
|
||||
return first_free_id;
|
||||
} else {
|
||||
first_free_id++;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
|
||||
const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
|
||||
const auto& [x, y, pressed] = touch_input;
|
||||
if (finger_id > MAX_FINGERS) {
|
||||
LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
|
||||
return MAX_FINGERS;
|
||||
}
|
||||
if (pressed) {
|
||||
Attributes attribute{};
|
||||
if (finger_id == MAX_FINGERS) {
|
||||
const auto first_free_id = GetUnusedFingerID();
|
||||
if (!first_free_id) {
|
||||
// Invalid finger id do nothing
|
||||
return MAX_FINGERS;
|
||||
}
|
||||
finger_id = first_free_id.value();
|
||||
fingers[finger_id].pressed = true;
|
||||
fingers[finger_id].id = static_cast<u32_le>(finger_id);
|
||||
attribute.start_touch.Assign(1);
|
||||
}
|
||||
fingers[finger_id].position = {x, y};
|
||||
fingers[finger_id].attribute = attribute;
|
||||
return finger_id;
|
||||
}
|
||||
|
||||
if (finger_id != MAX_FINGERS) {
|
||||
if (!fingers[finger_id].attribute.end_touch) {
|
||||
fingers[finger_id].attribute.end_touch.Assign(1);
|
||||
fingers[finger_id].attribute.start_touch.Assign(0);
|
||||
return finger_id;
|
||||
}
|
||||
fingers[finger_id].pressed = false;
|
||||
}
|
||||
|
||||
return MAX_FINGERS;
|
||||
touch_screen_lifo.WriteNextEntry(next_state);
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo));
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
@@ -9,18 +9,23 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/point.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Touchscreen final : public ControllerBase {
|
||||
public:
|
||||
// This is nn::hid::TouchScreenModeForNx
|
||||
enum class TouchScreenModeForNx : u8 {
|
||||
UseSystemSetting,
|
||||
Finger,
|
||||
Heat2,
|
||||
};
|
||||
|
||||
// This is nn::hid::TouchScreenConfigurationForNx
|
||||
struct TouchScreenConfigurationForNx {
|
||||
TouchScreenModeForNx mode;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x7);
|
||||
@@ -41,73 +46,24 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGERS = 16;
|
||||
|
||||
// Returns an unused finger id, if there is no fingers available std::nullopt will be returned
|
||||
std::optional<std::size_t> GetUnusedFingerID() const;
|
||||
|
||||
// If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
|
||||
// changes will be made. Updates the coordinates if the finger id it's already set. If the touch
|
||||
// ends delays the output by one frame to set the end_touch flag before finally freeing the
|
||||
// finger id
|
||||
std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
|
||||
std::size_t finger_id);
|
||||
|
||||
struct Attributes {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> start_touch;
|
||||
BitField<1, 1, u32> end_touch;
|
||||
};
|
||||
// This is nn::hid::TouchScreenState
|
||||
struct TouchScreenState {
|
||||
s64 sampling_number;
|
||||
s32 entry_count;
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
std::array<Core::HID::TouchState, MAX_FINGERS> states;
|
||||
};
|
||||
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
|
||||
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
|
||||
|
||||
struct TouchState {
|
||||
u64_le delta_time;
|
||||
Attributes attribute;
|
||||
u32_le finger;
|
||||
Common::Point<u32_le> position;
|
||||
u32_le diameter_x;
|
||||
u32_le diameter_y;
|
||||
u32_le rotation_angle;
|
||||
};
|
||||
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
|
||||
// This is nn::hid::detail::TouchScreenLifo
|
||||
Lifo<TouchScreenState> touch_screen_lifo{};
|
||||
static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
|
||||
TouchScreenState next_state{};
|
||||
|
||||
struct TouchScreenEntry {
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
s32_le entry_count;
|
||||
std::array<TouchState, MAX_FINGERS> states;
|
||||
};
|
||||
static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
|
||||
|
||||
struct TouchScreenSharedMemory {
|
||||
CommonHeader header;
|
||||
std::array<TouchScreenEntry, 17> shared_memory_entries{};
|
||||
INSERT_PADDING_BYTES(0x3c8);
|
||||
};
|
||||
static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
|
||||
"TouchScreenSharedMemory is an invalid size");
|
||||
|
||||
struct Finger {
|
||||
u64_le last_touch{};
|
||||
Common::Point<float> position;
|
||||
u32_le id{};
|
||||
bool pressed{};
|
||||
Attributes attribute;
|
||||
};
|
||||
|
||||
TouchScreenSharedMemory shared_memory{};
|
||||
std::unique_ptr<Input::TouchDevice> touch_mouse_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_udp_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_btn_device;
|
||||
std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
|
||||
std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
|
||||
std::array<std::size_t, MAX_FINGERS> udp_finger_id;
|
||||
std::array<Finger, MAX_FINGERS> fingers;
|
||||
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers;
|
||||
Core::HID::EmulatedConsole* console;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
@@ -19,28 +19,19 @@ void Controller_XPad::OnRelease() {}
|
||||
|
||||
void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
|
||||
std::size_t size) {
|
||||
for (auto& xpad_entry : shared_memory.shared_memory_entries) {
|
||||
xpad_entry.header.timestamp = core_timing.GetCPUTicks();
|
||||
xpad_entry.header.total_entry_count = 17;
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
xpad_entry.header.entry_count = 0;
|
||||
xpad_entry.header.last_entry_index = 0;
|
||||
return;
|
||||
}
|
||||
xpad_entry.header.entry_count = 16;
|
||||
|
||||
const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
|
||||
xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
|
||||
auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
if (!IsControllerActivated()) {
|
||||
basic_xpad_lifo.buffer_count = 0;
|
||||
basic_xpad_lifo.buffer_tail = 0;
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state;
|
||||
next_state.sampling_number = last_entry.sampling_number + 1;
|
||||
// TODO(ogniK): Update xpad states
|
||||
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
|
||||
basic_xpad_lifo.WriteNextEntry(next_state);
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
|
||||
}
|
||||
|
||||
void Controller_XPad::OnLoadInputDevices() {}
|
||||
} // namespace Service::HID
|
||||
|
@@ -8,7 +8,9 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_XPad final : public ControllerBase {
|
||||
@@ -25,13 +27,11 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
|
||||
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
struct Attributes {
|
||||
// This is nn::hid::BasicXpadAttributeSet
|
||||
struct BasicXpadAttributeSet {
|
||||
union {
|
||||
u32_le raw{};
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> is_connected;
|
||||
BitField<1, 1, u32> is_wired;
|
||||
BitField<2, 1, u32> is_left_connected;
|
||||
@@ -40,11 +40,12 @@ private:
|
||||
BitField<5, 1, u32> is_right_wired;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attributes) == 4, "Attributes is an invalid size");
|
||||
static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
|
||||
|
||||
struct Buttons {
|
||||
// This is nn::hid::BasicXpadButtonSet
|
||||
struct BasicXpadButtonSet {
|
||||
union {
|
||||
u32_le raw{};
|
||||
u32 raw{};
|
||||
// Button states
|
||||
BitField<0, 1, u32> a;
|
||||
BitField<1, 1, u32> b;
|
||||
@@ -88,35 +89,21 @@ private:
|
||||
BitField<30, 1, u32> handheld_left_b;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Buttons) == 4, "Buttons is an invalid size");
|
||||
static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
|
||||
|
||||
struct AnalogStick {
|
||||
s32_le x;
|
||||
s32_le y;
|
||||
// This is nn::hid::detail::BasicXpadState
|
||||
struct BasicXpadState {
|
||||
s64 sampling_number;
|
||||
BasicXpadAttributeSet attributes;
|
||||
BasicXpadButtonSet pad_states;
|
||||
Core::HID::AnalogStickState l_stick;
|
||||
Core::HID::AnalogStickState r_stick;
|
||||
};
|
||||
static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size");
|
||||
static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
|
||||
|
||||
struct XPadState {
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
Attributes attributes;
|
||||
Buttons pad_states;
|
||||
AnalogStick l_stick;
|
||||
AnalogStick r_stick;
|
||||
};
|
||||
static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
|
||||
|
||||
struct XPadEntry {
|
||||
CommonHeader header;
|
||||
std::array<XPadState, 17> pad_states{};
|
||||
INSERT_PADDING_BYTES(0x138);
|
||||
};
|
||||
static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
|
||||
|
||||
struct SharedMemory {
|
||||
std::array<XPadEntry, 4> shared_memory_entries{};
|
||||
};
|
||||
static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
|
||||
SharedMemory shared_memory{};
|
||||
// This is nn::hid::detail::BasicXpadLifo
|
||||
Lifo<BasicXpadState> basic_xpad_lifo{};
|
||||
static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
|
||||
BasicXpadState next_state{};
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
@@ -9,7 +9,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
@@ -36,10 +35,9 @@
|
||||
namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// HID is polled every 15ms, this value was derived from
|
||||
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
|
||||
constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz)
|
||||
constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
|
||||
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
|
||||
constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
|
||||
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
|
||||
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
|
||||
|
||||
IAppletResource::IAppletResource(Core::System& system_,
|
||||
@@ -91,7 +89,7 @@ IAppletResource::IAppletResource(Core::System& system_,
|
||||
system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
|
||||
system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
|
||||
|
||||
ReloadInputDevices();
|
||||
system.HIDCore().ReloadInputDevices();
|
||||
}
|
||||
|
||||
void IAppletResource::ActivateController(HidController controller) {
|
||||
@@ -119,11 +117,7 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
|
||||
std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
|
||||
for (const auto& controller : controllers) {
|
||||
if (should_reload) {
|
||||
controller->OnLoadInputDevices();
|
||||
}
|
||||
controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(),
|
||||
SHARED_MEMORY_SIZE);
|
||||
}
|
||||
@@ -893,7 +887,7 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()};
|
||||
const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
|
||||
|
||||
@@ -926,7 +920,7 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
|
||||
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.npad_id, parameters.applet_resource_user_id);
|
||||
@@ -948,7 +942,7 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
|
||||
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
|
||||
@@ -970,7 +964,7 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual);
|
||||
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Dual);
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.npad_id, parameters.applet_resource_user_id);
|
||||
@@ -1136,36 +1130,36 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
|
||||
|
||||
VibrationDeviceInfo vibration_device_info;
|
||||
Core::HID::VibrationDeviceInfo vibration_device_info;
|
||||
|
||||
switch (vibration_device_handle.npad_type) {
|
||||
case Controller_NPad::NpadType::ProController:
|
||||
case Controller_NPad::NpadType::Handheld:
|
||||
case Controller_NPad::NpadType::JoyconDual:
|
||||
case Controller_NPad::NpadType::JoyconLeft:
|
||||
case Controller_NPad::NpadType::JoyconRight:
|
||||
case Core::HID::NpadType::ProController:
|
||||
case Core::HID::NpadType::Handheld:
|
||||
case Core::HID::NpadType::JoyconDual:
|
||||
case Core::HID::NpadType::JoyconLeft:
|
||||
case Core::HID::NpadType::JoyconRight:
|
||||
default:
|
||||
vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
|
||||
vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
|
||||
break;
|
||||
case Controller_NPad::NpadType::GameCube:
|
||||
vibration_device_info.type = VibrationDeviceType::GcErm;
|
||||
case Core::HID::NpadType::GameCube:
|
||||
vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
|
||||
break;
|
||||
case Controller_NPad::NpadType::Pokeball:
|
||||
vibration_device_info.type = VibrationDeviceType::Unknown;
|
||||
case Core::HID::NpadType::Pokeball:
|
||||
vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vibration_device_handle.device_index) {
|
||||
case Controller_NPad::DeviceIndex::Left:
|
||||
vibration_device_info.position = VibrationDevicePosition::Left;
|
||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
|
||||
break;
|
||||
case Controller_NPad::DeviceIndex::Right:
|
||||
vibration_device_info.position = VibrationDevicePosition::Right;
|
||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
|
||||
break;
|
||||
case Controller_NPad::DeviceIndex::None:
|
||||
default:
|
||||
UNREACHABLE_MSG("DeviceIndex should never be None!");
|
||||
vibration_device_info.position = VibrationDevicePosition::None;
|
||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1280,7 +1274,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle vibration_device_handle;
|
||||
u64 applet_resource_user_id;
|
||||
VibrationGcErmCommand gc_erm_command;
|
||||
Core::HID::VibrationGcErmCommand gc_erm_command;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
|
||||
|
||||
@@ -1294,21 +1288,21 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
|
||||
*/
|
||||
const auto vibration_value = [parameters] {
|
||||
switch (parameters.gc_erm_command) {
|
||||
case VibrationGcErmCommand::Stop:
|
||||
case Core::HID::VibrationGcErmCommand::Stop:
|
||||
return Controller_NPad::VibrationValue{
|
||||
.amp_low = 0.0f,
|
||||
.freq_low = 160.0f,
|
||||
.amp_high = 0.0f,
|
||||
.freq_high = 320.0f,
|
||||
};
|
||||
case VibrationGcErmCommand::Start:
|
||||
case Core::HID::VibrationGcErmCommand::Start:
|
||||
return Controller_NPad::VibrationValue{
|
||||
.amp_low = 1.0f,
|
||||
.freq_low = 160.0f,
|
||||
.amp_high = 1.0f,
|
||||
.freq_high = 320.0f,
|
||||
};
|
||||
case VibrationGcErmCommand::StopHard:
|
||||
case Core::HID::VibrationGcErmCommand::StopHard:
|
||||
return Controller_NPad::VibrationValue{
|
||||
.amp_low = 0.0f,
|
||||
.freq_low = 0.0f,
|
||||
@@ -1350,7 +1344,7 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
const auto gc_erm_command = [last_vibration] {
|
||||
if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) {
|
||||
return VibrationGcErmCommand::Start;
|
||||
return Core::HID::VibrationGcErmCommand::Start;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1360,10 +1354,10 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
|
||||
* This is done to reuse the controller vibration functions made for regular controllers.
|
||||
*/
|
||||
if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) {
|
||||
return VibrationGcErmCommand::StopHard;
|
||||
return Core::HID::VibrationGcErmCommand::StopHard;
|
||||
}
|
||||
|
||||
return VibrationGcErmCommand::Stop;
|
||||
return Core::HID::VibrationGcErmCommand::Stop;
|
||||
}();
|
||||
|
||||
LOG_DEBUG(Service_HID,
|
||||
@@ -2039,10 +2033,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void ReloadInputDevices() {
|
||||
Settings::values.is_device_reload_pending.store(true);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
std::make_shared<Hid>(system)->InstallAsService(service_manager);
|
||||
std::make_shared<HidBus>(system)->InstallAsService(service_manager);
|
||||
|
@@ -161,38 +161,11 @@ private:
|
||||
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
|
||||
void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class VibrationDeviceType : u32 {
|
||||
Unknown = 0,
|
||||
LinearResonantActuator = 1,
|
||||
GcErm = 2,
|
||||
};
|
||||
|
||||
enum class VibrationDevicePosition : u32 {
|
||||
None = 0,
|
||||
Left = 1,
|
||||
Right = 2,
|
||||
};
|
||||
|
||||
enum class VibrationGcErmCommand : u64 {
|
||||
Stop = 0,
|
||||
Start = 1,
|
||||
StopHard = 2,
|
||||
};
|
||||
|
||||
struct VibrationDeviceInfo {
|
||||
VibrationDeviceType type{};
|
||||
VibrationDevicePosition position{};
|
||||
};
|
||||
static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
};
|
||||
|
||||
/// Reload input devices. Used when input configuration changed
|
||||
void ReloadInputDevices();
|
||||
|
||||
/// Registers all HID services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
|
56
src/core/hle/service/hid/ring_lifo.h
Executable file
56
src/core/hle/service/hid/ring_lifo.h
Executable file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t max_buffer_size = 17;
|
||||
|
||||
template <typename State>
|
||||
struct AtomicStorage {
|
||||
s64 sampling_number;
|
||||
State state;
|
||||
};
|
||||
|
||||
template <typename State>
|
||||
struct Lifo {
|
||||
s64 timestamp{};
|
||||
s64 total_buffer_count = max_buffer_size;
|
||||
s64 buffer_tail{};
|
||||
s64 buffer_count{};
|
||||
std::array<AtomicStorage<State>, max_buffer_size> entries{};
|
||||
|
||||
const AtomicStorage<State>& ReadCurrentEntry() const {
|
||||
return entries[buffer_tail];
|
||||
}
|
||||
|
||||
const AtomicStorage<State>& ReadPreviousEntry() const {
|
||||
return entries[GetPreviousEntryIndex()];
|
||||
}
|
||||
|
||||
std::size_t GetPreviousEntryIndex() const {
|
||||
return (buffer_tail + total_buffer_count - 1) % total_buffer_count;
|
||||
}
|
||||
|
||||
std::size_t GetNextEntryIndex() const {
|
||||
return (buffer_tail + 1) % total_buffer_count;
|
||||
}
|
||||
|
||||
void WriteNextEntry(const State& new_state) {
|
||||
if (buffer_count < total_buffer_count - 1) {
|
||||
buffer_count++;
|
||||
}
|
||||
buffer_tail = GetNextEntryIndex();
|
||||
const auto& previous_entry = ReadPreviousEntry();
|
||||
entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1;
|
||||
entries[buffer_tail].state = new_state;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Service::HID
|
Reference in New Issue
Block a user