Compare commits

...

10 Commits

Author SHA1 Message Date
David Marcec
8144fa42bd Using dual joycons as the default controller
Reason for the change is to allow both docked and undocked mode to work
2018-10-18 00:11:47 +11:00
David Marcec
98b760c645 Wip 2018-10-12 16:28:00 +11:00
David Marcec
85b0d9a7be Dynamically decide handheld variant based on supported npad id priority
Kirby input still doesn't work, should fix a lot of other games
2018-10-12 02:56:49 +11:00
David Marcec
9e924f2ef2 Added BeginPermitVibrationSession and EndPermitVibrationSession
Used by Mario Party
2018-10-11 00:58:47 +11:00
David Marcec
3d75c9cd7a Added GetLedPattern and HandheldVariant
HandheldVariant is for specific games which expect handheld controllers to be at position 8(kirby), however this doesn't fix all games as some games require handhelds to be at position 0(snipperclips)
2018-10-10 21:38:43 +11:00
David Marcec
46cdeb4549 Kirby expects handheld controllers to be at position 8 2018-10-10 14:21:56 +11:00
David Marcec
f43815af5d Added the ability to "disconnect" individual npads
Fixes arms
2018-10-10 13:15:39 +11:00
David Marcec
b79c294c02 Removed unneeded forward declarations 2018-10-10 13:15:37 +11:00
David Marcec
5857aea94e Addressed changes for better hid 2018-10-10 13:15:37 +11:00
David Marcec
56f35ab262 "Better Hid" rework part 1 2018-10-10 13:15:35 +11:00
22 changed files with 1721 additions and 649 deletions

View File

@@ -233,6 +233,24 @@ add_library(core STATIC
hle/service/hid/irs.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/controllers/controller_base.cpp
hle/service/hid/controllers/controller_base.h
hle/service/hid/controllers/debug_pad.cpp
hle/service/hid/controllers/debug_pad.h
hle/service/hid/controllers/gesture.cpp
hle/service/hid/controllers/gesture.h
hle/service/hid/controllers/keyboard.cpp
hle/service/hid/controllers/keyboard.h
hle/service/hid/controllers/mouse.cpp
hle/service/hid/controllers/mouse.h
hle/service/hid/controllers/npad.cpp
hle/service/hid/controllers/npad.h
hle/service/hid/controllers/stubbed.cpp
hle/service/hid/controllers/stubbed.h
hle/service/hid/controllers/touchscreen.cpp
hle/service/hid/controllers/touchscreen.h
hle/service/hid/controllers/xpad.cpp
hle/service/hid/controllers/xpad.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/ldn.cpp

View File

@@ -0,0 +1,28 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
ControllerBase::~ControllerBase() = default;
void ControllerBase::ActivateController() {
if (is_activated) {
OnRelease();
}
is_activated = true;
OnInit();
}
void ControllerBase::DeactivateController() {
if (is_activated) {
OnRelease();
}
is_activated = false;
}
bool ControllerBase::IsControllerActivated() const {
return is_activated;
}
} // namespace Service::HID

View File

@@ -0,0 +1,45 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "common/swap.h"
namespace Service::HID {
class ControllerBase {
public:
ControllerBase() = default;
virtual ~ControllerBase() = 0;
// Called when the controller is initialized
virtual void OnInit() = 0;
// When the controller is released
virtual void OnRelease() = 0;
// When the controller is requesting an update for the shared memory
virtual void OnUpdate(u8* data, std::size_t size) = 0;
// Called when input devices should be loaded
virtual void OnLoadInputDevices() = 0;
void ActivateController();
void DeactivateController();
bool IsControllerActivated() const;
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");
};
} // namespace Service::HID

View File

@@ -0,0 +1,42 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
namespace Service::HID {
Controller_DebugPad::Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}
void Controller_DebugPad::OnRelease() {}
void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
shared_memory.header.entry_count = 0;
shared_memory.header.last_entry_index = 0;
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;
// TODO(ogniK): Update debug pad states
std::memcpy(data, &shared_memory, sizeof(SharedMemory));
}
void Controller_DebugPad::OnLoadInputDevices() {}
} // namespace Service::HID

View File

@@ -0,0 +1,55 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
public:
Controller_DebugPad();
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(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 PadStates {
s64_le sampling_number;
s64_le sampling_number2;
u32_le attribute;
u32_le button_state;
AnalogStick r_stick;
AnalogStick l_stick;
};
static_assert(sizeof(PadStates) == 0x28, "PadStates 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{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,43 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/gesture.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
Controller_Gesture::Controller_Gesture() = default;
void Controller_Gesture::OnInit() {}
void Controller_Gesture::OnRelease() {}
void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
shared_memory.header.entry_count = 0;
shared_memory.header.last_entry_index = 0;
return;
}
shared_memory.header.entry_count = 16;
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];
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
// TODO(ogniK): Update gesture states
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
void Controller_Gesture::OnLoadInputDevices() {}
} // namespace Service::HID

View File

@@ -0,0 +1,62 @@
// Copyright 2018 yuzu emulator team
// 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"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_Gesture final : public ControllerBase {
public:
Controller_Gesture();
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(u8* data, size_t size) override;
// Called when input devices should be loaded
void OnLoadInputDevices() override;
private:
struct Locations {
s32_le x;
s32_le y;
};
struct GestureState {
s64_le sampling_number;
s64_le sampling_number2;
s64_le detection_count;
s32_le type;
s32_le dir;
s32_le x;
s32_le y;
s32_le delta_x;
s32_le delta_y;
f32 vel_x;
f32 vel_y;
s32_le attributes;
f32 scale;
f32 rotation;
s32_le location_count;
std::array<Locations, 4> locations;
};
static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
struct SharedMemory {
CommonHeader header;
std::array<GestureState, 17> gesture_states;
};
SharedMemory shared_memory{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,43 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/keyboard.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
Controller_Keyboard::Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}
void Controller_Keyboard::OnRelease() {}
void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
shared_memory.header.entry_count = 0;
shared_memory.header.last_entry_index = 0;
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;
// TODO(ogniK): Update keyboard states
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
void Controller_Keyboard::OnLoadInputDevices() {}
} // namespace Service::HID

View File

