fix(libScePad): queue input states for scePadRead
scePadRead reported only the current state, so input that changed and changed back between two reads was lost. Published states are now kept in a queue of up to 64 and drained oldest first; with none queued the current state is returned as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
efa5afbfbd
commit
efa6cd56f0
|
|
@ -139,13 +139,10 @@ int APS5_VABI scePadOpen_nid_postfix(int userId, int type, int index, const void
|
|||
return PAD_HANDLE;
|
||||
}
|
||||
|
||||
int APS5_VABI scePadReadState(int handle, PadData* data);
|
||||
|
||||
int APS5_VABI scePadRead_nid_postfix(int handle, PadData* data, int num) {
|
||||
if (handle != PAD_HANDLE) return PAD_ERROR_INVALID_HANDLE;
|
||||
if (data == nullptr || num <= 0) APS5_INVALID_ARG_EX;
|
||||
const int result = scePadReadState(handle, data);
|
||||
if (result != 0) return result;
|
||||
return 1;
|
||||
return Pad::Read(data, num);
|
||||
}
|
||||
|
||||
int APS5_VABI scePadReadState(int handle, PadData* data) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ struct PadOutputState {
|
|||
namespace Pad {
|
||||
void Initialize();
|
||||
PadData ReadState();
|
||||
// Drains up to `num` queued states, oldest first; with none queued, writes the current state. Returns the count written.
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
|
|
@ -12,6 +13,14 @@ namespace {
|
|||
PadInputState state;
|
||||
PadOutputState output;
|
||||
std::uint64_t timestamp = 0;
|
||||
|
||||
struct QueuedInput {
|
||||
PadInputState input;
|
||||
std::uint64_t timestamp = 0;
|
||||
};
|
||||
// States published since the last scePadRead; the oldest is dropped once 64 are waiting.
|
||||
constexpr std::size_t kQueueCapacity = 64;
|
||||
std::deque<QueuedInput> queue;
|
||||
std::exception_ptr failure;
|
||||
bool initialized = false;
|
||||
|
||||
|
|
@ -93,15 +102,14 @@ 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 {
|
||||
// Converts one input sample; motion fusion integrates up to `now` and touch ids advance per call.
|
||||
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];
|
||||
|
|
@ -123,7 +131,7 @@ PadData Pad::ReadState() {
|
|||
orientation = Quat{};
|
||||
biasIntegral[0] = biasIntegral[1] = biasIntegral[2] = 0.0f;
|
||||
}
|
||||
lastFuseTime = now;
|
||||
lastFuseTime = std::max(lastFuseTime, now);
|
||||
data.orientation_x = orientation.x;
|
||||
data.orientation_y = orientation.y;
|
||||
data.orientation_z = orientation.z;
|
||||
|
|
@ -138,7 +146,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 +171,32 @@ 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");
|
||||
if (queue.empty()) {
|
||||
data[0] = BuildData(state, timestamp, sceKernelGetProcessTime());
|
||||
return 1;
|
||||
}
|
||||
int count = 0;
|
||||
while (count < num && !queue.empty()) {
|
||||
const QueuedInput& entry = queue.front();
|
||||
data[count++] = BuildData(entry.input, entry.timestamp, entry.timestamp);
|
||||
queue.pop_front();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void Pad::SetVibration(std::uint8_t large, std::uint8_t small) {
|
||||
std::lock_guard lock(stateMutex);
|
||||
|
|
@ -280,6 +314,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) {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,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<std::uint32_t>(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);
|
||||
|
|
@ -81,6 +97,7 @@ int main() {
|
|||
Require(scePadGetHandle(user, 2, 0) == handle);
|
||||
CheckTiltCorrection(handle);
|
||||
CheckTouchContact();
|
||||
CheckReadQueue(handle);
|
||||
CheckReadStateHandle(handle);
|
||||
Require(scePadGetHandle(0xff, 16, 0) == handle);
|
||||
Require(scePadGetHandle(user, 16, 0) == noHandle);
|
||||
|
|
|
|||
|
|
@ -233,4 +233,3 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceFontOpenFontSet](../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) opens only the primary file of a system font set. Characters the console takes from the set's other files (Hangul, Thai and Arabic in Japanese and Chinese sets, Arabic in European sets) are reported as unsupported glyphs. The console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (`0x1A......`) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans CJK) is loaded instead. It is not the system font: its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for.
|
||||
- [Font writing lines](../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontCreateWritingLine`, `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics` and `sceFontWritingLineClear` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libFont.cpp): runs are placed left to right, each at the line's advance so far, with no spacing or adjustment. Only horizontal and left-to-right lines, writing attribute 0 and non-null run metrics are modelled; other values, the metrics of an empty line and unknown or null handles throw, since no reference gives their result or error code. `sceFontWritingLineGetOrderingSpace` still throws (KytyPS5's values are placeholders). Lines are allocated on the host, not in the font memory
|
||||
- [sceImeKeyboardGetResourceId](../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) reports that no USB keyboard is connected (`0x80bc0004`, as [shadPS4](https://github.com/shadps4-emu/shadPS4) does) because IME keyboard events are not delivered to the handler, although [libSceKeyboard](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) exposes the host keyboard
|
||||
- [scePadRead](../../core/libs/prx/libScePad/Export.cpp) (libScePad) keeps no queue of past states: each call writes the current state to the first entry and returns 1 whatever `num` is, so input that changes and changes back between two calls is not reported
|
||||
|
|
|
|||
Loading…
Reference in New Issue