// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include #include "common/assert.h" #include "common/bit_field.h" #include "common/common_types.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 size_t NPAD_OFFSET = 0x9A00; constexpr size_t MAX_CONTROLLER_COUNT = 9; enum class JoystickId : size_t { Joystick_Left, Joystick_Right }; constexpr std::array NPAD_ID_LIST{0, 1, 2, 3, 4, 5, 6, 7, 32}; size_t CONTROLLER_COUNT{}; std::array CONNECTED_CONTROLLERS{}; void Controller_NPad::InitNewlyAddedControler(size_t controller_idx) { const auto controller_type = CONNECTED_CONTROLLERS[controller_idx]; 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::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::Tabletop: UNIMPLEMENTED_MSG("Tabletop is not implemented"); 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_verticle.Assign(1); // TODO(ogniK): Swap joycons orientations } 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; size_t controller{}; supported_npad_id_types.resize(NPAD_ID_LIST.size()); 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); } std::memcpy(supported_npad_id_types.data(), NPAD_ID_LIST.data(), NPAD_ID_LIST.size() * sizeof(u32)); if (CONTROLLER_COUNT == 0) { AddNewController(NPadControllerType::Handheld); } } 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); std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(), Input::CreateDevice); } void Controller_NPad::OnRelease() {} void Controller_NPad::OnUpdate(u8* data, size_t data_len) { if (!IsControllerActivated()) return; for (size_t i = 0; i < shared_memory_entries.size(); i++) { auto& npad = shared_memory_entries[i]; const std::array 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; 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; } if (CONNECTED_CONTROLLERS[i] == NPadControllerType::None) { continue; } const auto& controller_type = CONNECTED_CONTROLLERS[i]; // Pad states auto pad_state = ControllerPadState{}; 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.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); pad_state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); pad_state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); pad_state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); pad_state.lstickleft.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); pad_state.lstickup.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); pad_state.lstickright.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); pad_state.lstickdown.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); pad_state.rstickleft.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); pad_state.rstickup.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); pad_state.rstickright.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); pad_state.rstickdown.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(JoystickId::Joystick_Left)]->GetStatus(); const auto [stick_r_x_f, stick_r_y_f] = sticks[static_cast(JoystickId::Joystick_Right)]->GetStatus(); lstick_entry.x = static_cast(stick_l_x_f * HID_JOYSTICK_MAX); lstick_entry.y = static_cast(stick_l_y_f * HID_JOYSTICK_MAX); rstick_entry.x = static_cast(stick_r_x_f * HID_JOYSTICK_MAX); rstick_entry.y = static_cast(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 } switch (controller_type) { case NPadControllerType::Handheld: handheld_entry.connection_status.IsConnected.Assign(1); handheld_entry.connection_status.IsWired.Assign(1); handheld_entry.pad_states.raw = pad_state.raw; handheld_entry.lstick = lstick_entry; handheld_entry.rstick = rstick_entry; break; case NPadControllerType::JoyLeft: left_entry.connection_status.IsConnected.Assign(1); left_entry.pad_states.raw = pad_state.raw; left_entry.lstick = lstick_entry; left_entry.rstick = rstick_entry; break; case NPadControllerType::JoyRight: right_entry.connection_status.IsConnected.Assign(1); right_entry.pad_states.raw = pad_state.raw; right_entry.lstick = lstick_entry; right_entry.rstick = rstick_entry; break; case NPadControllerType::Tabletop: // TODO(ogniK): Figure out how to add proper tabletop support dual_entry.pad_states.raw = pad_state.raw; dual_entry.lstick = lstick_entry; dual_entry.rstick = rstick_entry; dual_entry.connection_status.IsConnected.Assign(1); break; case NPadControllerType::Pokeball: pokeball_entry.connection_status.IsConnected.Assign(1); pokeball_entry.connection_status.IsWired.Assign(1); pokeball_entry.pad_states.raw = pad_state.raw; pokeball_entry.lstick = lstick_entry; pokeball_entry.rstick = rstick_entry; break; case NPadControllerType::ProController: main_controller.pad_states.raw = pad_state.raw; main_controller.lstick = lstick_entry; main_controller.rstick = rstick_entry; main_controller.connection_status.IsConnected.Assign(1); main_controller.connection_status.IsWired.Assign(1); break; } // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate // any controllers. libnx_entry.connection_status.IsConnected.Assign(1); libnx_entry.connection_status.IsWired.Assign(1); libnx_entry.pad_states.raw = pad_state.raw; libnx_entry.lstick = lstick_entry; libnx_entry.rstick = rstick_entry; } std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), shared_memory_entries.size() * sizeof(NPadEntry)); } 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, size_t length) { ASSERT(length > 0 && (length % sizeof(u32)) == 0); supported_npad_id_types.resize(length / 4); std::memcpy(supported_npad_id_types.data(), data, length); } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, 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()); } 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& controller_ids, const std::vector& vibrations) { for (size_t i = 0; i < controller_ids.size(); i++) { if (i >= CONTROLLER_COUNT) { continue; } // TODO(ogniK): Vibrate the physical controller } LOG_WARNING(Service_HID, "(STUBBED) called"); last_processed_vibration = vibrations.back(); } Kernel::SharedPtr 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_COUNT >= MAX_CONTROLLER_COUNT) { LOG_ERROR(Service_HID, "Cannot connect any more controllers!"); return; } CONNECTED_CONTROLLERS[CONTROLLER_COUNT] = controller; InitNewlyAddedControler(CONTROLLER_COUNT++); } }; // namespace Service::HID