@@ -0,0 +1,49 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
public:
Controller_Keyboard();
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(u8* data, std::size_t size) override;
// Called when input devices should be loaded
void OnLoadInputDevices() override;
private:
struct KeyboardState {
s64_le sampling_number;
s64_le sampling_number2;
s32_le modifier;
s32_le attribute;
std::array<u8, 32> key;
};
static_assert(sizeof(KeyboardState) == 0x38, "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{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,43 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/mouse.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
Controller_Mouse::Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
shared_memory.header.entry_count = 0;
shared_memory.header.last_entry_index = 0;
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];
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
// TODO(ogniK): Update mouse states
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
void Controller_Mouse::OnLoadInputDevices() {}
} // namespace Service::HID

View File

@@ -0,0 +1,49 @@
// Copyright 2018 yuzu emulator team
// 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"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
public:
Controller_Mouse();
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(u8* data, std::size_t size) override;
// Called when input devices should be loaded
void OnLoadInputDevices() override;
private:
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;
s32_le button;
s32_le attribute;
};
static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
struct SharedMemory {
CommonHeader header;
std::array<MouseState, 17> mouse_states;
};
SharedMemory shared_memory{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,429 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <array>
#include <cstring>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/input.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/settings.h"
namespace Service::HID {
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
Controller_NPad::Controller_NPad() = default;
void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
const auto controller_type = connected_controllers[controller_idx].type;
auto& controller = shared_memory_entries[controller_idx];
if (controller_type == NPadControllerType::None) {
return;
}
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
switch (controller_type) {
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.device_type.joycon_right.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::JoyLeft:
controller.joy_styles.joycon_left.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::JoyRight:
controller.joy_styles.joycon_right.Assign(1);
controller.device_type.joycon_right.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::Pokeball:
controller.joy_styles.pokeball.Assign(1);
controller.device_type.pokeball.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::ProController:
controller.joy_styles.pro_controller.Assign(1);
controller.device_type.pro_controller.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
}
controller.single_color_error = ColorReadError::ReadOk;
controller.single_color.body_color = 0;
controller.single_color.button_color = 0;
controller.dual_color_error = ColorReadError::ReadOk;
controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
controller.right_color.body_color = JOYCON_BODY_NEON_RED;
controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
}
void Controller_NPad::OnInit() {
auto& kernel = Core::System::GetInstance().Kernel();
styleset_changed_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
if (!IsControllerActivated())
return;
std::size_t controller{};
if (style.raw == 0) {
// We want to support all controllers
style.handheld.Assign(1);
style.joycon_left.Assign(1);
style.joycon_right.Assign(1);
style.joycon_dual.Assign(1);
style.pro_controller.Assign(1);
style.pokeball.Assign(1);
}
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
[](const ControllerHolder& controller) { return controller.is_connected; })) {
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
AddNewController(NPadControllerType::JoyDual);
}
}
void Controller_NPad::OnLoadInputDevices() {
std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
}
void Controller_NPad::OnRelease() {}
void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
if (!IsControllerActivated())
return;
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
auto& npad = shared_memory_entries[i];
const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
&npad.handheld_states,
&npad.dual_states,
&npad.left_joy_states,
&npad.right_joy_states,
&npad.pokeball_states,
&npad.libnx};
for (auto* main_controller : controller_npads) {
main_controller->common.entry_count = 16;
main_controller->common.total_entry_count = 17;
const auto& last_entry =
main_controller->npad[main_controller->common.last_entry_index];
main_controller->common.timestamp = CoreTiming::GetTicks();
main_controller->common.last_entry_index =
(main_controller->common.last_entry_index + 1) % 17;
auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
cur_entry.timestamp = last_entry.timestamp + 1;
cur_entry.timestamp2 = cur_entry.timestamp;
}
const auto& controller_type = connected_controllers[i].type;
if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
continue;
}
// Pad states
ControllerPadState pad_state{};
using namespace Settings::NativeButton;
pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
AnalogPosition lstick_entry{};
AnalogPosition rstick_entry{};
const auto [stick_l_x_f, stick_l_y_f] =
sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
const auto [stick_r_x_f, stick_r_y_f] =
sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
auto& main_controller =
npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
auto& handheld_entry =
npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
auto& right_entry =
npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
auto& pokeball_entry =
npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
if (hold_type == NpadHoldType::Horizontal) {
// TODO(ogniK): Remap buttons for different orientations
}
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
handheld_entry.connection_status.IsConnected.Assign(1);
if (!Settings::values.use_docked_mode) {
handheld_entry.connection_status.IsWired.Assign(1);
}
handheld_entry.pad_states.raw = pad_state.raw;
handheld_entry.l_stick = lstick_entry;
handheld_entry.r_stick = rstick_entry;
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
dual_entry.connection_status.IsRightJoyConnected.Assign(1);
dual_entry.connection_status.IsConnected.Assign(1);
libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
libnx_entry.connection_status.IsConnected.Assign(1);
dual_entry.pad_states.raw = pad_state.raw;
dual_entry.l_stick = lstick_entry;
dual_entry.r_stick = rstick_entry;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
left_entry.connection_status.IsConnected.Assign(1);
left_entry.pad_states.raw = pad_state.raw;
left_entry.l_stick = lstick_entry;
left_entry.r_stick = rstick_entry;
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
right_entry.connection_status.IsConnected.Assign(1);
right_entry.pad_states.raw = pad_state.raw;
right_entry.l_stick = lstick_entry;
right_entry.r_stick = rstick_entry;
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
pokeball_entry.connection_status.IsConnected.Assign(1);
pokeball_entry.connection_status.IsWired.Assign(1);
pokeball_entry.pad_states.raw = pad_state.raw;
pokeball_entry.l_stick = lstick_entry;
pokeball_entry.r_stick = rstick_entry;
break;
case NPadControllerType::ProController:
main_controller.connection_status.raw = 0;
main_controller.connection_status.IsConnected.Assign(1);
main_controller.connection_status.IsWired.Assign(1);
main_controller.pad_states.raw = pad_state.raw;
main_controller.l_stick = lstick_entry;
main_controller.r_stick = rstick_entry;
break;
}
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
// any controllers.
libnx_entry.pad_states.raw = pad_state.raw;
libnx_entry.l_stick = lstick_entry;
libnx_entry.r_stick = rstick_entry;
}
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
shared_memory_entries.size() * sizeof(NPadEntry));
} // namespace Service::HID
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
style.raw = style_set.raw;
}
Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
return style;
}
void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
ASSERT(length > 0 && (length % sizeof(u32)) == 0);
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
std::memcpy(supported_npad_id_types.data(), data, length);
}
const void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
ASSERT(max_length < supported_npad_id_types.size());
std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
}
std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
return supported_npad_id_types.size();
}
void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
hold_type = joy_hold_type;
}
Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
return hold_type;
}
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
ASSERT(npad_id < shared_memory_entries.size());
shared_memory_entries[npad_id].pad_assignment = assignment_mode;
}
void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
const std::vector<Vibration>& vibrations) {
if (!can_controllers_vibrate) {
return;
}
for (std::size_t i = 0; i < controller_ids.size(); i++) {
std::size_t controller_pos = i;
// Handheld controller conversion
if (controller_pos == 32) {
controller_pos = 8;
}
// Unknown controller conversion
if (controller_pos == 16) {
controller_pos = 9;
}
if (connected_controllers[controller_pos].is_connected) {
// TODO(ogniK): Vibrate the physical controller
}
}
LOG_WARNING(Service_HID, "(STUBBED) called");
last_processed_vibration = vibrations.back();
}
Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
return styleset_changed_event;
}
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
}
void Controller_NPad::AddNewController(NPadControllerType controller) {
if (controller == NPadControllerType::Handheld) {
connected_controllers[8] = {controller, true};
InitNewlyAddedControler(8);
return;
}
const auto pos =
std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
[](const ControllerHolder& holder) { return !holder.is_connected; });
if (pos == connected_controllers.end() - 2) {
LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
return;
}
const auto controller_id = std::distance(connected_controllers.begin(), pos);
connected_controllers[controller_id] = {controller, true};
InitNewlyAddedControler(controller_id);
}
void Controller_NPad::ConnectNPad(u32 npad_id) {
if (npad_id >= connected_controllers.size())
return;
connected_controllers[npad_id].is_connected = true;
}
void Controller_NPad::DisconnectNPad(u32 npad_id) {
if (npad_id >= connected_controllers.size())
return;
connected_controllers[npad_id].is_connected = false;
}
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
// These are controllers without led patterns
return LedPattern{0, 0, 0, 0};
}
switch (npad_id) {
case 0:
return LedPattern{1, 0, 0, 0};
case 1:
return LedPattern{0, 1, 0, 0};
case 2:
return LedPattern{0, 0, 1, 0};
case 3:
return LedPattern{0, 0, 0, 1};
case 4:
return LedPattern{1, 0, 0, 1};
case 5:
return LedPattern{1, 0, 1, 0};
case 6:
return LedPattern{1, 0, 1, 1};
case 7:
return LedPattern{0, 1, 1, 0};
default:
UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
return LedPattern{0, 0, 0, 0};
};
}
void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
can_controllers_vibrate = can_vibrate;
}
} // namespace Service::HID

