Compare commits

..

8 Commits

Author SHA1 Message Date
Sepalani
c66b5943a5 fixup! IOS/KBD: Add keyboard support for SDL3 backend 2025-05-25 15:36:54 +04:00
Sepalani
49fc7e78cc IOS/USB: Add "Connect USB Keyboard" support for HID 2025-05-24 23:15:23 +04:00
Sepalani
074add14ac Keyboard: Add a configuration window
Add MAIN_WII_KEYBOARD_TRANSLATION to toggle the layout translation
2025-05-24 23:14:47 +04:00
Sepalani
3a0aed2560 Common/Keyboard: Add keyboard layout support and partial translation 2025-05-24 23:14:47 +04:00
Sepalani
3469727435 IOS/KBD: Replace Windows implementation with SDL 2025-05-24 22:58:05 +04:00
Sepalani
80b8b52387 IOS/KBD: Add keyboard support for SDL3 backend 2025-05-24 22:50:05 +04:00
Sepalani
757d03896f IOS/KBD: Move keyboard logic to Common/Keyboard
The keypress loop was simplified and its keyboard state buffer removed.
2025-05-24 22:15:41 +04:00
Jordan Woyak
2fb1f17c89 InputCommon: Update to use SDL3 and bump the SDL submodule in Externals to release-3.2.14. 2025-05-23 18:39:11 -05:00
3 changed files with 40 additions and 57 deletions

2
Externals/SDL/SDL vendored

@ -1 +1 @@
Subproject commit c9a6709bd21750f1ad9597be21abace78c6378c9
Subproject commit 8d604353a53853fa56d1bdce0363535605ca868f

View File

