mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 14:44:16 +01:00
Fix Joy-Con Pair Crash + Implement More HID Service Functions
This fixes a Joy-Con Pair bug which caused a crash when a partner device was set to none while being set as a partner. In addition, the following HID service functions were implemented: * GetSupportedNpadStyleSet * ActivateNpadWithRevision * GetNpadJoyHoldType * AcquireNpadStyleSetUpdateEventHandle
This commit is contained in:
parent
6a931b95b0
commit
ee2fdbdf6a
@ -23,9 +23,6 @@ namespace skyline::input {
|
||||
if (!activated)
|
||||
return;
|
||||
|
||||
for (auto &npad : npads)
|
||||
npad.Disconnect();
|
||||
|
||||
for (auto &controller : controllers)
|
||||
controller.device = nullptr;
|
||||
|
||||
@ -75,6 +72,19 @@ 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) {
|
||||
if (controller.device == &device) {
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!connected)
|
||||
device.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void NpadManager::Activate() {
|
||||
|
@ -22,6 +22,8 @@ namespace skyline::input {
|
||||
bool activated{false}; //!< If this NpadManager is activated or not
|
||||
std::atomic<bool> updated{false}; //!< If this NpadManager has been updated by the guest
|
||||
|
||||
friend NpadDevice;
|
||||
|
||||
/**
|
||||
* @brief This translates an NPad's ID into it's index in the array
|
||||
* @param id The ID of the NPad to translate
|
||||
|
@ -5,9 +5,12 @@
|
||||
#include "npad.h"
|
||||
|
||||
namespace skyline::input {
|
||||
NpadDevice::NpadDevice(NpadManager &manager, NpadSection §ion, NpadId id) : manager(manager), section(section), id(id) {}
|
||||
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)
|
||||
return;
|
||||
|
||||
void NpadDevice::Connect(NpadControllerType type) {
|
||||
section.header.type = NpadControllerType::None;
|
||||
section.deviceType.raw = 0;
|
||||
section.buttonProperties.raw = 0;
|
||||
@ -15,7 +18,7 @@ namespace skyline::input {
|
||||
connectionState.raw = 0;
|
||||
connectionState.connected = true;
|
||||
|
||||
switch (type) {
|
||||
switch (newType) {
|
||||
case NpadControllerType::ProController:
|
||||
section.header.type = NpadControllerType::ProController;
|
||||
section.deviceType.fullKey = true;
|
||||
@ -86,10 +89,10 @@ namespace skyline::input {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw exception("Unsupported controller type: {}", type);
|
||||
throw exception("Unsupported controller type: {}", newType);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
switch (newType) {
|
||||
case NpadControllerType::ProController:
|
||||
case NpadControllerType::JoyconLeft:
|
||||
case NpadControllerType::JoyconRight:
|
||||
@ -114,19 +117,24 @@ namespace skyline::input {
|
||||
section.leftBatteryLevel = NpadBatteryLevel::Full;
|
||||
section.rightBatteryLevel = NpadBatteryLevel::Full;
|
||||
|
||||
this->type = type;
|
||||
type = newType;
|
||||
controllerInfo = &GetControllerInfo();
|
||||
|
||||
SetButtonState(NpadButton{}, NpadButtonState::Released);
|
||||
updateEvent->Signal();
|
||||
}
|
||||
|
||||
void NpadDevice::Disconnect() {
|
||||
if (type == NpadControllerType::None)
|
||||
return;
|
||||
|
||||
section = {};
|
||||
explicitAssignment = false;
|
||||
globalTimestamp = 0;
|
||||
|
||||
type = NpadControllerType::None;
|
||||
controllerInfo = nullptr;
|
||||
|
||||
updateEvent->Signal();
|
||||
}
|
||||
|
||||
NpadControllerInfo &NpadDevice::GetControllerInfo() {
|
||||
@ -149,8 +157,9 @@ namespace skyline::input {
|
||||
NpadControllerState &NpadDevice::GetNextEntry(NpadControllerInfo &info) {
|
||||
auto &lastEntry = info.state.at(info.header.currentEntry);
|
||||
|
||||
info.header.currentEntry = (info.header.currentEntry != constant::HidEntryCount - 1) ? info.header.currentEntry + 1 : 0;
|
||||
info.header.timestamp = util::GetTimeTicks();
|
||||
info.header.entryCount = std::min(static_cast<u8>(info.header.entryCount + 1), constant::HidEntryCount);
|
||||
info.header.currentEntry = (info.header.currentEntry != constant::HidEntryCount - 1) ? info.header.currentEntry + 1 : 0;
|
||||
|
||||
auto &entry = info.state.at(info.header.currentEntry);
|
||||
|
||||
@ -210,7 +219,7 @@ namespace skyline::input {
|
||||
mask = orientedMask;
|
||||
}
|
||||
|
||||
for (NpadControllerState& controllerEntry : {std::ref(GetNextEntry(section.defaultController)), std::ref(GetNextEntry(section.digitalController))})
|
||||
for (NpadControllerState &controllerEntry : {std::ref(GetNextEntry(section.defaultController)), std::ref(GetNextEntry(section.digitalController))})
|
||||
if (state == NpadButtonState::Pressed)
|
||||
controllerEntry.buttons.raw |= mask.raw;
|
||||
else
|
||||
@ -224,7 +233,7 @@ namespace skyline::input {
|
||||
return;
|
||||
|
||||
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)) {
|
||||
switch (axis) {
|
||||
@ -266,7 +275,7 @@ namespace skyline::input {
|
||||
}
|
||||
}
|
||||
|
||||
auto& digitalEntry = GetNextEntry(section.digitalController);
|
||||
auto &digitalEntry = GetNextEntry(section.digitalController);
|
||||
constexpr i16 threshold = 3276; // A 10% deadzone for the stick
|
||||
|
||||
digitalEntry.buttons.leftStickUp = defaultEntry.leftY >= threshold;
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kernel/types/KEvent.h>
|
||||
#include "shared_mem.h"
|
||||
|
||||
namespace skyline::input {
|
||||
@ -96,6 +97,7 @@ namespace skyline::input {
|
||||
NpadId id; //!< The ID of this controller
|
||||
NpadControllerType type{}; //!< The type of this controller
|
||||
NpadConnectionState connectionState{}; //!< The state of the connection
|
||||
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);
|
||||
@ -123,9 +125,9 @@ namespace skyline::input {
|
||||
|
||||
/**
|
||||
* @brief This connects this controller to the guest
|
||||
* @param type The type of controller to connect as
|
||||
* @param newType The type of controller to connect as
|
||||
*/
|
||||
void Connect(NpadControllerType type);
|
||||
void Connect(NpadControllerType newType);
|
||||
|
||||
/**
|
||||
* @brief This disconnects this controller from the guest
|
||||
|
@ -20,7 +20,7 @@ namespace skyline {
|
||||
*/
|
||||
struct CommonHeader {
|
||||
u64 timestamp; //!< The timestamp of the latest entry in ticks
|
||||
u64 entryCount{constant::HidEntryCount}; //!< The number of entries (17)
|
||||
u64 entryCount; //!< The number of written entries
|
||||
u64 currentEntry; //!< The index of the latest entry
|
||||
u64 maxEntry{constant::HidEntryCount - 1}; //!< The maximum entry index (16)
|
||||
};
|
||||
|
@ -10,10 +10,14 @@ namespace skyline::service::hid {
|
||||
IHidServer::IHidServer(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::hid_IHidServer, "hid:IHidServer", {
|
||||
{0x0, SFUNC(IHidServer::CreateAppletResource)},
|
||||
{0x64, SFUNC(IHidServer::SetSupportedNpadStyleSet)},
|
||||
{0x64, SFUNC(IHidServer::GetSupportedNpadStyleSet)},
|
||||
{0x66, SFUNC(IHidServer::SetSupportedNpadIdType)},
|
||||
{0x67, SFUNC(IHidServer::ActivateNpad)},
|
||||
{0x68, SFUNC(IHidServer::DeactivateNpad)},
|
||||
{0x6A, SFUNC(IHidServer::AcquireNpadStyleSetUpdateEventHandle)},
|
||||
{0x6D, SFUNC(IHidServer::ActivateNpadWithRevision)},
|
||||
{0x78, SFUNC(IHidServer::SetNpadJoyHoldType)},
|
||||
{0x79, SFUNC(IHidServer::GetNpadJoyHoldType)},
|
||||
{0x7A, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingleByDefault)},
|
||||
{0x7B, SFUNC(IHidServer::SetNpadJoyAssignmentModeSingle)},
|
||||
{0x7C, SFUNC(IHidServer::SetNpadJoyAssignmentModeDual)}
|
||||
@ -25,11 +29,17 @@ namespace skyline::service::hid {
|
||||
|
||||
void IHidServer::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto styleSet = request.Pop<NpadStyleSet>();
|
||||
state.input->npad.styles = styleSet;
|
||||
state.input->npad.Update();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void IHidServer::GetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push(state.input->npad.styles);
|
||||
}
|
||||
|
||||
void IHidServer::SetSupportedNpadIdType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
const auto &buffer = request.inputBuf.at(0);
|
||||
u64 address = buffer.address;
|
||||
@ -53,11 +63,23 @@ namespace skyline::service::hid {
|
||||
state.input->npad.Deactivate();
|
||||
}
|
||||
|
||||
void IHidServer::AcquireNpadStyleSetUpdateEventHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto id = request.Pop<NpadId>();
|
||||
request.copyHandles.push_back(state.process->InsertItem(state.input->npad.at(id).updateEvent));
|
||||
}
|
||||
|
||||
void IHidServer::ActivateNpadWithRevision(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
state.input->npad.Activate();
|
||||
}
|
||||
|
||||
void IHidServer::SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto appletResourceUID = request.Pop<u64>();
|
||||
state.input->npad.orientation = request.Pop<NpadJoyOrientation>();
|
||||
}
|
||||
|
||||
void IHidServer::GetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push(state.input->npad.orientation);
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -25,6 +25,11 @@ namespace skyline::service::hid {
|
||||
*/
|
||||
void SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief This gets the style of controllers supported (https://switchbrew.org/wiki/HID_services#GetSupportedNpadStyleSet)
|
||||
*/
|
||||
void GetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief This sets the NpadIds which are supported (https://switchbrew.org/wiki/HID_services#SetSupportedNpadIdType)
|
||||
*/
|
||||
@ -40,11 +45,26 @@ namespace skyline::service::hid {
|
||||
*/
|
||||
void DeactivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief This requests an event that's signalled on a specific NpadId changing (https://switchbrew.org/wiki/HID_services#AcquireNpadStyleSetUpdateEventHandle)
|
||||
*/
|
||||
void AcquireNpadStyleSetUpdateEventHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief This requests the activation of controllers with a specific HID revision (https://switchbrew.org/wiki/HID_services#ActivateNpadWithRevision)
|
||||
*/
|
||||
void ActivateNpadWithRevision(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Sets the Joy-Con hold mode (https://switchbrew.org/wiki/HID_services#SetNpadJoyHoldType)
|
||||
*/
|
||||
void SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Sets the Joy-Con hold mode (https://switchbrew.org/wiki/HID_services#GetNpadJoyHoldType)
|
||||
*/
|
||||
void GetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Sets the Joy-Con assignment mode to Single by default (https://switchbrew.org/wiki/HID_services#SetNpadJoyAssignmentModeSingleByDefault)
|
||||
*/
|
||||
|
@ -19,7 +19,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
/**
|
||||
* This is the instance of [PreferenceFragment] that is shown inside [R.id.settings]
|
||||
*/
|
||||
private val preferenceFragment : PreferenceFragment = PreferenceFragment()
|
||||
private val preferenceFragment = PreferenceFragment()
|
||||
|
||||
/**
|
||||
* This is an instance of [InputManager] used by [ControllerPreference]
|
||||
|
@ -148,11 +148,6 @@ class ControllerActivity : AppCompatActivity(), View.OnClickListener {
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
window.decorView.findViewById<View>(android.R.id.content).viewTreeObserver.addOnTouchModeChangeListener {
|
||||
if (!it)
|
||||
toolbar_layout.setExpanded(false)
|
||||
}
|
||||
|
||||
controller_list.layoutManager = LinearLayoutManager(this)
|
||||
controller_list.adapter = adapter
|
||||
|
||||
@ -173,23 +168,31 @@ class ControllerActivity : AppCompatActivity(), View.OnClickListener {
|
||||
override fun onClick(v : View?) {
|
||||
when (val tag = v!!.tag) {
|
||||
is ControllerTypeItem -> {
|
||||
val type = manager.controllers[id]!!.type
|
||||
val controller = manager.controllers[id]!!
|
||||
|
||||
val types = ControllerType.values().filter { !it.firstController || id == 0 }
|
||||
val typeNames = types.map { getString(it.stringRes) }.toTypedArray()
|
||||
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(tag.content)
|
||||
.setSingleChoiceItems(typeNames, types.indexOf(type)) { dialog, typeIndex ->
|
||||
manager.controllers[id] = when (types[typeIndex]) {
|
||||
ControllerType.None -> Controller(id, ControllerType.None)
|
||||
ControllerType.HandheldProController -> HandheldController(id)
|
||||
ControllerType.ProController -> ProController(id)
|
||||
ControllerType.JoyConLeft -> JoyConLeftController(id)
|
||||
ControllerType.JoyConRight -> JoyConRightController(id)
|
||||
}
|
||||
.setSingleChoiceItems(typeNames, types.indexOf(controller.type)) { dialog, typeIndex ->
|
||||
val selectedType = types[typeIndex]
|
||||
if (controller.type != selectedType) {
|
||||
if (controller is JoyConLeftController)
|
||||
controller.partnerId?.let { (manager.controllers[it] as JoyConRightController).partnerId = null }
|
||||
else if (controller is JoyConRightController)
|
||||
controller.partnerId?.let { (manager.controllers[it] as JoyConLeftController).partnerId = null }
|
||||
|
||||
update()
|
||||
manager.controllers[id] = when (selectedType) {
|
||||
ControllerType.None -> Controller(id, ControllerType.None)
|
||||
ControllerType.HandheldProController -> HandheldController(id)
|
||||
ControllerType.ProController -> ProController(id)
|
||||
ControllerType.JoyConLeft -> JoyConLeftController(id)
|
||||
ControllerType.JoyConRight -> JoyConRightController(id)
|
||||
}
|
||||
|
||||
update()
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
tools:context=".input.ControllerActivity">
|
||||
|
||||
<include layout="@layout/titlebar" />
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/titlebar" />
|
||||
@ -8,5 +9,6 @@
|
||||
<FrameLayout
|
||||
android:id="@+id/settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@ -4,7 +4,8 @@
|
||||
android:id="@+id/toolbar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:fitsSystemWindows="true"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed"
|
||||
app:liftOnScroll="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
|
Loading…
Reference in New Issue
Block a user