Initial C++ Input Implementation

This commit contains the C++ side of the initial Input implementation, this is based on the work done in the `hid` branch in `bylaws/skyline`.
Co-authored-by: ◱ PixelyIon <pixelyion@protonmail.com>
This commit is contained in:
Billy Laws 2020-04-26 05:04:35 +05:30 committed by ◱ PixelyIon
parent 817d37600e
commit 0219eda2db
17 changed files with 949 additions and 111 deletions

30
.idea/jarRepositories.xml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
</component>
</project>

View File

@ -45,6 +45,8 @@ add_library(skyline SHARED
${source_DIR}/skyline/gpu/syncpoint.cpp
${source_DIR}/skyline/gpu/texture.cpp
${source_DIR}/skyline/gpu/engines/maxwell_3d.cpp
${source_DIR}/skyline/input.cpp
${source_DIR}/skyline/input/npad.cpp
${source_DIR}/skyline/os.cpp
${source_DIR}/skyline/loader/loader.cpp
${source_DIR}/skyline/loader/nro.cpp

View File

@ -7,6 +7,7 @@
#include "skyline/common.h"
#include "skyline/os.h"
#include "skyline/jvm.h"
#include "skyline/input.h"
bool Halt;
jobject Surface;
@ -14,6 +15,7 @@ uint FaultCount;
skyline::GroupMutex JniMtx;
skyline::u16 fps;
skyline::u32 frametime;
skyline::input::Input *input;
void signalHandler(int signal) {
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal));
@ -50,30 +52,33 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
try {
skyline::kernel::OS os(jvmManager, logger, settings, std::string(appFilesPath));
input = os.state.input.get();
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
auto romUri = env->GetStringUTFChars(romUriJstring, nullptr);
logger->Info("Launching ROM {}", romUri);
env->ReleaseStringUTFChars(romUriJstring, romUri);
os.Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
} catch (std::exception &e) {
logger->Error(e.what());
} catch (...) {
logger->Error("An unknown exception has occurred");
}
logger->Info("Emulation has ended");
auto end = std::chrono::steady_clock::now();
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setHalt(JNIEnv *env, jobject instance, jboolean halt) {
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setHalt(JNIEnv *, jobject, jboolean halt) {
JniMtx.lock(skyline::GroupMutex::Group::Group2);
Halt = halt;
JniMtx.unlock();
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *env, jobject instance, jobject surface) {
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *env, jobject, jobject surface) {
JniMtx.lock(skyline::GroupMutex::Group::Group2);
if (!env->IsSameObject(Surface, nullptr))
env->DeleteGlobalRef(Surface);
@ -84,10 +89,19 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *
JniMtx.unlock();
}
extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *env, jobject thiz) {
extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *, jobject ) {
return fps;
}
extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *env, jobject thiz) {
extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *, jobject ) {
return static_cast<float>(frametime) / 100;
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jlong id, jint state) {
skyline::input::npad::NpadButton button{.raw = static_cast<skyline::u64>(id)};
input->npad[0]->SetButtonState(button, static_cast<skyline::input::npad::NpadButtonState>(state));
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue(JNIEnv *, jobject, jint id, jint value) {
input->npad[0]->SetAxisValue(static_cast<skyline::input::npad::NpadAxisId>(id), value);
}

View File

@ -31,13 +31,13 @@ namespace skyline::audio {
* @param releaseCallback The callback to call when a buffer has been released
* @return A shared pointer to a new AudioTrack object
*/
std::shared_ptr<AudioTrack> OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback);
std::shared_ptr<audio::AudioTrack> OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback);
/**
* @brief Closes a track and frees its data
* @param track The track to close
*/
void CloseTrack(std::shared_ptr<AudioTrack> &track);
void CloseTrack(std::shared_ptr<audio::AudioTrack> &track);
/**
* @brief The callback oboe uses to get audio sample data

View File

@ -1,12 +1,13 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <tinyxml2.h>
#include "common.h"
#include "nce.h"
#include "gpu.h"
#include "audio.h"
#include <kernel/types/KThread.h>
#include <tinyxml2.h>
#include "input.h"
#include "kernel/types/KThread.h"
namespace skyline {
void Mutex::lock() {
@ -154,6 +155,7 @@ namespace skyline {
nce = std::make_shared<NCE>(*this);
gpu = std::make_shared<gpu::GPU>(*this);
audio = std::make_shared<audio::Audio>(*this);
input = std::make_shared<input::Input>(*this);
}
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = nullptr;

View File

@ -364,6 +364,9 @@ namespace skyline {
namespace audio {
class Audio;
}
namespace input {
class Input;
}
namespace loader {
class Loader;
}
@ -381,6 +384,7 @@ namespace skyline {
std::shared_ptr<NCE> nce; //!< This holds a reference to the NCE class
std::shared_ptr<gpu::GPU> gpu; //!< This holds a reference to the GPU class
std::shared_ptr<audio::Audio> audio; //!< This holds a reference to the Audio class
std::shared_ptr<input::Input> input; //!< This holds a reference to the Input class
std::shared_ptr<loader::Loader> loader; //!< This holds a reference to the Loader class
std::shared_ptr<JvmManager> jvm; //!< This holds a reference to the JvmManager class
std::shared_ptr<Settings> settings; //!< This holds a reference to the Settings class

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#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));
}
}
}

View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "common.h"
#include "kernel/types/KSharedMemory.h"
#include "input/common.h"
namespace skyline::input {
/**
* @brief The Input class manages input devices
*/
class Input {
private:
const DeviceState &state; //!< The state of the device
public:
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
};
}

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "npad.h"
namespace skyline::input {
/**
* @brief Encapsulates hid shared memory
*/
struct HidSharedMemory {
DebugPadSection debugPad; //!< The debug pad section
TouchScreenSection touchScreen; //!< The touch screen section
MouseSection mouse; //!< The mouse section
KeyboardSection keyboard; //!< The keyboard section
std::array<BasicXpadSection, 4> xpad; //!< The xpads section
HomeButtonSection homeButton; //!< The home button section
SleepButtonSection sleepButton; //!< The sleep button section
CaptureButtonSection captureButton; //!< The capture button section
std::array<InputDetectorSection, 16> inputDetector; //!< The input detectors section
u64 _pad0_[0x80 * 0x10]; //!< Unique pad (<5.0.0)
std::array<npad::NpadSection, constant::NpadCount> npad; //!< The npads section
GestureSection gesture; //!< The gesture section
ConsoleSixAxisSensorSection consoleSixAxisSensor; //!< The gyro/accel section
u64 _pad1_[0x7BC];
};
static_assert(sizeof(HidSharedMemory) == 0x40000);
}

