mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 09:11:52 +01:00
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:
parent
ee2fdbdf6a
commit
07c2f2d891
@ -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"
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
@ -68,10 +70,11 @@ 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
|
||||
*/
|
||||
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
|
||||
|
@ -8,27 +8,37 @@ namespace skyline::input {
|
||||
NpadDevice::NpadDevice(NpadManager &manager, NpadSection §ion, 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;
|
||||
section.systemProperties.slSrButtonOriented = true;
|
||||
section.systemProperties.minusButtonCapability = true;
|
||||
if (manager.orientation == NpadJoyOrientation::Vertical)
|
||||
section.systemProperties.abxyButtonsOriented = true;
|
||||
else if (manager.orientation == NpadJoyOrientation::Horizontal)
|
||||
section.systemProperties.slSrButtonOriented = true;
|
||||
|
||||
section.systemProperties.minusButtonCapability = true;
|
||||
section.systemProperties.directionalButtonsSupported = true;
|
||||
|
||||
connectionState.connected = true;
|
||||
connectionState.leftJoyconConnected = true;
|
||||
break;
|
||||
|
||||
@ -80,11 +94,14 @@ namespace skyline::input {
|
||||
section.deviceType.joyconRight = true;
|
||||
section.header.assignment = NpadJoyAssignment::Single;
|
||||
|
||||
section.systemProperties.abxyButtonsOriented = 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.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;
|
||||
else
|
||||
controllerEntry.buttons.raw &= ~mask.raw;
|
||||
auto &defaultEntry = GetNextEntry(section.defaultController);
|
||||
if (pressed)
|
||||
defaultEntry.buttons.raw |= mask.raw;
|
||||
else
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -54,19 +46,19 @@ 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 {
|
||||
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,14 +70,14 @@ namespace skyline::input {
|
||||
private:
|
||||
NpadManager &manager; //!< The manager responsible for managing this NpadDevice
|
||||
NpadSection §ion; //!< 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
|
||||
* @param info The controller info of the NPad that needs to be updated
|
||||
* @return The next entry that has been created with values from the last entry
|
||||
*/
|
||||
* @brief This updates the headers and creates a new entry in HID Shared Memory
|
||||
* @param info The controller info of the NPad that needs to be updated
|
||||
* @return The next entry that has been created with values from the last entry
|
||||
*/
|
||||
NpadControllerState &GetNextEntry(NpadControllerInfo &info);
|
||||
|
||||
/**
|
||||
@ -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 §ion, 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) {
|
||||
section.header.assignment = assignment;
|
||||
explicitAssignment = true;
|
||||
} else if (!explicitAssignment) {
|
||||
section.header.assignment = assignment;
|
||||
}
|
||||
* @brief This sets a Joy-Con's Assignment Mode
|
||||
* @param assignment The assignment mode to set this controller to
|
||||
*/
|
||||
inline void SetAssignment(NpadJoyAssignment assignment) {
|
||||
section.header.assignment = assignment;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,28 +109,28 @@ namespace skyline::input {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This connects this controller to the guest
|
||||
* @param newType The type of controller to connect as
|
||||
*/
|
||||
* @brief This connects this controller to the guest
|
||||
* @param newType The type of controller to connect as
|
||||
*/
|
||||
void Connect(NpadControllerType newType);
|
||||
|
||||
/**
|
||||
* @brief This disconnects this controller from the guest
|
||||
*/
|
||||
* @brief This disconnects this controller from the guest
|
||||
*/
|
||||
void Disconnect();
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
void SetButtonState(NpadButton mask, NpadButtonState 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 pressed If the buttons were pressed or released
|
||||
*/
|
||||
void SetButtonState(NpadButton mask, bool pressed);
|
||||
|
||||
/**
|
||||
* @brief This sets the value of an axis to the specified value
|
||||
* @param axis The axis to set the value of
|
||||
* @param value The value to set
|
||||
*/
|
||||
* @brief This sets the value of an axis to the specified value
|
||||
* @param axis The axis to set the value of
|
||||
* @param value The value to set
|
||||
*/
|
||||
void SetAxisValue(NpadAxisId axis, i32 value);
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -89,9 +89,9 @@ namespace skyline {
|
||||
try {
|
||||
while (true) {
|
||||
std::lock_guard guard(JniMtx);
|
||||
if (!Halt) {
|
||||
state.gpu->Loop();
|
||||
}
|
||||
if (Halt)
|
||||
break;
|
||||
state.gpu->Loop();
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
state.logger->Error(e.what());
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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!!)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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!!)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user