@ -165,27 +165,9 @@ int HIDKeyboard::SubmitTransfer(std::unique_ptr<CtrlMessage> cmd)
}
case USBHDR(DIR_HOST2DEVICE, TYPE_CLASS, REC_INTERFACE, HIDRequestCodes::SET_IDLE):
{
const u8 duration = cmd->value >> 8;
const u8 report_id = cmd->value & 0xFF;
{
std::lock_guard lock(m_pending_lock);
if (duration == 0)
{
m_idle_duration.reset();
INFO_LOG_FMT(IOS_USB, "SET_IDLE duration to indefinite (report_id={:02x}, index={})",
report_id, cmd->index);
}
else
{
const auto idle_duration =
4 * duration; // 4 millisecond resolution (see HID specification)
m_idle_duration = std::chrono::milliseconds(idle_duration);
INFO_LOG_FMT(IOS_USB, "SET_IDLE duration to {} milliseconds (report_id={:02x}, index={})",
idle_duration, report_id, cmd->index);
}
}
WARN_LOG_FMT(IOS_USB, "SET_IDLE not implemented (value={:04x}, index={})", cmd->value,
cmd->index);
// TODO: Handle idle duration and implement NAK
ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
break;
}
@ -225,15 +207,23 @@ int HIDKeyboard::SubmitTransfer(std::unique_ptr<BulkMessage> cmd)
int HIDKeyboard::SubmitTransfer(std::unique_ptr<IntrMessage> cmd)
{
static auto start_time = std::chrono::steady_clock::now();
const auto current_time = std::chrono::steady_clock::now();
const bool should_poll =
(current_time - start_time) >= POLLING_RATE && ControlReference::GetInputGate();
const Common::HIDPressedState state =
should_poll ? m_keyboard_context->GetPressedState() : m_last_state;
// We can't use cmd->ScheduleTransferCompletion here as it might provoke
// invalid memory access with scheduled transfers when CancelTransfer is called.
auto transfer = std::make_shared<PendingTransfer>(std::move(cmd));
{
std::lock_guard lock(m_pending_lock);
m_pending_tranfers.insert(transfer);
}
m_worker.EmplaceItem(transfer);
EnqueueTransfer(std::move(cmd), state);
if (should_poll)
{
m_last_state = std::move(state);
start_time = std::chrono::steady_clock::now();
}
return IPC_SUCCESS;
}
@ -246,45 +236,47 @@ int HIDKeyboard::SubmitTransfer(std::unique_ptr<IsoMessage> cmd)
return IPC_SUCCESS;
}
void HIDKeyboard::EnqueueTransfer(std::unique_ptr<IntrMessage> msg,
const Common::HIDPressedState& state)
{
msg->FillBuffer(reinterpret_cast<const u8*>(&state), sizeof(state));
auto transfer = std::make_shared<PendingTransfer>(std::move(msg));
m_worker.EmplaceItem(transfer);
{
std::lock_guard lock(m_pending_lock);
m_pending_tranfers.insert(transfer);
}
}
void HIDKeyboard::HandlePendingTransfer(std::shared_ptr<PendingTransfer> transfer)
{
static constexpr auto SLEEP_DURATION = POLLING_RATE / 2;
std::unique_lock lock(m_pending_lock);
if (transfer->IsCanceled())
return;
Common::HIDPressedState state;
if (ControlReference::GetInputGate())
state = m_keyboard_context->GetPressedState();
while (state == m_last_state && transfer->Idle(m_idle_duration))
while (!transfer->IsReady())
{
lock.unlock();
std::this_thread::sleep_for(SLEEP_DURATION);
std::this_thread::sleep_for(POLLING_RATE / 2);
lock.lock();
if (transfer->IsCanceled())
return;
if (ControlReference::GetInputGate())
state = m_keyboard_context->GetPressedState();
}
transfer->Do(state);
transfer->Do();
m_pending_tranfers.erase(transfer);
m_last_state = std::move(state);
}
void HIDKeyboard::CancelPendingTransfers()
{
m_worker.Cancel();
{
std::lock_guard lock(m_pending_lock);
for (auto& transfer : m_pending_tranfers)
transfer->Cancel();
m_pending_tranfers.clear();
}
m_worker.Cancel();
}
HIDKeyboard::PendingTransfer::PendingTransfer(std::unique_ptr<IntrMessage> msg)
@ -301,17 +293,9 @@ HIDKeyboard::PendingTransfer::~PendingTransfer()
m_msg->ScheduleTransferCompletion(-5, 0);
}
bool HIDKeyboard::PendingTransfer::Idle(
std::optional<std::chrono::milliseconds> idle_duration) const
bool HIDKeyboard::PendingTransfer::IsReady() const
{
if (!idle_duration.has_value())
{
// Based on the HID specification for Set_Idle Request:
// - inhibit reporting forever,
// - only reporting when a change is detected in the report data
return true;
}
return (std::chrono::steady_clock::now() - m_time) < *idle_duration;
return (std::chrono::steady_clock::now() - m_time) >= POLLING_RATE;
}
bool HIDKeyboard::PendingTransfer::IsCanceled() const
@ -319,9 +303,8 @@ bool HIDKeyboard::PendingTransfer::IsCanceled() const
return m_is_canceled;
}
void HIDKeyboard::PendingTransfer::Do(const Common::HIDPressedState& state)
void HIDKeyboard::PendingTransfer::Do()
{
m_msg->FillBuffer(reinterpret_cast<const u8*>(&state), sizeof(state));
m_msg->ScheduleTransferCompletion(IPC_SUCCESS, 0);
m_pending = false;
}

View File

@ -47,9 +47,9 @@ private:
PendingTransfer(std::unique_ptr<IntrMessage> msg);
~PendingTransfer();
bool Idle(std::optional<std::chrono::milliseconds> idle_duration) const;
bool IsReady() const;
bool IsCanceled() const;
void Do(const Common::HIDPressedState& state);
void Do();
void Cancel();
private:
@ -59,13 +59,13 @@ private:
bool m_pending = true;
};
void EnqueueTransfer(std::unique_ptr<IntrMessage> msg, const Common::HIDPressedState& state);
void HandlePendingTransfer(std::shared_ptr<PendingTransfer> transfer);
void CancelPendingTransfers();
Common::WorkQueueThreadSP<std::shared_ptr<PendingTransfer>> m_worker;
std::mutex m_pending_lock;
std::set<std::shared_ptr<PendingTransfer>> m_pending_tranfers;
std::optional<std::chrono::milliseconds> m_idle_duration;
HIDProtocol m_current_protocol = HIDProtocol::Report;
Common::HIDPressedState m_last_state;