Compare commits

...

2 Commits

Author SHA1 Message Date
lizzie 29e75a8014 2026-09-12 01:12:32
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 12:37:45 +02:00
lizzie fac51d6ba8 2026-09-12 01:06:59
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 12:37:45 +02:00
3 changed files with 44 additions and 110 deletions
+39 -106
View File
@@ -28,8 +28,10 @@ JoyconDriver::~JoyconDriver() {
} }
void JoyconDriver::Stop() { void JoyconDriver::Stop() {
is_connected = false; if (input_thread.joinable()) {
input_thread = {}; input_thread.request_stop();
input_thread.join();
}
} }
Common::Input::DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) { Common::Input::DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
@@ -58,7 +60,6 @@ Common::Input::DriverResult JoyconDriver::InitializeDevice() {
return Common::Input::DriverResult::InvalidHandle; return Common::Input::DriverResult::InvalidHandle;
} }
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
disable_input_thread = true;
// Reset Counters // Reset Counters
error_counter = 0; error_counter = 0;
@@ -126,62 +127,44 @@ Common::Input::DriverResult JoyconDriver::InitializeDevice() {
right_stick_calibration, motion_calibration); right_stick_calibration, motion_calibration);
// Start polling for data // Start polling for data
is_connected = true; if (!input_thread.joinable()) {
if (!input_thread_running) { input_thread = std::jthread([this](std::stop_token stop_token) {
input_thread = InputThread(stop_token);
std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); }); });
} }
disable_input_thread = false;
return Common::Input::DriverResult::Success; return Common::Input::DriverResult::Success;
} }
void JoyconDriver::InputThread(std::stop_token stop_token) { void JoyconDriver::InputThread(std::stop_token stop_token) {
LOG_INFO(Input, "Joycon Adapter input thread started"); LOG_INFO(Input, "Joycon Adapter input thread started");
Common::SetCurrentThreadName("JoyconInput"); Common::SetCurrentThreadName("JoyconInput");
input_thread_running = true;
// Max update rate is 5ms, ensure we are always able to read a bit faster
constexpr int ThreadDelay = 3;
std::vector<u8> buffer(MaxBufferSize);
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
// Max update rate is 5ms, so just (timeout) at 300ms
constexpr int READ_TIMEOUT_MS = 300;
constexpr size_t MAX_VIBRATIONS = 4;
std::array<u8, MaxBufferSize> buffer; // Filled by SDL, don't zero-init
int status = 0; int status = 0;
if (IsInputThreadValid()) {
if (!IsInputThreadValid()) { // By disabling the input thread we can ensure custom commands will succeed as no package is
input_thread.request_stop(); // skipped
continue; status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(), READ_TIMEOUT_MS);
} if (IsPayloadCorrect(status, buffer)) {
OnNewData(buffer);
// By disabling the input thread we can ensure custom commands will succeed as no package is }
// skipped if (!vibration_queue.Empty()) {
if (!disable_input_thread) { VibrationValue vibration_value;
status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(), vibration_queue.Pop(vibration_value);
ThreadDelay); last_vibration_result = rumble_protocol->SendVibration(vibration_value);
}
// We can't keep up with vibrations. Start skipping.
while (vibration_queue.Size() >= MAX_VIBRATIONS) {
vibration_queue.Pop();
}
} else { } else {
std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay)); input_thread.request_stop();
} }
if (IsPayloadCorrect(status, buffer)) {
OnNewData(buffer);
}
if (!vibration_queue.Empty()) {
VibrationValue vibration_value;
vibration_queue.Pop(vibration_value);
last_vibration_result = rumble_protocol->SendVibration(vibration_value);
}
// We can't keep up with vibrations. Start skipping.
while (vibration_queue.Size() > 6) {
vibration_queue.Pop();
}
std::this_thread::yield();
} }
is_connected = false;
input_thread_running = false;
LOG_INFO(Input, "Joycon Adapter input thread stopped"); LOG_INFO(Input, "Joycon Adapter input thread stopped");
} }
@@ -271,11 +254,6 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
} }
Common::Input::DriverResult JoyconDriver::SetPollingMode() { Common::Input::DriverResult JoyconDriver::SetPollingMode() {
SCOPE_EXIT {
disable_input_thread = false;
};
disable_input_thread = true;
rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
if (motion_enabled && supported_features.motion) { if (motion_enabled && supported_features.motion) {
@@ -382,17 +360,12 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
} }
bool JoyconDriver::IsInputThreadValid() const { bool JoyconDriver::IsInputThreadValid() const {
if (!is_connected.load()) { if (hidapi_handle == nullptr || hidapi_handle->handle == nullptr)
return false; return false;
}
if (hidapi_handle->handle == nullptr) {
return false;
}
// Controller is not responding. Terminate connection // Controller is not responding. Terminate connection
if (error_counter > MaxErrorCount) { if (error_counter > MaxErrorCount)
return false; return false;
} return input_thread.joinable();
return true;
} }
bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) { bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
@@ -415,30 +388,18 @@ bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) { Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
vibration_queue.Push(vibration); vibration_queue.Push(vibration);
return last_vibration_result; return last_vibration_result;
} }
Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
return generic_protocol->SetLedPattern(led_pattern); return generic_protocol->SetLedPattern(led_pattern);
} }
Common::Input::DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) { Common::Input::DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
if (disable_input_thread) { return irs_protocol->SetIrsConfig(mode_, format_);
return Common::Input::DriverResult::HandleInUse;
}
disable_input_thread = true;
const auto result = irs_protocol->SetIrsConfig(mode_, format_);
disable_input_thread = false;
return result;
} }
Common::Input::DriverResult JoyconDriver::SetPassiveMode() { Common::Input::DriverResult JoyconDriver::SetPassiveMode() {
@@ -532,12 +493,7 @@ Common::Input::DriverResult JoyconDriver::StartNfcPolling() {
if (!nfc_protocol->IsEnabled()) { if (!nfc_protocol->IsEnabled()) {
return Common::Input::DriverResult::Disabled; return Common::Input::DriverResult::Disabled;
} }
return nfc_protocol->StartNFCPollingMode();
disable_input_thread = true;
const auto result = nfc_protocol->StartNFCPollingMode();
disable_input_thread = false;
return result;
} }
Common::Input::DriverResult JoyconDriver::StopNfcPolling() { Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
@@ -550,10 +506,7 @@ Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
return Common::Input::DriverResult::Disabled; return Common::Input::DriverResult::Disabled;
} }
disable_input_thread = true;
const auto result = nfc_protocol->StopNFCPollingMode(); const auto result = nfc_protocol->StopNFCPollingMode();
disable_input_thread = false;
if (amiibo_detected) { if (amiibo_detected) {
amiibo_detected = false; amiibo_detected = false;
joycon_poller->UpdateAmiibo({}); joycon_poller->UpdateAmiibo({});
@@ -576,11 +529,7 @@ Common::Input::DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_da
} }
out_data.resize(0x21C); out_data.resize(0x21C);
disable_input_thread = true; return nfc_protocol->ReadAmiibo(out_data);
const auto result = nfc_protocol->ReadAmiibo(out_data);
disable_input_thread = false;
return result;
} }
Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
@@ -595,12 +544,7 @@ Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data)
if (!amiibo_detected) { if (!amiibo_detected) {
return Common::Input::DriverResult::ErrorWritingData; return Common::Input::DriverResult::ErrorWritingData;
} }
return nfc_protocol->WriteAmiibo(data);
disable_input_thread = true;
const auto result = nfc_protocol->WriteAmiibo(data);
disable_input_thread = false;
return result;
} }
Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data, Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
@@ -616,12 +560,7 @@ Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareR
if (!amiibo_detected) { if (!amiibo_detected) {
return Common::Input::DriverResult::ErrorWritingData; return Common::Input::DriverResult::ErrorWritingData;
} }
return nfc_protocol->ReadMifare(data, out_data);
disable_input_thread = true;
const auto result = nfc_protocol->ReadMifare(data, out_data);
disable_input_thread = false;
return result;
} }
Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) { Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
@@ -636,17 +575,11 @@ Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const Mifare
if (!amiibo_detected) { if (!amiibo_detected) {
return Common::Input::DriverResult::ErrorWritingData; return Common::Input::DriverResult::ErrorWritingData;
} }
return nfc_protocol->WriteMifare(data);
disable_input_thread = true;
const auto result = nfc_protocol->WriteMifare(data);
disable_input_thread = false;
return result;
} }
bool JoyconDriver::IsConnected() const { bool JoyconDriver::IsConnected() const {
std::scoped_lock lock{mutex}; return input_thread.joinable();
return is_connected.load();
} }
bool JoyconDriver::IsVibrationEnabled() const { bool JoyconDriver::IsVibrationEnabled() const {
+3 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -160,8 +163,6 @@ private:
// Thread related // Thread related
mutable std::mutex mutex; mutable std::mutex mutex;
std::jthread input_thread; std::jthread input_thread;
bool input_thread_running{};
bool disable_input_thread{};
}; };
} // namespace InputCommon::Joycon } // namespace InputCommon::Joycon
@@ -22,7 +22,7 @@ void JoyconPoller::SetCallbacks(const JoyconCallbacks& callbacks_) {
void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status, void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
const RingStatus& ring_status) { const RingStatus& ring_status) {
InputReportActive data{}; InputReportActive data{};
memcpy(&data, buffer.data(), sizeof(InputReportActive)); std::memcpy(&data, buffer.data(), sizeof(InputReportActive));
switch (device_type) { switch (device_type) {
case ControllerType::Left: case ControllerType::Left:
@@ -47,7 +47,7 @@ void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& moti
void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) { void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
InputReportPassive data{}; InputReportPassive data{};
memcpy(&data, buffer.data(), sizeof(InputReportPassive)); std::memcpy(&data, buffer.data(), sizeof(InputReportPassive));
switch (device_type) { switch (device_type) {
case ControllerType::Left: case ControllerType::Left: