As on a PSP: pressing HOME in a game shows "Do you want to quit the game?"
over it. The game keeps running but gets no input while it's up (sceCtrl
samples are blanked). Left/right picks Yes or No, the confirm button (our
Confirmation button setting, or what compat forces) chooses, the other face
button or HOME closes it, and Yes/No can be tapped. The volume keys change
the game volume a step at a time, shown on the volume bar.
Yes runs the game's registered exit callback first, like the PSP does,
showing "Please wait..." meanwhile. Then, if the XMB launched the game, the
XMB is booted again; otherwise it's back to the game browser, like Exit to
menu. If the callback hasn't ended the game in 10 s, it's quit anyway. Not
in the XMB itself, the PS1 emulator (its own HOME menu) or GE dumps.
It lives in UI/ImposeHome.cpp; EmuScreen only feeds it input and frames and
draws it over the PSP display.
With a firmware dump installed, it's drawn with the firmware's own art from
flash0:/kd/resource/impose.rsc, which is what impose.prx draws it from:
prerendered text for each system language (the question, Yes/No normal and
highlighted, Enter/Back, Please wait...), the button icons, the lines, the
volume bar, the battery and the clock glyphs. The format is documented in
ImposeHome.cpp. Without one, the same screen is drawn with our font and
simple shapes at the same positions.
Where impose.rsc doesn't say where things go, the layout follows
impose_02g.prx (6.61), decrypted and disassembled:
- The screen is filled with 0x9CE7 (#393939); the images are opaque.
- Lines at (80, 96) and (80, 175); impose.prx grows them from the middle
when it opens, which isn't done here.
- Volume bar: the speaker at (28, 223), 30 steps from x 62, 12 apart. It
shows our game volume in the PSP's 30 steps.
- Button guide: the cross icon always left, the circle right, "Enter" and
"Back" next to whichever confirms and cancels.
- Status bar: the battery at (450, 5) (60% and up full, 30% two bars, 12%
one, then empty), the clock ending next to it at y 5: "M/D H:MM", or
"D/M" for the DD/MM/YYYY format, no leading zero on the month, day or
hour, AM/PM for the 12-hour clock.
The clock is the emulated PSP's local time (__RtcGetCurrentLocalTime, new),
in the system's date and time formats. The battery is this device's, since
the emulated PSP's is always full; a device without one shows it full.
For that, Windows now reports -1 for SYSPROP_BATTERY_PERCENTAGE without a
battery (was 100), as SDL already did; SDL polls the battery even when the
status overlay doesn't show it, and the overlay leaves it out when there's
none.
Core side: __CtrlSetGameInputBlocked; __KernelCanExitToVSH and
__KernelRequestExitToVSH to quit to the XMB from outside a syscall; and
__KernelInvokeRegisteredExitCallback(toVSH), so a callback that returns
without exiting ends in the XMB rather than powering down.
New strings: "Do you want to quit the game?", "Please wait..." (Dialog).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB244iFpvm14JH2FbwHXeQ
Windows also gets a filter of its own for them. iOS's picker takes any
file and Android's doesn't filter by extension.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SDL sends a quit only when the last window closes, so with the game
window open, closing the main one did nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A second Win32 window, added as a Game output like the SDL one. Keys
typed into it still go to the emulator; the mouse doesn't. It's for
testing multiple outputs (standing in for a TV over AirPlay) until real
multi-window support takes it over.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The SDL game window gets its Vulkan surface the same way the main window
does, so it should work on Linux too. Windows and OpenGL aren't done.
EmuScreen asks the draw context for the output's size, which follows a
recreated Vulkan swapchain.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
"In menus on Henrik's iPhone" if the platform gives us the name the user
gave the device, otherwise "In menus on your iPhone" (or Mac). iOS only
gives the name with an entitlement Apple has to grant, so for now it's
the model there.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The ScreenManager keeps a list of secondary outputs, each with its own
swapchain, recreated after a lost device. Platform code adds, resizes and
removes them from any thread through Native_AddOutput() and friends, which
take effect at the start of the next frame.
Each frame, every output is drawn by the topmost screen that claims it
(Screen::renderOutput). EmuScreen claims Game outputs: it presents the
game there, laid out for that output, and leaves it out of the backbuffer,
where the touch controls and the menus stay. Not in non-buffered mode,
which draws straight into the backbuffer.
--game-window opens such an output on Mac, with Metal. It's a smoke test
for multiple swapchains, and a stand-in for an AirPlay display.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Selectable with --graphics=metal, the backend choice in settings and the
Mac menu bar. SDL creates a Metal view for it, and headless renders
offscreen. Games run on the null GPU until there's a GPU/Metal.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Convert mouse and touch coordinates using window pixel density instead
of display scale. This fixes offset clicks on X11 with fractional
display scaling.
SDL3's Cocoa window already accepts file drags (as do the other SDL
video drivers), we just never handled SDL_EVENT_DROP_FILE.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since db8ff43, a leftover local shadowed lastImguiEnabled_ in runImDebugger,
so TEXT_GOTFOCUS was never sent and SDL never started text input for imgui.
Drive it from io.WantTextInput instead, so it's only on while editing.
The regular UI and the debugger now say independently whether they want text
input, and the platform only gets TEXT_LOSTFOCUS once neither does, so leaving
an imgui field doesn't break type-to-search on the screen underneath.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SDL 3.4.0 to 3.4.4 refuse text input on Wayland compositors without
zwp_text_input_v3, such as WSLg's Weston, so no SDL_EVENT_TEXT_INPUT ever
arrives and text fields can't be typed into. Fixed upstream in later 3.4.x.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The CPU-thread polling added for SDL was keyed off defined(SDL), which
headless and Core are built with too, hence the extra headless check.
Ask the host instead, with SYSPROP_MICROPHONE_NEEDS_POLLING, so other
hosts can hand their input over the same way.
startMic now sends the same startRecording command for every non-Win32
host, taking the rate from curSampleRate like the SDL path did. The
Android path read it from param, which leaked, and which is null when
coming from onMicDeviceChange.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Instead of the PSP's web browser, a dialog shows the URL the game wants
and opens it in the host's browser on X, or backs out on O. Either way the
game sees the browser closed normally. Platforms that can't open a URL
(the new SYSPROP_CAN_LAUNCH_URL) only offer to back out. Only plain
printable-ASCII http(s) addresses are handed over, and on Linux without a
shell.
What the firmware does (sceUtility_Driver, 6.61, plus
utility/dialog/htmlviewer): the HtmlViewer has its own state apart from the
other dialogs, so they don't block each other, and its calls return
WRONG_TYPE until one has started. The request size picks the 2.00 to 3.00
layout, and InitStart allocates 3.5MB of user memory (4.5MB with options
bit 0x400 from 2.70 on), failing with 800200d9 without it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SDL doesn't reliably pick Wayland on its own - on a machine with a
working Wayland compositor but no XDG_SESSION_TYPE it still chose x11,
putting us on XWayland. Ask for Wayland when WAYLAND_DISPLAY is set,
respect SDL_VIDEO_DRIVER if the user set it, and fall back to letting
SDL choose if Wayland then fails to initialize.
Also log the video driver we ended up with, which should help triage
the Wayland/X11 reports.
Fixes#21080
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JvJR8oJNSCimCM9KXVLjfq
Since 1.20 the "Show Memory Stick folder" button and the log folder
button in developer tools call System_LaunchUrl with LOCAL_FOLDER or
LOCAL_FILE. The SDL implementation only handled macOS there, so on
Linux the click did nothing. Before that they went through the
SHOW_FILE_IN_FOLDER request, which already forks xdg-open.
Move that fork/exec into a LaunchXdgOpen helper and use it from both
places. The child now calls _exit after a failed exec so it doesn't
run the parent's atexit handlers.
Previously the cursor was only hidden when entering fullscreen or
starting a game, and never came back on mouse movement. Now it hides
half a second after the mouse stops moving and reappears when it moves
again, which also covers the touch-controls case that used to be
excluded entirely.
Fixes#20572
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JvJR8oJNSCimCM9KXVLjfq
naett has had a complete libcurl backend all along; we just never built it,
so Linux ran with HTTPS_NOT_AVAILABLE. That means no homebrew store over
HTTPS, and RetroAchievements talking to plain http://retroachievements.org.
libcurl is loaded with dlopen rather than linked, the same way we handle the
Vulkan loader, so it stays a soft dependency: we need the curl headers at
build time, but a build made here still starts on a machine without libcurl
installed - it just reports HTTPS as unavailable, exactly like today. Distro
packagers get the behavior they'd expect either way, and certificate
validation comes free from the system CA store.
New net::HTTPSAvailable() answers "did that work", and SDLMain folds it into
SYSPROP_SUPPORTS_HTTPS, which everything downstream already degrades on.
Four fixes to the backend itself, all noted in ext/naett/README-ppsspp.md:
- panic() called exit(1) on a pipe or curl_multi_perform failure. Taking the
emulator down because a download failed isn't acceptable - the backend now
disables itself and requests complete with naettGenericError.
- CURLINFO_RESPONSE_CODE writes a long into res->code, which is an int. Eight
bytes into four, getting away with it only because the next field absorbs
the zeroes.
- curl_easy_setopt is varargs and wants a long for these options; int literals
and int variables are UB on LP64.
- naettPlatformCloseResponse called through a null function pointer when
libcurl was missing. Found by testing that path, which segfaulted.
CI needs libcurl4-openssl-dev (curl-dev on Alpine) or it would quietly keep
building without HTTPS.
SDL_GetPowerInfo on linux asks upower through dbus for power
information, which can block DrawFPS in UI/DebugOverlay.cpp and in
turn the main thread. Run SDL_GetPowerInfo on another thread
instead to avoid the issue.
It was removed during https://github.com/hrydgard/ppsspp/pull/21982
On Linux Wayland however this can be observed without:
34:25:550 Vulkan/VulkanContext.cpp:1401 I[G3D]: surfCapabilities_.current: -1x-1
34:25:550 Vulkan/VulkanContext.cpp:1406 I[G3D]: surfCapabilities_.current after clamp: -1x-1 min: 1x1 max: 32768x32768 computed: 1x1 cbdraw
Headless.cpp, NativeApp.cpp, and SDLMain.cpp each still hand-parsed a few
argv flags directly (mount/log/state/ignore/loglevel in headless and the
app, xres/yres/dpi/scale in SDL), duplicating and in some cases conflicting
with the shared CommandLineOptions parser. Consolidate all of it into
CmdLine.cpp/.h so there's a single source of truth, and drop the now-dead
remain_argc/remain_argv filtering in SDLMain.cpp since NativeInit no longer
reads argv itself.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PfFvWzpHxErWgRhKqqSewN