View File

@ -0,0 +1,177 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#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);
//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;
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);
}
}
}
}

View File

@ -0,0 +1,397 @@
// 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 {
namespace constant {
constexpr u32 NpadBatteryFull = 2; //!< The full battery state of an npad
constexpr u8 NpadCount = 10; //!< The number of npads in shared memory
}
namespace input::npad {
union NpadButton {
struct {
bool a : 1; //!< The A button
bool b : 1; //!< The B button
bool x : 1; //!< The X button
bool y : 1; //!< The Y button
bool l3 : 1; //!< The L3 button
bool r3 : 1; //!< The R3 button
bool l : 1; //!< The L trigger
bool r : 1; //!< The R button
bool zl : 1; //!< The ZL trigger
bool zr : 1; //!< The ZR trigger
bool plus : 1; //!< The + button
bool minus : 1; //!< The - button
bool dpadLeft : 1; //!< D-Pad left
bool dpadUp : 1; //!< D-Pad up
bool dpadRight : 1; //!< D-Pad right
bool dpadDown : 1; //!< D-Pad down
bool leftStickLeft : 1; //!< Left stick left
bool leftStickUp : 1; //!< Left stick up
bool leftStickRight : 1; //!< Left stick right
bool leftStickDown : 1; //!< Left stick down
bool rightStickLeft : 1; //!< Right stick left
bool rightStickUp : 1; //!< Right stick up
bool rightStickRight : 1; //!< Right stick right
bool rightStickDown : 1; //!< Right stick down
bool leftSL : 1; //!< Left Joy-Con SL button
bool leftSr : 1; //!< Left Joy-Con SR button
bool rightSl : 1; //!< Right Joy-Con SL button
bool rightSr : 1; //!< Right Joy-Con SR button
bool touch : 1; //!< The touch button
};
u64 raw;
};
static_assert(sizeof(NpadButton) == 0x8);
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
};
/**
* @brief This holds the controller styles supported
*/
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 holds a Controller's ID (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 denotes the orientation 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
Unset = 2 //!< Not set
};
/**
* @brief This denotes the assignment of the Joy-Con(s)
*/
enum class NpadJoyAssignment : u32 {
Dual = 0, //!< Dual Joy-Cons
Single = 1, //!< Single Joy-Con
Unset = 2 //!< Not set
};
/**
* @brief This denotes the colour read status of an npad
*/
enum class NpadColourReadStatus : u32 {
Success = 0, //!< Success
Invalid = 1, //!< Invalid color
Disconnected = 2 //!< Controller not connected
};
/**
* @brief This denotes the type of an npad
*/
enum class NpadControllerType {
None, //!< Nothing
ProController, //!< A Pro Controller
Handheld, //!< Handheld mode
JoyconDual, //!< Dual Joy-Cons
JoyconLeft, //!< Left Joy-Con
JoyconRight, //!< Right Joy-Con
Palma, //!< Poké Ball Plus
};
/**
* @brief This denotes the connection state of an npad
*/
union NpadConnectionState {
struct {
bool connected : 1; //!< Is connected
bool handheld : 1; //!< Is in handheld mode
bool leftJoyconConnected : 1; //!< Is the left Joy-Con connected
bool leftJoyconHandheld : 1; //!< Is the left Joy-Con handheld
bool rightJoyconConnected : 1; //!< Is the right Joy-Con connected
bool rightJoyconHandheld : 1; //!< Is the right Joy-Con handheld
};
u64 raw;
};
static_assert(sizeof(NpadConnectionState) == 0x8);
/**
* @brief This denotes the device type of an npad
*/
union NpadDeviceType {
struct {
bool fullKey : 1; //!< Pro/GC controller
bool handheld : 1; //!< Handheld mode
bool handheldLeft : 1; //!< Joy-Con/Famicom/NES left controller
bool handheldRight : 1; //!< Joy-Con/Famicom/NES right controller
bool joyconLeft : 1; //!< Left Joy-Con
bool joyconRight : 1; //!< Right Joy-Con
bool palma : 1; //!< Pokeball Plus controller
bool larkLeftFamicom : 1; //!< Famicom left Joy-Con
bool larkRightFamicom : 1;//!< Famicom right Joy-Con
bool larkLeftNES : 1; //!< NES left Joy-Con
bool larkRightNES : 1; //!< NES right Joy-Con
u32 _unk1_ : 4;
bool systemExt : 1; //!< Generic external controller
u32 _unk2_ : 14;
bool system : 1; //!< Generic controller
};
u32 raw;
};
static_assert(sizeof(NpadDeviceType) == 0x4);
/**
* @brief This denotes the system properties of the npad
*/
union NpadSystemProperties {
struct {
bool powerInfo0Charging : 1; //!< Info 0 Charging
bool powerInfo1Charging : 1; //!< Info 1 Charging
bool powerInfo2Charging : 1; //!< Info 2 Charging
bool powerInfo0PowerConnected : 1; //!< Info 0 Connected
bool powerInfo1PowerConnected : 1; //!< Info 1 Connected
bool powerInfo2PowerConnected : 1; //!< Info 2 Connected
u64 _unk_ : 3;
bool unsupportedButtonPressedSystem : 1; //!< Unsupported buttons are pressed on system controller
bool unsupportedButtonPressedSystemExt : 1; //!< Unsupported buttons are pressed on system external controller
bool ABXYButtonOriented : 1; //!< Are the ABXY Buttons oriented
bool SLSRuttonOriented : 1; //!< Are the SLSR Buttons oriented
bool plusButtonCapability : 1; //!< Does the + button exist
bool minusButtonCapability : 1; //!< Does the - button exist
bool directionalButtonsSupported : 1; //!< Does the controller have a D-Pad
};
u64 raw;
};
static_assert(sizeof(NpadSystemProperties) == 0x8);
/**
* @brief This denotes the system button properties of the npad
*/
union NpadSystemButtonProperties {
struct {
bool unintendedHomeButtonInputProtectionEnabled : 1; //!< Is unintended home button input protection enabled
};
u32 raw;
};
static_assert(sizeof(NpadSystemButtonProperties) == 0x4);
/**
* @brief This denotes the colour of an npad
*/
struct NpadColour {
u32 bodyColour; //!< The body colour
u32 buttonColour; //!< The button colour
};
static_assert(sizeof(NpadColour) == 0x8);
/**
* @brief This is the header of an npad entry
*/
struct NpadHeader {
NpadStyleSet styles; //!< The style set
NpadJoyAssignment assignment; //!< The pad assignment
NpadColourReadStatus singleColourStatus; //!< The single colour status
NpadColour singleColour; //!< The colours
NpadColourReadStatus dualColourStatus; //!< The dual colour status
NpadColour rightColour; //!< The right colours
NpadColour leftColour; //!< The left colours
};
static_assert(sizeof(NpadHeader) == 0x28);
/**
* @brief This contains controller input data
*/
struct NpadController {
NpadButton buttons; //!< The pressed buttons
u32 leftX; //!< The left stick X
u32 leftY; //!< The left stick Y
u32 rightX; //!< The right stick X
u32 rightY; //!< The right stick Y
};
static_assert(sizeof(NpadController) == 0x18);
/**
* @brief This contains info about controller input data
*/
struct NpadControllerState {
u64 globalTimestamp; //!< The global timestamp
u64 localTimestamp; //!< The local timestamp
NpadController controller; //!< The npad controller
NpadConnectionState status; //!< The npad connection status
};
static_assert(sizeof(NpadControllerState) == 0x30);
/**
* @brief This contains all the input states
*/
struct NpadControllerInfo {
CommonHeader header; //!< The common data header
std::array<NpadControllerState, 17> state; //!< The npad state
};
static_assert(sizeof(NpadControllerInfo) == 0x350);
/**
* @brief An npad section in shared memory
*/
struct NpadSection {
NpadHeader header; //!< The npad header
NpadControllerInfo fullKeyController; //!< The full key controller
NpadControllerInfo handheldController; //!< The handheld controller
NpadControllerInfo dualController; //!< The dual Joy-Con controller
NpadControllerInfo leftController; //!< The left Joy-Con controller
NpadControllerInfo rightController; //!< The right Joy-Con controller
NpadControllerInfo pokeballController; //!< The Pokeball Plus controller
NpadControllerInfo systemExtController; //!< The system external controller
u64 _unk_[0xE1 * 6]; //!< Unused sixaxis data
NpadDeviceType deviceType; //!< The device type
u32 _pad0_;
NpadSystemProperties properties; //!< The system properties
NpadSystemButtonProperties buttonProperties; //!< The button properties
u32 batteryLevel[3]; //!< The battery level reported
u32 _pad1_[0x395];
};
static_assert(sizeof(NpadSection) == 0x5000);
/**
* @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 {
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
/**
* @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();
public:
bool supported{false}; //!< If the npad marked as supported
/**
* @param shmemSection A reference to the controllers shared memory region
* @param id The ID of the npad
*/
NpadDevice(NpadSection &shmemSection, NpadId id);
/**
* @brief Sets the joycon assignment in shared memory
* @param assignment The assignment to set
*/
inline void SetAssignment(NpadJoyAssignment assignment) {
shmemSection.header.assignment = assignment;
}
/**
* @brief Connects a controller to the guest
* @param type The type of controller to connect
*/
void Connect(NpadControllerType type);
/**
* @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
*/
void Activate();
};
}
}

