BunnyFl ran firmware 13.42's libScePad natively over a slot table he
wrote, and oneandonlydean asked for the four calls to follow it.
scePadIsValidHandle matches the handle's open port type against the
argument, and answers false before scePadInit.
scePadResetLightBarAllByPortType returns INVALID_ARG for any type but 0,
1 and 2 before the initialisation check, and otherwise resets each open
handle of that type as scePadResetLightBar resets it;
scePadResetLightBarAll is type 0. So a standard port with no open handle
keeps its light bar, and types 3, 4 and 16 no longer throw.
scePadSetVibrationForce returns NOT_INITIALIZED before scePadInit, then
INVALID_ARG for a null parameter, without looking the handle up. There
is no pad driver here: a special handle returns PAD_OK and a standard
handle sets its port's motors, open or not. A handle with no pad port
throws, as the driver's answer for it is not measured.
The tests drop the closed and unknown handle checks that no longer hold
and check the measured answers; the debt entries cite the run and say
which parts are this model's.
Port types 3, 4 and 16 are valid ones the console answers.
scePadResetLightBarAllByPortType still refuses them, but a test that
requires the refusal would fail once they are supported. The positive
and invalid-argument checks stay.
Asked for in oneandonlydean's review.
On a standard-port handle the console answers NOT_PERMITTED (0x80920101):
Halo CE Universal's pad probe measured it on firmware 13.59 for two
connected DualSenses in three runs. The function returned 0 with
invented controller information instead. The special port keeps its
zeroed answer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
scePadSetVibrationForce takes the same handle and vibration parameter as
scePadSetVibration and returns its result. No system vibration-off state
exists here for it to override: scePadSetVibrationMode only stores its
mode.
scePadResetLightBarAll returns each standard port's light bar to its
default, as scePadResetLightBar does for one handle.
scePadResetLightBarAllByPortType does the same for the standard port
type and nothing for the special one. It throws for types 3, 4 and 16,
and rejects other types with INVALID_ARG, as scePadOpen does.
A handle is valid for its own port type while the handle table that
scePadGetHandle reads holds it open: a standard port that is open, a
special slot that is in use, or the open remote port. Any other port
type answers false.
Accept valid special handles in scePadGetControllerType by using KnownHandle, reporting PAD_CONTROLLER_TYPE_UNKNOWN (0) for special handles with no physical controller behind them.
A press latched between two scePadReadState calls was dropped after a
fixed 100 ms. A title that reads less often than that loses most quick
taps: Persona 3 Reload reads the pad only through scePadReadState and,
in its menus, about every 170 ms.
The latch now lasts one and a half times the interval between the
title's two previous reads, kept between 100 ms and 500 ms. The
interval from before a pause in reading still applies after it, so a
press older than that is not reported as a fresh one.
Main limits the standard port to logged-in users, so scePadOpen now checks the port type alone first, then the user and index, and leaves the standard port's user lookup to PortForUser.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- 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]>
A payload process on a PS5 (firmware 13.60) gets 0x809b0001 for user -1 on types 3 and 4 and on index 1, so scePadOpen answers those before it throws for the unknown types.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The tests for scePadSetLotusEnableIrKey, scePadSetLotusDisableIrKey,
scePadVirtualDeviceDisableButtonRemapping and scePadVirtualDeviceGetRemoteSetting
only checked that an unimplemented call throws. A test of that kind fails as
soon as someone implements the function, so it pins the absence of a feature
rather than behaviour. Tests should cover real behaviour and argument
validation.
This removes those asserts, the Throws helper and the declarations they were the
only users of. The rest of the pad test is unchanged.
scePadSetLotusEnableIrKey and scePadSetLotusDisableIrKey validate
the pad handle, returning PAD_ERROR_INVALID_HANDLE (0x80920002) for any
invalid handle and 0 on open pads as silent stubs.
scePadVirtualDeviceDisableButtonRemapping acts as a silent stub returning
0. scePadVirtualDeviceGetRemoteSetting recovers the 64-byte remote setting
layout, rejecting null buffers and non-positive device IDs with
PAD_ERROR_INVALID_ARG (0x80920001) and returning
PAD_ERROR_DEVICE_NOT_CONNECTED (0x80920007).
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]>
The ps4probe libScePad survey (PS4 Pro, firmware 10.71) measured the
special port with one user: a second scePadOpen returns ALREADY_OPENED
(0x80920004); scePadRead and scePadReadState give the rest record
(sticks 0x80, orientation w 1, acceleration y 1, connected 0);
scePadGetControllerInformation is zeroed;
scePadDeviceClassGetExtendedInformation reports device class -1; and
scePadSetVibration, scePadSetLightBar, scePadResetLightBar,
scePadSetMotionSensorState and scePadResetOrientation return 0.
The special handle is now accepted by every pad call: setters succeed
without touching the emulated pad, getters return the measured records.
Calls not covered by the survey follow the same rule and the debt entry
lists them.
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]>
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.
Inside Gran Turismo 7 on a PS5 (firmware 13.40) scePadGetControllerInformation for the DualSense reported touch pad resolution 1920 x 1080 and stick dead zones 13 and 13; pixel density 44.86 already matched.
Touch y coordinates now span that height: a host touch pad scales y to 0..1079 and the view-button and injected touches sit at y 540 instead of 0..942 and 471.
The name hashes to zBsgF0q8DIM. Signature and error order follow the
OS 10.01 system module: NOT_INITIALIZED, INVALID_ARG for a NULL output,
INVALID_HANDLE for a bad handle. The emulated pad is not an Access
controller and reports 0.