mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 08:25:30 +03:00
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:
parent
817d37600e
commit
0219eda2db
30
.idea/jarRepositories.xml
generated
Normal file
30
.idea/jarRepositories.xml
generated
Normal 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>
|
@ -45,6 +45,8 @@ add_library(skyline SHARED
|
|||||||
${source_DIR}/skyline/gpu/syncpoint.cpp
|
${source_DIR}/skyline/gpu/syncpoint.cpp
|
||||||
${source_DIR}/skyline/gpu/texture.cpp
|
${source_DIR}/skyline/gpu/texture.cpp
|
||||||
${source_DIR}/skyline/gpu/engines/maxwell_3d.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/os.cpp
|
||||||
${source_DIR}/skyline/loader/loader.cpp
|
${source_DIR}/skyline/loader/loader.cpp
|
||||||
${source_DIR}/skyline/loader/nro.cpp
|
${source_DIR}/skyline/loader/nro.cpp
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "skyline/common.h"
|
#include "skyline/common.h"
|
||||||
#include "skyline/os.h"
|
#include "skyline/os.h"
|
||||||
#include "skyline/jvm.h"
|
#include "skyline/jvm.h"
|
||||||
|
#include "skyline/input.h"
|
||||||
|
|
||||||
bool Halt;
|
bool Halt;
|
||||||
jobject Surface;
|
jobject Surface;
|
||||||
@ -14,6 +15,7 @@ uint FaultCount;
|
|||||||
skyline::GroupMutex JniMtx;
|
skyline::GroupMutex JniMtx;
|
||||||
skyline::u16 fps;
|
skyline::u16 fps;
|
||||||
skyline::u32 frametime;
|
skyline::u32 frametime;
|
||||||
|
skyline::input::Input *input;
|
||||||
|
|
||||||
void signalHandler(int signal) {
|
void signalHandler(int signal) {
|
||||||
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(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 {
|
try {
|
||||||
skyline::kernel::OS os(jvmManager, logger, settings, std::string(appFilesPath));
|
skyline::kernel::OS os(jvmManager, logger, settings, std::string(appFilesPath));
|
||||||
|
input = os.state.input.get();
|
||||||
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
|
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
|
||||||
|
|
||||||
auto romUri = env->GetStringUTFChars(romUriJstring, nullptr);
|
auto romUri = env->GetStringUTFChars(romUriJstring, nullptr);
|
||||||
logger->Info("Launching ROM {}", romUri);
|
logger->Info("Launching ROM {}", romUri);
|
||||||
env->ReleaseStringUTFChars(romUriJstring, romUri);
|
env->ReleaseStringUTFChars(romUriJstring, romUri);
|
||||||
|
|
||||||
os.Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
|
os.Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
logger->Error(e.what());
|
logger->Error(e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
logger->Error("An unknown exception has occurred");
|
logger->Error("An unknown exception has occurred");
|
||||||
}
|
}
|
||||||
|
|
||||||
logger->Info("Emulation has ended");
|
logger->Info("Emulation has ended");
|
||||||
|
|
||||||
auto end = std::chrono::steady_clock::now();
|
auto end = std::chrono::steady_clock::now();
|
||||||
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
|
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);
|
JniMtx.lock(skyline::GroupMutex::Group::Group2);
|
||||||
Halt = halt;
|
Halt = halt;
|
||||||
JniMtx.unlock();
|
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);
|
JniMtx.lock(skyline::GroupMutex::Group::Group2);
|
||||||
if (!env->IsSameObject(Surface, nullptr))
|
if (!env->IsSameObject(Surface, nullptr))
|
||||||
env->DeleteGlobalRef(Surface);
|
env->DeleteGlobalRef(Surface);
|
||||||
@ -84,10 +89,19 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *
|
|||||||
JniMtx.unlock();
|
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;
|
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;
|
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);
|
||||||
|
}
|
||||||
|
@ -31,13 +31,13 @@ namespace skyline::audio {
|
|||||||
* @param releaseCallback The callback to call when a buffer has been released
|
* @param releaseCallback The callback to call when a buffer has been released
|
||||||
* @return A shared pointer to a new AudioTrack object
|
* @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
|
* @brief Closes a track and frees its data
|
||||||
* @param track The track to close
|
* @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
|
* @brief The callback oboe uses to get audio sample data
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <tinyxml2.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "nce.h"
|
#include "nce.h"
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include <kernel/types/KThread.h>
|
#include "input.h"
|
||||||
#include <tinyxml2.h>
|
#include "kernel/types/KThread.h"
|
||||||
|
|
||||||
namespace skyline {
|
namespace skyline {
|
||||||
void Mutex::lock() {
|
void Mutex::lock() {
|
||||||
@ -154,6 +155,7 @@ namespace skyline {
|
|||||||
nce = std::make_shared<NCE>(*this);
|
nce = std::make_shared<NCE>(*this);
|
||||||
gpu = std::make_shared<gpu::GPU>(*this);
|
gpu = std::make_shared<gpu::GPU>(*this);
|
||||||
audio = std::make_shared<audio::Audio>(*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;
|
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = nullptr;
|
||||||
|
@ -364,6 +364,9 @@ namespace skyline {
|
|||||||
namespace audio {
|
namespace audio {
|
||||||
class Audio;
|
class Audio;
|
||||||
}
|
}
|
||||||
|
namespace input {
|
||||||
|
class Input;
|
||||||
|
}
|
||||||
namespace loader {
|
namespace loader {
|
||||||
class 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<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<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<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<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<JvmManager> jvm; //!< This holds a reference to the JvmManager class
|
||||||
std::shared_ptr<Settings> settings; //!< This holds a reference to the Settings class
|
std::shared_ptr<Settings> settings; //!< This holds a reference to the Settings class
|
||||||
|
14
app/src/main/cpp/skyline/input.cpp
Normal file
14
app/src/main/cpp/skyline/input.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
app/src/main/cpp/skyline/input.h
Normal file
27
app/src/main/cpp/skyline/input.h
Normal 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
|
||||||
|
};
|
||||||
|
}
|
29
app/src/main/cpp/skyline/input/common.h
Normal file
29
app/src/main/cpp/skyline/input/common.h
Normal 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);
|
||||||
|
}
|
177
app/src/main/cpp/skyline/input/npad.cpp
Normal file
177
app/src/main/cpp/skyline/input/npad.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
397
app/src/main/cpp/skyline/input/npad.h
Normal file
397
app/src/main/cpp/skyline/input/npad.h
Normal 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();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
216
app/src/main/cpp/skyline/input/shared_mem.h
Normal file
216
app/src/main/cpp/skyline/input/shared_mem.h
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <common.h>
|
#include "common.h"
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
namespace skyline {
|
namespace skyline {
|
||||||
|
@ -18,10 +18,8 @@ namespace skyline::kernel {
|
|||||||
* @brief The OS class manages the interaction between Skyline components and the underlying OS in NCE
|
* @brief The OS class manages the interaction between Skyline components and the underlying OS in NCE
|
||||||
*/
|
*/
|
||||||
class OS {
|
class OS {
|
||||||
private:
|
|
||||||
DeviceState state; //!< The state of the device
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
DeviceState state; //!< The state of the device
|
||||||
std::shared_ptr<type::KProcess> process; //!< The KProcess object for the emulator, representing the guest process
|
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
|
service::ServiceManager serviceManager; //!< This manages all of the service functions
|
||||||
MemoryManager memory; //!< The MemoryManager object for this process
|
MemoryManager memory; //!< The MemoryManager object for this process
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <input.h>
|
||||||
#include "IHidServer.h"
|
#include "IHidServer.h"
|
||||||
|
|
||||||
|
using namespace skyline::input;
|
||||||
|
|
||||||
namespace skyline::service::hid {
|
namespace skyline::service::hid {
|
||||||
IHidServer::IHidServer(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::hid_IHidServer, "hid:IHidServer", {
|
IHidServer::IHidServer(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::hid_IHidServer, "hid:IHidServer", {
|
||||||
{0x0, SFUNC(IHidServer::CreateAppletResource)},
|
{0x0, SFUNC(IHidServer::CreateAppletResource)},
|
||||||
@ -13,50 +16,53 @@ namespace skyline::service::hid {
|
|||||||
{0x7A, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingleByDefault)},
|
{0x7A, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingleByDefault)},
|
||||||
{0x7B, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingle)},
|
{0x7B, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingle)},
|
||||||
{0x7C, SFUNC(IHidServer::SetNpadJoyAssignmentModeDual)}
|
{0x7C, SFUNC(IHidServer::SetNpadJoyAssignmentModeDual)}
|
||||||
}) {}
|
}) {
|
||||||
|
state.input->commonNpad->Activate();
|
||||||
|
}
|
||||||
|
|
||||||
void IHidServer::CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
void IHidServer::CreateAppletResource(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
resource = std::make_shared<IAppletResource>(state, manager);
|
manager.RegisterService(SRVREG(IAppletResource), session, response);
|
||||||
manager.RegisterService(resource, session, response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &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>
|
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) {
|
void IHidServer::SetSupportedNpadIdType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
const auto &buffer = request.inputBuf.at(0);
|
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;
|
u64 address = buffer.address;
|
||||||
|
|
||||||
for (size_t i = 0; i < numId; i++) {
|
for (size_t i = 0; i < numId; i++) {
|
||||||
auto id = state.process->GetObject<NpadId>(address);
|
auto id = state.process->GetObject<npad::NpadId>(address);
|
||||||
deviceMap[id] = JoyConDevice(id);
|
state.input->npad.at(NpadIdToIndex(id))->supported = true;
|
||||||
address += sizeof(NpadId);
|
|
||||||
|
address += sizeof(npad::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) {}
|
||||||
|
|
||||||
void IHidServer::SetNpadJoyHoldType(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) {
|
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) {
|
void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
auto controllerId = request.Pop<NpadId>();
|
auto id = request.Pop<npad::NpadId>();
|
||||||
auto appletUserId = request.Pop<u64>();
|
state.input->npad.at(npad::NpadIdToIndex(id))->SetAssignment(npad::NpadJoyAssignment::Single);
|
||||||
|
|
||||||
deviceMap[controllerId].assignment = JoyConAssignment::Single;
|
|
||||||
deviceMap[controllerId].side = request.Pop<JoyConSide>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
* @brief IHidServer or hid service is used to access input devices (https://switchbrew.org/wiki/HID_services#hid)
|
||||||
*/
|
*/
|
||||||
class IHidServer : public BaseService {
|
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:
|
public:
|
||||||
IHidServer(const DeviceState &state, ServiceManager &manager);
|
IHidServer(const DeviceState &state, ServiceManager &manager);
|
||||||
|
|
||||||
|
@ -84,6 +84,10 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
|||||||
*/
|
*/
|
||||||
private external fun getFrametime() : Float
|
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
|
* This executes the specified ROM, [preferenceFd] is assumed to be valid beforehand
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user