Shares the iOS frontend, with camera, location, motion, photo/document
pickers, share sheet, clipboard, microphone, battery and rotation compiled
out. The view controller is a GCEventViewController, so controllers only
reach us through GCController, and the Siri Remote is read as a micro
gamepad and sent as arrow/enter/escape/back keys. tvOS has no persistent
storage besides NSUserDefaults, so the memory stick lives in Caches for now.
Configure with -DIOS_PLATFORM=TVOS. The MoltenVK submodule gets the tvOS
slice from the same stock v1.4.2 release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tvOS doesn't have sigaltstack, which broke the libretro tvOS build. The
exception handler now runs on the faulting thread's stack there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
It's a submodule we don't patch, and at /W4 it produced a wall of
conversion warnings in every build.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On iOS, the base search stepped up from 0x100000000 in 8MB increments,
right through the app's binary and early heap: about 100 failed
vm_remaps (each logged twice) every time a game started. On macOS,
Find4GBBase freed its probe allocation before the views were mapped.
Now the kernel picks a free range of exactly the span the views need,
which stays reserved and inaccessible, and the views are mapped over it
with VM_FLAGS_OVERWRITE. One try, no race, and the gaps between views
fault. Unmasked (macOS), ask for 4GB alignment first, which lets the
ARM64 JIT pointerify. The old search remains as a fallback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It checked views[i], the view that failed, instead of views[j], so after
a failed try it could release views it should skip, or skip ones it
should release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before Android 11, decorFitsSystemWindows(false) sets LAYOUT_STABLE, and
the system bar insets then keep the space of hidden bars. So in
landscape, where the status bar is always hidden, the UI had an empty
band at the top (seen on a Galaxy S8 and a Pocophone F1). Count only
the bars we show, and reapply the insets after changing that.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With 270 degree pre-rotation, the rotated y runs along the physical
height, but was clamped to the physical width, so scissors on the left
of the screen moved. This was #15773, which got that pre-rotation
disabled. The 90 degree case had the bounds swapped too, but only too
loose there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 270 degree pre-rotation was disabled in e6b7817cc6 for #15773 (UI
disappearing in reverse landscape on a Poco X3 Pro), which left reverse
landscape to the compositor, and every present returning SUBOPTIMAL. Try
it again: rotation is handled in place now, and the stale extent after a
rotation is fixed, so it needs retesting on Adreno.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before Android 11, decorFitsSystemWindows(false) sets LAYOUT_STABLE, and
the system bar insets then keep the space of hidden bars. So in
landscape, where the status bar is always hidden, the UI had an empty
band at the top (seen on a Galaxy S8 and a Pocophone F1). Count only
the bars we show, and reapply the insets after changing that.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Android it's set on the Java UI thread right after writing g_display,
and read on the render thread, which then copies the dp size into the UI
bounds just once. With a plain bool the render thread could see the flag
before the new size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since rotation is handled in place, the swapchain is recreated right
after surfaceChanged, and the surface can still report the previous
orientation's extent. On a Galaxy S8+ this happened in about one in four
rotations, leaving a portrait swapchain on a landscape window or the
other way around, squashed into half the screen until the next rotation.
If the extent is exactly the window size transposed, use the window size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It only replaced the top screen, but most callers switch from a dialog:
Play on the game screen, the store, the installers, the framedump
launcher. The screens under it stayed alive, so launching a game from the
game screen and exiting it left [MainScreen, MainScreen], one more each
time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Remove From Recent" and "Delete Game" called switchScreen(new MainScreen)
from a popup, which only replaces the top screen, so the old MainScreen
and the GameScreen of the removed game stayed in the stack underneath.
Close the GameScreen instead once the popups above it are gone, and have
MainScreen refresh its browsers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ContentUri catches Exception, but an Error got through, and none of the
native wrappers checked. A thrown int call returned 0, which reads as
SUCCESS, and the exception stayed pending, so the next JNI call aborted.
copyDocument/moveDocument are API 24/25 while the folder browser is
offered from 21, so those threw NoSuchMethodError there - now they return
a failure so the callers fall back.
Also convert non-URI names to Java's modified UTF-8.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Catch takePersistableUriPermission failing in ShortcutActivity, as
DocumentResultProxyActivity already does. Some pickers don't grant it.
- Treat a cancelled permission request (empty results) as denied.
- Handle getCacheDir() returning null, like getFilesDir().
- Don't let the input device debug string, read from the render thread,
throw ConcurrentModificationException into native code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
requestLocationUpdates() throws IllegalArgumentException for a provider
that doesn't exist (WiFi-only tablets, TVs, Chromebooks), which crashed
any game opening the GPS. Register each provider separately.
Without an IR blaster, the IR service is null, and InfraredHelper was
created anyway, so a sceSircs call NPE'd. Also catch transmit() failing,
like the SecurityException on API 21/22.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It does file I/O, which goes through Java for a content URI memstick, and
getEnv() asserts on an unattached thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Process() doesn't run while achievements block execution at boot, so
exiting then left the auto-load queued. The next game ran it, loading the
previous game's state, and calling back into the deleted EmuScreen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UpdateSample() can replace and free a sample that Mix() is reading on the
audio thread, such as when changing the achievement sound while
previewing it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Back choice is hidden during the move, but the key still closed the
screen. The destructor then blocked the UI thread until the move was
done, and FinishFolderMove() never ran, leaving the config pointing at
the old, now emptied folder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
That set dialogFinished_ to the caller, which processFinishDialog() then
deleted while it was still in the stack, leaking the popup. Take the
pointer out first and loop. Nothing does this now (MemStickScreen and
DevMenuScreen work around it), but it's easy to do by accident.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ignoreInput_ doesn't stop a tap outside the box or Enter on the default
button, which both call TriggerFinish() again during the lead-out, firing
OnCompleted and the choice's OnChange a second time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
noSearchResults_ and clearSearchChoice_ only exist with the search tab,
which depends on orientation, and were never reset. Rotating with a
filter set wrote into the deleted views, and GAMESETTINGS_SEARCH, which
goes to every screen in the stack, hit null ones on screens without
search controls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetRequesterToken() only returns the views token inside CreateViews(),
but tabbed screens create every tab other than the first on demand, and
the persist restore after CreateViews() can create one too. So text
inputs and file choosers there (Networking tab nickname, search filter,
RemoteISO and achievement settings) registered their [this] callbacks
under the screen token, which a recreate doesn't forget. Open the native
input box, rotate, press OK, and the callback wrote through the deleted
view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Popups are transparent, so the screen under one still renders, and
render() ran DoRecreateViews(). A RecreateViews() on that screen (a
resize or rotation on DisplayLayoutScreen or TouchControlLayoutScreen,
MainScreen on RECENT_FILES_CHANGED from the recent-files thread, the
achievements login finishing) then deleted the choice that opened the
popup. Closing the popup wrote through it and called its handlers.
Leave the old views in place until the screen is back on top, where
update() recreates them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NativeIsAtTopLevel() made a virtual call on the top screen from the Java
UI thread, while the render thread could be deleting it. Two quick Back
presses could hit that. Cache the flag in ScreenManager::Update()
instead, like passInputToMapper_ (now atomic too).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixes#21391
Makes the hardware backends tessellate Bezier patches and splines with the
GE-exact evaluation the software renderer uses. Fixes the z-fighting glow
under the floor grates in Coded Arms.
AGENTS.md: Put "- Fixes #N" first in commit messages
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Fixes#18091
The upstream part hadn't been synced since 2023. Tools/update_gamecontrollerdb.py
replaces it with the current upstream file and keeps PPSSPP's own mappings,
dropping those whose GUID upstream now has (after SDL's old-GUID conversion),
since a later line overrides an earlier one. 193 of 207 were covered,
including the PS3 and 8BitDo Ultimate 2C one-offs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>