View File

@@ -0,0 +1,288 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/settings.h"
namespace Service::HID {
class Controller_NPad final : public ControllerBase {
public:
Controller_NPad();
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(u8* data, std::size_t size) override;
// Called when input devices should be loaded
void OnLoadInputDevices() override;
struct NPadType {
union {
u32_le raw{};
BitField<0, 1, u32_le> pro_controller;
BitField<1, 1, u32_le> handheld;
BitField<2, 1, u32_le> joycon_dual;
BitField<3, 1, u32_le> joycon_left;
BitField<4, 1, u32_le> joycon_right;
BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
};
};
static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
struct Vibration {
f32 amp_low;
f32 freq_low;
f32 amp_high;
f32 freq_high;
};
static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
enum class NpadHoldType : u64 {
Vertical = 0,
Horizontal = 1,
};
enum class NPadAssignments : u32_le {
Dual = 0,
Single = 1,
};
enum class NPadControllerType {
None,
ProController,
Handheld,
JoyDual,
JoyLeft,
JoyRight,
Pokeball,
};
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
position1.Assign(light1);
position1.Assign(light2);
position1.Assign(light3);
position1.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(NPadType style_set);
NPadType GetSupportedStyleSet() const;
void SetSupportedNPadIdTypes(u8* data, std::size_t length);
const void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
std::size_t GetSupportedNPadIdTypesSize() const;
void SetHoldType(NpadHoldType joy_hold_type);
NpadHoldType GetHoldType() const;
void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
void VibrateController(const std::vector<u32>& controller_ids,
const std::vector<Vibration>& vibrations);
Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const;
Vibration GetLastVibration() const;
void AddNewController(NPadControllerType controller);
void ConnectNPad(u32 npad_id);
void DisconnectNPad(u32 npad_id);
LedPattern GetLedPattern(u32 npad_id);
void SetVibrationEnabled(bool can_vibrate);
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");
struct ControllerColor {
u32_le body_color;
u32_le button_color;
};
static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
struct ControllerPadState {
union {
u64_le raw{};
// Button states
BitField<0, 1, u64_le> a;
BitField<1, 1, u64_le> b;
BitField<2, 1, u64_le> x;
BitField<3, 1, u64_le> y;
BitField<4, 1, u64_le> l_stick;
BitField<5, 1, u64_le> r_stick;
BitField<6, 1, u64_le> l;
BitField<7, 1, u64_le> r;
BitField<8, 1, u64_le> zl;
BitField<9, 1, u64_le> zr;
BitField<10, 1, u64_le> plus;
BitField<11, 1, u64_le> minus;
// D-Pad
BitField<12, 1, u64_le> d_left;
BitField<13, 1, u64_le> d_up;
BitField<14, 1, u64_le> d_right;
BitField<15, 1, u64_le> d_down;
// Left JoyStick
BitField<16, 1, u64_le> l_stick_left;
BitField<17, 1, u64_le> l_stick_up;
BitField<18, 1, u64_le> l_stick_right;
BitField<19, 1, u64_le> l_stick_down;
// Right JoyStick
BitField<20, 1, u64_le> r_stick_left;
BitField<21, 1, u64_le> r_stick_up;
BitField<22, 1, u64_le> r_stick_right;
BitField<23, 1, u64_le> r_stick_down;
// Not always active?
BitField<24, 1, u64_le> sl;
BitField<25, 1, u64_le> sr;
};
};
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{};
BitField<0, 1, u32_le> IsConnected;
BitField<1, 1, u32_le> IsWired;
BitField<2, 1, u32_le> IsLeftJoyConnected;
BitField<3, 1, u32_le> IsLeftJoyWired;
BitField<4, 1, u32_le> IsRightJoyConnected;
BitField<5, 1, u32_le> IsRightJoyWired;
};
};
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
struct GenericStates {
s64_le timestamp;
s64_le timestamp2;
ControllerPadState pad_states;
AnalogPosition l_stick;
AnalogPosition r_stick;
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");
enum class ColorReadError : u32_le {
ReadOk = 0,
ColorDoesntExist = 1,
NoController = 2,
};
struct NPadProperties {
union {
s64_le raw{};
BitField<11, 1, s64_le> is_vertical;
BitField<12, 1, s64_le> is_horizontal;
BitField<13, 1, s64_le> use_plus;
BitField<14, 1, s64_le> use_minus;
};
};
struct NPadDevice {
union {
u32_le raw{};
BitField<0, 1, s32_le> pro_controller;
BitField<1, 1, s32_le> handheld;
BitField<2, 1, s32_le> handheld_left;
BitField<3, 1, s32_le> handheld_right;
BitField<4, 1, s32_le> joycon_left;
BitField<5, 1, s32_le> joycon_right;
BitField<6, 1, s32_le> pokeball;
};
};
struct NPadEntry {
NPadType joy_styles;
NPadAssignments pad_assignment;
ColorReadError single_color_error;
ControllerColor single_color;
ColorReadError dual_color_error;
ControllerColor left_color;
ControllerColor right_color;
NPadGeneric main_controller_states;
NPadGeneric handheld_states;
NPadGeneric dual_states;
NPadGeneric left_joy_states;
NPadGeneric right_joy_states;
NPadGeneric pokeball_states;
NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
// relying on this for the time being
INSERT_PADDING_BYTES(
0x708 *
6); // TODO(ogniK): SixAxis states, require more information before implementation
NPadDevice device_type;
NPadProperties properties;
INSERT_PADDING_WORDS(1);
std::array<u32, 3> battery_level;
INSERT_PADDING_BYTES(0x5c);
INSERT_PADDING_BYTES(0xdf8);
};
static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
struct ControllerHolder {
Controller_NPad::NPadControllerType type;
bool is_connected;
};
NPadType style{};
std::array<NPadEntry, 10> shared_memory_entries{};
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> sticks;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
std::size_t dump_idx{};
Vibration last_processed_vibration{};
static constexpr std::array<u32, 10> npad_id_list{0, 1, 2, 3, 4, 5, 6, 7, 32, 16};
std::array<ControllerHolder, 10> connected_controllers{};
bool can_controllers_vibrate{true};
void InitNewlyAddedControler(std::size_t controller_idx);
};
} // namespace Service::HID

