Significantly Improve Accuracy of HID

This commit significantly increases the accuracy of the prior HID code due to testing on the Switch. It is now fully accurate in all supported scenarios, them being assignment mode, orientation, color writes and system properties. In addition, review comments were addressed and fixed in the PR.
This commit is contained in:
◱ PixelyIon 2020-08-21 00:01:32 +05:30 committed by ◱ PixelyIon
parent ee2fdbdf6a
commit 07c2f2d891
29 changed files with 326 additions and 273 deletions

View File

@ -59,7 +59,6 @@
android:configChanges="orientation|screenSize"
android:launchMode="singleInstance"
android:screenOrientation="landscape"
android:theme="@style/EmuTheme"
tools:ignore="LockedOrientationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"

View File

@ -15,7 +15,7 @@ uint FaultCount;
skyline::GroupMutex JniMtx;
skyline::u16 fps;
skyline::u32 frametime;
skyline::input::Input *input;
std::weak_ptr<skyline::input::Input> inputWeak;
void signalHandler(int signal) {
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal));
@ -52,7 +52,7 @@ 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();
inputWeak = os.state.input;
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
auto romUri = env->GetStringUTFChars(romUriJstring, nullptr);
@ -66,6 +66,8 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
logger->Error("An unknown exception has occurred");
}
inputWeak.reset();
logger->Info("Emulation has ended");
auto end = std::chrono::steady_clock::now();
@ -98,30 +100,38 @@ extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIE
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
while (input == nullptr)
asm("yield");
while (inputWeak.expired()); // If this isn't called then the guest won't know that the following host controller exists
auto input = inputWeak.lock();
std::lock_guard guard(input->npad.mutex);
input->npad.controllers[index] = skyline::input::GuestController{static_cast<skyline::input::NpadControllerType>(type), static_cast<skyline::i8>(partnerIndex)};
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_updateControllers(JNIEnv *, jobject) {
while (input == nullptr)
asm("yield");
input->npad.Update(true);
while (inputWeak.expired()); // If this isn't called then the mappings will not update unless the guest initiates an update itself
auto input = inputWeak.lock();
std::unique_lock lock(input->npad.mutex);
input->npad.Update(lock, true);
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jint state) {
if (input) {
try {
auto input = inputWeak.lock();
auto device = input->npad.controllers[index].device;
skyline::input::NpadButton button{.raw = static_cast<skyline::u64>(mask)};
if (device)
device->SetButtonState(button, static_cast<skyline::input::NpadButtonState>(state));
device->SetButtonState(button, state);
} catch (const std::bad_weak_ptr) {
// We don't mind if we miss button updates while input hasn't been initialized
}
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue(JNIEnv *, jobject, jint index, jint axis, jint value) {
if (input) {
try {
auto input = inputWeak.lock();
auto device = input->npad.controllers[index].device;
if (device)
device->SetAxisValue(static_cast<skyline::input::NpadAxisId>(axis), value);
} catch (const std::bad_weak_ptr) {
// We don't mind if we miss axis updates while input hasn't been initialized
}
}

View File

@ -18,7 +18,7 @@ namespace skyline::audio {
private:
oboe::AudioStreamBuilder builder; //!< The audio stream builder, used to open
oboe::ManagedStream outputStream; //!< The output oboe audio stream
std::vector<std::shared_ptr<audio::AudioTrack>> audioTracks; //!< A vector of shared_ptr to every open audio track
std::vector<std::shared_ptr<AudioTrack>> audioTracks; //!< A vector of shared_ptr to every open audio track
Mutex trackLock; //!< This mutex is used to ensure that audioTracks isn't modified while it is being used
public:
@ -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<audio::AudioTrack> OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback);
std::shared_ptr<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<audio::AudioTrack> &track);
void CloseTrack(std::shared_ptr<AudioTrack> &track);
/**
* @brief The callback oboe uses to get audio sample data

View File

@ -14,13 +14,13 @@ namespace skyline::input {
*/
class Input {
private:
const DeviceState &state; //!< The state of the device
const DeviceState &state;
public:
std::shared_ptr<kernel::type::KSharedMemory> kHid; //!< The kernel shared memory object for HID Shared Memory
input::HidSharedMemory *hid; //!< A pointer to HID Shared Memory on the host
HidSharedMemory *hid; //!< A pointer to HID Shared Memory on the host
input::NpadManager npad; //!< This manages all the NPad controllers
NpadManager npad; //!< This manages all the NPad controllers
Input(const DeviceState &state);
};

View File

@ -13,12 +13,15 @@ namespace skyline::input {
{*this, hid->npad[8], NpadId::Unknown}, {*this, hid->npad[9], NpadId::Handheld},
} {}
void NpadManager::Update(bool host) {
if (host)
void NpadManager::Update(std::unique_lock<std::mutex> &lock, bool host) {
if (host) {
updated = true;
else
while (!updated)
asm("yield");
} else if (!updated) {
lock.unlock();
while (!updated);
lock.lock();
return;
}
if (!activated)
return;
@ -52,19 +55,13 @@ namespace skyline::input {
style = NpadStyleSet{.raw = style.raw & styles.raw};
if (style.raw) {
if (style.proController) {
device.Connect(NpadControllerType::ProController);
if (style.proController || style.joyconHandheld || style.joyconLeft || style.joyconRight) {
device.Connect(controller.type);
controller.device = &device;
} else if (style.joyconHandheld) {
device.Connect(NpadControllerType::Handheld);
controller.device = &device;
} else if (style.joyconDual && device.GetAssignment() == NpadJoyAssignment::Dual) {
} else if (style.joyconDual && orientation == NpadJoyOrientation::Vertical && device.GetAssignment() == NpadJoyAssignment::Dual) {
device.Connect(NpadControllerType::JoyconDual);
controller.device = &device;
controllers.at(controller.partnerIndex).device = &device;
} else if (style.joyconLeft || style.joyconRight) {
device.Connect(controller.type);
controller.device = &device;
} else {
continue;
}
@ -75,8 +72,8 @@ namespace skyline::input {
// We do this to prevent triggering the event unless there's a real change in a device's style, which would be caused if we disconnected all controllers then reconnected them
for (auto &device : npads) {
bool connected = false;
for (auto &controller : controllers) {
bool connected{};
for (const auto &controller : controllers) {
if (controller.device == &device) {
connected = true;
break;
@ -88,14 +85,18 @@ namespace skyline::input {
}
void NpadManager::Activate() {
std::unique_lock lock(mutex);
supportedIds = {NpadId::Handheld, NpadId::Player1, NpadId::Player2, NpadId::Player3, NpadId::Player4, NpadId::Player5, NpadId::Player6, NpadId::Player7, NpadId::Player8};
styles = {.proController = true, .joyconHandheld = true, .joyconDual = true, .joyconLeft = true, .joyconRight = true};
activated = true;
Update();
Update(lock);
}
void NpadManager::Deactivate() {
std::unique_lock lock(mutex);
supportedIds = {};
styles = {};
activated = false;

View File

@ -6,8 +6,11 @@
#include "npad_device.h"
namespace skyline::input {
/**
* @brief A controller equivalent to a physical one connected to the Switch, it's translation into a Player (NpadDevice) is also encapsulated here
*/
struct GuestController {
NpadControllerType type{}; //!< The type of the controller
NpadControllerType type{};
i8 partnerIndex{-1}; //!< The index of a Joy-Con partner, if this has one
NpadDevice *device{nullptr}; //!< A pointer to the NpadDevice that all events from this are redirected to
};
@ -17,8 +20,7 @@ namespace skyline::input {
*/
class NpadManager {
private:
const DeviceState &state; //!< The state of the device
std::array<NpadDevice, constant::NpadCount> npads; //!< An array of all the NPad devices
const DeviceState &state;
bool activated{false}; //!< If this NpadManager is activated or not
std::atomic<bool> updated{false}; //!< If this NpadManager has been updated by the guest
@ -41,10 +43,12 @@ namespace skyline::input {
}
public:
std::array<GuestController, constant::ControllerCount> controllers; //!< An array of all the available guest controllers
std::mutex mutex; //!< This mutex must be locked before any modifications to class members
std::array<NpadDevice, constant::NpadCount> npads;
std::array<GuestController, constant::ControllerCount> controllers;
std::vector<NpadId> supportedIds; //!< The NpadId(s) that are supported by the application
NpadStyleSet styles; //!< The styles that are supported by the application
NpadJoyOrientation orientation{}; //!< The Joy-Con orientation to use
NpadJoyOrientation orientation{}; //!< The orientation all of Joy-Cons are in (This affects stick transformation for them)
/**
* @param hid A pointer to HID Shared Memory on the host
@ -52,7 +56,6 @@ namespace skyline::input {
NpadManager(const DeviceState &state, input::HidSharedMemory *hid);
/**
* @param id The ID of the NPad to return
* @return A reference to the NPad with the specified ID
*/
constexpr inline NpadDevice &at(NpadId id) {
@ -60,7 +63,6 @@ namespace skyline::input {
}
/**
* @param id The ID of the NPad to return
* @return A reference to the NPad with the specified ID
*/
constexpr inline NpadDevice &operator[](NpadId id) noexcept {
@ -69,9 +71,10 @@ namespace skyline::input {
/**
* @brief This deduces all the mappings from guest controllers -> players based on the configuration supplied by HID services and available controllers
* @param lock A unique_lock which locks the mutex in the class, it should be locked before modifications to any members and must not be passed in an unlocked state
* @param host If the update is host-initiated rather than the guest
*/
void Update(bool host = false);
void Update(std::unique_lock<std::mutex> &lock, bool host = false);
/**
* @brief This activates the mapping between guest controllers -> players, a call to this is required for function

View File

@ -8,27 +8,37 @@ namespace skyline::input {
NpadDevice::NpadDevice(NpadManager &manager, NpadSection &section, NpadId id) : manager(manager), section(section), id(id), updateEvent(std::make_shared<kernel::type::KEvent>(manager.state)) {}
void NpadDevice::Connect(NpadControllerType newType) {
if (type == newType)
if (type == newType) {
if (type == NpadControllerType::JoyconLeft || type == NpadControllerType::JoyconRight) {
switch (manager.orientation) {
case NpadJoyOrientation::Vertical:
section.systemProperties.abxyButtonsOriented = true;
section.systemProperties.slSrButtonOriented = false;
break;
case NpadJoyOrientation::Horizontal:
section.systemProperties.abxyButtonsOriented = false;
section.systemProperties.slSrButtonOriented = true;
break;
}
}
return;
}
section.header.type = NpadControllerType::None;
section.deviceType.raw = 0;
section.buttonProperties.raw = 0;
section = {};
controllerInfo = nullptr;
connectionState.raw = 0;
connectionState.connected = true;
connectionState = {.connected = true};
switch (newType) {
case NpadControllerType::ProController:
section.header.type = NpadControllerType::ProController;
section.deviceType.fullKey = true;
section.systemProperties.directionalButtonsSupported = true;
section.systemProperties.abxyButtonsOriented = true;
section.systemProperties.plusButtonCapability = true;
section.systemProperties.minusButtonCapability = true;
connectionState.connected = true;
connectionState.handheld = true;
break;
case NpadControllerType::Handheld:
@ -36,13 +46,15 @@ namespace skyline::input {
section.deviceType.handheldLeft = true;
section.deviceType.handheldRight = true;
section.systemProperties.directionalButtonsSupported = true;
section.systemProperties.abxyButtonsOriented = true;
section.systemProperties.plusButtonCapability = true;
section.systemProperties.minusButtonCapability = true;
section.systemProperties.directionalButtonsSupported = true;
connectionState.handheld = true;
connectionState.leftJoyconConnected = true;
connectionState.leftJoyconHandheld = true;
connectionState.rightJoyconConnected = true;
connectionState.rightJoyconHandheld = true;
break;
@ -52,12 +64,11 @@ namespace skyline::input {
section.deviceType.joyconRight = true;
section.header.assignment = NpadJoyAssignment::Dual;
section.systemProperties.directionalButtonsSupported = true;
section.systemProperties.abxyButtonsOriented = true;
section.systemProperties.plusButtonCapability = true;
section.systemProperties.minusButtonCapability = true;
section.systemProperties.directionalButtonsSupported = true;
connectionState.connected = true;
connectionState.leftJoyconConnected = true;
connectionState.rightJoyconConnected = true;
break;
@ -67,11 +78,14 @@ namespace skyline::input {
section.deviceType.joyconLeft = true;
section.header.assignment = NpadJoyAssignment::Single;
section.systemProperties.directionalButtonsSupported = true;
if (manager.orientation == NpadJoyOrientation::Vertical)
section.systemProperties.abxyButtonsOriented = true;
else if (manager.orientation == NpadJoyOrientation::Horizontal)
section.systemProperties.slSrButtonOriented = true;
section.systemProperties.minusButtonCapability = true;
connectionState.connected = true;
section.systemProperties.minusButtonCapability = true;
section.systemProperties.directionalButtonsSupported = true;
connectionState.leftJoyconConnected = true;
break;
@ -80,11 +94,14 @@ namespace skyline::input {
section.deviceType.joyconRight = true;
section.header.assignment = NpadJoyAssignment::Single;
if (manager.orientation == NpadJoyOrientation::Vertical)
section.systemProperties.abxyButtonsOriented = true;
else if (manager.orientation == NpadJoyOrientation::Horizontal)
section.systemProperties.slSrButtonOriented = true;
section.systemProperties.slSrButtonOriented = true;
section.systemProperties.plusButtonCapability = true;
connectionState.connected = true;
connectionState.rightJoyconConnected = true;
break;
@ -97,16 +114,20 @@ namespace skyline::input {
case NpadControllerType::JoyconLeft:
case NpadControllerType::JoyconRight:
section.header.singleColorStatus = NpadColorReadStatus::Success;
section.header.dualColorStatus = NpadColorReadStatus::Disconnected;
section.header.singleColor = {0, 0};
if (newType == NpadControllerType::ProController)
section.header.singleColor = {0xFF2D2D2D, 0xFFE6E6E6}; // Normal Pro-Controller
else
section.header.singleColor = {0x4655F5, 0x00000A}; // Blue Joy-Con (https://switchbrew.org/wiki/Joy-Con#Colors)
break;
case NpadControllerType::Handheld:
case NpadControllerType::JoyconDual:
section.header.singleColorStatus = NpadColorReadStatus::Disconnected;
section.header.dualColorStatus = NpadColorReadStatus::Success;
section.header.leftColor = {0, 0};
section.header.rightColor = {0, 0};
section.header.leftColor = {0x4655F5, 0x00000A};
section.header.rightColor = {0x4655F5, 0x00000A};
section.header.singleColorStatus = NpadColorReadStatus::Success; // Single color is also written for dual controllers
section.header.singleColor = section.header.leftColor; // and is set to the color of the left JC
break;
case NpadControllerType::None:
@ -120,6 +141,10 @@ namespace skyline::input {
type = newType;
controllerInfo = &GetControllerInfo();
GetNextEntry(*controllerInfo);
GetNextEntry(section.defaultController);
globalTimestamp++;
updateEvent->Signal();
}
@ -128,7 +153,6 @@ namespace skyline::input {
return;
section = {};
explicitAssignment = false;
globalTimestamp = 0;
type = NpadControllerType::None;
@ -175,18 +199,18 @@ namespace skyline::input {
return entry;
}
void NpadDevice::SetButtonState(NpadButton mask, NpadButtonState state) {
if (!connectionState.connected || !controllerInfo)
void NpadDevice::SetButtonState(NpadButton mask, bool pressed) {
if (!connectionState.connected)
return;
auto &entry = GetNextEntry(*controllerInfo);
if (state == NpadButtonState::Pressed)
if (pressed)
entry.buttons.raw |= mask.raw;
else
entry.buttons.raw &= ~mask.raw;
if ((type == NpadControllerType::JoyconLeft || type == NpadControllerType::JoyconRight) && manager.orientation == NpadJoyOrientation::Horizontal) {
if (manager.orientation == NpadJoyOrientation::Horizontal && (type == NpadControllerType::JoyconLeft || type == NpadControllerType::JoyconRight)) {
NpadButton orientedMask{};
if (mask.dpadUp)
@ -219,75 +243,108 @@ namespace skyline::input {
mask = orientedMask;
}
for (NpadControllerState &controllerEntry : {std::ref(GetNextEntry(section.defaultController)), std::ref(GetNextEntry(section.digitalController))})
if (state == NpadButtonState::Pressed)
controllerEntry.buttons.raw |= mask.raw;
auto &defaultEntry = GetNextEntry(section.defaultController);
if (pressed)
defaultEntry.buttons.raw |= mask.raw;
else
controllerEntry.buttons.raw &= ~mask.raw;
defaultEntry.buttons.raw &= ~mask.raw;
globalTimestamp++;
}
void NpadDevice::SetAxisValue(NpadAxisId axis, i32 value) {
if (!connectionState.connected || !controllerInfo)
if (!connectionState.connected)
return;
auto &controllerEntry = GetNextEntry(*controllerInfo);
auto &defaultEntry = GetNextEntry(section.defaultController);
if (manager.orientation == NpadJoyOrientation::Vertical && (type != NpadControllerType::JoyconLeft && type != NpadControllerType::JoyconRight)) {
constexpr i16 threshold = std::numeric_limits<i16>::max() / 2; // A 50% deadzone for the stick buttons
if (manager.orientation == NpadJoyOrientation::Vertical || (type != NpadControllerType::JoyconLeft && type != NpadControllerType::JoyconRight)) {
switch (axis) {
case NpadAxisId::LX:
controllerEntry.leftX = value;
defaultEntry.leftX = value;
controllerEntry.buttons.leftStickLeft = controllerEntry.leftX <= -threshold;
defaultEntry.buttons.leftStickLeft = controllerEntry.buttons.leftStickLeft;
controllerEntry.buttons.leftStickRight = controllerEntry.leftX >= threshold;
defaultEntry.buttons.leftStickRight = controllerEntry.buttons.leftStickRight;
break;
case NpadAxisId::LY:
controllerEntry.leftY = value;
defaultEntry.leftY = value;
defaultEntry.buttons.leftStickUp = controllerEntry.buttons.leftStickUp;
controllerEntry.buttons.leftStickUp = controllerEntry.leftY >= threshold;
controllerEntry.buttons.leftStickDown = controllerEntry.leftY <= -threshold;
defaultEntry.buttons.leftStickDown = controllerEntry.buttons.leftStickDown;
break;
case NpadAxisId::RX:
controllerEntry.rightX = value;
defaultEntry.rightX = value;
controllerEntry.buttons.rightStickLeft = controllerEntry.rightX <= -threshold;
defaultEntry.buttons.rightStickLeft = controllerEntry.buttons.rightStickLeft;
controllerEntry.buttons.rightStickRight = controllerEntry.rightX >= threshold;
defaultEntry.buttons.rightStickRight = controllerEntry.buttons.rightStickRight;
break;
case NpadAxisId::RY:
controllerEntry.rightY = value;
defaultEntry.rightY = value;
controllerEntry.buttons.rightStickUp = controllerEntry.rightY >= threshold;
defaultEntry.buttons.rightStickUp = controllerEntry.buttons.rightStickUp;
controllerEntry.buttons.rightStickDown = controllerEntry.rightY <= -threshold;
defaultEntry.buttons.rightStickDown = controllerEntry.buttons.rightStickDown;
break;
}
} else {
switch (axis) {
case NpadAxisId::LX:
controllerEntry.leftX = value;
defaultEntry.leftY = value;
controllerEntry.leftY = value;
controllerEntry.buttons.leftStickUp = controllerEntry.leftY >= threshold;
controllerEntry.buttons.leftStickDown = controllerEntry.leftY <= -threshold;
defaultEntry.leftX = value;
defaultEntry.buttons.leftStickLeft = defaultEntry.leftX <= -threshold;
defaultEntry.buttons.leftStickRight = defaultEntry.leftX >= threshold;
break;
case NpadAxisId::LY:
controllerEntry.leftY = value;
defaultEntry.leftX = -value;
controllerEntry.leftX = -value;
controllerEntry.buttons.leftStickLeft = controllerEntry.leftX <= -threshold;
controllerEntry.buttons.leftStickRight = controllerEntry.leftX >= threshold;
defaultEntry.leftY = value;
defaultEntry.buttons.leftStickUp = defaultEntry.leftY >= threshold;
defaultEntry.buttons.leftStickDown = defaultEntry.leftY <= -threshold;
break;
case NpadAxisId::RX:
controllerEntry.rightX = value;
defaultEntry.rightY = value;
controllerEntry.rightY = value;
controllerEntry.buttons.rightStickUp = controllerEntry.rightY >= threshold;
controllerEntry.buttons.rightStickDown = controllerEntry.rightY <= -threshold;
defaultEntry.rightX = value;
defaultEntry.buttons.rightStickLeft = defaultEntry.rightX <= -threshold;
defaultEntry.buttons.rightStickRight = defaultEntry.rightX >= threshold;
break;
case NpadAxisId::RY:
controllerEntry.rightY = value;
defaultEntry.rightX = -value;
controllerEntry.rightX = -value;
controllerEntry.buttons.rightStickLeft = controllerEntry.rightX <= -threshold;
controllerEntry.buttons.rightStickRight = controllerEntry.rightX >= threshold;
defaultEntry.rightY = value;
defaultEntry.buttons.rightStickUp = defaultEntry.rightY >= threshold;
defaultEntry.buttons.rightStickDown = defaultEntry.rightY <= -threshold;
break;
}
}
auto &digitalEntry = GetNextEntry(section.digitalController);
constexpr i16 threshold = 3276; // A 10% deadzone for the stick
digitalEntry.buttons.leftStickUp = defaultEntry.leftY >= threshold;
digitalEntry.buttons.leftStickDown = defaultEntry.leftY <= -threshold;
digitalEntry.buttons.leftStickLeft = defaultEntry.leftX <= -threshold;
digitalEntry.buttons.leftStickRight = defaultEntry.leftX >= threshold;
digitalEntry.buttons.rightStickUp = defaultEntry.rightY >= threshold;
digitalEntry.buttons.rightStickDown = defaultEntry.rightY <= -threshold;
digitalEntry.buttons.rightStickLeft = defaultEntry.rightX <= -threshold;
digitalEntry.buttons.rightStickRight = defaultEntry.rightX >= threshold;
globalTimestamp++;
}
}

View File

@ -8,20 +8,20 @@
namespace skyline::input {
/**
* @brief This enumerates all the orientations of the Joy-Con(s)
* @brief This enumerates the orientations the Joy-Con(s) can be held in
*/
enum class NpadJoyOrientation : i64 {
Vertical = 0, //!< The Joy-Con is held vertically (Default)
Horizontal = 1, //!< The Joy-Con is held horizontally
Vertical = 0,
Horizontal = 1,
};
/**
* @brief This holds all the NPad styles (https://switchbrew.org/wiki/HID_services#NpadStyleTag)
* @brief A union of all the NPad styles (https://switchbrew.org/wiki/HID_services#NpadStyleTag)
*/
union NpadStyleSet {
u32 raw;
struct {
bool proController : 1; //!< The Pro Controller
bool proController : 1; //!< 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
@ -36,15 +36,7 @@ namespace skyline::input {
static_assert(sizeof(NpadStyleSet) == 0x4);
/**
* @brief This enumerates the states that a button can be in
*/
enum class NpadButtonState : bool {
Released = false, //!< The button isn't being pressed
Pressed = true //!< The button is being pressed
};
/**
* @brief This enumerates all of the axis on NPad controllers
* @brief This enumerates all of the axis on NPads
*/
enum class NpadAxisId {
LX, //!< Left Stick X
@ -57,16 +49,16 @@ namespace skyline::input {
* @brief This enumerates all the possible IDs for an NPad (https://switchbrew.org/wiki/HID_services#NpadIdType)
*/
enum class NpadId : u32 {
Player1 = 0x0, //!< 1st Player
Player2 = 0x1, //!< 2nd Player
Player3 = 0x2, //!< 3rd Player
Player4 = 0x3, //!< 4th Player
Player5 = 0x4, //!< 5th Player
Player6 = 0x5, //!< 6th Player
Player7 = 0x6, //!< 7th Player
Player8 = 0x7, //!< 8th Player
Unknown = 0x10, //!< Unknown
Handheld = 0x20 //!< Handheld mode
Player1 = 0x0,
Player2 = 0x1,
Player3 = 0x2,
Player4 = 0x3,
Player5 = 0x4,
Player6 = 0x5,
Player7 = 0x6,
Player8 = 0x7,
Unknown = 0x10,
Handheld = 0x20,
};
class NpadManager;
@ -78,8 +70,8 @@ namespace skyline::input {
private:
NpadManager &manager; //!< The manager responsible for managing this NpadDevice
NpadSection &section; //!< The section in HID shared memory for this controller
NpadControllerInfo *controllerInfo; //!< The controller info for this controller's type
u64 globalTimestamp{}; //!< The global timestamp of the state entries
NpadControllerInfo *controllerInfo; //!< The NpadControllerInfo for this controller's type
u64 globalTimestamp{}; //!< An incrementing timestamp that's common across all sections
/**
* @brief This updates the headers and creates a new entry in HID Shared Memory
@ -94,26 +86,19 @@ namespace skyline::input {
NpadControllerInfo &GetControllerInfo();
public:
NpadId id; //!< The ID of this controller
NpadControllerType type{}; //!< The type of this controller
NpadConnectionState connectionState{}; //!< The state of the connection
NpadId id;
NpadControllerType type{};
NpadConnectionState connectionState{};
std::shared_ptr<kernel::type::KEvent> updateEvent; //!< This event is triggered on the controller's style changing
bool explicitAssignment{false}; //!< If an assignment has explicitly been set or is the default for this controller
NpadDevice(NpadManager &manager, NpadSection &section, NpadId id);
/**
* @brief This sets a Joy-Con's Assignment Mode
* @param assignment The assignment mode to set this controller to
* @param isDefault If this is setting the default assignment mode of the controller
*/
inline void SetAssignment(NpadJoyAssignment assignment, bool isDefault) {
if (!isDefault) {
inline void SetAssignment(NpadJoyAssignment assignment) {
section.header.assignment = assignment;
explicitAssignment = true;
} else if (!explicitAssignment) {
section.header.assignment = assignment;
}
}
/**
@ -137,9 +122,9 @@ namespace skyline::input {
/**
* @brief This changes the state of buttons to the specified state
* @param mask A bit-field mask of all the buttons to change
* @param state The state to change the buttons to
* @param pressed If the buttons were pressed or released
*/
void SetButtonState(NpadButton mask, NpadButtonState state);
void SetButtonState(NpadButton mask, bool pressed);
/**
* @brief This sets the value of an axis to the specified value

View File

@ -19,8 +19,8 @@ namespace skyline::input {
* @brief The structure of the BasicXpad section (https://switchbrew.org/wiki/HID_Shared_Memory#BasicXpad)
*/
struct BasicXpadSection {
CommonHeader header; //!< The header for this section
std::array<BasicXpadState, constant::HidEntryCount> entries; //!< An array of all of the entries
CommonHeader header;
std::array<BasicXpadState, constant::HidEntryCount> entries;
u64 _pad_[0x27];
};
static_assert(sizeof(BasicXpadSection) == 0x400);

View File

@ -19,8 +19,8 @@ namespace skyline::input {
* @brief The section structure for all Button sections (HomeButton, SleepButton, CaptureButton)
*/
struct ButtonSection {
CommonHeader header; //!< The header for this section
std::array<ButtonState, constant::HidEntryCount> entries; //!< An array of all of the entries
CommonHeader header;
std::array<ButtonState, constant::HidEntryCount> entries;
u64 _pad_[0x9];
};
static_assert(sizeof(ButtonSection) == 0x200);

View File

@ -7,15 +7,15 @@
namespace skyline::input {
/**
* @brief The structure of the ConsoleSixAxisSensor section (https://switchbrew.org/wiki/HID_Shared_Memory#ConsoleSixAxisSensor)
* @brief The structure of the ConsoleSixAxisSensor (SevenSixAxisSensor) section (https://switchbrew.org/wiki/HID_Shared_Memory#ConsoleSixAxisSensor)
* @note This is seemingly used to calibrate the gyroscope bias values over time
*/
struct ConsoleSixAxisSensorSection {
u64 timestamp; //!< The timestamp in samples
bool resting; //!< If the sensors are at rest or not (The calibration is performed when the devices are at rest)
bool resting; //!< If the sensors are at rest or not (Calibration is performed when the sensors are at rest)
u8 _pad0_[0x3];
u32 verticalizationError; //!< The error in verticalization ?
u32 gyroBias[3]; //!< The gyroscope's sensor bias in all axis
u32 verticalizationError; //!< The error in sensor fusion
std::array<u32, 3> gyroBias; //!< The gyroscope's sensor bias in all axis
u32 _pad1_;
};
static_assert(sizeof(ConsoleSixAxisSensorSection) == 0x20);

View File

@ -19,8 +19,8 @@ namespace skyline::input {
* @brief The structure of the DebugPad section (https://switchbrew.org/wiki/HID_Shared_Memory#DebugPad)
*/
struct DebugPadSection {
CommonHeader header; //!< The header for this section
std::array<DebugPadState, constant::HidEntryCount> entries; //!< An array of all of the entries
CommonHeader header;
std::array<DebugPadState, constant::HidEntryCount> entries;
u64 _pad_[0x27];
};
static_assert(sizeof(DebugPadSection) == 0x400);

View File

@ -19,8 +19,8 @@ namespace skyline::input {
* @brief The structure of the Gesture section (https://switchbrew.org/wiki/HID_Shared_Memory#Gesture)
*/
struct GestureSection {
CommonHeader header; //!< The header for this section
std::array<GestureState, constant::HidEntryCount> entries; //!< An array of all of the entries
CommonHeader header;
std::array<GestureState, constant::HidEntryCount> entries;
u64 _pad_[0x1F];
};
static_assert(sizeof(GestureSection) == 0x800);

View File

@ -19,8 +19,8 @@ namespace skyline::input {
* @brief The structure of the InputDetector section (https://switchbrew.org/wiki/HID_Shared_Memory#InputDetector)
*/
struct InputDetectorSection {
CommonHeader header; //!< The header for this section
std::array<InputDetectorState, 2> entries; //!< An array of all of the entries
CommonHeader header;
std::array<InputDetectorState, 2> entries;
u64 _pad_[0x6];
};
static_assert(sizeof(InputDetectorSection) == 0x80);

View File

@ -12,17 +12,17 @@ namespace skyline::input {
union ModifierKey {
u64 raw;
struct {
bool LControl : 1; //!< Left Control Key
bool LShift : 1; //!< Left Shift Key
bool LAlt : 1; //!< Left Alt Key
bool LWindows : 1; //!< Left Windows Key
bool RControl : 1; //!< Right Control Key
bool RShift : 1; //!< Right Shift Key
bool RAlt : 1; //!< Right Alt Key
bool RWindows : 1; //!< Right Windows Key
bool CapsLock : 1; //!< Caps-Lock Key
bool ScrLock : 1; //!< Scroll-Lock Key
bool NumLock : 1; //!< Num-Lock Key
bool lControl : 1; //!< Left Control Key
bool lShift : 1; //!< Left Shift Key
bool lAlt : 1; //!< Left Alt Key
bool lWindows : 1; //!< Left Windows Key
bool rControl : 1; //!< Right Control Key
bool rShift : 1; //!< Right Shift Key
bool rAlt : 1; //!< Right Alt Key
bool rWindows : 1; //!< Right Windows Key
bool capsLock : 1; //!< Caps-Lock Key
bool scrLock : 1; //!< Scroll-Lock Key
bool numLock : 1; //!< Num-Lock Key
};
};

View File

@ -30,8 +30,8 @@ namespace skyline::input {
* @brief The structure of the Mouse section (https://switchbrew.org/wiki/HID_Shared_Memory#Mouse)
*/
struct MouseSection {
CommonHeader header; //!< The header for this section
std::array<MouseState, constant::HidEntryCount> entries; //!< An array of all of the entries
CommonHeader header;
std::array<MouseState, constant::HidEntryCount> entries;
u64 _pad_[0x16];
};
static_assert(sizeof(MouseSection) == 0x400);

View File

@ -11,12 +11,12 @@ namespace skyline::input {
* @brief This enumerates all of the types of an NPad controller
*/
enum class NpadControllerType : u32 {
None = 0, //!< No Controller
ProController = 0b1, //!< Pro Controller
Handheld = 0b10, //!< Handheld Controllers
JoyconDual = 0b100, //!< Dual Joy-Cons Controller
JoyconLeft = 0b1000, //!< Left Joy-Con Controller
JoyconRight = 0b10000, //!< Right Joy-Con Controller
None = 0,
ProController = 0b1,
Handheld = 0b10,
JoyconDual = 0b100,
JoyconLeft = 0b1000,
JoyconRight = 0b10000,
};
// @fmt:on
@ -24,8 +24,8 @@ namespace skyline::input {
* @brief This enumerates all the possible assignments of the Joy-Con(s)
*/
enum class NpadJoyAssignment : u32 {
Dual = 0, //!< Dual Joy-Cons (Default)
Single = 1, //!< Single Joy-Con
Dual = 0, //!< Dual Joy-Cons (A pair of Joy-Cons are combined into a single player, if possible)
Single = 1, //!< Single Joy-Con (A single Joy-Con translates into a single player)
};
/**
@ -41,8 +41,8 @@ namespace skyline::input {
* @brief This structure stores the color of a controller
*/
struct NpadColor {
u32 bodyColor; //!< The color of the controller's body
u32 buttonColor; //!< The color of the controller's buttons
u32 bodyColor; //!< The color of the controller's body (This isn't always accurate and sometimes has magic values, especially with the Pro Controller)
u32 buttonColor; //!< The color of the controller's buttons (Same as above)
};
static_assert(sizeof(NpadColor) == 0x8);
@ -50,13 +50,13 @@ namespace skyline::input {
* @brief The structure of the NPad headers (https://switchbrew.org/wiki/HID_Shared_Memory#NpadStateHeader)
*/
struct NpadHeader {
NpadControllerType type; //!< The type of this controller
NpadJoyAssignment assignment; //!< The Joy-Con assignment of this controller
NpadControllerType type;
NpadJoyAssignment assignment;
NpadColorReadStatus singleColorStatus; //!< The status of reading color from a single controller (Single Joy-Con or Pro Controller)
NpadColorReadStatus singleColorStatus{NpadColorReadStatus::Disconnected}; //!< The status of reading color from a single controller (Single Joy-Con or Pro Controller)
NpadColor singleColor; //!< The color of the single controller
NpadColorReadStatus dualColorStatus; //!< The status of reading color from dual controllers (Dual Joy-Cons)
NpadColorReadStatus dualColorStatus{NpadColorReadStatus::Disconnected}; //!< The status of reading color from dual controllers (Dual Joy-Cons)
NpadColor rightColor; //!< The color of the right Joy-Con
NpadColor leftColor; //!< The color of the left Joy-Con
};
@ -106,11 +106,11 @@ namespace skyline::input {
union NpadConnectionState {
u64 raw;
struct {
bool connected : 1; //!< If the controller is connected (Not in handheld mode)
bool handheld : 1; //!< If the controller is in handheld mode
bool leftJoyconConnected : 1; //!< If the left Joy-Con is connected (Not in handheld mode)
bool connected : 1; //!< If the controller is connected
bool handheld : 1; //!< If both Joy-Cons are in handheld mode (or a Pro Controller)
bool leftJoyconConnected : 1; //!< If the left Joy-Con is connected
bool leftJoyconHandheld : 1; //!< If the left Joy-Con is handheld
bool rightJoyconConnected : 1; //!< If the right Joy-Con is connected (Not in handheld mode)
bool rightJoyconConnected : 1; //!< If the right Joy-Con is connected
bool rightJoyconHandheld : 1; //!< If the right Joy-Con is handheld
};
};
@ -131,7 +131,7 @@ namespace skyline::input {
i32 rightX; //!< The right stick X (32768 to -32768)
i32 rightY; //!< The right stick Y (32768 to -32768)
NpadConnectionState status; //!< The connection status of the controller
NpadConnectionState status;
};
static_assert(sizeof(NpadControllerState) == 0x30);
@ -139,8 +139,8 @@ namespace skyline::input {
* @brief This structure contains the header and entries for the controller input
*/
struct NpadControllerInfo {
CommonHeader header; //!< The header for this controller
std::array<NpadControllerState, constant::HidEntryCount> state; //!< An array of all of the entries
CommonHeader header;
std::array<NpadControllerState, constant::HidEntryCount> state;
};
static_assert(sizeof(NpadControllerInfo) == 0x350);
@ -162,9 +162,9 @@ namespace skyline::input {
u64 _unk0_;
u64 localTimestamp; //!< The local timestamp in samples
SixaxisVector accelerometer; //!< The data from the accelerometer
SixaxisVector gyroscope; //!< The data from the gyroscope
SixaxisVector _unk1_; //!< The data from an unknown sensor
SixaxisVector accelerometer;
SixaxisVector gyroscope;
SixaxisVector rotation;
std::array<SixaxisVector, 3> orientation; //!< The orientation basis data as a matrix
u64 _unk2_; //!< This is always 1
@ -175,8 +175,8 @@ namespace skyline::input {
* @brief This structure contains header and entries for the IMU (Sixaxis) data
*/
struct NpadSixaxisInfo {
CommonHeader header; //!< The header for the controller's IMU data
std::array<NpadSixaxisState, constant::HidEntryCount> state; //!< An array of all of the entries
CommonHeader header;
std::array<NpadSixaxisState, constant::HidEntryCount> state;
};
static_assert(sizeof(NpadSixaxisInfo) == 0x708);
@ -220,25 +220,25 @@ namespace skyline::input {
bool singlePowerConnected : 1; //!< If a single unit is connected to a power source (Handheld, Pro-Con)
bool leftPowerConnected : 1; //!< If the left Joy-Con is connected to a power source
bool rightPowerConnected : 1; //!< If the right Joy-Con is connected to a power source
u64 _unk_ : 3;
u8 _unk_ : 3;
bool unsupportedButtonPressedSystem : 1; //!< If an unsupported buttons was pressed on system controller
bool unsupportedButtonPressedSystemExt : 1; //!< If an unsupported buttons was pressed on system external controller
bool abxyButtonsOriented : 1; //!< If the ABXY Buttons oriented
bool slSrButtonOriented : 1; //!< If the SL/SR Buttons oriented
bool abxyButtonsOriented : 1; //!< If the controller is oriented so that ABXY buttons are oriented correctly (Vertical for Joy-Cons)
bool slSrButtonOriented : 1; //!< If the Joy-Con is oriented so that the SL/SR Buttons are accessible (Horizontal)
bool plusButtonCapability : 1; //!< If the + button exists
bool minusButtonCapability : 1; //!< If the - button exists
bool directionalButtonsSupported : 1; //!< If the controller has a D-Pad
bool directionalButtonsSupported : 1; //!< If the controller has explicit directional buttons (Not a HAT like on the Pro Controller)
};
};
static_assert(sizeof(NpadSystemProperties) == 0x8);
/**
* @brief This structure holds data about the System Buttons (Home, Sleep and Capture) on an NPad (https://switchbrew.org/wiki/HID_Shared_Memory#NpadSystemButtonProperties)
* @brief This structure holds properties regarding the System Buttons (Home, Sleep and Capture) on an NPad (https://switchbrew.org/wiki/HID_Shared_Memory#NpadSystemButtonProperties)
*/
union NpadSystemButtonProperties {
u32 raw;
struct {
bool unintendedHomeButtonInputProtectionEnabled : 1; //!< If the Unintended Home Button Input Protection is enabled or not
bool unintendedHomeButtonInputProtectionEnabled : 1;
};
};
static_assert(sizeof(NpadSystemButtonProperties) == 0x4);
@ -247,40 +247,40 @@ namespace skyline::input {
* @brief This enumerates all the possible values for the NPad's battery level
*/
enum class NpadBatteryLevel : u32 {
Empty = 0, //!< The battery is empty
Low = 1, //!< The battery is nearly empty
Medium = 2, //!< The battery is fairly full
High = 3, //!< The battery is almost full
Full = 4, //!< The battery is 100% full
Empty = 0,
Low = 1,
Medium = 2,
High = 3,
Full = 4,
};
/**
* @brief The structure of the Npad section (https://switchbrew.org/wiki/HID_Shared_Memory#NpadState)
*/
struct NpadSection {
NpadHeader header; //!< The header for this section
NpadHeader header;
NpadControllerInfo fullKeyController; //!< The Pro/GC controller data
NpadControllerInfo handheldController; //!< The Handheld controller data
NpadControllerInfo dualController; //!< The Dual Joy-Con controller data (Only in Dual Mode, no input rotation based on rotation)
NpadControllerInfo leftController; //!< The Left Joy-Con controller data (Only in Single Mode, no input rotation based on rotation)
NpadControllerInfo rightController; //!< The Right Joy-Con controller data (Only in Single Mode, no input rotation based on rotation)
NpadControllerInfo digitalController; //!< The Default Digital controller data (Same as Default but Analog Sticks are converted into 8-directional Digital Sticks)
NpadControllerInfo palmaController; //!< The Poké Ball Plus controller data
NpadControllerInfo defaultController; //!< The Default controller data (Inputs are rotated based on orientation and SL/SR are mapped to L/R incase it is a single JC)
NpadSixaxisInfo fullKeySixaxis; //!< The Pro/GC IMU data
NpadSixaxisInfo handheldSixaxis; //!< The Handheld IMU data
NpadSixaxisInfo dualLeftSixaxis; //!< The Left Joy-Con in dual mode IMU data
NpadSixaxisInfo dualRightSixaxis; //!< The Left Joy-Con in dual mode IMU data
NpadSixaxisInfo dualLeftSixaxis; //!< The Left Joy-Con in dual mode's IMU data
NpadSixaxisInfo dualRightSixaxis; //!< The Left Joy-Con in dual mode's IMU data
NpadSixaxisInfo leftSixaxis; //!< The Left Joy-Con IMU data
NpadSixaxisInfo rightSixaxis; //!< The Right Joy-Con IMU data
NpadDeviceType deviceType; //!< The device type of this controller
NpadDeviceType deviceType;
u32 _pad0_;
NpadSystemProperties systemProperties; //!< The system properties of this controller
NpadSystemButtonProperties buttonProperties; //!< The system button properties of this controller
NpadSystemProperties systemProperties;
NpadSystemButtonProperties buttonProperties;
NpadBatteryLevel singleBatteryLevel; //!< The battery level of a single unit (Handheld, Pro-Con)
NpadBatteryLevel leftBatteryLevel; //!< The battery level of the left Joy-Con
NpadBatteryLevel rightBatteryLevel; //!< The battery level of the right Joy-Con

View File

@ -42,8 +42,8 @@ namespace skyline::input {
* @brief The structure of the TouchScreen section (https://switchbrew.org/wiki/HID_Shared_Memory#TouchScreen)
*/
struct TouchScreenSection {
CommonHeader header; //!< The header for this section
std::array<TouchScreenState, constant::HidEntryCount> entries; //!< An array of all of the entries
CommonHeader header;
std::array<TouchScreenState, constant::HidEntryCount> entries;
u64 _pad_[0x79];
};
static_assert(sizeof(TouchScreenSection) == 0x3000);

View File

@ -10,7 +10,7 @@ namespace skyline {
namespace constant {
constexpr u8 HidEntryCount = 17; //!< The amount of entries in each HID device
constexpr u8 NpadCount = 10; //!< The amount of NPads in shared memory
constexpr u8 ControllerCount = 8; //!< The maximum amount of host controllers
constexpr u8 ControllerCount = 8; //!< The maximum amount of guest controllers
constexpr u32 NpadBatteryFull = 2; //!< The full battery state of an npad
}

View File

@ -89,10 +89,10 @@ namespace skyline {
try {
while (true) {
std::lock_guard guard(JniMtx);
if (!Halt) {
if (Halt)
break;
state.gpu->Loop();
}
}
} catch (const std::exception &e) {
state.logger->Error(e.what());
} catch (...) {

View File

@ -27,7 +27,7 @@ namespace skyline::kernel {
throw exception("Unsupported ROM extension.");
}
auto process = CreateProcess(constant::BaseAddress, 0, constant::DefStackSize);
process = CreateProcess(constant::BaseAddress, 0, constant::DefStackSize);
state.loader->LoadProcessData(process, state);
process->InitializeMemory();
process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread
@ -50,9 +50,7 @@ namespace skyline::kernel {
throw exception("Call to clone() has failed: {}", strerror(errno));
state.logger->Debug("Successfully created process with PID: {}", pid);
process = std::make_shared<kernel::type::KProcess>(state, pid, argument, stack, tlsMem);
return process;
return std::make_shared<kernel::type::KProcess>(state, pid, argument, stack, tlsMem);
}
void OS::KillThread(pid_t pid) {

View File

@ -29,8 +29,9 @@ namespace skyline::service::hid {
void IHidServer::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto styleSet = request.Pop<NpadStyleSet>();
std::unique_lock lock(state.input->npad.mutex);
state.input->npad.styles = styleSet;
state.input->npad.Update();
state.input->npad.Update(lock);
state.logger->Debug("Controller Support:\nPro-Controller: {}\nJoy-Con: Handheld: {}, Dual: {}, L: {}, R: {}\nGameCube: {}\nPokeBall: {}\nNES: {}, NES Handheld: {}, SNES: {}", static_cast<bool>(styleSet.proController), static_cast<bool>(styleSet.joyconHandheld), static_cast<bool>(styleSet.joyconDual), static_cast<bool>(styleSet.joyconLeft), static_cast<bool>
(styleSet.joyconRight), static_cast<bool>(styleSet.gamecube), static_cast<bool>(styleSet.palma), static_cast<bool>(styleSet.nes), static_cast<bool>(styleSet.nesHandheld), static_cast<bool>(styleSet.snes));
@ -51,8 +52,9 @@ namespace skyline::service::hid {
address += sizeof(NpadId);
}
std::unique_lock lock(state.input->npad.mutex);
state.input->npad.supportedIds = supportedIds;
state.input->npad.Update();
state.input->npad.Update(lock);
}
void IHidServer::ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
@ -73,7 +75,10 @@ namespace skyline::service::hid {
}
void IHidServer::SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::unique_lock lock(state.input->npad.mutex);
request.Skip<u64>();
state.input->npad.orientation = request.Pop<NpadJoyOrientation>();
state.input->npad.Update(lock);
}
void IHidServer::GetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
@ -82,17 +87,22 @@ namespace skyline::service::hid {
void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>();
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single, true);
state.input->npad.Update();
std::unique_lock lock(state.input->npad.mutex);
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single);
state.input->npad.Update(lock);
}
void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>();
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single, false);
std::unique_lock lock(state.input->npad.mutex);
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single);
state.input->npad.Update(lock);
}
void IHidServer::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>();
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Dual, false);
std::unique_lock lock(state.input->npad.mutex);
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Dual);
state.input->npad.Update(lock);
}
}

View File

@ -9,7 +9,7 @@ namespace skyline::service::settings {
{0x3, SFUNC(ISystemSettingsServer::GetFirmwareVersion)}}) {}
void ISystemSettingsServer::GetFirmwareVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
SysVerTitle title{.major=9, .minor=0, .micro=0, .revMajor=4, .revMinor=0, .platform="NX", .verHash="4de65c071fd0869695b7629f75eb97b2551dbf2f", .dispVer="9.0.0", .dispTitle="NintendoSDK Firmware for NX 9.0.0-4.0"};
SysVerTitle title{.major=5, .minor=0, .micro=0, .revMajor=4, .revMinor=0, .platform="NX", .verHash="4de65c071fd0869695b7629f75eb97b2551dbf2f", .dispVer="9.0.0", .dispTitle="NintendoSDK Firmware for NX 9.0.0-4.0"};
state.process->WriteMemory(title, request.outputBuf.at(0).address);
}
}

View File

@ -39,7 +39,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
/**
* The [InputManager] class handles loading/saving the input data
*/
lateinit var input : InputManager
private lateinit var input : InputManager
/**
* A boolean flag denoting the current operation mode of the emulator (Docked = true/Handheld = false)
@ -104,11 +104,14 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
* @param index The arbitrary index of the controller, this is to handle matching with a partner Joy-Con
* @param type The type of the host controller
* @param partnerIndex The index of a partner Joy-Con if there is one
* @note This is blocking and will stall till input has been initialized on the guest
*/
private external fun setController(index : Int, type : Int, partnerIndex : Int = -1)
/**
* This flushes the controller updates on the guest
*
* @note This is blocking and will stall till input has been initialized on the guest
*/
private external fun updateControllers()
@ -172,7 +175,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
runOnUiThread { initializeControllers() }
executeApplication(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, applicationContext.filesDir.canonicalPath + "/")
executeApplication(rom.toString(), romType, romFd.fd, preferenceFd.fd, applicationContext.filesDir.canonicalPath + "/")
if (shouldFinish)
runOnUiThread { finish() }
@ -195,7 +198,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
window.insetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
} else {
@Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@ -225,7 +228,8 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
operationMode = sharedPreferences.getBoolean("operation_mode", operationMode)
windowManager.defaultDisplay.supportedModes.maxBy { it.refreshRate + (it.physicalHeight * it.physicalWidth) }?.let { window.attributes.preferredDisplayModeId = it.modeId }
@Suppress("DEPRECATION") val display = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) display!! else windowManager.defaultDisplay
display?.supportedModes?.maxBy { it.refreshRate + (it.physicalHeight * it.physicalWidth) }?.let { window.attributes.preferredDisplayModeId = it.modeId }
executeApplication(intent.data!!)
}

View File

@ -184,9 +184,8 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
override fun onScrolled(recyclerView : RecyclerView, dx : Int, dy : Int) {
y += dy
if (!app_list.isInTouchMode) {
if (!app_list.isInTouchMode)
toolbar_layout.setExpanded(y == 0)
}
super.onScrolled(recyclerView, dx, dy)
}

View File

@ -56,7 +56,7 @@ class SettingsActivity : AppCompatActivity() {
public override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (refreshKey != null) {
refreshKey?.let {
inputManager.syncObjects()
preferenceFragment.refreshPreference(refreshKey!!)

View File

@ -15,7 +15,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.snackbar.Snackbar.SnackbarLayout
/**
* Custom linear layout with support for [CoordinatorLayout] to move children, when [com.google.android.material.snackbar.Snackbar] shows up.
* Custom linear layout with support for [CoordinatorLayout] to move children, when [com.google.android.material.snackbar.Snackbar] shows up
*/
class CustomLinearLayout : LinearLayout, CoordinatorLayout.AttachedBehavior {
constructor(context : Context) : this(context, null)
@ -26,16 +26,9 @@ class CustomLinearLayout : LinearLayout, CoordinatorLayout.AttachedBehavior {
override fun requestFocus(direction: Int, previouslyFocusedRect: Rect): Boolean = getChildAt(if (direction == View.FOCUS_UP) childCount - 1 else 0 )?.requestFocus() ?: false
/*
override fun onRequestFocusInDescendants(dir : Int, rect : Rect?) : Boolean {
Log.i("DESC", "$dir and $rect")
return getChildAt(0).requestFocus()
}
*/
/**
* Defines behaviour when [com.google.android.material.snackbar.Snackbar] is shown.
* Simply sets an offset to y translation to move children out of the way.
* Defines behaviour when [com.google.android.material.snackbar.Snackbar] is shown
* Simply sets an offset to y translation to move children out of the way
*/
class MoveUpwardBehavior : CoordinatorLayout.Behavior<CustomLinearLayout>() {
override fun layoutDependsOn(parent : CoordinatorLayout, child : CustomLinearLayout, dependency : View) : Boolean = dependency is SnackbarLayout

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"

View File

@ -9,11 +9,6 @@
<item name="colorOnSecondary">@color/colorOnSecondary</item>
</style>
<style name="EmuTheme" parent="AppTheme">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
<style name="AppTheme.ActionBar" parent="">
<item name="android:textColorPrimary">@color/colorOnPrimary</item>
<item name="android:textColorSecondary">@color/colorOnPrimary</item>