Commit Graph
6081 Commits
Author SHA1 Message Date
Henrik Rydgård aa9dfa85f6 Merge pull request #22788 from hrydgard/iphone-duo
iOS: iPhone Duo support (insets on the cover and inner screens)
2026-10-26 14:04:22 +01:00
Henrik RydgårdandClaude Opus 5.5 0ce79d4d7a Pause screen: Update the save state slots when a save finishes writing
Saves are written on a worker thread, so a state saved just before opening the pause
menu may not be on disk yet when the slots are listed, and the slot stayed empty.
Post SAVESTATE_SLOT_SAVED when a slot save is written, and recreate the pause
screen's views on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 11:49:44 +01:00
Henrik Rydgård 3aec770b22 Set Auto rotation to default, move more settings around. 2026-10-26 11:36:59 +01:00
Henrik RydgårdandClaude Opus 5.5 89d7d69551 Metal: Tell 16-bit format support by GPU family, not by platform
The simulator check didn't compile on macOS, where the define doesn't exist.
The simulator's device reports only the Apple1 and Apple2 families, so require
Apple3 instead, which every device iOS 15 runs on has. Also silence the BC
format availability warnings in DataFormatToMetal, since GetDataFormatSupport
already checks for iOS 16.4.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 10:34:29 +01:00
Henrik RydgårdandClaude Opus 5.5 8c8ecc8c32 Metal: Check the 16-bit texture formats through the context
The iOS simulator reports an Apple GPU but doesn't have the packed 16-bit
formats, so creating one tripped Metal's validation and aborted the app.
thin3d now maps them and only reports them as supported on an Apple GPU outside
the simulator, and GPU_Metal asks GetDataFormatSupport like the Vulkan backend
does. Without them, textures decode to 8888.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 09:50:10 +01:00
Henrik RydgårdandClaude Opus 5.5 f364f297f1 Touch controls: Use the full width when the side insets are only for a top corner
On the iPhone Duo's cover screen, the camera sits in a box in the top right
corner, which gives a side inset down the whole right edge in portrait. The
controls are far below it, so they lose width for nothing. iOS 27.1 reports
the box as an occlusion region, and from that we report how far down the side
insets are needed (SYSPROP_DISPLAY_SIDE_INSETS_BOTTOM). When that's within the
top third, the game screen and the touch layout editor skip the side insets.
The rest of the UI, including the FPS counter, still keeps them.

The regions can show up after the size and insets have settled, so the insets
are now recomputed on every layout, and a screen recreates its views when its
layout mode changes, since the touch controls are placed for the bounds they
were created in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 09:39:49 +01:00
Henrik RydgårdandClaude Opus 5.5 cccb296050 UI: Only even out the side insets in landscape
In portrait, a camera cutout is at the top, so a one-sided side inset is a
real edge and mirroring it just wastes space. The iPhone Duo's cover screen
has its sensor bar on the right in portrait, which left an equally wide
empty strip on the left of the main screen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 23:21:12 +01:00
Henrik RydgårdandClaude Opus 5.5 178aaadf1a iOS: Build and run on the simulator
Adds an arm64 simulator platform to the iOS toolchain. The simulator build
renders with Metal only: it doesn't link MoltenVK, and loads Vulkan at
runtime like the libretro core, so Vulkan reports itself unavailable and
the config falls back to Metal.

b-ios-simulator.sh builds the App Store variant and runs it on the booted
simulator, or on one of a given device type, for App Store screenshots of
devices we don't have.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:48:02 +01:00
Henrik Rydgård 3a41fca43d Merge pull request #22782 from hrydgard/play-crash-fixes-2
Fixes for crashes and ANRs from the 1.21-beta1 Play reports
2026-10-25 20:22:21 +01:00
Henrik Rydgård 8585cc77bf Merge pull request #22761 from cscd98/libretro-vr
Add Stereoscopic and Immersive VR to libretro core
2026-10-25 20:00:14 +01:00
Henrik RydgårdandClaude Opus 5.5 70f205f2ff Take output screenshots after the ImGui debugger has drawn
The screenshot readback ran in ScreenManager's post-render callback, and
on Vulkan it ends the backbuffer render step. renderImDebugger() draws
after that, so taking a screenshot with the debugger open made its first
SetViewport() write through a null render step (an assert in debug
builds, a Play crash report in release). Do the readback at the end of
the frame in NativeFrame instead, which also puts the debugger in the
screenshot, and remove the now unused callback.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 19:44:21 +01:00
Henrik RydgårdandClaude Opus 5.5 2ce1164725 Log: Don't open the log file while holding its lock
SetFileLogPath() held logFileLock_ while creating the folder and opening
the file, and both log (CreateDir at INFO, OpenCFile for content URIs and
on errors). Logging to the file takes logFileLock_, so the thread
deadlocked on itself, and every thread that logged after it blocked too.
A Play ANR shows the loading thread stuck in CPU_Init's
SetFileLogPath, and the main thread waiting on the same lock to log a
key press.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 19:37:37 +01:00
Henrik RydgårdandClaude Opus 5.5 daf46b1dbc Don't set up a signal alt stack in the memory fault handler
sigaltstack() is per thread. InstallExceptionHandler() runs in CPU_Init()
on the loading thread, which exits after boot, while the faults come from
the CPU thread and UninstallExceptionHandler() runs on the emu thread. So
our stack was never used, and the uninstall "restored" the loading
thread's previous alt stack onto the emu thread. On Android that's the
one bionic gives each thread and frees when the thread exits, so every
later fault on the emu thread ran its handler on freed memory. A Play
crash report shows scudo finding a corrupted chunk header when freeing our
stack in UninstallExceptionHandler().