View File

@ -0,0 +1,216 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <array>
#include <common.h>
namespace skyline {
namespace constant {
constexpr u8 HidEntryCount = 17; //!< The amount of state entries in each hid device
}
namespace input {
struct CommonHeader {
u64 timestamp;
u64 entryCount;
u64 currentEntry;
u64 maxEntry;
};
static_assert(sizeof(CommonHeader) == 0x20);
struct DebugPadState {
u64 timestamp;
u8 _unk_[0x20];
};
static_assert(sizeof(DebugPadState) == 0x28);
struct DebugPadSection {
CommonHeader header;
std::array<DebugPadState, constant::HidEntryCount> state;
u64 _pad_[0x27];
};
static_assert(sizeof(DebugPadSection) == 0x400);
struct TouchScreenStateData {
u64 timestamp;
u32 _pad0_;
u32 index;
u32 positionX;
u32 positionY;
u32 diameterX;
u32 diameterY;
u32 angle;
u32 _pad1_;
};
static_assert(sizeof(TouchScreenStateData) == 0x28);
struct TouchScreenState {
u64 globalTimestamp;
u64 localTimestamp;
u64 touchCount;
std::array<TouchScreenStateData, 16> data;
};
static_assert(sizeof(TouchScreenState) == 0x298);
struct TouchScreenSection {
CommonHeader header;
std::array<TouchScreenState, constant::HidEntryCount> state;
u64 _pad_[0x79];
};
static_assert(sizeof(TouchScreenSection) == 0x3000);
struct MouseState {
u64 globalTimestamp;
u64 localTimestamp;
u32 positionX;
u32 positionY;
u32 changeX;
u32 changeY;
u32 scrollChangeY;
u32 scrollChangeX;
u64 buttons;
};
static_assert(sizeof(MouseState) == 0x30);
struct MouseSection {
CommonHeader header;
std::array<MouseState, constant::HidEntryCount> state;
u64 _pad_[22];
};
static_assert(sizeof(MouseSection) == 0x400);
struct KeyboardState {
u64 globalTimestamp;
u64 localTimestamp;
u64 modifers;
u64 keysDown[4];
};
static_assert(sizeof(KeyboardState) == 0x38);
struct KeyboardSection {
CommonHeader header;
std::array<KeyboardState, constant::HidEntryCount> state;
u64 _pad_[5];
};
static_assert(sizeof(KeyboardSection) == 0x400);
struct BasicXpadState {
u64 globalTimestamp;
u64 _unk_[4];
};
static_assert(sizeof(BasicXpadState) == 0x28);
struct BasicXpadSection {
CommonHeader header;
std::array<BasicXpadState, constant::HidEntryCount> state;
u64 _pad_[39];
};
static_assert(sizeof(BasicXpadSection) == 0x400);
struct HomeButtonState {
u64 globalTimestamp;
u64 _unk_[2];
};
static_assert(sizeof(HomeButtonState) == 0x18);
struct HomeButtonSection {
CommonHeader header;
std::array<HomeButtonState, constant::HidEntryCount> state;
u64 _pad_[9];
};
static_assert(sizeof(HomeButtonSection) == 0x200);
struct SleepButtonState {
u64 globalTimestamp;
u64 _unk_[2];
};
static_assert(sizeof(SleepButtonState) == 0x18);
struct SleepButtonSection {
CommonHeader header;
std::array<SleepButtonState, constant::HidEntryCount> state;
u64 _pad_[9];
};
static_assert(sizeof(SleepButtonSection) == 0x200);
struct CaptureButtonState {
u64 globalTimestamp;
u64 _unk_[2];
};
static_assert(sizeof(CaptureButtonState) == 0x18);
struct CaptureButtonSection {
CommonHeader header;
std::array<CaptureButtonState, constant::HidEntryCount> state;
u64 _pad_[9];
};
static_assert(sizeof(CaptureButtonSection) == 0x200);
struct InputDetectorState {
u64 globalTimestamp;
u64 _unk_[2];
};
static_assert(sizeof(InputDetectorState) == 0x18);
struct InputDetectorSection {
CommonHeader header;
std::array<InputDetectorState, 2> state;
u64 _pad_[6];
};
static_assert(sizeof(InputDetectorSection) == 0x80);
struct GestureState {
u64 globalTimestamp;
u64 _unk_[12];
};
static_assert(sizeof(GestureState) == 0x68);
struct GestureSection {
CommonHeader header;
std::array<GestureState, constant::HidEntryCount> state;
u64 _pad_[31];
};
static_assert(sizeof(GestureSection) == 0x800);
struct ConsoleSixAxisSensorSection {
u64 timestamp;
bool resting : 1;
u32 _pad0_ : 3;
u32 verticalizationError;
u32 gyroBias[3];
u32 _pad1_;
};
static_assert(sizeof(ConsoleSixAxisSensorSection) == 0x20);
}
}

