Compare commits

..

2 Commits

Author SHA1 Message Date
lizzie 93ddf9599e 2026-09-12 01:12:32
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-12 01:12:32 +00:00
lizzie b67698c622 2026-09-12 01:06:59
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-12 01:06:59 +00:00
3 changed files with 44 additions and 110 deletions
+39 -106
View File
@@ -28,8 +28,10 @@ JoyconDriver::~JoyconDriver() {
}
void JoyconDriver::Stop() {
is_connected = false;
input_thread = {};
if (input_thread.joinable()) {
input_thread.request_stop();
input_thread.join();
}
}
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;
}
std::scoped_lock lock{mutex};
disable_input_thread = true;
// Reset Counters
error_counter = 0;
@@ -126,62 +127,44 @@ Common::Input::DriverResult JoyconDriver::InitializeDevice() {
right_stick_calibration, motion_calibration);
// Start polling for data
is_connected = true;
if (!input_thread_running) {
input_thread =
std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
if (!input_thread.joinable()) {
input_thread = std::jthread([this](std::stop_token stop_token) {
InputThread(stop_token);
});
}
disable_input_thread = false;
return Common::Input::DriverResult::Success;
}
void JoyconDriver::InputThread(std::stop_token stop_token) {
LOG_INFO(Input, "Joycon Adapter input thread started");
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()) {
// 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;
if (!IsInputThreadValid()) {
input_thread.request_stop();
continue;
}
// By disabling the input thread we can ensure custom commands will succeed as no package is
// skipped
if (!disable_input_thread) {
status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
ThreadDelay);
if (IsInputThreadValid()) {
// By disabling the input thread we can ensure custom commands will succeed as no package is
// skipped
status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(), READ_TIMEOUT_MS);
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() >= MAX_VIBRATIONS) {
vibration_queue.Pop();
}
} 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");
}
@@ -271,11 +254,6 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
}
Common::Input::DriverResult JoyconDriver::SetPollingMode() {
SCOPE_EXIT {
disable_input_thread = false;
};
disable_input_thread = true;
rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
if (motion_enabled && supported_features.motion) {
@@ -382,17 +360,12 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
}
bool JoyconDriver::IsInputThreadValid() const {
if (!is_connected.load()) {
if (hidapi_handle == nullptr || hidapi_handle->handle == nullptr)
return false;
}
if (hidapi_handle->handle == nullptr) {
return false;
}
// Controller is not responding. Terminate connection
if (error_counter > MaxErrorCount) {
if (error_counter > MaxErrorCount)
return false;
}
return true;
return input_thread.joinable();
}
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) {
std::scoped_lock lock{mutex};
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
vibration_queue.Push(vibration);
return last_vibration_result;
}
Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
std::scoped_lock lock{mutex};
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
return generic_protocol->SetLedPattern(led_pattern);
}
Common::Input::DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
std::scoped_lock lock{mutex};
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
disable_input_thread = true;
const auto result = irs_protocol->SetIrsConfig(mode_, format_);
disable_input_thread = false;
return result;
return irs_protocol->SetIrsConfig(mode_, format_);
}
Common::Input::DriverResult JoyconDriver::SetPassiveMode() {
@@ -532,12 +493,7 @@ Common::Input::DriverResult JoyconDriver::StartNfcPolling() {
if (!nfc_protocol->IsEnabled()) {
return Common::Input::DriverResult::Disabled;
}
disable_input_thread = true;
const auto result = nfc_protocol->StartNFCPollingMode();
disable_input_thread = false;
return result;
return nfc_protocol->StartNFCPollingMode();
}
Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
@@ -550,10 +506,7 @@ Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
return Common::Input::DriverResult::Disabled;
}
disable_input_thread = true;
const auto result = nfc_protocol->StopNFCPollingMode();
disable_input_thread = false;
if (amiibo_detected) {
amiibo_detected = false;
joycon_poller->UpdateAmiibo({});
@@ -576,11 +529,7 @@ Common::Input::DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_da
}
out_data.resize(0x21C);
disable_input_thread = true;
const auto result = nfc_protocol->ReadAmiibo(out_data);
disable_input_thread = false;
return result;
return nfc_protocol->ReadAmiibo(out_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) {
return Common::Input::DriverResult::ErrorWritingData;
}
disable_input_thread = true;
const auto result = nfc_protocol->WriteAmiibo(data);
disable_input_thread = false;
return result;
return nfc_protocol->WriteAmiibo(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) {
return Common::Input::DriverResult::ErrorWritingData;
}
disable_input_thread = true;
const auto result = nfc_protocol->ReadMifare(data, out_data);
disable_input_thread = false;
return result;
return nfc_protocol->ReadMifare(data, out_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) {
return Common::Input::DriverResult::ErrorWritingData;
}
disable_input_thread = true;
const auto result = nfc_protocol->WriteMifare(data);
disable_input_thread = false;
return result;
return nfc_protocol->WriteMifare(data);
}
bool JoyconDriver::IsConnected() const {
std::scoped_lock lock{mutex};
return is_connected.load();
return input_thread.joinable();
}
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-License-Identifier: GPL-2.0-or-later
@@ -160,8 +163,6 @@ private:
// Thread related
mutable std::mutex mutex;
std::jthread input_thread;
bool input_thread_running{};
bool disable_input_thread{};
};
} // namespace InputCommon::Joycon
@@ -22,7 +22,7 @@ void JoyconPoller::SetCallbacks(const JoyconCallbacks& callbacks_) {
void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
const RingStatus& ring_status) {
InputReportActive data{};
memcpy(&data, buffer.data(), sizeof(InputReportActive));
std::memcpy(&data, buffer.data(), sizeof(InputReportActive));
switch (device_type) {
case ControllerType::Left:
@@ -47,7 +47,7 @@ void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& moti
void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
InputReportPassive data{};
memcpy(&data, buffer.data(), sizeof(InputReportPassive));
std::memcpy(&data, buffer.data(), sizeof(InputReportPassive));
switch (device_type) {
case ControllerType::Left: