service/hid: Implement SetNpadJoyAssignmentMode

This commit is contained in:
german77 2021-12-05 16:17:52 -06:00
parent fae07919af
commit d6ae9c68f8
5 changed files with 174 additions and 38 deletions

View File

@ -145,6 +145,16 @@ NpadIdType HIDCore::GetFirstNpadId() const {
return NpadIdType::Player1; return NpadIdType::Player1;
} }
NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
const auto* const controller = GetEmulatedControllerByIndex(player_index);
if (!controller->IsConnected()) {
return controller->GetNpadIdType();
}
}
return NpadIdType::Player1;
}
void HIDCore::EnableAllControllerConfiguration() { void HIDCore::EnableAllControllerConfiguration() {
player_1->EnableConfiguration(); player_1->EnableConfiguration();
player_2->EnableConfiguration(); player_2->EnableConfiguration();

View File

@ -45,6 +45,9 @@ public:
/// Returns the first connected npad id /// Returns the first connected npad id
NpadIdType GetFirstNpadId() const; NpadIdType GetFirstNpadId() const;
/// Returns the first disconnected npad id
NpadIdType GetFirstDisconnectedNpadId() const;
/// Sets all emulated controllers into configuring mode. /// Sets all emulated controllers into configuring mode.
void EnableAllControllerConfiguration(); void EnableAllControllerConfiguration();

View File

@ -110,7 +110,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
UpdateControllerAt(npad_type, npad_id, is_connected); UpdateControllerAt(npad_type, npad_id, is_connected);
break; break;
case Core::HID::ControllerTriggerType::Battery: { case Core::HID::ControllerTriggerType::Battery: {
if (!controller.is_connected) { if (!controller.device->IsConnected()) {
return; return;
} }
auto& shared_memory = controller.shared_memory_entry; auto& shared_memory = controller.shared_memory_entry;
@ -150,7 +150,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory.system_properties.is_vertical.Assign(1); shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.system_properties.use_plus.Assign(1); shared_memory.system_properties.use_plus.Assign(1);
shared_memory.system_properties.use_minus.Assign(1); shared_memory.system_properties.use_minus.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController; shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
break; break;
case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::Handheld:
@ -166,21 +165,30 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
break; break;
case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconDual:
shared_memory.style_tag.joycon_dual.Assign(1); shared_memory.style_tag.joycon_dual.Assign(1);
shared_memory.device_type.joycon_left.Assign(1); if (controller.is_dual_left_connected) {
shared_memory.device_type.joycon_right.Assign(1); shared_memory.device_type.joycon_left.Assign(1);
shared_memory.system_properties.is_vertical.Assign(1); shared_memory.system_properties.use_minus.Assign(1);
shared_memory.system_properties.use_plus.Assign(1); }
shared_memory.system_properties.use_minus.Assign(1); if (controller.is_dual_right_connected) {
shared_memory.device_type.joycon_right.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
}
shared_memory.system_properties.use_directional_buttons.Assign(1); shared_memory.system_properties.use_directional_buttons.Assign(1);
shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual; shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory.applet_footer.type = AppletFooterUiType::JoyDual; if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
} else if (controller.is_dual_left_connected) {
shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
} else {
shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
}
break; break;
case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconLeft:
shared_memory.style_tag.joycon_left.Assign(1); shared_memory.style_tag.joycon_left.Assign(1);
shared_memory.device_type.joycon_left.Assign(1); shared_memory.device_type.joycon_left.Assign(1);
shared_memory.system_properties.is_horizontal.Assign(1); shared_memory.system_properties.is_horizontal.Assign(1);
shared_memory.system_properties.use_minus.Assign(1); shared_memory.system_properties.use_minus.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
break; break;
case Core::HID::NpadStyleIndex::JoyconRight: case Core::HID::NpadStyleIndex::JoyconRight:
@ -188,7 +196,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory.device_type.joycon_right.Assign(1); shared_memory.device_type.joycon_right.Assign(1);
shared_memory.system_properties.is_horizontal.Assign(1); shared_memory.system_properties.is_horizontal.Assign(1);
shared_memory.system_properties.use_plus.Assign(1); shared_memory.system_properties.use_plus.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
break; break;
case Core::HID::NpadStyleIndex::GameCube: case Core::HID::NpadStyleIndex::GameCube:
@ -200,7 +207,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
case Core::HID::NpadStyleIndex::Pokeball: case Core::HID::NpadStyleIndex::Pokeball:
shared_memory.style_tag.palma.Assign(1); shared_memory.style_tag.palma.Assign(1);
shared_memory.device_type.palma.Assign(1); shared_memory.device_type.palma.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
break; break;
case Core::HID::NpadStyleIndex::NES: case Core::HID::NpadStyleIndex::NES:
shared_memory.style_tag.lark.Assign(1); shared_memory.style_tag.lark.Assign(1);
@ -443,11 +449,15 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconDual:
pad_state.connection_status.raw = 0; pad_state.connection_status.raw = 0;
pad_state.connection_status.is_connected.Assign(1); pad_state.connection_status.is_connected.Assign(1);
pad_state.connection_status.is_left_connected.Assign(1); if (controller.is_dual_left_connected) {
pad_state.connection_status.is_right_connected.Assign(1); pad_state.connection_status.is_left_connected.Assign(1);
libnx_state.connection_status.is_left_connected.Assign(1);
}
if (controller.is_dual_right_connected) {
pad_state.connection_status.is_right_connected.Assign(1);
libnx_state.connection_status.is_right_connected.Assign(1);
}
libnx_state.connection_status.is_left_connected.Assign(1);
libnx_state.connection_status.is_right_connected.Assign(1);
pad_state.sampling_number = pad_state.sampling_number =
npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad.joy_dual_lifo.WriteNextEntry(pad_state); npad.joy_dual_lifo.WriteNextEntry(pad_state);
@ -687,7 +697,7 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode; return communication_mode;
} }
void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode) { NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) { if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
@ -698,6 +708,62 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
if (controller.shared_memory_entry.assignment_mode != assignment_mode) { if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
controller.shared_memory_entry.assignment_mode = assignment_mode; controller.shared_memory_entry.assignment_mode = assignment_mode;
} }
if (!controller.device->IsConnected()) {
return;
}
if (assignment_mode == NpadJoyAssignmentMode::Dual) {
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
DisconnectNpad(npad_id);
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
return;
}
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
DisconnectNpad(npad_id);
controller.is_dual_left_connected = false;
controller.is_dual_right_connected = true;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
return;
}
return;
}
// This is for NpadJoyAssignmentMode::Single
// Only JoyconDual get affected by this function
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
return;
}
if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
return;
}
if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
return;
}
// We have two controllers connected to the same npad_id we need to split them
const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId();
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
DisconnectNpad(npad_id);
if (npad_device_type == NpadJoyDeviceType::Left) {
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
controller_2.is_dual_left_connected = false;
controller_2.is_dual_right_connected = true;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
} else {
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
controller_2.is_dual_left_connected = true;
controller_2.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
}
} }
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@ -907,6 +973,7 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
} }
auto& shared_memory_entry = controller.shared_memory_entry; auto& shared_memory_entry = controller.shared_memory_entry;
// Don't reset shared_memory_entry.assignment_mode this value is persistent
shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
shared_memory_entry.device_type.raw = 0; shared_memory_entry.device_type.raw = 0;
shared_memory_entry.system_properties.raw = 0; shared_memory_entry.system_properties.raw = 0;
@ -923,9 +990,10 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
.left = {}, .left = {},
.right = {}, .right = {},
}; };
shared_memory_entry.assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory_entry.applet_footer.type = AppletFooterUiType::None; shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = true;
controller.is_connected = false; controller.is_connected = false;
controller.device->Disconnect(); controller.device->Disconnect();
SignalStyleSetChangedEvent(npad_id); SignalStyleSetChangedEvent(npad_id);
@ -1022,19 +1090,70 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
npad_id_2); npad_id_2);
return; return;
} }
auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device; auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device; auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
bool merge_controllers = false;
// If the controllers at both npad indices form a pair of left and right joycons, merge them. // If the controllers at both npad indices form a pair of left and right joycons, merge them.
// Otherwise, do nothing. // Otherwise, do nothing.
if ((controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft && if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) || controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
(controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft && merge_controllers = true;
controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight)) { }
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (merge_controllers) {
// Disconnect the joycon at the second id and connect the dual joycon at the first index. // Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNpad(npad_id_2); DisconnectNpad(npad_id_2);
controller_1.is_dual_left_connected = true;
controller_1.is_dual_right_connected = true;
AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
return;
} }
LOG_WARNING(Service_HID,
"Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
"dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
controller_2.is_dual_right_connected);
} }
void Controller_NPad::StartLRAssignmentMode() { void Controller_NPad::StartLRAssignmentMode() {

View File

@ -113,7 +113,8 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const; NpadCommunicationMode GetNpadCommunicationMode() const;
void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyAssignmentMode assignment_mode); void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value); const Core::HID::VibrationValue& vibration_value);
@ -464,7 +465,10 @@ private:
std::array<VibrationData, 2> vibration{}; std::array<VibrationData, 2> vibration{};
bool unintended_home_button_input_protection{}; bool unintended_home_button_input_protection{};
bool is_connected{}; bool is_connected{};
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
// Dual joycons can have only one side connected
bool is_dual_left_connected{true};
bool is_dual_right_connected{true};
// Motion parameters // Motion parameters
bool sixaxis_at_rest{true}; bool sixaxis_at_rest{true};

View File

@ -975,35 +975,35 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
const auto parameters{rp.PopRaw<Parameters>()}; const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad) applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single); .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.npad_id, parameters.applet_resource_user_id); parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
struct Parameters { struct Parameters {
Core::HID::NpadIdType npad_id; Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1); INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id; u64 applet_resource_user_id;
u64 npad_joy_device_type; Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
}; };
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()}; const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad) applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single); .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_WARNING(Service_HID, LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
"(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", parameters.npad_id, parameters.applet_resource_user_id,
parameters.npad_id, parameters.applet_resource_user_id, parameters.npad_joy_device_type);
parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -1021,10 +1021,10 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()}; const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad) applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Dual); .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.npad_id, parameters.applet_resource_user_id); parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);