The alt stack came with the Dolphin code, which needs it to handle its own
stack overflows. Ours only handles guest memory faults, so let the handler
run on the faulting thread's stack, or the alt stack that thread already
has. This also leaves a libretro frontend's alt stack alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 19:37:23 +01:00
Henrik Rydgård 518795e35f Cut down on outdated/useless REPORTing and logging 2026-10-25 17:33:44 +01:00
Henrik RydgårdandClaude Opus 5.5 b91bd6af86 Settings: Build the settings screen from pages described as data
Each tab is now a SettingsPage: a list of items (headers, settings, buttons,
hints, links to other pages) that a function builds and views are created
from. Settings are added with page->Setting(&g_Config.x), which takes
the label, the kind of view, the range or choices, units, labels for special
values and the default from the config. The pages keep what depends on the
UI (which settings to show and when, enable rules, and behavior that needs the
screen), and reach the screen through the page's SettingsPageHost.

A page can link to other pages, which open on top of it in the same tab,
under a bar showing where you are, like Tools > Developer tools > Graphics,
where the earlier pages can be clicked to go back to them. The bar has back,
search and help buttons, and the back key also goes back a page (or out of a
search) before leaving the screen. Each page remembers its scroll position.
In portrait there's no tab strip: a page links to the tabs, and the bar shows
just the current page's title. Rotating keeps showing the same page. Moving
between pages only focuses an item when navigating with a keyboard or
controller.

The developer tools are now pages linked from the Tools tab, instead of a
screen of their own. The in-game developer menu and
--start-screen=developertools open the settings on that page. The toggles of
live state (the remote debugger and the MIPS tracer) show and change it
through functions, and pretending to be in-game for the notification tests is
a flag on the OSD. The memory map and memstick tests, only reachable from
commented-out code, are removed.

Search lists the matching settings from all tabs and the pages they link to,
grouped by where they are. Each result goes to the setting on its page,
scrolled to and highlighted (outlined, when picked with a mouse or touch), or
for a page, opens it. This replaces the search tab and the old filter that
hid the views that didn't match, and with them ShowSearchControls. Changing
tabs leaves the search.

The pages, the bar and search are in SettingsPagesDialogScreen, between the
tabbed dialog screen and the settings screen, so the other tabbed screens
keep plain tabs.

Other visible changes:
- Reset buttons that went to the wrong value now go to the real default: the
  touch control auto-hide delay (was 20 seconds instead of 8), the mouse
  wheel delay (1 ms, below its own range, instead of 80) and the UI size (0
  instead of -1 on desktop). The alternative speeds get a reset button.
- Units are shown on more sliders (frames, MHz, seconds, Hz), and popups no
  longer spell out what 0 means in the units, since the zero label does.
  Units of up to three characters (%, x, ms, MHz) aren't translated.
- The Android HW scaler hides the scales the display is too small for,
  instead of cutting the list short.