View File

@@ -0,0 +1,39 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/stubbed.h"
namespace Service::HID {
Controller_Stubbed::Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
void Controller_Stubbed::OnRelease() {}
void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
if (!smart_update) {
return;
}
CommonHeader header{};
header.timestamp = CoreTiming::GetTicks();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
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

View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_Stubbed final : public ControllerBase {
public:
Controller_Stubbed();
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(u8* data, std::size_t size) override;
// Called when input devices should be loaded
void OnLoadInputDevices() override;
void SetCommonHeaderOffset(std::size_t off);
private:
bool smart_update{};
std::size_t common_offset{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,65 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_types.h"
#include "common/swap.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"
#include "core/settings.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
Controller_Touchscreen::Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() {}
void Controller_Touchscreen::OnRelease() {}
void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
shared_memory.header.entry_count = 0;
shared_memory.header.last_entry_index = 0;
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];
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
const auto [x, y, pressed] = touch_device->GetStatus();
auto& touch_entry = cur_entry.states[0];
if (pressed) {
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
touch_entry.diameter_x = 15;
touch_entry.diameter_y = 15;
touch_entry.rotation_angle = 0;
const u64 tick = CoreTiming::GetTicks();
touch_entry.delta_time = tick - last_touch;
last_touch = tick;
touch_entry.finger = 0;
cur_entry.entry_count = 1;
} else {
cur_entry.entry_count = 0;
}
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
}
void Controller_Touchscreen::OnLoadInputDevices() {
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
}
} // namespace Service::HID

View File

