mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-27 15:35:29 +03:00
Refactor C++ Input
This commit refactors the C++ end of Input so it'll be in line with the rest of the codebase and be ready for the extension with multiple players and controller configuration.
This commit is contained in:
parent
5fec7eefd0
commit
102f26d08e
@ -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
|
||||
|
@ -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<skyline::u64>(id)};
|
||||
input->npad[0]->SetButtonState(button, static_cast<skyline::input::npad::NpadButtonState>(state));
|
||||
skyline::input::NpadButton button{.raw = static_cast<skyline::u64>(id)};
|
||||
input->npad.at(skyline::input::NpadId::Player1).SetButtonState(button, static_cast<skyline::input::NpadButtonState>(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<skyline::input::npad::NpadAxisId>(id), value);
|
||||
input->npad.at(skyline::input::NpadId::Player1).SetAxisValue(static_cast<skyline::input::NpadAxisId>(id), value);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -4,11 +4,5 @@
|
||||
#include "input.h"
|
||||
|
||||
namespace skyline::input {
|
||||
Input::Input(const DeviceState &state) : state(state), commonNpad(std::make_shared<npad::CommonNpad>(state)), hidKMem(std::make_shared<kernel::type::KSharedMemory>(state, NULL, sizeof(HidSharedMemory), memory::Permission(true, false, false))) {
|
||||
hidMem = reinterpret_cast<HidSharedMemory *>(hidKMem->kernel.address);
|
||||
|
||||
for (uint i = 0; i < constant::NpadCount; i++) {
|
||||
npad.at(i) = std::make_shared<npad::NpadDevice>(hidMem->npad.at(i), npad::IndexToNpadId(i));
|
||||
}
|
||||
}
|
||||
Input::Input(const DeviceState &state) : state(state), kHid(std::make_shared<kernel::type::KSharedMemory>(state, NULL, sizeof(HidSharedMemory), memory::Permission(true, false, false))), hid(reinterpret_cast<HidSharedMemory *>(kHid->kernel.address)), npad(state, hid) {}
|
||||
}
|
||||
|
@ -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<kernel::type::KSharedMemory> 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<npad::CommonNpad> commonNpad; //!< The common npad device
|
||||
std::array<std::shared_ptr<npad::NpadDevice>, constant::NpadCount> npad; //!< Array of npad devices
|
||||
|
||||
std::shared_ptr<kernel::type::KSharedMemory> hidKMem; //!< The shared memory reserved for HID input
|
||||
HidSharedMemory *hidMem; //!< A pointer to the root of HID shared memory
|
||||
};
|
||||
}
|
||||
|
@ -4,174 +4,29 @@
|
||||
#include <input.h>
|
||||
#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<u32>(id);
|
||||
}
|
||||
}
|
||||
namespace skyline::input {
|
||||
|
||||
NpadId IndexToNpadId(u32 index) {
|
||||
switch (index) {
|
||||
case 8:
|
||||
return NpadId::Unknown;
|
||||
case 9:
|
||||
return NpadId::Handheld;
|
||||
default:
|
||||
return static_cast<NpadId>(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<void *>(&controller.state.at(stateEntryIndex)), reinterpret_cast<void *>(&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();
|
||||
}
|
||||
}
|
||||
|
@ -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<NpadDevice, constant::NpadCount> 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<size_t>(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();
|
||||
};
|
||||
}
|
||||
|
149
app/src/main/cpp/skyline/input/npad_device.cpp
Normal file
149
app/src/main/cpp/skyline/input/npad_device.cpp
Normal file
@ -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++;
|
||||
}
|
||||
}
|
130
app/src/main/cpp/skyline/input/npad_device.h
Normal file
130
app/src/main/cpp/skyline/input/npad_device.h
Normal file
@ -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);
|
||||
};
|
||||
}
|
@ -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<type::KSharedMemory>(state.input->hidKMem);
|
||||
auto handle = state.process->InsertItem<type::KSharedMemory>(state.input->kHid);
|
||||
state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
|
||||
|
||||
response.copyHandles.push_back(handle);
|
||||
|
@ -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<npad::NpadStyleSet>();
|
||||
auto styleSet = request.Pop<NpadStyleSet>();
|
||||
|
||||
state.logger->Debug("Controller Support:\nPro-Controller: {}\nJoy-Con: Handheld: {}, Dual: {}, L: {}, R: {}\nGameCube: {}\nPokeBall: {}\nNES: {}, NES Handheld: {}, SNES: {}", static_cast<bool>(styleSet.proController), static_cast<bool>(styleSet.joyconHandheld), static_cast<bool>(styleSet.joyconDual), static_cast<bool>(styleSet.joyconLeft), static_cast<bool>
|
||||
(styleSet.joyconRight), static_cast<bool>(styleSet.gamecube), static_cast<bool>(styleSet.palma), static_cast<bool>(styleSet.nes), static_cast<bool>(styleSet.nesHandheld), static_cast<bool>(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<npad::NpadId>(address);
|
||||
state.input->npad.at(NpadIdToIndex(id))->supported = true;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
auto id = state.process->GetObject<NpadId>(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<u64>();
|
||||
state.input->commonNpad->orientation = request.Pop<npad::NpadJoyOrientation>();
|
||||
state.input->npad.orientation = request.Pop<NpadJoyOrientation>();
|
||||
}
|
||||
|
||||
void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto id = request.Pop<npad::NpadId>();
|
||||
state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Single);
|
||||
auto id = request.Pop<NpadId>();
|
||||
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single);
|
||||
}
|
||||
|
||||
void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto id = request.Pop<npad::NpadId>();
|
||||
state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Single);
|
||||
auto id = request.Pop<NpadId>();
|
||||
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single);
|
||||
}
|
||||
|
||||
void IHidServer::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto id = request.Pop<npad::NpadId>();
|
||||
state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Dual);
|
||||
auto id = request.Pop<NpadId>();
|
||||
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Dual);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
*/
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user