diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index b5a281ddd..54b97e7ba 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -156,14 +156,10 @@ int APS5_VABI scePadSetFeatureReport() { NotImplemented_nid_no_patch(__func__); int APS5_VABI scePadOutputReport() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadReadState(int handle, PadData* data); - int APS5_VABI scePadRead_nid_postfix(int handle, PadData* data, int num) { - // The title drains the queued states; one current state is reported per call. - if (data == nullptr || num <= 0) APS5_INVALID_ARG_EX; - const int result = scePadReadState(handle, data); - if (result != 0) return result; - return 1; + if (handle != PAD_HANDLE) return PAD_ERROR_INVALID_HANDLE; + if (data == nullptr || num <= 0) APS5_INVALID_ARG_EX; + return Pad::Read(data, num); } int APS5_VABI scePadReadState(int handle, PadData* data) { diff --git a/core/libs/prx/libScePad/include/PadState.hpp b/core/libs/prx/libScePad/include/PadState.hpp index c23b02c4d..8100e33c5 100644 --- a/core/libs/prx/libScePad/include/PadState.hpp +++ b/core/libs/prx/libScePad/include/PadState.hpp @@ -51,6 +51,7 @@ struct PadOutputState { namespace Pad { void Initialize(); PadData ReadState(); +int Read(PadData* data, int num); void SetVibration(std::uint8_t large, std::uint8_t small); void SetVibrationMode(int mode); void SetLightBar(bool valid, std::uint8_t r, std::uint8_t g, std::uint8_t b); diff --git a/core/libs/prx/libScePad/src/PadState.cpp b/core/libs/prx/libScePad/src/PadState.cpp index ea3e212d4..1bd213078 100644 --- a/core/libs/prx/libScePad/src/PadState.cpp +++ b/core/libs/prx/libScePad/src/PadState.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -12,6 +13,13 @@ namespace { PadInputState state; PadOutputState output; std::uint64_t timestamp = 0; + + struct QueuedInput { + PadInputState input; + std::uint64_t timestamp = 0; + }; + constexpr std::size_t kQueueCapacity = 64; + std::deque queue; std::exception_ptr failure; bool initialized = false; @@ -93,15 +101,13 @@ void Pad::Initialize() { if (!initialized) { timestamp = sceKernelGetProcessTime(); lastFuseTime = timestamp; + queue.clear(); } initialized = true; } -PadData Pad::ReadState() { - std::lock_guard lock(stateMutex); - if (failure) std::rethrow_exception(failure); - if (!initialized) throw std::runtime_error("Pad: read before initialization"); - const std::uint64_t now = sceKernelGetProcessTime(); +namespace { +PadData BuildData(const PadInputState& state, std::uint64_t changeTime, std::uint64_t now) { PadData data{}; data.buttons = state.buttons; data.left_stick_x = state.sticks[0]; @@ -138,7 +144,7 @@ PadData Pad::ReadState() { data.connected = true; data.connected_count = 1; - data.timestamp = timestamp; + data.timestamp = changeTime; std::uint8_t touchNum = 0; for (int i = 0; i < 2; ++i) { @@ -163,6 +169,33 @@ PadData Pad::ReadState() { data.touch_data_touch_num = touchNum; return data; } +} + +PadData Pad::ReadState() { + std::lock_guard lock(stateMutex); + if (failure) std::rethrow_exception(failure); + if (!initialized) throw std::runtime_error("Pad: read before initialization"); + return BuildData(state, timestamp, sceKernelGetProcessTime()); +} + +int Pad::Read(PadData* data, int num) { + if (data == nullptr || num <= 0) throw std::invalid_argument("Pad: invalid read buffer"); + std::lock_guard lock(stateMutex); + if (failure) std::rethrow_exception(failure); + if (!initialized) throw std::runtime_error("Pad: read before initialization"); + const std::uint64_t now = sceKernelGetProcessTime(); + if (queue.empty()) { + data[0] = BuildData(state, timestamp, now); + return 1; + } + int count = 0; + while (count < num && !queue.empty()) { + const QueuedInput& entry = queue.front(); + data[count++] = BuildData(entry.input, entry.timestamp, now); + queue.pop_front(); + } + return count; +} void Pad::SetVibration(std::uint8_t large, std::uint8_t small) { std::lock_guard lock(stateMutex); @@ -280,6 +313,8 @@ extern "C" void PadPublishInput_nid_postfix(const PadInputState& input) { state.touch == input.touch && state.deviceKind == input.deviceKind) return; state = input; timestamp = sceKernelGetProcessTime(); + if (queue.size() == kQueueCapacity) queue.pop_front(); + queue.push_back({input, timestamp}); } extern "C" void PadReportInputFailure_nid_postfix(std::exception_ptr error) { diff --git a/core/libs/tests/GuestPad.cpp b/core/libs/tests/GuestPad.cpp index 213c3cc88..bf874be0a 100644 --- a/core/libs/tests/GuestPad.cpp +++ b/core/libs/tests/GuestPad.cpp @@ -63,6 +63,22 @@ static void CheckTouchContact() { Pad::ReadState(); } +static void CheckReadQueue(int handle) { + PadData drained[64]{}; + scePadRead_nid_postfix(handle, drained, 64); + PadInputState pressed; + pressed.buttons = static_cast(Pad::PadButton::Cross); + PadPublishInput_nid_postfix(pressed); + PadPublishInput_nid_postfix(PadInputState{}); + PadData data[4]{}; + Require(scePadRead_nid_postfix(handle, data, 4) == 2); + Require((data[0].buttons & pressed.buttons) != 0); + Require((data[1].buttons & pressed.buttons) == 0); + Require(data[0].timestamp <= data[1].timestamp); + Require(scePadRead_nid_postfix(handle, data, 4) == 1); + Require(data[0].buttons == 0); +} + static void CheckReadStateHandle(int handle) { PadData data{}; Require(scePadReadState(0, &data) == PAD_ERROR_INVALID_HANDLE); @@ -99,6 +115,7 @@ int main() { Require(scePadGetHandle(user, 2, 0) == handle); CheckTiltCorrection(handle); CheckTouchContact(); + CheckReadQueue(handle); CheckReadStateHandle(handle); CheckRemoteController(handle); Require(scePadSetVibrationMode(handle, 1) == 0);