- Choices are translated in the setting's own section.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 15:26:58 +01:00
Henrik RydgårdandClaude Opus 5.5 30c9dd3b45 UI: Let clickable views be highlighted without the focus
SetHighlighted outlines the view, following its round corners, to point it
out without giving it the focus, which would turn on keyboard navigation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 13:44:44 +01:00
Henrik RydgårdandClaude Opus 5.5 1c067f32a4 UI: Add ScrollView::ScrollToCenter and GetEnclosingScrollView
For bringing a given view into the middle of the scroll view it's in, rather
than just into sight like focusing does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 13:44:43 +01:00
Henrik Rydgård 965b07c527 A lot more round corner fixes and layout fixes. 2026-10-25 10:28:40 +01:00
Henrik RydgårdandClaude Opus 5.5 5567f51eb8 UI: Don't apply the large-screen inset on phones and tablets
They're held close and have their own system insets.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 09:41:38 +01:00
Henrik RydgårdandClaude Opus 5.5 8e353761c7 UI: Let scroll views extend under the bottom inset when they don't quite reach the edge
A vertical ScrollView that extends into the bottom inset (like a navigation bar) adds
that much space after its contents, so the end can be scrolled out from under it.
It only did that when the view reached the very bottom of the screen, and then added
the whole inset. Since 58fa522d48 inset the content area of dialog screens on
screens over 600 dp, a phone like the S21 Ultra (617 dp wide) got a 1.7 dp inset,
which turned the extra space off. Add exactly the part of the view that lies in the
inset instead, and skip the bottom shadow in that case.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 09:41:38 +01:00
Craig Carnell 28ac694bf6 Add libretro VR support 2026-10-24 17:32:15 +01:00
Henrik RydgårdandClaude Opus 5.5 58fa522d48 UI: Limit the width of dialog screens, and keep content off the edges of large screens
Dialog screens (tabbed, two-pane, simple and the others built on
UIBaseDialogScreen) center their content at up to 1400 wide, instead of
stretching settings rows and buttons across very wide windows.

On large screens, those screens and the main screen also get an inset on all
sides, since controls right at the edge are hard to see there. It grows with
the screen's smaller dimension, from nothing at 600 to the full inset at
1000, and is always full on TVs. The game, popups, and the screens that
position things to match the game's screen (display layout, touch control
layout and tests, custom button and visual mapping) are left as they were.

The root's bounds are computed by UIScreen::GetContentBounds, since
UI::Margins can't hold widths this large.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 16:12:01 +02:00
Henrik Rydgård 695fc865ab Fix some look problems on TabbedDialogScreen 2026-10-24 14:12:53 +02:00
Henrik RydgårdandClaude Opus 5.5 b54056685e UI: Space ChoiceGroups from their container instead of their own margins
ChoiceGroups no longer have hardcoded margins. A new ChoiceGroupList holds them,
with padding and spacing that give the same look as before. Settings tabs, developer
tools, the two-pane settings columns, the pause menu and the rest put their groups in
one, and a few screens adjust its padding and spacing to their layout.

Also add the two groups in the remote disc streaming settings that were created but
never added, and make the settings search look inside groups: it only checked a tab's
direct children, so with ChoiceGroups a filter hid everything.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:01:58 +02:00
Henrik Rydgård 13ffe2873f Merge pull request #22757 from hrydgard/more-ui-work
UI: Manual additions and some Claude code review
2026-10-24 13:46:50 +02:00
Henrik Rydgård 5a20ef6616 Merge pull request #22753 from hrydgard/pathbrowser-network-folder
PathBrowser: Add a Network folder on Windows
2026-10-24 13:16:21 +02:00
Henrik Rydgård 505ae1c965 Cleanup 2026-10-24 12:54:49 +02:00
Henrik RydgårdandClaude Opus 5.5 8b1ffb9b39 UI: Apply GridLayout children's margins, drop the unused grid orientation
Grid children are given LinearLayoutParams almost everywhere, so their margins are
taken from those as well as from GridLayoutParams, which gains margins.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:49:45 +02:00
Henrik RydgårdandClaude Opus 5.5 7ddd0ce33b UI: Skip hidden views in AnchorLayout
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:49:44 +02:00
Henrik RydgårdandClaude Opus 5.5 5c4b1be7f5 UI: Let LinearLayout place its children as a group along its direction
SetContentGravity centers the children, or moves them to the end, when they don't
fill the layout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:49:44 +02:00
Henrik RydgårdandClaude Opus 5.5 bff97cbb09 UI: Fix two LinearLayout measuring and layout bugs
Lay out children across the layout direction within the layout's bounds rather than
its measured size, which differ when a parent clamps it. And count weighted
children's margins when growing to fit them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:49:32 +02:00
Henrik RydgårdandClaude Opus 5.5 e5b1760bff UI: Add padding to GridLayout
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:36:17 +02:00
Henrik RydgårdandClaude Opus 5.5 d9126ad685 UI: Remove the unused FrameLayout
Nothing used it, and it didn't work: it never set its own size, centered vertically
using the width, and didn't lay out its children.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:36:16 +02:00
Henrik RydgårdandClaude Opus 5.5 7fa69ac27f UI: Center views inside their margins
Centering gravity centered in the whole cell, ignoring the margins, so a view with
uneven margins ended up off center.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:36:07 +02:00
Henrik RydgårdandClaude Opus 5.5 81d078efc4 UI: Fix ScrollView's handling of its child's margins
The child was measured inside its margins, then had them subtracted from its bounds
again, and the leading margin along the scroll direction was never applied, so the
end of the content couldn't be scrolled to. Also include the margins when wrapping
the content, and page horizontally by the width rather than the x position.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:35:56 +02:00
Henrik RydgårdandClaude Opus 5.5 cfb0b1131b UI: Clamp fixed-size views to an AT_MOST spec
MeasureBySpec compared the spec against the view's previous measurement instead of
its fixed size, so a fixed-size view larger than the space offered wasn't clamped
unless an earlier measure happened to be larger.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:35:55 +02:00
Henrik RydgårdandClaude Opus 5.5 e30c8f7141 UI: Give LinearLayout a working padding setting
LinearLayout had a public padding member that only DisplayLayoutScreen set. Make it
private with a setter, and fix the measuring: the vertical retry used the horizontal
padding, the AT_MOST retries and the weighted children ignored it, and growing to fit
weighted children counted it twice.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 12:24:21 +02:00
Henrik Rydgård ad132d8f1b Apply rounded corners to all Choices by default, apply proper group rounding in many places. 2026-10-24 11:21:40 +02:00
Henrik Rydgård 3dc01bd318 Turn off roundrect test on LogoScreen, reduce logging 2026-10-24 10:35:11 +02:00
Henrik RydgårdandClaude Opus 5.5 adfbd7a259 PathBrowser: Abandon a network listing when navigating away
PathBrowser lists on one thread, so going up from the network folder
waited for its listing to finish (up to 8 seconds) before the drive list
showed. Pass GetFilesInDir a cancel flag that PathBrowser sets whenever
the path changes, or on destruction, and check it while waiting.

