diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 02edf8ee..70cc6156 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -47,6 +47,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/gpu/engines/maxwell_3d.cpp ${source_DIR}/skyline/input.cpp ${source_DIR}/skyline/input/npad.cpp + ${source_DIR}/skyline/input/npad_device.cpp ${source_DIR}/skyline/os.cpp ${source_DIR}/skyline/loader/loader.cpp ${source_DIR}/skyline/loader/nro.cpp diff --git a/app/src/main/cpp/emu_jni.cpp b/app/src/main/cpp/emu_jni.cpp index 9272c4c2..4a463fb9 100644 --- a/app/src/main/cpp/emu_jni.cpp +++ b/app/src/main/cpp/emu_jni.cpp @@ -99,12 +99,12 @@ extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIE extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jlong id, jint state) { if (input) { - skyline::input::npad::NpadButton button{.raw = static_cast(id)}; - input->npad[0]->SetButtonState(button, static_cast(state)); + skyline::input::NpadButton button{.raw = static_cast(id)}; + input->npad.at(skyline::input::NpadId::Player1).SetButtonState(button, static_cast(state)); } } extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue(JNIEnv *, jobject, jint id, jint value) { if (input) - input->npad[0]->SetAxisValue(static_cast(id), value); + input->npad.at(skyline::input::NpadId::Player1).SetAxisValue(static_cast(id), value); } diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index e93881e2..33867f6d 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -74,6 +74,16 @@ namespace skyline { return ((ticks / frequency) * constant::NsInSecond) + (((ticks % frequency) * constant::NsInSecond + (frequency / 2)) / frequency); } + /** + * @brief Returns the current time in arbitrary ticks + * @return The current time in ticks + */ + inline u64 GetTimeTicks() { + u64 ticks; + asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); + return ticks; + } + /** * @brief Aligns up a value to a multiple of two * @tparam Type The type of the values diff --git a/app/src/main/cpp/skyline/input.cpp b/app/src/main/cpp/skyline/input.cpp index d0d36d21..a2431681 100644 --- a/app/src/main/cpp/skyline/input.cpp +++ b/app/src/main/cpp/skyline/input.cpp @@ -4,11 +4,5 @@ #include "input.h" namespace skyline::input { - Input::Input(const DeviceState &state) : state(state), commonNpad(std::make_shared(state)), hidKMem(std::make_shared(state, NULL, sizeof(HidSharedMemory), memory::Permission(true, false, false))) { - hidMem = reinterpret_cast(hidKMem->kernel.address); - - for (uint i = 0; i < constant::NpadCount; i++) { - npad.at(i) = std::make_shared(hidMem->npad.at(i), npad::IndexToNpadId(i)); - } - } + Input::Input(const DeviceState &state) : state(state), kHid(std::make_shared(state, NULL, sizeof(HidSharedMemory), memory::Permission(true, false, false))), hid(reinterpret_cast(kHid->kernel.address)), npad(state, hid) {} } diff --git a/app/src/main/cpp/skyline/input.h b/app/src/main/cpp/skyline/input.h index 6a1a8cc4..21d6906d 100644 --- a/app/src/main/cpp/skyline/input.h +++ b/app/src/main/cpp/skyline/input.h @@ -6,22 +6,22 @@ #include "common.h" #include "kernel/types/KSharedMemory.h" #include "input/shared_mem.h" +#include "input/npad.h" namespace skyline::input { /** - * @brief The Input class manages input devices + * @brief The Input class manages translating host input to guest input */ class Input { private: const DeviceState &state; //!< The state of the device public: + std::shared_ptr kHid; //!< The kernel shared memory object for HID Shared Memory + input::HidSharedMemory *hid; //!< A pointer to HID Shared Memory on the host + + input::NpadManager npad; //!< This manages all the NPad controllers + Input(const DeviceState &state); - - std::shared_ptr commonNpad; //!< The common npad device - std::array, constant::NpadCount> npad; //!< Array of npad devices - - std::shared_ptr hidKMem; //!< The shared memory reserved for HID input - HidSharedMemory *hidMem; //!< A pointer to the root of HID shared memory }; } diff --git a/app/src/main/cpp/skyline/input/npad.cpp b/app/src/main/cpp/skyline/input/npad.cpp index 2d69cd74..5045ad85 100644 --- a/app/src/main/cpp/skyline/input/npad.cpp +++ b/app/src/main/cpp/skyline/input/npad.cpp @@ -4,174 +4,29 @@ #include #include "npad.h" -namespace skyline::input::npad { - u32 NpadIdToIndex(NpadId id) { - switch (id) { - case NpadId::Unknown: - return 8; - case NpadId::Handheld: - return 9; - default: - return static_cast(id); - } - } +namespace skyline::input { - NpadId IndexToNpadId(u32 index) { - switch (index) { - case 8: - return NpadId::Unknown; - case 9: - return NpadId::Handheld; - default: - return static_cast(index); - } - } + NpadManager::NpadManager(const DeviceState &state, input::HidSharedMemory *hid) : state(state), npads + {NpadDevice{hid->npad[0], NpadId::Player1}, {hid->npad[1], NpadId::Player2}, + {hid->npad[2], NpadId::Player3}, {hid->npad[3], NpadId::Player4}, + {hid->npad[4], NpadId::Player5}, {hid->npad[5], NpadId::Player6}, + {hid->npad[6], NpadId::Player7}, {hid->npad[7], NpadId::Player8}, + {hid->npad[8], NpadId::Unknown}, {hid->npad[9], NpadId::Handheld}, + } {} - NpadDevice::NpadDevice(NpadSection &shmemSection, NpadId id) : shmemSection(shmemSection), id(id) {} - - void NpadDevice::Disconnect() { - connectionState.connected = false; - } - - void NpadDevice::Connect(NpadControllerType type) { - shmemSection.header.styles.raw = 0; - shmemSection.deviceType.raw = 0; - shmemSection.properties.raw = 0; - - connectionState.raw = 0; - connectionState.connected = true; - - switch (type) { - case NpadControllerType::Handheld: - shmemSection.header.styles.joyconHandheld = true; - shmemSection.deviceType.handheld = true; - shmemSection.deviceType.handheldLeft = true; - shmemSection.deviceType.handheldRight = true; - shmemSection.header.assignment = NpadJoyAssignment::Dual; - shmemSection.properties.ABXYButtonOriented = true; - shmemSection.properties.plusButtonCapability = true; - shmemSection.properties.minusButtonCapability = true; - - connectionState.handheld = true; - connectionState.leftJoyconConnected = true; - connectionState.rightJoyconConnected = true; - connectionState.leftJoyconHandheld = true; - connectionState.rightJoyconHandheld = true; - break; - case NpadControllerType::ProController: - shmemSection.header.styles.proController = true; - shmemSection.deviceType.fullKey = true; - shmemSection.deviceType.joyconRight = true; - shmemSection.header.assignment = NpadJoyAssignment::Single; - shmemSection.properties.ABXYButtonOriented = true; - shmemSection.properties.plusButtonCapability = true; - shmemSection.properties.minusButtonCapability = true; - break; - default: - throw exception("Unsupported controller type: {}", type); + void NpadManager::Activate() { + if (styles.raw == 0) { + styles.proController = true; + styles.joyconHandheld = true; } - controllerType = type; - - shmemSection.header.singleColourStatus = NpadColourReadStatus::Success; - shmemSection.header.singleColour = {0, 0}; - - shmemSection.header.dualColourStatus = NpadColourReadStatus::Success; - shmemSection.header.leftColour = {0, 0}; //TODO: make these configurable - shmemSection.header.rightColour = {0, 0}; - - shmemSection.batteryLevel[0] = constant::NpadBatteryFull; - shmemSection.batteryLevel[1] = constant::NpadBatteryFull; - shmemSection.batteryLevel[2] = constant::NpadBatteryFull; - - // Set controllers initial state - SetButtonState(NpadButton{}, NpadButtonState::Released); - - //TODO: signal npad event + at(NpadId::Player1).Connect(state.settings->GetBool("operation_mode") ? NpadControllerType::Handheld : NpadControllerType::ProController); } - NpadControllerInfo &NpadDevice::GetControllerDeviceInfo() { - switch (controllerType) { - case NpadControllerType::Handheld: - return shmemSection.handheldController; - case NpadControllerType::ProController: - default: - return shmemSection.fullKeyController; - } - } + void NpadManager::Deactivate() { + styles.raw = 0; - void NpadDevice::UpdateHeaders(NpadControllerInfo &controller, uint lastStateEntryIndex) { - controller.header.entryCount = constant::HidEntryCount; - controller.header.maxEntry = constant::HidEntryCount - 1; - controller.header.currentEntry = stateEntryIndex; - controller.header.timestamp = util::GetTimeNs(); - - memcpy(reinterpret_cast(&controller.state.at(stateEntryIndex)), reinterpret_cast(&controller.state.at(lastStateEntryIndex)), sizeof(NpadControllerState)); - - controller.state.at(stateEntryIndex).globalTimestamp++; - controller.state.at(stateEntryIndex).localTimestamp++; - controller.state.at(stateEntryIndex).status.raw = connectionState.raw; - } - - void NpadDevice::SetButtonState(NpadButton button, NpadButtonState state) { - NpadControllerInfo &controllerDeviceInfo = GetControllerDeviceInfo(); - uint lastStateEntryIndex = stateEntryIndex; - stateEntryIndex = (stateEntryIndex + 1) % constant::HidEntryCount; - - for (NpadControllerInfo &controllerInfo : {std::ref(controllerDeviceInfo), std::ref(shmemSection.systemExtController)}) { - UpdateHeaders(controllerInfo, lastStateEntryIndex); - - if (state == NpadButtonState::Pressed) - controllerInfo.state.at(stateEntryIndex).controller.buttons.raw |= button.raw; - else - controllerInfo.state.at(stateEntryIndex).controller.buttons.raw &= ~(button.raw); - } - } - - void NpadDevice::SetAxisValue(NpadAxisId axis, int value) { - NpadControllerInfo controllerDeviceInfo = GetControllerDeviceInfo(); - uint lastStateEntryIndex = stateEntryIndex; - stateEntryIndex = (stateEntryIndex + 1) % constant::HidEntryCount; - - for (NpadControllerInfo &controllerInfo : {std::ref(controllerDeviceInfo), std::ref(shmemSection.systemExtController)}) { - UpdateHeaders(controllerInfo, lastStateEntryIndex); - - switch (axis) { - case NpadAxisId::LX: - controllerInfo.state.at(stateEntryIndex).controller.leftX = value; - break; - case NpadAxisId::LY: - controllerInfo.state.at(stateEntryIndex).controller.leftY = -value; // Invert Y axis - break; - case NpadAxisId::RX: - controllerInfo.state.at(stateEntryIndex).controller.rightX = value; - break; - case NpadAxisId::RY: - controllerInfo.state.at(stateEntryIndex).controller.rightY = -value; // Invert Y axis - break; - } - } - } - - CommonNpad::CommonNpad(const DeviceState &state) : state(state) {} - - void CommonNpad::Activate() { - // Always mark controllers we support as supported - if (supportedStyles.raw == 0) { - supportedStyles.proController = true; - supportedStyles.joyconHandheld = true; - } - - for (uint i = 0; i < constant::NpadCount; i++) { - bool shouldConnect = (i == 0); // P1 - - //TODO: Read this as well as controller type from settings based off of NpadID - if (shouldConnect) { - if (state.settings->GetBool("operation_mode")) - state.input->npad.at(i)->Connect(NpadControllerType::Handheld); - else - state.input->npad.at(i)->Connect(NpadControllerType::ProController); - } - } + for (auto &npad : npads) + npad.Disconnect(); } } diff --git a/app/src/main/cpp/skyline/input/npad.h b/app/src/main/cpp/skyline/input/npad.h index d2ce9564..ba1403c3 100644 --- a/app/src/main/cpp/skyline/input/npad.h +++ b/app/src/main/cpp/skyline/input/npad.h @@ -3,155 +3,66 @@ #pragma once -#include "shared_mem.h" - -namespace skyline::input::npad { - enum class NpadAxisId { - RX, //!< Right stick X - RY, //!< Right stick Y - LX, //!< Left stick X - LY //!< Left stick Y - }; - - enum class NpadButtonState : bool { - Released = false, //!< Released button - Pressed = true //!< Pressed button - }; +#include "npad_device.h" +namespace skyline::input { /** - * @brief This enumerates all the possible IDs for an NPad (https://switchbrew.org/wiki/HID_services#NpadIdType) - */ - enum class NpadId : u32 { - Player1 = 0x0, //!< 1st Player - Player2 = 0x1, //!< 2nd Player - Player3 = 0x2, //!< 3rd Player - Player4 = 0x3, //!< 4th Player - Player5 = 0x4, //!< 5th Player - Player6 = 0x5, //!< 6th Player - Player7 = 0x6, //!< 7th Player - Player8 = 0x7, //!< 8th Player - Unknown = 0x10, //!< Unknown - Handheld = 0x20 //!< Handheld mode - }; - - /** - * @brief This enumerates all the orientations of the Joy-Con(s) - */ - enum class NpadJoyOrientation : u64 { - Vertical = 0, //!< The Joy-Con is held vertically - Horizontal = 1, //!< The Joy-Con is held horizontally - }; - /** - * @brief This holds all the NPad styles (https://switchbrew.org/wiki/HID_services#NpadStyleTag) - */ - union NpadStyleSet { - struct { - bool proController : 1; //!< The Pro Controller - bool joyconHandheld : 1; //!< Joy-Cons in handheld mode - bool joyconDual : 1; //!< Joy-Cons in a pair - bool joyconLeft : 1; //!< Left Joy-Con only - bool joyconRight : 1; //!< Right Joy-Con only - bool gamecube : 1; //!< GameCube controller - bool palma : 1; //!< Poké Ball Plus controller - bool nes : 1; //!< NES controller - bool nesHandheld : 1; //!< NES controller in handheld mode - bool snes : 1; //!< SNES controller - }; - u32 raw; - }; - static_assert(sizeof(NpadStyleSet) == 0x4); - - /** - * @brief Converts the ID of an npad to the index in shared memory - * @param id The ID of an npad - * @return The index in shared memory - */ - u32 NpadIdToIndex(NpadId id); - - /** - * @brief Converts the index in shared memory to the ID of an npad - * @param id The index in shared memory - * @return The ID of the npad - */ - NpadId IndexToNpadId(u32 index); - - class NpadDevice { + * @brief This class is used to + */ + class NpadManager { private: - NpadId id; //!< The ID of the npad - NpadControllerType controllerType{NpadControllerType::None}; //!< The type of controller - uint stateEntryIndex{}; //!< The index of the current state entry - - NpadConnectionState connectionState{}; //!< The state of the connection - NpadSection &shmemSection; //!< The section in HID shared memory for this controller + const DeviceState &state; //!< The state of the device + std::array npads; //!< An array of all the NPad devices /** - * @brief Updates headers for a new shared memory entry - * @param controller The controller to operate on - * @param lastStateEntryIndex The index of the previous state entry - */ - void UpdateHeaders(NpadControllerInfo &controller, uint lastStateEntryIndex); - - /** - * @return The controller device info appropriate for the controllers type - */ - NpadControllerInfo &GetControllerDeviceInfo(); + * @brief This translates an NPad's ID into it's index in the array + * @param id The ID of the NPad to translate + * @return The corresponding index of the NPad in the array + */ + constexpr inline size_t Translate(NpadId id) { + switch (id) { + case NpadId::Unknown: + return 8; + case NpadId::Handheld: + return 9; + default: + return static_cast(id); + } + } public: - bool supported{false}; //!< If the npad marked as supported + NpadStyleSet styles{}; //!< The styles that are supported in accordance to the host input + NpadJoyOrientation orientation{}; //!< The Joy-Con orientation to use /** - * @param shmemSection A reference to the controllers shared memory region - * @param id The ID of the npad - */ - NpadDevice(NpadSection &shmemSection, NpadId id); + * @param hid A pointer to HID Shared Memory on the host + */ + NpadManager(const DeviceState &state, input::HidSharedMemory *hid); /** - * @brief Sets the joycon assignment in shared memory - * @param assignment The assignment to set - */ - inline void SetAssignment(NpadJoyAssignment assignment) { - shmemSection.header.assignment = assignment; + * @param id The ID of the NPad to return + * @return A reference to the NPad with the specified ID + */ + constexpr inline NpadDevice& at(NpadId id) { + return npads.at(Translate(id)); } /** - * @brief Connects a controller to the guest - * @param type The type of controller to connect - */ - void Connect(NpadControllerType type); + * @param id The ID of the NPad to return + * @return A reference to the NPad with the specified ID + */ + constexpr inline NpadDevice& operator[](NpadId id) noexcept { + return npads.operator[](Translate(id)); + } /** - * @brief Disconnects the controller from the guest - */ - void Disconnect(); - - /** - * @brief Changes a button's state on the virtual controller - * @param button The button work on - * @param state Whether to release or press the button - */ - void SetButtonState(NpadButton button, NpadButtonState state); - - /** - * @brief Sets an axis to a value on the virtual controller - * @param axis The axis to change - * @param value The value to use - */ - void SetAxisValue(NpadAxisId axis, int value); - }; - - class CommonNpad { - private: - const DeviceState &state; //!< The state of the device - - public: - NpadStyleSet supportedStyles{}; //!< The supported style sets - NpadJoyOrientation orientation{NpadJoyOrientation::Unset}; //!< The Joy-Con orientation to use - - CommonNpad(const DeviceState &state); - - /** - * @brief Activates npad support + * @brief This activates the controllers */ void Activate(); + + /** + * @brief This deactivates the controllers + */ + void Deactivate(); }; } diff --git a/app/src/main/cpp/skyline/input/npad_device.cpp b/app/src/main/cpp/skyline/input/npad_device.cpp new file mode 100644 index 00000000..9197ebb4 --- /dev/null +++ b/app/src/main/cpp/skyline/input/npad_device.cpp @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include "npad_device.h" + +namespace skyline::input { + NpadDevice::NpadDevice(NpadSection §ion, NpadId id) : section(section), id(id) {} + + void NpadDevice::Connect(NpadControllerType type) { + section.header.type = NpadControllerType::None; + section.deviceType.raw = 0; + section.buttonProperties.raw = 0; + + connectionState.raw = 0; + connectionState.connected = true; + + switch (type) { + case NpadControllerType::Handheld: + section.header.type = NpadControllerType::Handheld; + section.deviceType.handheldLeft = true; + section.deviceType.handheldRight = true; + section.header.assignment = NpadJoyAssignment::Dual; + section.systemProperties.ABXYButtonOriented = true; + section.systemProperties.plusButtonCapability = true; + section.systemProperties.minusButtonCapability = true; + + connectionState.handheld = true; + connectionState.leftJoyconConnected = true; + connectionState.rightJoyconConnected = true; + connectionState.leftJoyconHandheld = true; + connectionState.rightJoyconHandheld = true; + break; + case NpadControllerType::ProController: + section.header.type = NpadControllerType::ProController; + section.deviceType.fullKey = true; + section.deviceType.joyconRight = true; + section.header.assignment = NpadJoyAssignment::Single; + section.systemProperties.ABXYButtonOriented = true; + section.systemProperties.plusButtonCapability = true; + section.systemProperties.minusButtonCapability = true; + break; + default: + throw exception("Unsupported controller type: {}", type); + } + + controllerType = type; + + section.header.singleColorStatus = NpadColorReadStatus::Success; + section.header.singleColor = {0, 0}; + + section.header.dualColorStatus = NpadColorReadStatus::Success; + section.header.leftColor = {0, 0}; //TODO: make these configurable + section.header.rightColor = {0, 0}; + + section.batteryLevel[0] = NpadBatteryLevel::Full; + section.batteryLevel[1] = NpadBatteryLevel::Full; + section.batteryLevel[2] = NpadBatteryLevel::Full; + + SetButtonState(NpadButton{}, NpadButtonState::Released); + } + + void NpadDevice::Disconnect() { + connectionState.connected = false; + + GetNextEntry(GetControllerInfo()); + GetNextEntry(section.systemExtController); + } + + NpadControllerInfo &NpadDevice::GetControllerInfo() { + switch (controllerType) { + case NpadControllerType::ProController: + return section.fullKeyController; + case NpadControllerType::Handheld: + return section.handheldController; + case NpadControllerType::JoyconDual: + return section.dualController; + case NpadControllerType::JoyconLeft: + return section.leftController; + case NpadControllerType::JoyconRight: + return section.rightController; + default: + throw exception("Cannot find corresponding section for ControllerType: {}", controllerType); + } + } + + NpadControllerState &NpadDevice::GetNextEntry(NpadControllerInfo &info) { + auto &lastEntry = info.state.at(info.header.currentEntry); + + info.header.entryCount = constant::HidEntryCount; + info.header.maxEntry = constant::HidEntryCount - 1; + info.header.currentEntry = (info.header.currentEntry != constant::HidEntryCount - 1) ? info.header.currentEntry + 1 : 0; + info.header.timestamp = util::GetTimeTicks(); + + auto &entry = info.state.at(info.header.currentEntry); + + entry.globalTimestamp = globalTimestamp; + entry.localTimestamp = lastEntry.localTimestamp + 1; + entry.buttons = lastEntry.buttons; + entry.leftX = lastEntry.leftX; + entry.leftY = lastEntry.leftY; + entry.rightX = lastEntry.rightX; + entry.rightY = lastEntry.rightY; + entry.status.raw = connectionState.raw; + + return entry; + } + + void NpadDevice::SetButtonState(NpadButton mask, NpadButtonState state) { + if (!connectionState.connected) + return; + + for (NpadControllerInfo &controllerInfo : {std::ref(GetControllerInfo()), std::ref(section.systemExtController)}) { + auto &entry = GetNextEntry(controllerInfo); + + if (state == NpadButtonState::Pressed) + entry.buttons.raw |= mask.raw; + else + entry.buttons.raw &= ~(mask.raw); + } + + globalTimestamp++; + } + + void NpadDevice::SetAxisValue(NpadAxisId axis, i32 value) { + if (!connectionState.connected) + return; + + for (NpadControllerInfo &controllerInfo : {std::ref(GetControllerInfo()), std::ref(section.systemExtController)}) { + auto &entry = GetNextEntry(controllerInfo); + + switch (axis) { + case NpadAxisId::LX: + entry.leftX = value; + break; + case NpadAxisId::LY: + entry.leftY = -value; // Invert Y axis + break; + case NpadAxisId::RX: + entry.rightX = value; + break; + case NpadAxisId::RY: + entry.rightY = -value; // Invert Y axis + break; + } + } + + globalTimestamp++; + } +} diff --git a/app/src/main/cpp/skyline/input/npad_device.h b/app/src/main/cpp/skyline/input/npad_device.h new file mode 100644 index 00000000..94a84285 --- /dev/null +++ b/app/src/main/cpp/skyline/input/npad_device.h @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "shared_mem.h" + +namespace skyline::input { + /** + * @brief This enumerates all the orientations of the Joy-Con(s) + */ + enum class NpadJoyOrientation : u64 { + Vertical = 0, //!< The Joy-Con is held vertically + Horizontal = 1, //!< The Joy-Con is held horizontally + }; + + /** + * @brief This holds all the NPad styles (https://switchbrew.org/wiki/HID_services#NpadStyleTag) + */ + union NpadStyleSet { + struct { + bool proController : 1; //!< The Pro Controller + bool joyconHandheld : 1; //!< Joy-Cons in handheld mode + bool joyconDual : 1; //!< Joy-Cons in a pair + bool joyconLeft : 1; //!< Left Joy-Con only + bool joyconRight : 1; //!< Right Joy-Con only + bool gamecube : 1; //!< GameCube controller + bool palma : 1; //!< Poké Ball Plus controller + bool nes : 1; //!< NES controller + bool nesHandheld : 1; //!< NES controller in handheld mode + bool snes : 1; //!< SNES controller + }; + u32 raw; + }; + static_assert(sizeof(NpadStyleSet) == 0x4); + + /** + * @brief This enumerates the states that a button can be in + */ + enum class NpadButtonState : bool { + Released = false, //!< The button isn't being pressed + Pressed = true //!< The button is being pressed + }; + + /** + * @brief This enumerates all of the axis on NPad controllers + */ + enum class NpadAxisId { + RX, //!< Right Stick X + RY, //!< Right Stick Y + LX, //!< Left Stick X + LY //!< Left Stick Y + }; + + /** + * @brief This enumerates all the possible IDs for an NPad (https://switchbrew.org/wiki/HID_services#NpadIdType) + */ + enum class NpadId : u32 { + Player1 = 0x0, //!< 1st Player + Player2 = 0x1, //!< 2nd Player + Player3 = 0x2, //!< 3rd Player + Player4 = 0x3, //!< 4th Player + Player5 = 0x4, //!< 5th Player + Player6 = 0x5, //!< 6th Player + Player7 = 0x6, //!< 7th Player + Player8 = 0x7, //!< 8th Player + Unknown = 0x10, //!< Unknown + Handheld = 0x20 //!< Handheld mode + }; + + class NpadDevice { + private: + NpadId id; //!< The ID of this controller + NpadControllerType controllerType{}; //!< The type of this controller + u8 globalTimestamp{}; //!< The global timestamp of the state entries + + NpadConnectionState connectionState{}; //!< The state of the connection + NpadSection §ion; //!< The section in HID shared memory for this controller + + /** + * @brief This updates the headers and creates a new entry in HID Shared Memory + * @param info The controller info of the NPad that needs to be updated + * @return The next entry that has been created with values from the last entry + */ + NpadControllerState &GetNextEntry(NpadControllerInfo &info); + + /** + * @return The NpadControllerInfo for this controller based on it's type + */ + NpadControllerInfo &GetControllerInfo(); + + public: + bool supported{false}; //!< If this specific NpadId was marked by the application as supported + + NpadDevice(NpadSection §ion, NpadId id); + + /** + * @brief This sets a Joy-Con's Assignment Mode + * @param assignment The assignment mode to set this controller to + */ + inline void SetAssignment(NpadJoyAssignment assignment) { + section.header.assignment = assignment; + } + + /** + * @brief This connects this controller to the guest + * @param type The type of controller to connect + */ + void Connect(NpadControllerType type); + + /** + * @brief This disconnects this controller from the guest + */ + void Disconnect(); + + /** + * @brief This changes the state of buttons to the specified state + * @param mask A bit-field mask of all the buttons to change + * @param state The state to change the buttons to + */ + void SetButtonState(NpadButton mask, NpadButtonState state); + + /** + * @brief This sets the value of an axis to the specified value + * @param axis The axis to set the value of + * @param value The value to set + */ + void SetAxisValue(NpadAxisId axis, i32 value); + }; +} diff --git a/app/src/main/cpp/skyline/services/hid/IAppletResource.cpp b/app/src/main/cpp/skyline/services/hid/IAppletResource.cpp index 15ec3f86..2cbc5865 100644 --- a/app/src/main/cpp/skyline/services/hid/IAppletResource.cpp +++ b/app/src/main/cpp/skyline/services/hid/IAppletResource.cpp @@ -10,7 +10,7 @@ namespace skyline::service::hid { }) {} void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto handle = state.process->InsertItem(state.input->hidKMem); + auto handle = state.process->InsertItem(state.input->kHid); state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle); response.copyHandles.push_back(handle); diff --git a/app/src/main/cpp/skyline/services/hid/IHidServer.cpp b/app/src/main/cpp/skyline/services/hid/IHidServer.cpp index 26b47198..7ee56e21 100644 --- a/app/src/main/cpp/skyline/services/hid/IHidServer.cpp +++ b/app/src/main/cpp/skyline/services/hid/IHidServer.cpp @@ -12,12 +12,13 @@ namespace skyline::service::hid { {0x64, SFUNC(IHidServer::SetSupportedNpadStyleSet)}, {0x66, SFUNC(IHidServer::SetSupportedNpadIdType)}, {0x67, SFUNC(IHidServer::ActivateNpad)}, + {0x68, SFUNC(IHidServer::DeactivateNpad)}, {0x78, SFUNC(IHidServer::SetNpadJoyHoldType)}, {0x7A, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingleByDefault)}, {0x7B, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingle)}, {0x7C, SFUNC(IHidServer::SetNpadJoyAssignmentModeDual)} }) { - state.input->commonNpad->Activate(); + state.input->npad.Activate(); } void IHidServer::CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { @@ -25,7 +26,7 @@ namespace skyline::service::hid { } void IHidServer::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto styleSet = request.Pop(); + auto styleSet = request.Pop(); state.logger->Debug("Controller Support:\nPro-Controller: {}\nJoy-Con: Handheld: {}, Dual: {}, L: {}, R: {}\nGameCube: {}\nPokeBall: {}\nNES: {}, NES Handheld: {}, SNES: {}", static_cast(styleSet.proController), static_cast(styleSet.joyconHandheld), static_cast(styleSet.joyconDual), static_cast(styleSet.joyconLeft), static_cast (styleSet.joyconRight), static_cast(styleSet.gamecube), static_cast(styleSet.palma), static_cast(styleSet.nes), static_cast(styleSet.nesHandheld), static_cast(styleSet.snes)); @@ -33,36 +34,42 @@ namespace skyline::service::hid { void IHidServer::SetSupportedNpadIdType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { const auto &buffer = request.inputBuf.at(0); - size_t numId = buffer.size / sizeof(npad::NpadId); u64 address = buffer.address; + size_t size = buffer.size / sizeof(NpadId); - for (size_t i = 0; i < numId; i++) { - auto id = state.process->GetObject(address); - state.input->npad.at(NpadIdToIndex(id))->supported = true; + for (size_t i = 0; i < size; i++) { + auto id = state.process->GetObject(address); + state.input->npad.at(id).supported = true; - address += sizeof(npad::NpadId); + address += sizeof(NpadId); } } - void IHidServer::ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {} + void IHidServer::ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + state.input->npad.Activate(); + } + + void IHidServer::DeactivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + state.input->npad.Deactivate(); + } void IHidServer::SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto appletResourceUID = request.Pop(); - state.input->commonNpad->orientation = request.Pop(); + state.input->npad.orientation = request.Pop(); } void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto id = request.Pop(); - state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Single); + auto id = request.Pop(); + state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single); } void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto id = request.Pop(); - state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Single); + auto id = request.Pop(); + state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single); } void IHidServer::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto id = request.Pop(); - state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Dual); + auto id = request.Pop(); + state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Dual); } } diff --git a/app/src/main/cpp/skyline/services/hid/IHidServer.h b/app/src/main/cpp/skyline/services/hid/IHidServer.h index ae326bc8..1a36a9f4 100644 --- a/app/src/main/cpp/skyline/services/hid/IHidServer.h +++ b/app/src/main/cpp/skyline/services/hid/IHidServer.h @@ -31,10 +31,15 @@ namespace skyline::service::hid { void SetSupportedNpadIdType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); /** - * @brief This requests the activation of a controller. This is stubbed as we don't have to activate anything. + * @brief This requests the activation of controllers (https://switchbrew.org/wiki/HID_services#ActivateNpad) */ void ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** + * @brief This requests the deactivation of controllers (https://switchbrew.org/wiki/HID_services#DeactivateNpad) + */ + void DeactivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** * @brief Sets the Joy-Con hold mode (https://switchbrew.org/wiki/HID_services#SetNpadJoyHoldType) */ diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index 4624bfc4..a6abfd10 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -200,7 +200,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { * This sets [surface] to [holder].surface and passes it into libskyline */ override fun surfaceCreated(holder : SurfaceHolder) { - Log.d("surfaceCreated", "Holder: ${holder.toString()}") + Log.d("surfaceCreated", "Holder: $holder") surface = holder.surface setSurface(surface) } @@ -209,14 +209,14 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { * This is purely used for debugging surface changes */ override fun surfaceChanged(holder : SurfaceHolder, format : Int, width : Int, height : Int) { - Log.d("surfaceChanged", "Holder: ${holder.toString()}, Format: $format, Width: $width, Height: $height") + Log.d("surfaceChanged", "Holder: $holder, Format: $format, Width: $width, Height: $height") } /** * This sets [surface] to null and passes it into libskyline */ override fun surfaceDestroyed(holder : SurfaceHolder) { - Log.d("surfaceDestroyed", "Holder: ${holder.toString()}") + Log.d("surfaceDestroyed", "Holder: $holder") surface = null setSurface(surface) }