Accept valid special handles in scePadGetControllerType by using KnownHandle, reporting PAD_CONTROLLER_TYPE_UNKNOWN (0) for special handles with no physical controller behind them.
- Track up to 4 pad slots in libScePad with per-slot inputs, outputs, sensor fusion, and deadbands
- Support dynamic user login/logout events and multi-user registration in libSceUserService
- Map SDL controllers 0-3 to Players 1-4 in libSceVideoOut while preserving Player 1 keyboard and mouse composition
- Add test coverage for multi-controller pads and multi-user services
scePadOpen for the system user's remote control port (type 16) returned the
DualSense handle, so scePadReadState on the "remote" reported the controller's
buttons as connected. SDL's PS5 video driver maps that port's buttons to keys,
so every Cross also arrived as Return and every D-pad press as an arrow key.
In Raptor (PPSA94002) the Return overwrote the key typed on the on-screen
keyboard before the name field read it, and Circle closed the dialog instead
of the keyboard.
The port now opens its own handle that reads no buttons with connected false,
and closes independently of the DualSense handle. Unlike the special port
handles, it stays out of KnownHandle, so calls that act on the DualSense reject
it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The special port table had four slots and threw for a fifth user, which
the progress report counted as scePadOpen going back to a stub. A PS4
holds at most 16 accounts, so the table now has 16 slots and a 17th
distinct user id gets 0x809b0081, the code the console returned for an
unknown user id in the libScePad survey (PS4 Pro, firmware 10.71).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
A single flag and one handle served every user's special port, so a
second user's scePadClose dropped the first user's handle from
scePadGetHandle. Each user now gets a handle of its own
(PAD_SPECIAL_HANDLE + slot, four slots), a repeated open returns it
again, and close frees only that slot. Whether the console shares or
separates the handles is not measured yet; the debt entry says so.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
scePadOpen returned DEVICE_NOT_CONNECTED (0x80920007) for a special port
(type 2) since #3838, which guessed the console behaviour. A PS4 Pro on
firmware 10.71 with no special controller connected opens the special
port of any valid user and returns a handle: scePadRead on it returns one
record with connected false, scePadGetControllerInformation succeeds, and
scePadRead(-1) is INVALID_HANDLE.
Unity's PS4 input module opens the standard and the special port of a
user and keeps the pad handle only if both opens succeed; otherwise it
calls scePadRead with handle -1 every frame and the title never sees a
button. Knowledge is Power (CUSA09558) and Knowledge is Power: Decades
(CUSA12435) had no controller input for that reason.
The special port now opens PAD_SPECIAL_HANDLE, tracked apart from the
standard handle by scePadClose and scePadGetHandle. scePadRead and
scePadReadState on it return a zeroed record, scePadGetControllerInformation
and scePadDeviceClassParseData zeroed structures, and the other pad calls
reject it with INVALID_HANDLE. The standard handle still reads its own
queue, so the double read of one queue that #3838 fixed does not return.
scePadOpenExt is unchanged. The unmeasured parts are recorded in
docs/dev/debt/libScePad.md.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
F12 (the new Screenshot input action) requests a capture of the next
presented display buffer. The presenter records it with the existing
APS5_DUMP_FRAMES GPU readback at full resolution, and the frame dump
writer thread encodes it with decoder_png into anyps5-screenshots/
beside the executable, or ANYPS5_SCREENSHOTS.
The controller information reported 1920 by 943, the DualShock 4 height with
the DualSense pixel density, while the host controller's fingers were scaled to
1920 by 942 and the keyboard touch contacts sat on the 942 centre line. A title
that maps touch coordinates through the reported resolution lost the lower part
of the touchpad. The width and height now live beside the pad state and all
three places use them.
Some titles stop on a prompt to shake the controller, which keyboard players
and controllers without motion sensors cannot answer. While the Shake input is
held (X by default), the composed pad state reports motion with the
acceleration and angular velocity flipping sign every 50 ms; a controller's own
sensor data is used otherwise.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
APS5_PAD_INPUT_FILE commands can set bit 0x200000 or 0x400000 to put a
finger on the left or right half of the touchpad, as the TouchLeft and
TouchRight keys do, so scripted runs can press touchpad combinations.
tools/pad_input.py queues them as touch-left and touch-right.
AI-assisted: yes
The default bindings map the left and right mouse buttons to Square and
R2, and mouse mode maps motion to the right stick. A title that opens the
mouse with sceMouseOpen therefore gets every click twice: as a mouse
button and as a pad press. PPSA15388 treats the pad press as a switch to
gamepad input, re-centres its cursor and drops the click, so menu items
cannot be clicked.
While libSceMouse has a handle open, KeyboardMouse ignores mouse motion
and mouse-button presses. Releases still pass, so a button held before
the title opened the mouse is let go. Keys and the wheel are unchanged,
as with the IME guard next to it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's StorageTextureBudget calls largestDeviceLocalHeap, the helper this branch made public as LargestDeviceLocalHeap for the overlay's VRAM line. The merge renames that call, so the merged tree builds.
Measured on a PS5 (firmware 13.60): before scePadInit the call returns
NOT_INITIALIZED (0x80920005); after it, a mode of 0 or below returns
INVALID_ARG before the handle is checked, and a bad handle with a
higher mode returns INVALID_HANDLE.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
anyps5-input.ini accepts LeftStickDeadzone and RightStickDeadzone (whole
percent, 0 to 90), a radial deadzone on the controller sticks whose
remaining travel is rescaled to the full range, and MouseSensitivity
(above 0, at most 10), which replaces the fixed factor of 1 for mouse
look. The defaults keep the current mapping exactly.
Add an optional input helper for test runs without desktop input injection. Preserve physical input and release injected buttons automatically.
Validation: guest_pad tests and Python helper CLI smoke checks passed. Written with AI assistance.