Also don't probe the drives in the drive list for game folders.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 02:44:17 +02:00
Henrik RydgårdandClaude Opus 5.5 494102acb1 PathBrowser: Add a Network folder on Windows that lists the servers
The drive list gets a Network entry, the path //, which lists the
servers in the shell's network folder (the old WNet enumeration finds
nothing on a modern network). Going up from \\server now lands there
instead of in the drive list.

Network listings run on a thread of their own with a deadline, since
they can block for a long time and can't be cancelled: the network
folder keeps its enumerator open for half a minute after it has found
everything, and NetShareEnum on a server that doesn't answer hangs too.
We take what has turned up after a quiet second, or after 8 seconds at
the most.

GameBrowser skips probing the servers for game folders, which would
contact each one from the UI thread.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 02:44:05 +02:00
Henrik Rydgård 0028f30da6 Merge pull request #22751 from hrydgard/ui-roundrects
UI: Introduce rounded rectangle primitive, round off the corners of settings and some main screen buttons
2026-10-24 02:29:52 +02:00
Henrik Rydgård 1a2d140652 Fix corner rounding bug when there's only one item 2026-10-24 01:52:34 +02:00
Henrik Rydgård c44a12363f Prototype the new ChoiceGroup component 2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 2ed5aa264c UI: Let round rects keep some corners square
A RoundCorners mask picks the corners to round. A square corner is drawn
from the corner image's edge profiles, split along the diagonal, so its
antialiasing matches the straight edges and it needs no extra draw call.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 1fb8c3090b UI: Fade out the scroll bob when the scroll view is left alone
It stays for a second after the last activity, then fades out over half
a second. Any scrolling, touch, bob drag, or the mouse over the bob's
strip brings it back within a tenth of a second. It also shows briefly
when the view appears, so it's clear that it scrolls.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 110fb7fd6a UI: Rounded rectangles from precomputed atlas corners
UIAtlas generates an antialiased white corner for each radius in
ROUND_RECT_RADII (8, 16, 24, 32 UI units) and each style (filled, and
thin/medium/thick outlines from ROUND_RECT_THICKNESS) at the atlas DPI.
DrawBuffer::DrawRoundRect mirrors the corner into all four corners and
stretches the corner's last texel row/column along the edges, so a
round rect is 9 quads (8 for outlines).

Other radii use the nearest corner, scaled. The LogoScreen temporarily
draws some for testing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 ce7512c491 PathBrowser: List the shares of a network server on Windows
Navigating up from a share like \\QUAD\PSP ISO lands on \\QUAD, which
FindFirstFile can't list, so it showed as an empty folder. Enumerate the
server's disk shares with NetShareEnum instead, skipping admin shares.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 16:12:52 +02:00
Henrik Rydgård 01508a9f38 Merge pull request #22743 from hrydgard/text-drop-shadow
TextDrawer: Soft drop shadow flag, shared with the UI atlas
2026-10-23 13:54:42 +02:00
Henrik RydgårdandClaude Opus 5.5 e86113afa3 Fix the Windows build of draw_text.cpp
CommonWindows.h defines WIN32_LEAN_AND_MEAN, which leaves out what the
Direct2D/DirectWrite headers in draw_text_uwp.h need. Include
<windows.h> with just NOMINMAX instead, and keep std::max out of
DropShadow.h so it doesn't depend on which order windows.h comes in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:04:18 +02:00