View File

@ -3,7 +3,7 @@
#pragma once
#include <common.h>
#include "common.h"
#include <jni.h>
namespace skyline {

View File

@ -18,10 +18,8 @@ namespace skyline::kernel {
* @brief The OS class manages the interaction between Skyline components and the underlying OS in NCE
*/
class OS {
private:
DeviceState state; //!< The state of the device
public:
DeviceState state; //!< The state of the device
std::shared_ptr<type::KProcess> process; //!< The KProcess object for the emulator, representing the guest process
service::ServiceManager serviceManager; //!< This manages all of the service functions
MemoryManager memory; //!< The MemoryManager object for this process

View File

@ -1,8 +1,11 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <input.h>
#include "IHidServer.h"
using namespace skyline::input;
namespace skyline::service::hid {
IHidServer::IHidServer(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::hid_IHidServer, "hid:IHidServer", {
{0x0, SFUNC(IHidServer::CreateAppletResource)},
@ -13,50 +16,53 @@ namespace skyline::service::hid {
{0x7A, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingleByDefault)},
{0x7B, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingle)},
{0x7C, SFUNC(IHidServer::SetNpadJoyAssignmentModeDual)}
}) {}
}) {
state.input->commonNpad->Activate();
}
void IHidServer::CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
resource = std::make_shared<IAppletResource>(state, manager);
manager.RegisterService(resource, session, response);
manager.RegisterService(SRVREG(IAppletResource), session, response);
}
void IHidServer::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto styleSet = request.Pop<StyleSet>();
auto styleSet = request.Pop<npad::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.pokeball), static_cast<bool>(styleSet.nes), static_cast<bool>(styleSet.nesHandheld), static_cast<bool>(styleSet.snes));
(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));
}
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(NpadId);
size_t numId = buffer.size / sizeof(npad::NpadId);
u64 address = buffer.address;
for (size_t i = 0; i < numId; i++) {
auto id = state.process->GetObject<NpadId>(address);
deviceMap[id] = JoyConDevice(id);
address += sizeof(NpadId);
auto id = state.process->GetObject<npad::NpadId>(address);
state.input->npad.at(NpadIdToIndex(id))->supported = true;
address += sizeof(npad::NpadId);
}
}
void IHidServer::ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {}
void IHidServer::SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
deviceMap[request.Pop<NpadId>()].assignment = JoyConAssignment::Single;
auto appletResourceUID = request.Pop<u64>();
state.input->commonNpad->orientation = request.Pop<npad::NpadJoyOrientation>();
}
void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
orientation = request.Pop<JoyConOrientation>();
auto id = request.Pop<npad::NpadId>();
state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Single);
}
void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto controllerId = request.Pop<NpadId>();
auto appletUserId = request.Pop<u64>();
deviceMap[controllerId].assignment = JoyConAssignment::Single;
deviceMap[controllerId].side = request.Pop<JoyConSide>();
auto id = request.Pop<npad::NpadId>();
state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Single);
}
void IHidServer::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
deviceMap[request.Pop<NpadId>()].assignment = JoyConAssignment::Dual;
auto id = request.Pop<npad::NpadId>();
state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Dual);
}
}

