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

View File

@ -15,7 +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; std::weak_ptr<skyline::input::Input> inputWeak;
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));
@ -52,7 +52,7 @@ 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(); inputWeak = os.state.input;
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath); env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
auto romUri = env->GetStringUTFChars(romUriJstring, nullptr); 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"); logger->Error("An unknown exception has occurred");
} }
inputWeak.reset();
logger->Info("Emulation has ended"); logger->Info("Emulation has ended");
auto end = std::chrono::steady_clock::now(); 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) { extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
while (input == nullptr) while (inputWeak.expired()); // If this isn't called then the guest won't know that the following host controller exists
asm("yield"); 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)}; 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) { extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_updateControllers(JNIEnv *, jobject) {
while (input == nullptr) while (inputWeak.expired()); // If this isn't called then the mappings will not update unless the guest initiates an update itself
asm("yield"); auto input = inputWeak.lock();
input->npad.Update(true); 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) { 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; auto device = input->npad.controllers[index].device;
skyline::input::NpadButton button{.raw = static_cast<skyline::u64>(mask)}; skyline::input::NpadButton button{.raw = static_cast<skyline::u64>(mask)};
if (device) 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) { 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; auto device = input->npad.controllers[index].device;
if (device) if (device)
device->SetAxisValue(static_cast<skyline::input::NpadAxisId>(axis), value); 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: private:
oboe::AudioStreamBuilder builder; //!< The audio stream builder, used to open oboe::AudioStreamBuilder builder; //!< The audio stream builder, used to open
oboe::ManagedStream outputStream; //!< The output oboe audio stream 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 Mutex trackLock; //!< This mutex is used to ensure that audioTracks isn't modified while it is being used
public: public:
@ -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<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 * @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<audio::AudioTrack> &track); void CloseTrack(std::shared_ptr<AudioTrack> &track);
/** /**
* @brief The callback oboe uses to get audio sample data * @brief The callback oboe uses to get audio sample data

View File

@ -14,13 +14,13 @@ namespace skyline::input {
*/ */
class Input { class Input {
private: private:
const DeviceState &state; //!< The state of the device const DeviceState &state;
public: public:
std::shared_ptr<kernel::type::KSharedMemory> kHid; //!< The kernel shared memory object for HID Shared Memory 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); 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}, {*this, hid->npad[8], NpadId::Unknown}, {*this, hid->npad[9], NpadId::Handheld},
} {} } {}
void NpadManager::Update(bool host) { void NpadManager::Update(std::unique_lock<std::mutex> &lock, bool host) {
if (host) if (host) {
updated = true; updated = true;
else } else if (!updated) {
while (!updated) lock.unlock();
asm("yield"); while (!updated);
lock.lock();
return;
}
if (!activated) if (!activated)
return; return;
@ -52,19 +55,13 @@ namespace skyline::input {
style = NpadStyleSet{.raw = style.raw & styles.raw}; style = NpadStyleSet{.raw = style.raw & styles.raw};
if (style.raw) { if (style.raw) {
if (style.proController) { if (style.proController || style.joyconHandheld || style.joyconLeft || style.joyconRight) {
device.Connect(NpadControllerType::ProController); device.Connect(controller.type);
controller.device = &device; controller.device = &device;
} else if (style.joyconHandheld) { } else if (style.joyconDual && orientation == NpadJoyOrientation::Vertical && device.GetAssignment() == NpadJoyAssignment::Dual) {
device.Connect(NpadControllerType::Handheld);
controller.device = &device;
} else if (style.joyconDual && device.GetAssignment() == NpadJoyAssignment::Dual) {
device.Connect(NpadControllerType::JoyconDual); device.Connect(NpadControllerType::JoyconDual);
controller.device = &device; controller.device = &device;
controllers.at(controller.partnerIndex).device = &device; controllers.at(controller.partnerIndex).device = &device;
} else if (style.joyconLeft || style.joyconRight) {
device.Connect(controller.type);
controller.device = &device;
} else { } else {
continue; 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 // 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) { for (auto &device : npads) {
bool connected = false; bool connected{};
for (auto &controller : controllers) { for (const auto &controller : controllers) {
if (controller.device == &device) { if (controller.device == &device) {
connected = true; connected = true;
break; break;
@ -88,14 +85,18 @@ namespace skyline::input {
} }
void NpadManager::Activate() { 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}; 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}; styles = {.proController = true, .joyconHandheld = true, .joyconDual = true, .joyconLeft = true, .joyconRight = true};
activated = true; activated = true;
Update(); Update(lock);
} }
void NpadManager::Deactivate() { void NpadManager::Deactivate() {
std::unique_lock lock(mutex);
supportedIds = {}; supportedIds = {};
styles = {}; styles = {};
activated = false; activated = false;

View File

@ -6,8 +6,11 @@
#include "npad_device.h" #include "npad_device.h"
namespace skyline::input { 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 { 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 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 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 { class NpadManager {
private: private:
const DeviceState &state; //!< The state of the device const DeviceState &state;
std::array<NpadDevice, constant::NpadCount> npads; //!< An array of all the NPad devices
bool activated{false}; //!< If this NpadManager is activated or not bool activated{false}; //!< If this NpadManager is activated or not
std::atomic<bool> updated{false}; //!< If this NpadManager has been updated by the guest std::atomic<bool> updated{false}; //!< If this NpadManager has been updated by the guest
@ -41,10 +43,12 @@ namespace skyline::input {
} }
public: 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 std::vector<NpadId> supportedIds; //!< The NpadId(s) that are supported by the application
NpadStyleSet styles; //!< The styles 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 * @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); 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 * @return A reference to the NPad with the specified ID
*/ */
constexpr inline NpadDevice &at(NpadId 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 * @return A reference to the NPad with the specified ID
*/ */
constexpr inline NpadDevice &operator[](NpadId id) noexcept { 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 * @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 * @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 * @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)) {} 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) { 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; return;
}
section.header.type = NpadControllerType::None; section = {};
section.deviceType.raw = 0; controllerInfo = nullptr;
section.buttonProperties.raw = 0;
connectionState.raw = 0; connectionState = {.connected = true};
connectionState.connected = true;
switch (newType) { switch (newType) {
case NpadControllerType::ProController: case NpadControllerType::ProController:
section.header.type = NpadControllerType::ProController; section.header.type = NpadControllerType::ProController;
section.deviceType.fullKey = true; section.deviceType.fullKey = true;
section.systemProperties.directionalButtonsSupported = true;
section.systemProperties.abxyButtonsOriented = true; section.systemProperties.abxyButtonsOriented = true;
section.systemProperties.plusButtonCapability = true; section.systemProperties.plusButtonCapability = true;
section.systemProperties.minusButtonCapability = true; section.systemProperties.minusButtonCapability = true;
connectionState.connected = true; connectionState.handheld = true;
break; break;
case NpadControllerType::Handheld: case NpadControllerType::Handheld:
@ -36,13 +46,15 @@ namespace skyline::input {
section.deviceType.handheldLeft = true; section.deviceType.handheldLeft = true;
section.deviceType.handheldRight = true; section.deviceType.handheldRight = true;
section.systemProperties.directionalButtonsSupported = true;
section.systemProperties.abxyButtonsOriented = true; section.systemProperties.abxyButtonsOriented = true;
section.systemProperties.plusButtonCapability = true; section.systemProperties.plusButtonCapability = true;
section.systemProperties.minusButtonCapability = true; section.systemProperties.minusButtonCapability = true;
section.systemProperties.directionalButtonsSupported = true;
connectionState.handheld = true; connectionState.handheld = true;
connectionState.leftJoyconConnected = true;
connectionState.leftJoyconHandheld = true; connectionState.leftJoyconHandheld = true;
connectionState.rightJoyconConnected = true;
connectionState.rightJoyconHandheld = true; connectionState.rightJoyconHandheld = true;
break; break;
@ -52,12 +64,11 @@ namespace skyline::input {
section.deviceType.joyconRight = true; section.deviceType.joyconRight = true;
section.header.assignment = NpadJoyAssignment::Dual; section.header.assignment = NpadJoyAssignment::Dual;
section.systemProperties.directionalButtonsSupported = true;
section.systemProperties.abxyButtonsOriented = true; section.systemProperties.abxyButtonsOriented = true;
section.systemProperties.plusButtonCapability = true; section.systemProperties.plusButtonCapability = true;
section.systemProperties.minusButtonCapability = true; section.systemProperties.minusButtonCapability = true;
section.systemProperties.directionalButtonsSupported = true;
connectionState.connected = true;
connectionState.leftJoyconConnected = true; connectionState.leftJoyconConnected = true;
connectionState.rightJoyconConnected = true; connectionState.rightJoyconConnected = true;
break; break;
@ -67,11 +78,14 @@ namespace skyline::input {
section.deviceType.joyconLeft = true; section.deviceType.joyconLeft = true;
section.header.assignment = NpadJoyAssignment::Single; 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.slSrButtonOriented = true;
section.systemProperties.minusButtonCapability = true;
connectionState.connected = true; section.systemProperties.minusButtonCapability = true;
section.systemProperties.directionalButtonsSupported = true;
connectionState.leftJoyconConnected = true; connectionState.leftJoyconConnected = true;
break; break;
@ -80,11 +94,14 @@ namespace skyline::input {
section.deviceType.joyconRight = true; section.deviceType.joyconRight = true;
section.header.assignment = NpadJoyAssignment::Single; section.header.assignment = NpadJoyAssignment::Single;
if (manager.orientation == NpadJoyOrientation::Vertical)
section.systemProperties.abxyButtonsOriented = true; section.systemProperties.abxyButtonsOriented = true;
else if (manager.orientation == NpadJoyOrientation::Horizontal)
section.systemProperties.slSrButtonOriented = true;
section.systemProperties.slSrButtonOriented = true; section.systemProperties.slSrButtonOriented = true;
section.systemProperties.plusButtonCapability = true; section.systemProperties.plusButtonCapability = true;
connectionState.connected = true;
connectionState.rightJoyconConnected = true; connectionState.rightJoyconConnected = true;
break; break;
@ -97,16 +114,20 @@ namespace skyline::input {
case NpadControllerType::JoyconLeft: case NpadControllerType::JoyconLeft:
case NpadControllerType::JoyconRight: case NpadControllerType::JoyconRight:
section.header.singleColorStatus = NpadColorReadStatus::Success; section.header.singleColorStatus = NpadColorReadStatus::Success;
section.header.dualColorStatus = NpadColorReadStatus::Disconnected; if (newType == NpadControllerType::ProController)
section.header.singleColor = {0, 0}; 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; break;
case NpadControllerType::Handheld: case NpadControllerType::Handheld:
case NpadControllerType::JoyconDual: case NpadControllerType::JoyconDual:
section.header.singleColorStatus = NpadColorReadStatus::Disconnected;
section.header.dualColorStatus = NpadColorReadStatus::Success; section.header.dualColorStatus = NpadColorReadStatus::Success;
section.header.leftColor = {0, 0}; section.header.leftColor = {0x4655F5, 0x00000A};
section.header.rightColor = {0, 0}; 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; break;
case NpadControllerType::None: case NpadControllerType::None:
@ -120,6 +141,10 @@ namespace skyline::input {
type = newType; type = newType;
controllerInfo = &GetControllerInfo(); controllerInfo = &GetControllerInfo();
GetNextEntry(*controllerInfo);
GetNextEntry(section.defaultController);
globalTimestamp++;
updateEvent->Signal(); updateEvent->Signal();
} }
@ -128,7 +153,6 @@ namespace skyline::input {
return; return;
section = {}; section = {};
explicitAssignment = false;
globalTimestamp = 0; globalTimestamp = 0;
type = NpadControllerType::None; type = NpadControllerType::None;
@ -175,18 +199,18 @@ namespace skyline::input {
return entry; return entry;
} }
void NpadDevice::SetButtonState(NpadButton mask, NpadButtonState state) { void NpadDevice::SetButtonState(NpadButton mask, bool pressed) {
if (!connectionState.connected || !controllerInfo) if (!connectionState.connected)
return; return;
auto &entry = GetNextEntry(*controllerInfo); auto &entry = GetNextEntry(*controllerInfo);
if (state == NpadButtonState::Pressed) if (pressed)
entry.buttons.raw |= mask.raw; entry.buttons.raw |= mask.raw;
else else
entry.buttons.raw &= ~mask.raw; 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{}; NpadButton orientedMask{};
if (mask.dpadUp) if (mask.dpadUp)
@ -219,75 +243,108 @@ namespace skyline::input {
mask = orientedMask; mask = orientedMask;
} }
for (NpadControllerState &controllerEntry : {std::ref(GetNextEntry(section.defaultController)), std::ref(GetNextEntry(section.digitalController))}) auto &defaultEntry = GetNextEntry(section.defaultController);
if (state == NpadButtonState::Pressed) if (pressed)
controllerEntry.buttons.raw |= mask.raw; defaultEntry.buttons.raw |= mask.raw;
else else
controllerEntry.buttons.raw &= ~mask.raw; defaultEntry.buttons.raw &= ~mask.raw;
globalTimestamp++; globalTimestamp++;
} }
void NpadDevice::SetAxisValue(NpadAxisId axis, i32 value) { void NpadDevice::SetAxisValue(NpadAxisId axis, i32 value) {
if (!connectionState.connected || !controllerInfo) if (!connectionState.connected)
return; return;
auto &controllerEntry = GetNextEntry(*controllerInfo); auto &controllerEntry = GetNextEntry(*controllerInfo);
auto &defaultEntry = GetNextEntry(section.defaultController); 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) { switch (axis) {
case NpadAxisId::LX: case NpadAxisId::LX:
controllerEntry.leftX = value; controllerEntry.leftX = value;
defaultEntry.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; break;
case NpadAxisId::LY: case NpadAxisId::LY:
controllerEntry.leftY = value; controllerEntry.leftY = value;
defaultEntry.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; break;
case NpadAxisId::RX: case NpadAxisId::RX:
controllerEntry.rightX = value; controllerEntry.rightX = value;
defaultEntry.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; break;
case NpadAxisId::RY: case NpadAxisId::RY:
controllerEntry.rightY = value; controllerEntry.rightY = value;
defaultEntry.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; break;
} }
} else { } else {
switch (axis) { switch (axis) {
case NpadAxisId::LX: case NpadAxisId::LX:
controllerEntry.leftX = value; controllerEntry.leftY = value;
defaultEntry.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; break;
case NpadAxisId::LY: case NpadAxisId::LY:
controllerEntry.leftY = value; controllerEntry.leftX = -value;
defaultEntry.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; break;
case NpadAxisId::RX: case NpadAxisId::RX:
controllerEntry.rightX = value; controllerEntry.rightY = value;
defaultEntry.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; break;
case NpadAxisId::RY: case NpadAxisId::RY:
controllerEntry.rightY = value; controllerEntry.rightX = -value;
defaultEntry.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; 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++; globalTimestamp++;
} }
} }

View File

@ -8,20 +8,20 @@
namespace skyline::input { 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 { enum class NpadJoyOrientation : i64 {
Vertical = 0, //!< The Joy-Con is held vertically (Default) Vertical = 0,
Horizontal = 1, //!< The Joy-Con is held horizontally 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 { union NpadStyleSet {
u32 raw; u32 raw;
struct { struct {
bool proController : 1; //!< The Pro Controller bool proController : 1; //!< Pro Controller
bool joyconHandheld : 1; //!< Joy-Cons in handheld mode bool joyconHandheld : 1; //!< Joy-Cons in handheld mode
bool joyconDual : 1; //!< Joy-Cons in a pair bool joyconDual : 1; //!< Joy-Cons in a pair
bool joyconLeft : 1; //!< Left Joy-Con only bool joyconLeft : 1; //!< Left Joy-Con only
@ -36,15 +36,7 @@ namespace skyline::input {
static_assert(sizeof(NpadStyleSet) == 0x4); static_assert(sizeof(NpadStyleSet) == 0x4);
/** /**
* @brief This enumerates the states that a button can be in * @brief This enumerates all of the axis on NPads
*/
enum class NpadButtonState : bool {
Released = false, //!< The button isn't being pressed
Pressed = true //!< The button is being pressed
};
/**
* @brief This enumerates all of the axis on NPad controllers
*/ */
enum class NpadAxisId { enum class NpadAxisId {
LX, //!< Left Stick X 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) * @brief This enumerates all the possible IDs for an NPad (https://switchbrew.org/wiki/HID_services#NpadIdType)
*/ */
enum class NpadId : u32 { enum class NpadId : u32 {
Player1 = 0x0, //!< 1st Player Player1 = 0x0,
Player2 = 0x1, //!< 2nd Player Player2 = 0x1,
Player3 = 0x2, //!< 3rd Player Player3 = 0x2,
Player4 = 0x3, //!< 4th Player Player4 = 0x3,
Player5 = 0x4, //!< 5th Player Player5 = 0x4,
Player6 = 0x5, //!< 6th Player Player6 = 0x5,
Player7 = 0x6, //!< 7th Player Player7 = 0x6,
Player8 = 0x7, //!< 8th Player Player8 = 0x7,
Unknown = 0x10, //!< Unknown Unknown = 0x10,
Handheld = 0x20 //!< Handheld mode Handheld = 0x20,
}; };
class NpadManager; class NpadManager;
@ -78,8 +70,8 @@ namespace skyline::input {
private: private:
NpadManager &manager; //!< The manager responsible for managing this NpadDevice NpadManager &manager; //!< The manager responsible for managing this NpadDevice
NpadSection &section; //!< The section in HID shared memory for this controller NpadSection &section; //!< The section in HID shared memory for this controller
NpadControllerInfo *controllerInfo; //!< The controller info for this controller's type NpadControllerInfo *controllerInfo; //!< The NpadControllerInfo for this controller's type
u64 globalTimestamp{}; //!< The global timestamp of the state entries 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 * @brief This updates the headers and creates a new entry in HID Shared Memory
@ -94,26 +86,19 @@ namespace skyline::input {
NpadControllerInfo &GetControllerInfo(); NpadControllerInfo &GetControllerInfo();
public: public:
NpadId id; //!< The ID of this controller NpadId id;
NpadControllerType type{}; //!< The type of this controller NpadControllerType type{};
NpadConnectionState connectionState{}; //!< The state of the connection NpadConnectionState connectionState{};
std::shared_ptr<kernel::type::KEvent> updateEvent; //!< This event is triggered on the controller's style changing 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); NpadDevice(NpadManager &manager, NpadSection &section, NpadId id);
/** /**
* @brief This sets a Joy-Con's Assignment Mode * @brief This sets a Joy-Con's Assignment Mode
* @param assignment The assignment mode to set this controller to * @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) { inline void SetAssignment(NpadJoyAssignment assignment) {
if (!isDefault) {
section.header.assignment = 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 * @brief This changes the state of buttons to the specified state
* @param mask A bit-field mask of all the buttons to change * @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 * @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) * @brief The structure of the BasicXpad section (https://switchbrew.org/wiki/HID_Shared_Memory#BasicXpad)
*/ */
struct BasicXpadSection { struct BasicXpadSection {
CommonHeader header; //!< The header for this section CommonHeader header;
std::array<BasicXpadState, constant::HidEntryCount> entries; //!< An array of all of the entries std::array<BasicXpadState, constant::HidEntryCount> entries;
u64 _pad_[0x27]; u64 _pad_[0x27];
}; };
static_assert(sizeof(BasicXpadSection) == 0x400); 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) * @brief The section structure for all Button sections (HomeButton, SleepButton, CaptureButton)
*/ */
struct ButtonSection { struct ButtonSection {
CommonHeader header; //!< The header for this section CommonHeader header;
std::array<ButtonState, constant::HidEntryCount> entries; //!< An array of all of the entries std::array<ButtonState, constant::HidEntryCount> entries;
u64 _pad_[0x9]; u64 _pad_[0x9];
}; };
static_assert(sizeof(ButtonSection) == 0x200); static_assert(sizeof(ButtonSection) == 0x200);

View File

@ -7,15 +7,15 @@
namespace skyline::input { 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 * @note This is seemingly used to calibrate the gyroscope bias values over time
*/ */
struct ConsoleSixAxisSensorSection { struct ConsoleSixAxisSensorSection {
u64 timestamp; //!< The timestamp in samples 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]; u8 _pad0_[0x3];
u32 verticalizationError; //!< The error in verticalization ? u32 verticalizationError; //!< The error in sensor fusion
u32 gyroBias[3]; //!< The gyroscope's sensor bias in all axis std::array<u32, 3> gyroBias; //!< The gyroscope's sensor bias in all axis
u32 _pad1_; u32 _pad1_;
}; };
static_assert(sizeof(ConsoleSixAxisSensorSection) == 0x20); 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) * @brief The structure of the DebugPad section (https://switchbrew.org/wiki/HID_Shared_Memory#DebugPad)
*/ */
struct DebugPadSection { struct DebugPadSection {
CommonHeader header; //!< The header for this section CommonHeader header;
std::array<DebugPadState, constant::HidEntryCount> entries; //!< An array of all of the entries std::array<DebugPadState, constant::HidEntryCount> entries;
u64 _pad_[0x27]; u64 _pad_[0x27];
}; };
static_assert(sizeof(DebugPadSection) == 0x400); 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) * @brief The structure of the Gesture section (https://switchbrew.org/wiki/HID_Shared_Memory#Gesture)
*/ */
struct GestureSection { struct GestureSection {
CommonHeader header; //!< The header for this section CommonHeader header;
std::array<GestureState, constant::HidEntryCount> entries; //!< An array of all of the entries std::array<GestureState, constant::HidEntryCount> entries;
u64 _pad_[0x1F]; u64 _pad_[0x1F];
}; };
static_assert(sizeof(GestureSection) == 0x800); 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) * @brief The structure of the InputDetector section (https://switchbrew.org/wiki/HID_Shared_Memory#InputDetector)
*/ */
struct InputDetectorSection { struct InputDetectorSection {
CommonHeader header; //!< The header for this section CommonHeader header;
std::array<InputDetectorState, 2> entries; //!< An array of all of the entries std::array<InputDetectorState, 2> entries;
u64 _pad_[0x6]; u64 _pad_[0x6];
}; };
static_assert(sizeof(InputDetectorSection) == 0x80); static_assert(sizeof(InputDetectorSection) == 0x80);

View File

@ -12,17 +12,17 @@ namespace skyline::input {
union ModifierKey { union ModifierKey {
u64 raw; u64 raw;
struct { struct {
bool LControl : 1; //!< Left Control Key bool lControl : 1; //!< Left Control Key
bool LShift : 1; //!< Left Shift Key bool lShift : 1; //!< Left Shift Key
bool LAlt : 1; //!< Left Alt Key bool lAlt : 1; //!< Left Alt Key
bool LWindows : 1; //!< Left Windows Key bool lWindows : 1; //!< Left Windows Key
bool RControl : 1; //!< Right Control Key bool rControl : 1; //!< Right Control Key
bool RShift : 1; //!< Right Shift Key bool rShift : 1; //!< Right Shift Key
bool RAlt : 1; //!< Right Alt Key bool rAlt : 1; //!< Right Alt Key
bool RWindows : 1; //!< Right Windows Key bool rWindows : 1; //!< Right Windows Key
bool CapsLock : 1; //!< Caps-Lock Key bool capsLock : 1; //!< Caps-Lock Key
bool ScrLock : 1; //!< Scroll-Lock Key bool scrLock : 1; //!< Scroll-Lock Key
bool NumLock : 1; //!< Num-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) * @brief The structure of the Mouse section (https://switchbrew.org/wiki/HID_Shared_Memory#Mouse)
*/ */
struct MouseSection { struct MouseSection {
CommonHeader header; //!< The header for this section CommonHeader header;
std::array<MouseState, constant::HidEntryCount> entries; //!< An array of all of the entries std::array<MouseState, constant::HidEntryCount> entries;
u64 _pad_[0x16]; u64 _pad_[0x16];
}; };
static_assert(sizeof(MouseSection) == 0x400); 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 * @brief This enumerates all of the types of an NPad controller
*/ */
enum class NpadControllerType : u32 { enum class NpadControllerType : u32 {
None = 0, //!< No Controller None = 0,
ProController = 0b1, //!< Pro Controller ProController = 0b1,
Handheld = 0b10, //!< Handheld Controllers Handheld = 0b10,
JoyconDual = 0b100, //!< Dual Joy-Cons Controller JoyconDual = 0b100,
JoyconLeft = 0b1000, //!< Left Joy-Con Controller JoyconLeft = 0b1000,
JoyconRight = 0b10000, //!< Right Joy-Con Controller JoyconRight = 0b10000,
}; };
// @fmt:on // @fmt:on
@ -24,8 +24,8 @@ namespace skyline::input {
* @brief This enumerates all the possible assignments of the Joy-Con(s) * @brief This enumerates all the possible assignments of the Joy-Con(s)
*/ */
enum class NpadJoyAssignment : u32 { enum class NpadJoyAssignment : u32 {
Dual = 0, //!< Dual Joy-Cons (Default) Dual = 0, //!< Dual Joy-Cons (A pair of Joy-Cons are combined into a single player, if possible)
Single = 1, //!< Single Joy-Con 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 * @brief This structure stores the color of a controller
*/ */
struct NpadColor { struct NpadColor {
u32 bodyColor; //!< The color of the controller's body 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 u32 buttonColor; //!< The color of the controller's buttons (Same as above)
}; };
static_assert(sizeof(NpadColor) == 0x8); 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) * @brief The structure of the NPad headers (https://switchbrew.org/wiki/HID_Shared_Memory#NpadStateHeader)
*/ */
struct NpadHeader { struct NpadHeader {
NpadControllerType type; //!< The type of this controller NpadControllerType type;
NpadJoyAssignment assignment; //!< The Joy-Con assignment of this controller 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 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 rightColor; //!< The color of the right Joy-Con
NpadColor leftColor; //!< The color of the left Joy-Con NpadColor leftColor; //!< The color of the left Joy-Con
}; };
@ -106,11 +106,11 @@ namespace skyline::input {
union NpadConnectionState { union NpadConnectionState {
u64 raw; u64 raw;
struct { struct {
bool connected : 1; //!< If the controller is connected (Not in handheld mode) bool connected : 1; //!< If the controller is connected
bool handheld : 1; //!< If the controller is in handheld mode 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 (Not in handheld mode) bool leftJoyconConnected : 1; //!< If the left Joy-Con is connected
bool leftJoyconHandheld : 1; //!< If the left Joy-Con is handheld 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 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 rightX; //!< The right stick X (32768 to -32768)
i32 rightY; //!< The right stick Y (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); static_assert(sizeof(NpadControllerState) == 0x30);
@ -139,8 +139,8 @@ namespace skyline::input {
* @brief This structure contains the header and entries for the controller input * @brief This structure contains the header and entries for the controller input
*/ */
struct NpadControllerInfo { struct NpadControllerInfo {
CommonHeader header; //!< The header for this controller CommonHeader header;
std::array<NpadControllerState, constant::HidEntryCount> state; //!< An array of all of the entries std::array<NpadControllerState, constant::HidEntryCount> state;
}; };
static_assert(sizeof(NpadControllerInfo) == 0x350); static_assert(sizeof(NpadControllerInfo) == 0x350);
@ -162,9 +162,9 @@ namespace skyline::input {
u64 _unk0_; u64 _unk0_;
u64 localTimestamp; //!< The local timestamp in samples u64 localTimestamp; //!< The local timestamp in samples
SixaxisVector accelerometer; //!< The data from the accelerometer SixaxisVector accelerometer;
SixaxisVector gyroscope; //!< The data from the gyroscope SixaxisVector gyroscope;
SixaxisVector _unk1_; //!< The data from an unknown sensor SixaxisVector rotation;
std::array<SixaxisVector, 3> orientation; //!< The orientation basis data as a matrix std::array<SixaxisVector, 3> orientation; //!< The orientation basis data as a matrix
u64 _unk2_; //!< This is always 1 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 * @brief This structure contains header and entries for the IMU (Sixaxis) data
*/ */
struct NpadSixaxisInfo { struct NpadSixaxisInfo {
CommonHeader header; //!< The header for the controller's IMU data CommonHeader header;
std::array<NpadSixaxisState, constant::HidEntryCount> state; //!< An array of all of the entries std::array<NpadSixaxisState, constant::HidEntryCount> state;
}; };
static_assert(sizeof(NpadSixaxisInfo) == 0x708); 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 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 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 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 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 unsupportedButtonPressedSystemExt : 1; //!< If an unsupported buttons was pressed on system external controller
bool abxyButtonsOriented : 1; //!< If the ABXY 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 SL/SR Buttons oriented 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 plusButtonCapability : 1; //!< If the + button exists
bool minusButtonCapability : 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); 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 { union NpadSystemButtonProperties {
u32 raw; u32 raw;
struct { struct {
bool unintendedHomeButtonInputProtectionEnabled : 1; //!< If the Unintended Home Button Input Protection is enabled or not bool unintendedHomeButtonInputProtectionEnabled : 1;
}; };
}; };
static_assert(sizeof(NpadSystemButtonProperties) == 0x4); 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 * @brief This enumerates all the possible values for the NPad's battery level
*/ */
enum class NpadBatteryLevel : u32 { enum class NpadBatteryLevel : u32 {
Empty = 0, //!< The battery is empty Empty = 0,
Low = 1, //!< The battery is nearly empty Low = 1,
Medium = 2, //!< The battery is fairly full Medium = 2,
High = 3, //!< The battery is almost full High = 3,
Full = 4, //!< The battery is 100% full Full = 4,
}; };
/** /**
* @brief The structure of the Npad section (https://switchbrew.org/wiki/HID_Shared_Memory#NpadState) * @brief The structure of the Npad section (https://switchbrew.org/wiki/HID_Shared_Memory#NpadState)
*/ */
struct NpadSection { struct NpadSection {
NpadHeader header; //!< The header for this section NpadHeader header;
NpadControllerInfo fullKeyController; //!< The Pro/GC controller data NpadControllerInfo fullKeyController; //!< The Pro/GC controller data
NpadControllerInfo handheldController; //!< The Handheld 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 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 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 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) 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 fullKeySixaxis; //!< The Pro/GC IMU data
NpadSixaxisInfo handheldSixaxis; //!< The Handheld IMU data NpadSixaxisInfo handheldSixaxis; //!< The Handheld IMU data
NpadSixaxisInfo dualLeftSixaxis; //!< 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 IMU data NpadSixaxisInfo dualRightSixaxis; //!< The Left Joy-Con in dual mode's IMU data
NpadSixaxisInfo leftSixaxis; //!< The Left Joy-Con IMU data NpadSixaxisInfo leftSixaxis; //!< The Left Joy-Con IMU data
NpadSixaxisInfo rightSixaxis; //!< The Right Joy-Con IMU data NpadSixaxisInfo rightSixaxis; //!< The Right Joy-Con IMU data
NpadDeviceType deviceType; //!< The device type of this controller NpadDeviceType deviceType;
u32 _pad0_; u32 _pad0_;
NpadSystemProperties systemProperties; //!< The system properties of this controller NpadSystemProperties systemProperties;
NpadSystemButtonProperties buttonProperties; //!< The system button properties of this controller NpadSystemButtonProperties buttonProperties;
NpadBatteryLevel singleBatteryLevel; //!< The battery level of a single unit (Handheld, Pro-Con) NpadBatteryLevel singleBatteryLevel; //!< The battery level of a single unit (Handheld, Pro-Con)
NpadBatteryLevel leftBatteryLevel; //!< The battery level of the left Joy-Con NpadBatteryLevel leftBatteryLevel; //!< The battery level of the left Joy-Con
NpadBatteryLevel rightBatteryLevel; //!< The battery level of the right 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) * @brief The structure of the TouchScreen section (https://switchbrew.org/wiki/HID_Shared_Memory#TouchScreen)
*/ */
struct TouchScreenSection { struct TouchScreenSection {
CommonHeader header; //!< The header for this section CommonHeader header;
std::array<TouchScreenState, constant::HidEntryCount> entries; //!< An array of all of the entries std::array<TouchScreenState, constant::HidEntryCount> entries;
u64 _pad_[0x79]; u64 _pad_[0x79];
}; };
static_assert(sizeof(TouchScreenSection) == 0x3000); static_assert(sizeof(TouchScreenSection) == 0x3000);

View File

@ -10,7 +10,7 @@ namespace skyline {
namespace constant { namespace constant {
constexpr u8 HidEntryCount = 17; //!< The amount of entries in each HID device 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 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 constexpr u32 NpadBatteryFull = 2; //!< The full battery state of an npad
} }

View File

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

View File

@ -27,7 +27,7 @@ namespace skyline::kernel {
throw exception("Unsupported ROM extension."); 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); state.loader->LoadProcessData(process, state);
process->InitializeMemory(); process->InitializeMemory();
process->threads.at(process->pid)->Start(); // The kernel itself is responsible for starting the main thread 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)); throw exception("Call to clone() has failed: {}", strerror(errno));
state.logger->Debug("Successfully created process with PID: {}", pid); state.logger->Debug("Successfully created process with PID: {}", pid);
process = std::make_shared<kernel::type::KProcess>(state, pid, argument, stack, tlsMem); return std::make_shared<kernel::type::KProcess>(state, pid, argument, stack, tlsMem);
return process;
} }
void OS::KillThread(pid_t pid) { 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) { void IHidServer::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto styleSet = request.Pop<NpadStyleSet>(); auto styleSet = request.Pop<NpadStyleSet>();
std::unique_lock lock(state.input->npad.mutex);
state.input->npad.styles = styleSet; 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> 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)); (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); address += sizeof(NpadId);
} }
std::unique_lock lock(state.input->npad.mutex);
state.input->npad.supportedIds = supportedIds; 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) { 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) { 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.orientation = request.Pop<NpadJoyOrientation>();
state.input->npad.Update(lock);
} }
void IHidServer::GetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { 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) { void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>(); auto id = request.Pop<NpadId>();
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single, true); std::unique_lock lock(state.input->npad.mutex);
state.input->npad.Update(); 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) { void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>(); 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) { void IHidServer::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>(); 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)}}) {} {0x3, SFUNC(ISystemSettingsServer::GetFirmwareVersion)}}) {}
void ISystemSettingsServer::GetFirmwareVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { 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); 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 * 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) * 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 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 type The type of the host controller
* @param partnerIndex The index of a partner Joy-Con if there is one * @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) private external fun setController(index : Int, type : Int, partnerIndex : Int = -1)
/** /**
* This flushes the controller updates on the guest * 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() private external fun updateControllers()
@ -172,7 +175,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
runOnUiThread { initializeControllers() } 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) if (shouldFinish)
runOnUiThread { finish() } runOnUiThread { finish() }
@ -195,7 +198,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
window.insetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE window.insetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
} else { } else {
@Suppress("DEPRECATION") @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_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@ -225,7 +228,8 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
operationMode = sharedPreferences.getBoolean("operation_mode", operationMode) 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!!) executeApplication(intent.data!!)
} }

View File

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

View File

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

View File

@ -15,7 +15,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.snackbar.Snackbar.SnackbarLayout 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 { class CustomLinearLayout : LinearLayout, CoordinatorLayout.AttachedBehavior {
constructor(context : Context) : this(context, null) 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 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. * 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. * Simply sets an offset to y translation to move children out of the way
*/ */
class MoveUpwardBehavior : CoordinatorLayout.Behavior<CustomLinearLayout>() { class MoveUpwardBehavior : CoordinatorLayout.Behavior<CustomLinearLayout>() {
override fun layoutDependsOn(parent : CoordinatorLayout, child : CustomLinearLayout, dependency : View) : Boolean = dependency is SnackbarLayout 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"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"

View File

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