Skip to content

Commit

Permalink
Refactor C++ Input
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
PixelyIon committed Aug 21, 2020
1 parent 5fec7ee commit 102f26d
Show file tree
Hide file tree
Showing 13 changed files with 391 additions and 329 deletions.
1 change: 1 addition & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/cpp/emu_jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
10 changes: 10 additions & 0 deletions app/src/main/cpp/skyline/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 1 addition & 7 deletions app/src/main/cpp/skyline/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
}
12 changes: 6 additions & 6 deletions app/src/main/cpp/skyline/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Input(const DeviceState &state);
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

std::shared_ptr<npad::CommonNpad> commonNpad; //!< The common npad device
std::array<std::shared_ptr<npad::NpadDevice>, constant::NpadCount> npad; //!< Array of npad devices
input::NpadManager npad; //!< This manages all the NPad controllers

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
Input(const DeviceState &state);
};
}
179 changes: 17 additions & 162 deletions app/src/main/cpp/skyline/input/npad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

NpadId IndexToNpadId(u32 index) {
switch (index) {
case 8:
return NpadId::Unknown;
case 9:
return NpadId::Handheld;
default:
return static_cast<NpadId>(index);
}
}

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);
}

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);
namespace skyline::input {

//TODO: signal npad event
}

NpadControllerInfo &NpadDevice::GetControllerDeviceInfo() {
switch (controllerType) {
case NpadControllerType::Handheld:
return shmemSection.handheldController;
case NpadControllerType::ProController:
default:
return shmemSection.fullKeyController;
}
}

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;
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},
} {}

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 NpadManager::Activate() {
if (styles.raw == 0) {
styles.proController = true;
styles.joyconHandheld = true;
}
}

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;
}
}
at(NpadId::Player1).Connect(state.settings->GetBool("operation_mode") ? NpadControllerType::Handheld : NpadControllerType::ProController);
}

CommonNpad::CommonNpad(const DeviceState &state) : state(state) {}
void NpadManager::Deactivate() {
styles.raw = 0;

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();
}
}
Loading

0 comments on commit 102f26d

Please sign in to comment.