View File

@ -12,88 +12,6 @@ namespace skyline::service::hid {
* @brief IHidServer or hid service is used to access input devices (https://switchbrew.org/wiki/HID_services#hid)
*/
class IHidServer : public BaseService {
private:
/**
* @brief This holds the controller styles supported by an application
*/
struct StyleSet {
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 pokeball : 1; //!< Poké Ball Plus controller
bool nes : 1; //!< NES controller
bool nesHandheld : 1; //!< NES controller in handheld mode
bool snes : 1; //!< SNES controller
u32 _pad0_ : 22;
};
static_assert(sizeof(StyleSet) == 4);
/**
* @brief This holds a Controller's ID (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 holds a Controller's Assignment mode
*/
enum class JoyConAssignment {
Dual, //!< Dual Joy-Cons
Single, //!< Single Joy-Con
Unset //!< Not set
};
/**
* @brief This holds which Joy-Con to use Single mode (Not if SetNpadJoyAssignmentModeSingleByDefault is used)
*/
enum class JoyConSide : i64 {
Left, //!< Left Joy-Con
Right, //!< Right Joy-Con
Unset //!< Not set
};
/**
* @brief This denotes the orientation of the Joy-Con(s)
*/
enum class JoyConOrientation : u64 {
Vertical, //!< The Joy-Con is held vertically
Horizontal, //!< The Joy-Con is held horizontally
Unset //!< Not set
};
// TODO: Replace JoyConDevice with base NpadDevice class
/**
* @brief This holds the state of a single Npad device
*/
struct JoyConDevice {
NpadId id; //!< The ID of this device
JoyConAssignment assignment{JoyConAssignment::Unset}; //!< The assignment mode of this device
JoyConSide side{JoyConSide::Unset}; //!< The type of the device
JoyConDevice() : id(NpadId::Unknown) {}
JoyConDevice(const NpadId &id) : id(id) {}
};
std::shared_ptr<IAppletResource> resource{}; //!< A shared pointer to the applet resource
std::optional<StyleSet> styleSet; //!< The controller styles supported by the application
std::unordered_map<NpadId, JoyConDevice> deviceMap; //!< Mapping from a controller's ID to it's corresponding JoyConDevice
JoyConOrientation orientation{JoyConOrientation::Unset}; //!< The Orientation of the Joy-Con(s)
public:
IHidServer(const DeviceState &state, ServiceManager &manager);

View File

@ -84,6 +84,10 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
*/
private external fun getFrametime() : Float
private external fun setButtonState(id : Long, state : Int)
private external fun setAxisValue(id : Int, value : Int)
/**
* This executes the specified ROM, [preferenceFd] is assumed to be valid beforehand
*