@@ -0,0 +1,62 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_Touchscreen final : public ControllerBase {
public:
Controller_Touchscreen();
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(u8* data, std::size_t size) override;
// Called when input devices should be loaded
void OnLoadInputDevices() override;
private:
struct TouchState {
u64_le delta_time;
u32_le attribute;
u32_le finger;
u32_le x;
u32_le y;
u32_le diameter_x;
u32_le diameter_y;
u32_le rotation_angle;
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
struct TouchScreenEntry {
s64_le sampling_number;
s64_le sampling_number2;
s32_le entry_count;
std::array<TouchState, 16> 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");
TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_device;
s64_le last_touch{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,45 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
Controller_XPad::Controller_XPad() = default;
void Controller_XPad::OnInit() {}
void Controller_XPad::OnRelease() {}
void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
for (auto& xpad_entry : shared_memory.shared_memory_entries) {
xpad_entry.header.timestamp = CoreTiming::GetTicks();
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;
}
// TODO(ogniK): Update xpad states
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
void Controller_XPad::OnLoadInputDevices() {}
} // namespace Service::HID

View File

@@ -0,0 +1,60 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_XPad final : public ControllerBase {
public:
Controller_XPad();
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(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, "AnalogStick is an invalid size");
struct XPadState {
s64_le sampling_number;
s64_le sampling_number2;
s32_le attributes;
u32_le pad_states;
AnalogStick x_stick;
AnalogStick y_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{};
};
} // namespace Service::HID

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -19,6 +21,16 @@
#include "core/hle/service/service.h"
#include "core/settings.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
#include "core/hle/service/hid/controllers/gesture.h"
#include "core/hle/service/hid/controllers/keyboard.h"
#include "core/hle/service/hid/controllers/mouse.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/stubbed.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
// Updating period for each HID device.
@@ -26,6 +38,22 @@ namespace Service::HID {
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
enum class HidController : std::size_t {
DebugPad,
Touchscreen,
Mouse,
Keyboard,
XPad,
Unknown1,
Unknown2,
Unknown3,
SixAxisSensor,
NPad,
Gesture,
MaxControllers,
};
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
@@ -37,19 +65,57 @@ public:
auto& kernel = Core::System::GetInstance().Kernel();
shared_mem = Kernel::SharedMemory::Create(
kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite,
kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
MakeController<Controller_DebugPad>(HidController::DebugPad);
MakeController<Controller_Touchscreen>(HidController::Touchscreen);
MakeController<Controller_Mouse>(HidController::Mouse);
MakeController<Controller_Keyboard>(HidController::Keyboard);
MakeController<Controller_XPad>(HidController::XPad);
MakeController<Controller_Stubbed>(HidController::Unknown1);
MakeController<Controller_Stubbed>(HidController::Unknown2);
MakeController<Controller_Stubbed>(HidController::Unknown3);
MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
MakeController<Controller_NPad>(HidController::NPad);
MakeController<Controller_Gesture>(HidController::Gesture);
// Homebrew doesn't try to activate some controllers, so we activate them by default
GetController<Controller_NPad>(HidController::NPad).ActivateController();
GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
// Register update callbacks
pad_update_event = CoreTiming::RegisterEvent(
"HID::UpdatePadCallback",
[this](u64 userdata, int cycles_late) { UpdatePadCallback(userdata, cycles_late); });
[this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
}
void ActivateController(HidController controller) {
controllers[static_cast<size_t>(controller)]->ActivateController();
}
void DeactivateController(HidController controller) {
controllers[static_cast<size_t>(controller)]->DeactivateController();
}
template <typename T>
void MakeController(HidController controller) {
controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
}
template <typename T>
T& GetController(HidController controller) {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
~IAppletResource() {
CoreTiming::UnscheduleEvent(pad_update_event, 0);
}
@@ -62,200 +128,15 @@ private:
LOG_DEBUG(Service_HID, "called");
}
void LoadInputDevices() {
std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
// TODO(shinyquagsire23): gyro, mouse, keyboard
}
void UpdatePadCallback(u64 userdata, int cycles_late) {
SharedMemory mem{};
std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
if (Settings::values.is_device_reload_pending.exchange(false))
LoadInputDevices();
// Set up controllers as neon red+blue Joy-Con attached to console
ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
controller_header.type = ControllerType_Handheld;
controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
controller_header.right_color_body = JOYCON_BODY_NEON_RED;
controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) {
for (auto& layout : mem.controllers[controller].layouts) {
layout.header.num_entries = HID_NUM_ENTRIES;
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
// HID shared memory stores the state of the past 17 samples in a circlular buffer,
// each with a timestamp in number of samples since boot.
const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
layout.header.timestamp_ticks = CoreTiming::GetTicks();
layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
entry.timestamp = last_entry.timestamp + 1;
// TODO(shinyquagsire23): Is this always identical to timestamp?
entry.timestamp_2 = entry.timestamp;
// TODO(shinyquagsire23): More than just handheld input
if (controller != Controller_Handheld)
continue;
entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
// TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
// For now everything is just the default handheld layout, but split Joy-Con will
// rotate the face buttons and directions for certain layouts.
ControllerPadState& state = entry.buttons;
using namespace Settings::NativeButton;
state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
state.lstick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
state.rstick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
state.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
state.lstick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
state.lstick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
state.lstick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
state.lstick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
state.rstick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
state.rstick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
state.rstick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
state.rstick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
const auto [stick_l_x_f, stick_l_y_f] = sticks[Joystick_Left]->GetStatus();
const auto [stick_r_x_f, stick_r_y_f] = sticks[Joystick_Right]->GetStatus();
entry.joystick_left_x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
entry.joystick_left_y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
entry.joystick_right_x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
entry.joystick_right_y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
void UpdateControllers(u64 userdata, int cycles_late) {
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
for (const auto& controller : controllers) {
if (should_reload) {
controller->OnLoadInputDevices();
}
controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
}
TouchScreen& touchscreen = mem.touchscreen;
const u64 last_entry = touchscreen.header.latest_entry;
const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
const u64 timestamp = CoreTiming::GetTicks();
const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1;
touchscreen.header.timestamp_ticks = timestamp;
touchscreen.header.num_entries = touchscreen.entries.size();
touchscreen.header.latest_entry = curr_entry;
touchscreen.header.max_entry_index = touchscreen.entries.size();
touchscreen.header.timestamp = timestamp;
touchscreen.entries[curr_entry].header.timestamp = sample_counter;
TouchScreenEntryTouch touch_entry{};
auto [x, y, pressed] = touch_device->GetStatus();
touch_entry.timestamp = timestamp;
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
touch_entry.touch_index = 0;
// TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
touch_entry.diameter_x = 15;
touch_entry.diameter_y = 15;
touch_entry.angle = 0;
// TODO(DarkLordZach): Implement multi-touch support
if (pressed) {
touchscreen.entries[curr_entry].header.num_touches = 1;
touchscreen.entries[curr_entry].touches[0] = touch_entry;
} else {
touchscreen.entries[curr_entry].header.num_touches = 0;
}
// TODO(shinyquagsire23): Properly implement mouse
Mouse& mouse = mem.mouse;
const u64 last_mouse_entry = mouse.header.latest_entry;
const u64 curr_mouse_entry = (mouse.header.latest_entry + 1) % mouse.entries.size();
const u64 mouse_sample_counter = mouse.entries[last_mouse_entry].timestamp + 1;
mouse.header.timestamp_ticks = timestamp;
mouse.header.num_entries = mouse.entries.size();
mouse.header.max_entry_index = mouse.entries.size();
mouse.header.latest_entry = curr_mouse_entry;
mouse.entries[curr_mouse_entry].timestamp = mouse_sample_counter;
mouse.entries[curr_mouse_entry].timestamp_2 = mouse_sample_counter;
// TODO(shinyquagsire23): Properly implement keyboard
Keyboard& keyboard = mem.keyboard;
const u64 last_keyboard_entry = keyboard.header.latest_entry;
const u64 curr_keyboard_entry =
(keyboard.header.latest_entry + 1) % keyboard.entries.size();
const u64 keyboard_sample_counter = keyboard.entries[last_keyboard_entry].timestamp + 1;
keyboard.header.timestamp_ticks = timestamp;
keyboard.header.num_entries = keyboard.entries.size();
keyboard.header.latest_entry = last_keyboard_entry;
keyboard.header.max_entry_index = keyboard.entries.size();
keyboard.entries[curr_keyboard_entry].timestamp = keyboard_sample_counter;
keyboard.entries[curr_keyboard_entry].timestamp_2 = keyboard_sample_counter;
// TODO(shinyquagsire23): Figure out what any of these are
for (auto& input : mem.unk_input_1) {
const u64 last_input_entry = input.header.latest_entry;
const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
input.header.timestamp_ticks = timestamp;
input.header.num_entries = input.entries.size();
input.header.latest_entry = last_input_entry;
input.header.max_entry_index = input.entries.size();
input.entries[curr_input_entry].timestamp = input_sample_counter;
input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
}
for (auto& input : mem.unk_input_2) {
input.header.timestamp_ticks = timestamp;
input.header.num_entries = 17;
input.header.latest_entry = 0;
input.header.max_entry_index = 0;
}
UnkInput3& input = mem.unk_input_3;
const u64 last_input_entry = input.header.latest_entry;
const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
input.header.timestamp_ticks = timestamp;
input.header.num_entries = input.entries.size();
input.header.latest_entry = last_input_entry;
input.header.max_entry_index = input.entries.size();
input.entries[curr_input_entry].timestamp = input_sample_counter;
input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
// TODO(shinyquagsire23): Signal events
std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory));
// Reschedule recurrent event
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
}
@@ -265,11 +146,8 @@ private:
// CoreTiming update events
CoreTiming::EventType* pad_update_event;
// Stored input state info
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> sticks;
std::unique_ptr<Input::TouchDevice> touch_device;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
};
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -301,7 +179,7 @@ public:
{31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
{40, nullptr, "AcquireXpadIdEventHandle"},
{41, nullptr, "ReleaseXpadIdEventHandle"},
{51, nullptr, "ActivateXpad"},
{51, &Hid::ActivateXpad, "ActivateXpad"},
{55, nullptr, "GetXpadIds"},
{56, nullptr, "ActivateJoyXpad"},
{58, nullptr, "GetJoyXpadLifoHandle"},
@@ -362,8 +240,8 @@ public:
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
{207, nullptr, "SendVibrationGcErmCommand"},
{208, nullptr, "GetActualVibrationGcErmCommand"},
{209, nullptr, "BeginPermitVibrationSession"},
{210, nullptr, "EndPermitVibrationSession"},
{209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
{210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
{300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
{301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
{302, nullptr, "StopConsoleSixAxisSensor"},
@@ -401,16 +279,11 @@ public:
// clang-format on
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle");
}
~Hid() = default;
private:
std::shared_ptr<IAppletResource> applet_resource;
u32 joy_hold_type{0};
Kernel::SharedPtr<Kernel::Event> event;
void CreateAppletResource(Kernel::HLERequestContext& ctx) {
if (applet_resource == nullptr) {
@@ -423,31 +296,59 @@ private:
LOG_DEBUG(Service_HID, "called");
}
void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
void ActivateXpad(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::XPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::DebugPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::Touchscreen);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void ActivateMouse(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::Mouse);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::Keyboard);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void ActivateGesture(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::Gesture);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
// Should have no effect with how our npad sets up the data
applet_resource->ActivateController(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto handle = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -468,84 +369,168 @@ private:
}
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto supported_styleset = rp.PopRaw<u32>();
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetSupportedStyleSet({supported_styleset});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
LOG_WARNING(Service_HID, "(STUBBED) called");
rb.Push<u32>(controller.GetSupportedStyleSet().raw);
LOG_DEBUG(Service_HID, "called");
}
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void ActivateNpad(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
applet_resource->ActivateController(HidController::NPad);
LOG_DEBUG(Service_HID, "called");
}
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto npad_id = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
LOG_WARNING(Service_HID, "(STUBBED) called");
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetStyleSetChangedEvent());
LOG_DEBUG(Service_HID, "called");
}
void DisconnectNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto npad_id = rp.PopRaw<u32>();
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.DisconnectNPad(npad_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
IPC::RequestParser rp{ctx};
auto npad_id = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetLedPattern(npad_id)
.raw);
LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::RequestParser rp{ctx};
const auto hold_type = rp.PopRaw<u64>();
controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
const auto& controller =
applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(joy_hold_type);
LOG_WARNING(Service_HID, "(STUBBED) called");
rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto npad_id = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetVibrationEnabled(true);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetVibrationEnabled(false);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto controller_id = rp.PopRaw<u32>();
const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateController({controller_id}, {vibration_values});
LOG_DEBUG(Service_HID, "called");
}
void SendVibrationValues(Kernel::HLERequestContext& ctx) {
const auto controllers = ctx.ReadBuffer(0);
const auto vibrations = ctx.ReadBuffer(1);
std::vector<u32> controller_list(controllers.size() / sizeof(u32));
std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
sizeof(Controller_NPad::Vibration));
std::memcpy(controller_list.data(), controllers.data(), controllers.size());
std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
[](u32 controller_id) { return controller_id - 3; });
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateController(controller_list, vibration_list);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
rb.PushRaw<Controller_NPad::Vibration>(
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetLastVibration());
LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id = rp.PopRaw<u32>();
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_DEBUG(Service_HID, "called");
}
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -555,6 +540,8 @@ private:
}
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto mode = rp.PopRaw<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -563,8 +550,9 @@ private:
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
LOG_WARNING(Service_HID, "(STUBBED) called");
rb.Push<u32>(1);
rb.Push<u32>(0);
LOG_DEBUG(Service_HID, "called");
}
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
@@ -574,12 +562,6 @@ private:
LOG_DEBUG(Service_HID, "called");
}
void SendVibrationValues(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -597,18 +579,6 @@ private:
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateGesture(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
class HidDbg final : public ServiceFramework<HidDbg> {

View File

@@ -4,408 +4,12 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/service.h"
namespace SM {
class ServiceManager;
}
namespace Service::HID {
// Begin enums and output structs
constexpr u32 HID_NUM_ENTRIES = 17;
constexpr u32 HID_NUM_LAYOUTS = 7;
constexpr s32 HID_JOYSTICK_MAX = 0x8000;
constexpr s32 HID_JOYSTICK_MIN = -0x8000;
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
enum ControllerType : u32 {
ControllerType_ProController = 1 << 0,
ControllerType_Handheld = 1 << 1,
ControllerType_JoyconPair = 1 << 2,
ControllerType_JoyconLeft = 1 << 3,
ControllerType_JoyconRight = 1 << 4,
};
enum ControllerLayoutType : u32 {
Layout_ProController = 0, // Pro Controller or HID gamepad
Layout_Handheld = 1, // Two Joy-Con docked to rails
Layout_Single = 2, // Horizontal single Joy-Con or pair of Joy-Con, adjusted for orientation
Layout_Left = 3, // Only raw left Joy-Con state, no orientation adjustment
Layout_Right = 4, // Only raw right Joy-Con state, no orientation adjustment
Layout_DefaultDigital = 5, // Same as next, but sticks have 8-direction values only
Layout_Default = 6, // Safe default, single Joy-Con have buttons/sticks rotated for orientation
};
enum ControllerColorDescription {
ColorDesc_ColorsNonexistent = 1 << 1,
};
enum ControllerConnectionState {
ConnectionState_Connected = 1 << 0,
ConnectionState_Wired = 1 << 1,
};
enum ControllerJoystick {
Joystick_Left = 0,
Joystick_Right = 1,
};
enum ControllerID {
Controller_Player1 = 0,
Controller_Player2 = 1,
Controller_Player3 = 2,
Controller_Player4 = 3,
Controller_Player5 = 4,
Controller_Player6 = 5,
Controller_Player7 = 6,
Controller_Player8 = 7,
Controller_Handheld = 8,
Controller_Unknown = 9,
};
// End enums and output structs
// Begin UnkInput3
struct UnkInput3Header {
u64 timestamp_ticks;
u64 num_entries;
u64 latest_entry;
u64 max_entry_index;
};
static_assert(sizeof(UnkInput3Header) == 0x20, "HID UnkInput3 header structure has incorrect size");
struct UnkInput3Entry {
u64 timestamp;
u64 timestamp_2;
u64 unk_8;
u64 unk_10;
u64 unk_18;
};
static_assert(sizeof(UnkInput3Entry) == 0x28, "HID UnkInput3 entry structure has incorrect size");
struct UnkInput3 {
UnkInput3Header header;
std::array<UnkInput3Entry, 17> entries;
std::array<u8, 0x138> padding;
};
static_assert(sizeof(UnkInput3) == 0x400, "HID UnkInput3 structure has incorrect size");
// End UnkInput3
// Begin TouchScreen
struct TouchScreenHeader {
u64 timestamp_ticks;
u64 num_entries;
u64 latest_entry;
u64 max_entry_index;
u64 timestamp;
};
static_assert(sizeof(TouchScreenHeader) == 0x28,
"HID touch screen header structure has incorrect size");
struct TouchScreenEntryHeader {
u64 timestamp;
u64 num_touches;
};
static_assert(sizeof(TouchScreenEntryHeader) == 0x10,
"HID touch screen entry header structure has incorrect size");
struct TouchScreenEntryTouch {
u64 timestamp;
u32 padding;
u32 touch_index;
u32 x;
u32 y;
u32 diameter_x;
u32 diameter_y;
u32 angle;
u32 padding_2;
};
static_assert(sizeof(TouchScreenEntryTouch) == 0x28,
"HID touch screen touch structure has incorrect size");
struct TouchScreenEntry {
TouchScreenEntryHeader header;
std::array<TouchScreenEntryTouch, 16> touches;
u64 unk;
};
static_assert(sizeof(TouchScreenEntry) == 0x298,
"HID touch screen entry structure has incorrect size");
struct TouchScreen {
TouchScreenHeader header;
std::array<TouchScreenEntry, 17> entries;
std::array<u8, 0x3c0> padding;
};
static_assert(sizeof(TouchScreen) == 0x3000, "HID touch screen structure has incorrect size");
// End TouchScreen
// Begin Mouse
struct MouseHeader {
u64 timestamp_ticks;
u64 num_entries;
u64 latest_entry;
u64 max_entry_index;
};
static_assert(sizeof(MouseHeader) == 0x20, "HID mouse header structure has incorrect size");
struct MouseButtonState {
union {
u64 hex{};
// Buttons
BitField<0, 1, u64> left;
BitField<1, 1, u64> right;
BitField<2, 1, u64> middle;
BitField<3, 1, u64> forward;
BitField<4, 1, u64> back;
};
};
struct MouseEntry {
u64 timestamp;
u64 timestamp_2;
u32 x;
u32 y;
u32 velocity_x;
u32 velocity_y;
u32 scroll_velocity_x;
u32 scroll_velocity_y;
MouseButtonState buttons;
};
static_assert(sizeof(MouseEntry) == 0x30, "HID mouse entry structure has incorrect size");
struct Mouse {
MouseHeader header;
std::array<MouseEntry, 17> entries;
std::array<u8, 0xB0> padding;
};
static_assert(sizeof(Mouse) == 0x400, "HID mouse structure has incorrect size");
// End Mouse
// Begin Keyboard
struct KeyboardHeader {
u64 timestamp_ticks;
u64 num_entries;
u64 latest_entry;
u64 max_entry_index;
};
static_assert(sizeof(KeyboardHeader) == 0x20, "HID keyboard header structure has incorrect size");
struct KeyboardModifierKeyState {
union {
u64 hex{};
// Buttons
BitField<0, 1, u64> lctrl;
BitField<1, 1, u64> lshift;
BitField<2, 1, u64> lalt;
BitField<3, 1, u64> lmeta;
BitField<4, 1, u64> rctrl;
BitField<5, 1, u64> rshift;
BitField<6, 1, u64> ralt;
BitField<7, 1, u64> rmeta;
BitField<8, 1, u64> capslock;
BitField<9, 1, u64> scrolllock;
BitField<10, 1, u64> numlock;
};
};
struct KeyboardEntry {
u64 timestamp;
u64 timestamp_2;
KeyboardModifierKeyState modifier;
u32 keys[8];
};
static_assert(sizeof(KeyboardEntry) == 0x38, "HID keyboard entry structure has incorrect size");
struct Keyboard {
KeyboardHeader header;
std::array<KeyboardEntry, 17> entries;
std::array<u8, 0x28> padding;
};
static_assert(sizeof(Keyboard) == 0x400, "HID keyboard structure has incorrect size");
// End Keyboard
// Begin UnkInput1
struct UnkInput1Header {
u64 timestamp_ticks;
u64 num_entries;
u64 latest_entry;
u64 max_entry_index;
};
static_assert(sizeof(UnkInput1Header) == 0x20, "HID UnkInput1 header structure has incorrect size");
struct UnkInput1Entry {
u64 timestamp;
u64 timestamp_2;
u64 unk_8;
u64 unk_10;
u64 unk_18;
};
static_assert(sizeof(UnkInput1Entry) == 0x28, "HID UnkInput1 entry structure has incorrect size");
struct UnkInput1 {
UnkInput1Header header;
std::array<UnkInput1Entry, 17> entries;
std::array<u8, 0x138> padding;
};
static_assert(sizeof(UnkInput1) == 0x400, "HID UnkInput1 structure has incorrect size");
// End UnkInput1
// Begin UnkInput2
struct UnkInput2Header {
u64 timestamp_ticks;
u64 num_entries;
u64 latest_entry;
u64 max_entry_index;
};
static_assert(sizeof(UnkInput2Header) == 0x20, "HID UnkInput2 header structure has incorrect size");
struct UnkInput2 {
UnkInput2Header header;
std::array<u8, 0x1E0> padding;
};
static_assert(sizeof(UnkInput2) == 0x200, "HID UnkInput2 structure has incorrect size");
// End UnkInput2
// Begin Controller
struct ControllerMAC {
u64 timestamp;
std::array<u8, 0x8> mac;
u64 unk;
u64 timestamp_2;
};
static_assert(sizeof(ControllerMAC) == 0x20, "HID controller MAC structure has incorrect size");
struct ControllerHeader {
u32 type;
u32 is_half;
u32 single_colors_descriptor;
u32 single_color_body;
u32 single_color_buttons;
u32 split_colors_descriptor;
u32 left_color_body;
u32 left_color_buttons;
u32 right_color_body;
u32 right_color_buttons;
};
static_assert(sizeof(ControllerHeader) == 0x28,
"HID controller header structure has incorrect size");
struct ControllerLayoutHeader {
u64 timestamp_ticks;
u64 num_entries;
u64 latest_entry;
u64 max_entry_index;
};
static_assert(sizeof(ControllerLayoutHeader) == 0x20,
"HID controller layout header structure has incorrect size");
struct ControllerPadState {
union {
u64 hex{};
// Buttons
BitField<0, 1, u64> a;
BitField<1, 1, u64> b;
BitField<2, 1, u64> x;
BitField<3, 1, u64> y;
BitField<4, 1, u64> lstick;
BitField<5, 1, u64> rstick;
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 buttons
BitField<12, 1, u64> dleft;
BitField<13, 1, u64> dup;
BitField<14, 1, u64> dright;
BitField<15, 1, u64> ddown;
// Left stick directions
BitField<16, 1, u64> lstick_left;
BitField<17, 1, u64> lstick_up;
BitField<18, 1, u64> lstick_right;
BitField<19, 1, u64> lstick_down;
// Right stick directions
BitField<20, 1, u64> rstick_left;
BitField<21, 1, u64> rstick_up;
BitField<22, 1, u64> rstick_right;
BitField<23, 1, u64> rstick_down;
BitField<24, 1, u64> sl;
BitField<25, 1, u64> sr;
};
};
struct ControllerInputEntry {
u64 timestamp;
u64 timestamp_2;
ControllerPadState buttons;
s32 joystick_left_x;
s32 joystick_left_y;
s32 joystick_right_x;
s32 joystick_right_y;
u64 connection_state;
};
static_assert(sizeof(ControllerInputEntry) == 0x30,
"HID controller input entry structure has incorrect size");
struct ControllerLayout {
ControllerLayoutHeader header;
std::array<ControllerInputEntry, 17> entries;
};
static_assert(sizeof(ControllerLayout) == 0x350,
"HID controller layout structure has incorrect size");
struct Controller {
ControllerHeader header;
std::array<ControllerLayout, HID_NUM_LAYOUTS> layouts;
std::array<u8, 0x2a70> unk_1;
ControllerMAC mac_left;
ControllerMAC mac_right;
std::array<u8, 0xdf8> unk_2;
};
static_assert(sizeof(Controller) == 0x5000, "HID controller structure has incorrect size");
// End Controller
struct SharedMemory {
UnkInput3 unk_input_3;
TouchScreen touchscreen;
Mouse mouse;
Keyboard keyboard;
std::array<UnkInput1, 4> unk_input_1;
std::array<UnkInput2, 3> unk_input_2;
std::array<u8, 0x800> unk_section_8;
std::array<u8, 0x4000> controller_serials;
std::array<Controller, 10> controllers;
std::array<u8, 0x4600> unk_section_9;
};
static_assert(sizeof(SharedMemory) == 0x40000, "HID Shared Memory structure has incorrect size");
/// Reload input devices. Used when input configuration changed
void ReloadInputDevices();

View File

@@ -144,7 +144,7 @@ private:
}
const u64 device_handle{0xDEAD};
const HID::ControllerID npad_id{HID::Controller_Player1};
const u32 npad_id{0}; // This is the first player controller id
State state{State::NonInitialized};
DeviceState device_state{DeviceState::Initialized};
Kernel::SharedPtr<Kernel::Event> activate_event;