Six flips under ANYPS5_FPS_LIMIT=20 may not complete in under five periods
of 50 ms. The check is a lower bound only, so a loaded host cannot fail it;
without the hook the six flips complete in about 100 ms at the output's
rate.
The wait shows as frame_limit in the VideoOut.Flip timing, so a title held
by ANYPS5_FPS_LIMIT is told apart from one held by the vblank wait in the
trace and in the phases of the performance overlay's second page.
With ANYPS5_FPS_LIMIT set, processFlip waits for the pacer's deadline after
the flip's vblank wait and before the frame is presented, so the delay
falls on the presentation and on the flip's completion event, which is
what paces the title. The wait takes no lock, sleeps with PreciseSleepUs in
slices of 20 ms and re-checks between slices whether the output was closed
or failed. The limit is read at the first flip, where an invalid value
stops the title with the error that names it. Without the variable the
pacer does not exist and processFlip does what it did. Flips of the overlay
bus are not paced.
setenv is not part of the MinGW Windows CRT. The test sets the variable
for the process, which PadIdentify.hpp reads with std::getenv. SDL_setenv
calls SetEnvironmentVariableA on Windows, which does not update the CRT
table that std::getenv reads, so _putenv_s is used there. Linux keeps
setenv.
The libScePad scheduler test already defines a target called
pad_identify_tests, so configuring the tree failed with 'another target
with the same name already exists'. The VideoOut test target is renamed
to pad_identify_pad_input_tests; its ctest names are unchanged.
Four process-level checks on an SDL virtual controller whose rumble
callback records the motor edges: connect sends exactly one pulse for
player 1 after the quiet window; off sends nothing; a title rumbling
at 200 gets no identify pulse while it rumbles and one pulse after it
stops; an invalid ANYPS5_PAD_IDENTIFY stops the start with an error
naming the variable and the value. The test skips with code 77 when the
host has joysticks, as pad_sdl does.
When ANYPS5_PAD_IDENTIFY is connect or always, a controller that is
bound to a player slot gets a short pulse pattern once the title has
been quiet for 500 ms: one pulse for player 1, two for player 2 and so
on up to four. The scheduler runs in the existing output pass, so a
title's own rumble, its refreshes and its stop always take precedence,
and the pulse is scaled by ANYPS5_RUMBLE. A controller without rumble
support logs one line and gets no pulse. The keyboard and mouse never
get a pulse, since they are not bound to a slot. The mode is read when
the pad input starts, so an invalid value stops the title there. With
the default off, the output pass sends exactly what it sent before.
Match pad_sdl: under WIN32 the test gets an ENVIRONMENT_MODIFICATION that prepends the library directories of libSceVideoOut, libScePad, libSceIme, libSceUserService and libc to PATH, so a plain ctest run can load them. Linux is unchanged.
A scripted button that answered a dialog stayed masked from the title
until a title read sampled it released. When the title did not read
during the release, a later scripted press of the same button was
masked as well. The mask now keeps only the buttons the command
timeline held without a break since the last read, so a press that
ends, or a new command, clears it whether or not it was sampled.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Flip.cpp dispatches 26 modes, but CMake registered 17. present,
backtoback, dcc, pacing, aftergpu, onedevice, deferred and unregistered
now run with the other display tests: they need a Vulkan window and skip
with 77 without one. deferred and unregistered cover flips submitted
before their buffers are registered (c9fe124c10). unavailable expects
sceVideoOutOpen to fail, so it runs under SDL's dummy video driver,
which has no Vulkan.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The removal and reconnect checks sampled only at the hold's edge, so a hold
of zero still passed them. A sample halfway through the hold must now stay
pending, which a zero hold does not do.
A scripted press that answers a modal dialog or the IME was merged into
the host state for the capture, but video out's heldThroughCapture only
sees host buttons, so once the capture ended Pad::ReadState OR-ed the
still-active injected press into the title's read. A 400 ms Cross that
answered the dialog then also reached the title for the rest of the
press.
Record port 0's injected buttons while the pad is captured and mask them
from the title until they are released, as host buttons already are.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The host volume hooks libSceVideoOut calls now come from libScePad, so libSceVideoOut no longer needs libSceAudioOut. That library links its own static SDL2, and loading it with libSceVideoOut is the dependency the Windows pad_sdl abort points at; the libSceVideoOut link line is back to what main has.
The volume, mute and focus state that libSceAudioOut applies and libSceVideoOut changes now lives in libScePad, which both libraries already link, instead of in libSceAudioOut. The tests that compile the state directly and the environment reference follow the file.
libSceVideoOut reads and changes the shared host volume state, and linking libSceAudioOut for it puts a second shared library with its own static SDL2 into libSceVideoOut. Both libraries already link libScePad, so the headers that describe that state move there first; the include paths and guards follow.
Require aborted without saying which check failed, so a failure on a CI host that cannot be reproduced locally left no trace of the assertion in the log. It now prints the source line before aborting.
With the option on, a window that opens behind another one starts silent instead of waiting for a first focus event that never comes. With the option off this changes nothing.
The display window's focus events drive the shared host volume. With ANYPS5_MUTE_ON_FOCUS_LOSS unset or 0 they change nothing; with 1 the audio is silent from focus lost until focus gained, and each change prints one line on stderr.
The requests are handled only for key events of the display window, like fullscreen, the overlay and the screenshot, and each handled press prints one line with the resulting level or mute.
PadInput applies ANYPS5_VOLUME and ANYPS5_MUTE_ON_FOCUS_LOSS next to the input mapping, so an invalid value stops the game at the same point as an invalid mapping file, with an error naming the variable.
Nothing happens by default, focus lost silences and focus gained restores exactly unity with the option on, other window events, other windows and key events are ignored, and the user's own mute survives a focus round trip.
Only focus lost and gained events of the display window count. Minimizing, restoring, closing and the events of other windows change nothing, and the shared volume only acts on them when the focus option is on.
Register pad_host_volume. An empty request set changes nothing, volume up and down move one step and return to exactly unity, mute toggles, combined requests run up then down then mute, and the other host requests are ignored.
ApplyHostVolumeRequests runs the requested commands in the order up, down, mute and reports whether there was one, leaving the final state for the caller to print.
The volume keys and the focus rule live in the video output's input handling and drive the audio output's shared host volume. libSceAudioOut does not depend on libSceVideoOut.
No built-in key is bound twice, the three host volume keys are bound by default as plain keyboard keys, a key the user shares with another action keeps both bindings as it does for the existing actions, and action names are exact and case-insensitive.