Commit Graph
514 Commits
Author SHA1 Message Date
LibretroAdmin a86d86a22c HAVE_RGIF: GIF images through image_texture, image_transfer and the tasks
rgif now loads GIFs wherever RetroArch loads images: thumbnails and
backgrounds through task_image, the image viewer core, the file browser
and the image_texture/image_transfer layer underneath them all.

rgif:
- the image_transfer interface the other decoders have:
  rgif_set_buf_ptr, rgif_set_avail, rgif_header_ready and
  rgif_process_image, giving a 32-bit texture of its own, ARGB32 or
  ABGR32 in native byte order
- RGIF_ABGR, for ABGR32 on big-endian targets
- a file not all read yet gives IMAGE_PROCESS_WAIT and is not touched;
  the image is decoded in one call once it is all in

Wiring, as HAVE_RTGA is wired:
- IMAGE_TYPE_GIF (image.h) and NBIO_TYPE_GIF (task_file_transfer.h),
  both appended so no other value moves
- image_texture_get_type: .gif
- image_transfer: new, free, start, is_valid, set_buffer_ptr, process,
  set_avail; the support matrix gains a GIF column
- task_image and task_file_transfer: GIF is an image task
- FILE_TYPE_GIF ("gif", "GIF"): opened as image content
- the file browser's image extensions, the image viewer core's
  extensions, the System Information list ("GIF (RGIF)") and
  --features ("RGIF"); SUPPORTS_RGIF in config.features.h
- griffin.c, Makefile.common (rgif.o), configure (HAVE_RGIF=yes), and
  next to HAVE_RTGA in every platform Makefile, the Android, Apple,
  MSVC, UWP and QNX projects and the CI compile matrix
- tools/settings_check.py and tools/stack_budget.py check with it

Also: menu_displaylist's image viewer extension list wrote "tga"
without moving its length on, so "webp" and "dds" landed on top of it
and TGA dropped out of the list; it moves on now.

Tests: rgif_test checks the texture in both byte orders against
rgif_decode, the wait before the whole file is in, and a refused file;
image_decode_fuzz sweeps GIF too (every byte of a small GIF set to
edge values, and every truncation), under ASan and UBSan. The griffin
unity build with every codec, the touched objects in a configured
build, the MinGW/C89 syntax check, the overlap check and the stack
budget pass.
2026-11-10 20:00:24 +01:00
LibretroAdmin 07bdbe49bd android: a display move only crosses JNI when it can start
A move to a display no longer in the list went C to Java, posted to the
UI thread and came back through nativeMoveFailed() to report what C
already knew. It now fails in C. The display id comes straight from its
list place rather than a search, and the reflected Display.getType() is
read once per display id.
2026-11-09 18:56:12 +01:00
LibretroAdmin 7f5b2f47e6 android: a display list's numbers are read where Java wrote them
nativeDisplaysChanged() took five int arrays and made twelve JNI calls
to measure and copy them. The numbers now go into the buffer the touches
use, with each display's size packed, so only the names cross as
objects. publish() no longer clones the flags either.
2026-11-09 18:56:09 +01:00
LibretroAdmin e4b48e9f9b android: a second display's touch crosses JNI once per event
SecondDisplayController called native code and woke the app thread's
looper once per finger for every MotionEvent. The pointers now go into
a direct buffer shared with native code, packed as VIDEO_POS_PACK
positions, and one nativeTouches() call reads them in place and wakes
the looper once. The ring's records carry the packed position the poll
stores, so it no longer packs them.
2026-11-09 18:56:05 +01:00
Davey Hughes 56952c9a25 Android: follow-up fixes for screens on other displays (gl and vulkan) (#20044)
* rgui: upload the menu again to the video driver a reinit makes

Closing content with RGUI up left the screen black until a key press.
The staged content load presents frames between its stages, so RGUI
can upload its framebuffer to the old video driver and clear FB_DIRTY
just before the driver stage replaces that driver. The new one has no
menu texture, nothing in the menu changes, and RGUI never uploads
again.

command_event_reinit sets FB_DIRTY after its reinit, but the staged
load and video-only resets reach the menu only through context_reset,
so RGUI marks its framebuffer dirty there. The menu_icon_ownership
harness gets an rgui lane that reinits the video driver and checks
the next upload reaches the new one.

* video: draw a frame queued before a view count shrink with its views

Under threaded video the wrapper ran SET_VIEW_COUNT ahead of the frame
it had already claimed, so a closing core's last queued frame drew
after its view chains were gone: whole, both screens stacked, until the
menu's first frame replaced it.

The wrapper still answers at once, but a smaller count reaches the
driver only once the frames queued before it have drawn. A grow applies
at once as before, with the main thread waiting for the preset parse.
Vulkan and glcore therefore build view chains only when the count
grows, so a deferred shrink never parses a preset. They also keep the
headset's whole-frame chain across a count change: it does not depend
on the count, and with chains built only on a grow nothing would
rebuild it after a shrink to 0.

* android: don't make a second screen's EGL surface once Java took it back

Java can take the second screen's surface back between the poll that
hands it over and the frame that makes its EGL surface. Skip making one
then; the screens go back to the main window as they do when a make
fails. Java can still take the surface back during the make itself; the
node's own ANativeWindow reference keeps the window alive through that.

* android: ask a display move by display id and number it

A move named its display by its place in the list, and Java looked
that up later on the UI thread against whatever list was newest, so a
display coming or going in between could send RetroArch elsewhere.
Java now gets the display id and fails the move when that display is
gone. Native code now counts the ids when it compares display lists,
so a display unplugged and plugged back is a new list and its failures
clear.

A failure was a bare flag, so one move's failure could end the next
move and mark its display unable. Each move now carries a number and a
failure counts only for the move it names. The move code also loses
two stores that were always overwritten.

* android: end the refocus hold after a move only when it is done

A move onto a display that is no longer valid now fails at once
instead of waiting out its timeout. The hold that keeps RetroArch's
window unfocusable after a move ends only on the focus loss Android
answers it with, not on a gain that was already queued; an earlier
hold ends only once the new move has started; and onStop() ends a
running hold rather than leaving it to its timer.

* video_windows: tell an external monitor by its display type

Android lists the AYN Thor's two panels and a DP monitor with the same
unknown connection type, so Auto took the monitor for a second panel:
on a one-screen phone Game Screen 2 went to it. Java now reads the
hidden Display.getType() by reflection where it can and passes
internal or external with each display. A display typed internal is
never a monitor; where the type is unknown, a non-default display with
an unknown connection and a product name now counts as one.

* video: give a Presentation on another display its own UI wait

The menu waits up to a second for the Presentation it is laid out for
to show, but the wait was only counted from the first one. Once a
Presentation had shown, one made on another display had no wait left
and the menu fell back to the main window at once.

* input: keep the menu's mouse where it was while the menu is in a window

With Mouse Support on, the menu moving into the Presentation read as a
mouse move to (0,0) for a frame, which marks the pointer active there.
The view keeps the main window's last mouse position instead.

* input: match a window's touches only from a windowed publication

A publication without windows after one with them left the old window
ids in place, so a window's touch lookup could still match them. Only
touches published in a window match now. No Android path publishes
that way today.

* vulkan: copy a waiting window's views on a new frame

A screen window that was up but had no free image on a new frame did
not get its views copied, so a later repeat that drew it, as a pause
with the menu there does, left black areas where those views go. They
are copied on every new frame now.

* video: load the idle logo from the assets directory without widgets

The idle logo's path came from the widgets, which only start with
On-Screen Notifications on, so with them off the idle window stayed
black. Without the widgets' path it is built from the assets
directory the way the widgets build it.

* android: hold the focus when Android moves RetroArch between displays

When the display RetroArch is on, or moving to, goes away, Android
moves the Activity to the default display and can send that display's
focus gain before the removed display's loss. A task moved to another
display and straight back gets no configuration change, and can read
the old display's loss last too. With the loss last, RetroArch pauses
while Android keeps it focused, and no later focus event wakes it.

Count these as moves, as RetroArch's own moves are: a removal, and any
change of RetroArch's display it did not ask for. From Android 10, with
more than one display, so is losing and regaining the top-resumed place
while focused, or regaining it unfocused within a second of losing it
after a loss no hold asked for. The refocus hold that covers a move's
late loss then covers these too: the window is made unfocusable until
Android has taken the focus, then gets the gain again. A gain already on
its way skips the hold, and a move marked while RetroArch is focused
ends after a second with no focus event.

* samples: close test gaps in video_windows and android_focus

The is_default check set the flag and read it back, so it could not
fail; Auto's fallback to the default display now shows the flag at
work. Cover a display listed without an ordinal, and display keys at
sizes 0 to 3, two unnamed displays and a 63-character name. Drop a
stale note from android_focus_test's header.

* video: fix wording left over from the display rows

The Screen Display sublabel still named the Separate mode, a setting
the display rows removed; it now says "when it has a window of its
own". The warnings for a screen window that can't be drawn, or whose
surface is gone, said its screens go back to the main window, but a
window that shows only the menu reaches them too. They now read like
the one for a window that can't be made: what it shows stays in the
main window.

Comments that no longer matched the code are reworded. pointer_frame's
model of the driver before the publication is named was_* rather than
today_*, and a wrong commit hash and a plan tag go from the tests.
video_driver_free_internal() uses VIDEO_ANDROID_RELEASE_KEPT_WINDOWS()
rather than open-coding it, with no change in behaviour.
2026-11-09 18:59:16 +01:00
davey 6d99b615d4 android: tell native code which display is the default one
Each published display now says whether it is Android's
DEFAULT_DISPLAY. On a dual-screen handheld both panels report the
same unknown connection, so this is the only way to tell the main
panel from the second one when choosing a display automatically.
2026-11-07 21:28:26 +01:00
davey 78dda5539e android: drop the second display's key relay and focus count
The second display's Presentation never has the focus now, so it never
receives a key or a stick move and never reports a focus change. Its key
and motion relay, the ring that carried them to native code, its focus
commands and its part in the focus count go: RetroArch's focus is its
Activity's, as before the second display existed.
2026-11-07 21:28:26 +01:00
davey 8ccf91badb android: take the focus back after RetroArch moves to another display
When the focused Activity moves between displays, Android sends the new
display's focus gain and the old display's loss in either order. If the
loss comes last, the Activity is never told it has focus again, so
RetroArch stays paused and stops drawing.

After a move that leaves it unfocused, the window is made unfocusable
until Android has taken the focus, then focusable again, which makes
Android send the gain. Under load the window manager can apply a short
toggle's set and clear back to back, and the focus never moves, so the
flag is held until that focus event, or a second at most. A gain ends
the move only once it has outlasted a traversal, so a loss that follows
it still refocuses.
2026-11-07 21:28:26 +01:00
davey 218637a061 android: let RetroArch move its Activity onto another display
Native code can now ask Java to move RetroArch's Activity onto a
display of the published list. The request is posted to the UI thread
and never waited on. Java starts the running Activity again with
setLaunchDisplayId and a copy of its own intent, so the same content
is seen and nothing restarts; Android then moves the Activity in place.

When the move cannot start (before Android 8, a display that has gone,
or a launch Android refuses), Java logs it once and raises a flag the
main thread reads on its next frame.
2026-11-07 21:28:26 +01:00
davey 254c6ab2c4 android: publish refused displays and display models
A display whose Presentation failed is now published as refused instead
of invalid. It stays a valid target, so Auto and a saved name still
point at it and RetroArch can move onto it; only a Presentation is kept
off it. A list that changes only in which display is RetroArch's own
keeps it refused.

Each display also carries its DeviceProductInfo name (Android 12 and
later), which the menu shows next to the display's name.
2026-11-07 21:28:26 +01:00
davey 2242e5f4f0 android: keep the pad on RetroArch after a tap on the second display
The second display's Presentation could take the focus, so a tap there
gave its display the keys. When the content closed and the Presentation
went, the keys stayed on that display and reached the app under it: on
the AYN Thor the pad drove the launcher's bottom-screen activity instead
of RetroArch's menu. Taking the focus back with moveTaskToFront did not
help, since RetroArch's task was already in front on its own display.

The Presentation is now never focusable. Touches still reach it; keys
and sticks always go to RetroArch's Activity.
2026-11-07 21:28:26 +01:00
davey 579027cb3f android: keep running when the display density changes
Changing Android's Display size setting, or RetroArch's window going to
a display of another density, relaunched the Activity and RetroArch
exited. The Activity now handles a density change itself.
2026-11-07 21:28:26 +01:00
davey 088da6b2bc android: read a display's size without the Activity's adjustments
From API 31 a Display obtained with the Activity's Resources reports
RetroArch's own window, so the size published for a display could be
RetroArch's window's, and it changed when that window went to another
display. A Display with no Resources attached reports the display's own
logical size.
2026-11-07 21:28:26 +01:00
Craig c4f751bd99 android: only enable CoreSideloadActivity on debug builds (#20024) 2026-11-07 03:04:46 +01:00
665064a6f4 Android: custom Vulkan driver packages (Turnip) on Adreno (#19955)
* Android: load custom Vulkan driver packages on Adreno

Sideloaded arm64 builds can load a Vulkan driver package (a zip with
meta.json and one .so, such as Mesa's Turnip) on Adreno GPUs with
Android 9 or later. As libadrenotools does, a private copy of the
system Vulkan loader is opened in a linker namespace of its own, where
a small hook library hands it the custom driver as its HAL. The new
video_vulkan_driver_path setting names the zip; empty is the system
driver, and core, content-directory and game overrides carry it. The
package is checked and extracted to internal storage once per version.

Packages from any vendor are accepted. Only Mesa ones have been
tested, so any other vendor logs that it is untested and that its load
cannot be confirmed. A Mesa package that leaves the stock Qualcomm
driver running, as the hook does when the custom library fails to
load, counts as failed.

When the custom driver fails to create an instance, a device or the
first swapchain, the context is redone on the system driver in the same
process. A driver that takes the process down during its init is turned
off for that core until it is picked again, and a "Safe start" launcher
shortcut starts RetroArch once on the system driver. Play Store, Quest
and API 16 builds do not carry any of it.

Co-authored-by: lim2222 <[email protected]>

* Menu: Custom Vulkan Driver setting

Settings > Video > Output gains a Custom Vulkan Driver row on builds
and devices that can load one. OK opens a browser for driver zips in
<system>/vulkan_drivers, Start goes back to the system driver, and the
sublabel says when the chosen driver crashed during its last start and
is off for the running core. While RetroArch runs from the Safe start
shortcut, the row reads "System (Safe start)" and the sublabel names
the driver the next normal start uses. The choice applies at the next
Vulkan start; picking a driver never restarts video.

Co-authored-by: lim2222 <[email protected]>

* archive: a streamed Deflate member writes no more than its size

A Deflate member streamed to its file a window at a time is now
refused as soon as it inflates past the size its headers give, so no
more than that size reaches the disk, as for Deflate64, LZMA, zstd and
bzip2. Both the zlib and the built-in inflate paths.

Lane: archive_stream_test "inflate bound", a member of stored blocks
that holds 1 MiB under a 300 KiB size, counting what the VFS writes.

* Vulkan custom driver: merge onto master, ask the GPU once

The menu row moves one entry up its table, clear of the manual-scan
row master renamed. Whether the device can take a custom driver is
asked once and kept, rather than each time the Video settings list is
built.

* Vulkan custom driver: extract a picked package in a task

Picking a driver pushes a task that checks the package and extracts
it on a worker, into the dir the next Vulkan start looks in, so that
start only maps the library rather than inflating up to 64 MiB while
the main thread waits. The task reads only what it was handed.

It writes through a .prep dir of its own; a context init that gets
there first extracts through its .tmp as before, and whichever
finishes second keeps the other's. The once-per-process prune leaves
.prep dirs alone while a task runs. A driver set by an override,
never picked, is still extracted at its first start.

---------

Co-authored-by: lim2222 <[email protected]>
Co-authored-by: LibretroAdmin <[email protected]>
2026-11-04 21:46:42 +01:00
Codex d44a83afda android: integrate asynchronous native GPU recording
Connect the MediaCodec recording frontend to worker-owned encoders and
Vulkan capture without frame-path readbacks or new waits. Keep capture
leases through native retirement and defer cold queue work on contention.

Calibrate AAC presentation edits, omit an empty audio source, and drain
healthy recordings before video resets. Retain the teardown path for
failed or ambiguous GPU work. Include portable lifecycle, audio, file
and capture regressions.
2026-10-27 13:02:24 +01:00
Codex 2f46e4c979 android: integrate hardware movie decoders with Vulkan imports 2026-10-27 10:40:52 +01:00
Codex ac57896750 Android: align RELRO for every 64-bit flavor
Quest sets PLAY_STORE_BUILD, which excluded it from the explicit 16 KiB linker settings. NDK 29 aligned its LOAD segments but left RELRO ending at a 4 KiB boundary. Apply both maximum and common page sizes by ABI for every flavor, including Quest and Play Store builds. Keep the APK alignment checks unchanged.
2026-10-27 05:09:06 +01:00
Bradleyw801andClaude Opus 5.5 3b233712a4 Android: ask for unbuffered stick and touch input (#19925)
Android batches motion events and hands them to the window at the next
display frame, so a stick move or a finger drag reached the input
queue up to a frame late. Ask for unbuffered dispatch of joystick and
pointer sources (API 30+), posted to the decor view since the request
only takes on an attached view.


Claude-Session: https://claude.ai/code/session_01YTBjv1trXRxAXvNwCt89Ka

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-27 04:14:41 +01:00
Codex dc34e9b166 Android: preserve 16 KiB alignment in sideload builds
Use 16 KiB maximum and common linker page sizes on arm64 and x86_64 so sideloaded libraries do not require the Android compatibility mode. Check LOAD and RELRO segments in the media-enabled frontend and packaged 64-bit APKs, with ZIP alignment validation.
2026-10-27 01:41:32 +01:00
LibretroAdmin ccffeb553a slang: drop vendored glslang and SPIRV-Cross
rslc is the slang compiler everywhere. ./configure --enable-glslang
--enable-spirv_cross links the system's glslang and SPIRV-Cross next to
it when both are found, and only then does Settings > Drivers offer a
Shader Compiler choice between the two: one setting read per stage
picks the path, with nothing copied.

The MSVC 2010 project builds Vulkan and rslc. Samples that used the
vendored copies run on rslc or the system's SPIRV-Cross.
2026-10-25 08:52:52 +01:00
LibretroAdmin 762fd1c38f slang: rslc alone in the MSVC, older Xcode and griffin builds
The MSVC projects (2012 to 2026, UWP and the legacy Android one) build
rslc through griffin_rslc.c in place of glslang and SPIRV-Cross, as do
RetroArch_OSX107 and RetroArch_iOS11_Metal through griffin.c, and the
unix griffin build in Makefile.griffin.  WANT_GLSLANG, which no
platform makefile set any more, is gone from Makefile.griffin, ngc and
wii.
2026-10-25 07:53:49 +01:00
LibretroAdmin 08fcf5c5e0 slang: rslc alone on Linux, Windows, Android and Wii U
configure builds rslc by default, and outside Apple builds glslang and
SPIRV-Cross only when asked for (--enable-builtinglslang
--enable-builtinspirv_cross); Metal keeps them on macOS.  Android
builds rslc alone (HAVE_RSLC_ONLY=0 brings the two back), and Wii U
takes rslc in place of SPIRV-Cross.  The vendored glslang and
SPIRV-Cross stay in deps/.

rslc reads the literal strings of SPIR-V lowest byte first on every
host, as the words store them, so names and the GLSL.std.450 import
read right on big-endian Wii U.
2026-10-25 03:19:08 +01:00
LibretroAdmin fcfd5bf4eb android, msvc: build rslc into the slang drivers
Android's Vulkan slang drivers and the MSVC 2015-2026 projects now
build rslc (griffin/griffin_rslc.c) beside glslang and SPIRV-Cross, so
rslc compiles their stages with those two behind it for one it
refuses. On Android HAVE_RSLC_ONLY=1 builds without them: 13.8 MB
for arm64-v8a against 18.9 MB with them.

rslc's GLSL printer indexes its swizzle strings rather than adding to
them, which clang warns about.
2026-10-24 14:56:18 +02:00
Davey Hughesandlibretroadmin 74b7b06fa5 Android second display output for video views (gl and vulkan) (#19905)
* android: drain the sensor queue with both sensors off

Losing focus disables the accelerometer and gyroscope, but an event
already in the sensor queue stays there, and android_input_poll_user()
only read the queue while a sensor was on. The queue's fd stays
readable, so ALooper_pollOnce() keeps returning LOOPER_ID_USER and
android_input_poll() spins at full CPU. The pause and stop
acknowledgements wait for the next runloop iteration, which never
comes: RetroArch logs "App thread did not acknowledge activity state",
the launcher shows late and the app hangs until it gets focus back.
A swipe home hits it often, since focus is lost before onPause.

Always drain the queue and apply the events only while a sensor is on.

* gl2: make a regrown pass texture as the pass's first one

gl2_recreate_fbo() gave the larger texture no filter, so it kept GL's
default GL_NEAREST_MIPMAP_LINEAR with a single level. On GLES that
texture is incomplete, and once a viewport-scaled pass grew, the next
pass sampled black; desktop GL with texture storage gets a complete
one-level texture. Make it with gl2_create_fbo_texture(), as at init, so
it also keeps the pass's filter, wrap mode and float or sRGB format. The
feedback texture is regrown the same way.

* video: per-screen window settings and the screens' window plan

Each screen gets window settings in the config,
video_screenN_window_mode, _x, _y, _width, _height, _monitor and
_fullscreen: Attached, Separate or Hidden, and where a window of its own
goes and how big it is. Screen 1 is always Attached; the others default
to Separate. video_windows_enable, off by default, is the switch that
lets screens leave the main window at all.

video_windows_plan() turns those settings into a frame's plan: the
screens the main window draws, the ones that go to windows of their own
and the hidden ones. A screen stays in the main window while the switch
is off, where the driver has no windows, or once the renderer has sent
it back. The views layout takes exclude_screens, so the main window lays
out only the screens it keeps, and a window only its own.

This is the base the desktop secondary windows and the Android second
display share. The video_windows harness, run in the Linux gfx samples
workflow, checks the plan and a layout with screens left out.

* video: contract for secondary screens' native windows

A context can offer windows of their own for secondary core screens
through an optional gfx_ctx_window_driver_t, the last member of
gfx_ctx_driver_t: create, update, poll and destroy, and bind and
present for GL contexts. Requests, native handles and events carry a
generation from video_driver_window_generation(), a frontend counter
that a driver or context restart does not reset, so an event that
outlives its window cannot be taken for its replacement's.

GFX_CTX_FLAGS_VIDEO_WINDOWS is what a video driver will report once it
draws to such windows; nothing sets it yet.

* egl: window surfaces for secondary screens

egl_window_surface_new/bind/swap/free give a secondary screen's window
a surface on the main context's display and config, with the main
window's attributes. Bind makes the context current on it at swap
interval 0, or on the main surface again; free puts the main surface
back first where the freed one is current. None of them log.

* video: share the secondary windows' event bookkeeping

What a native layer keeps of each secondary window for the frontend -
its owner, record, request, position, the events waiting one bit per
kind, and whether it closed - is a video_window_common_t at the start
of its own state, and the rules on it live once in gfx/video_windows.c:
marking a window closed (CLOSE, with FOCUS_LOSS where it had the focus,
a waiting gain or resume dropped), poll's drain in its fixed order with
whatever has no room left waiting, and the sweep of an owner's windows
at context teardown.

* video: one placement clamp for secondary windows

video_window_place_rect() puts saved bounds on a monitor or its work
area: bounds partly on it are moved onto it, automatic ones and those
not on it at all are centred, and the size is clamped. Whether bounds
are on it is judged as saved, before the size is clamped, so an
oversized window partly on it keeps its position on the other axis.

* video: frame snapshot for secondary windows

A frame presented with views now carries what a driver needs to make,
place and lay out the screens' own windows without reading settings:
each screen's window settings, the window plan, the core's own map and
frame size, the layout parameters and a restore count, from which
video_driver_window_layout() lays out one screen's window. The snapshot
lives in video_driver_state_t and video_frame_info_t points at it, NULL
while windows are off or the driver cannot make them; the threaded
wrapper copies it into the frame's slot, as it does the statistics text.

While the driver can make windows, the main window's layout leaves out
the screens that have one of their own or are hidden.
video_driver_restore_windows() clears the screens a close attached and
counts the request, and window generations can be taken on any thread.

* video: published layouts and translate for secondary windows

A renderer publishes each secondary window's layout as drawn, with the
core map it was made from and the window's client size, through
video_driver_publish_window_layout() from the thread that runs the
context. Latest wins with no lock: each window has three slots handed
over by index exchange, as the window title is, and NULL clears it.

video_driver_translate_window_point() maps a point in a window to the
core's frame: window 0 through the viewport, as before, and a secondary
through its published layout, scaled from client to drawn pixels. A
point in a window that has no layout, was cleared or has another
generation is not mapped.

* input: window-aware pointer and touch publication

input_driver_publish_window_pointers() and _touches() take each device's
and each touch's window: 0 for the main window, else a screen's own with
its generation. A device in a screen's window is placed through that
window's layout for the pointer and the lightgun, and has no place on
the main window's screen; one whose window is gone reads off-screen.
Relative motion, buttons and the ports' devices are as they were.

input_driver_publish_pointers() and _touches() publish into the main
window, so the input drivers that call them need no change.

* samples: video_screens harness for the screens' own windows

The shared windows base is tested against the shipping configuration,
video and input objects: per-screen window settings across saves and
overrides, a screen's own layout, the frame snapshot and its copy
under the threaded wrapper, published layouts and point translation,
window-aware pointers and touches, and window generations. build.py
links the test with a configured non-Qt build's objects; the OpenXR
desktop jobs, which make one, run it.

* video: the window plan can keep a screen other than 0 attached

video_windows_plan() takes the screen that stays in the main window
whatever its mode, and screen 0 takes that screen's mode. Callers pass
0, which plans as before. Android's external monitor rule needs it:
screen 0 goes to the monitor and screen 1 stays on the device, where
the touch screen is.

* video: route the separate screens to a platform's displays

video_windows_route() chooses the display a platform's Separate
screens go to, from a list the platform publishes. A screen's monitor
setting N names the display of ordinal N; 0 is Auto, which takes an
on, valid and physical display that is neither private nor the main
window's: an external one (DIRECT or TRANSITIVE) first, then the
lowest ordinal. On an external monitor Auto puts screen 0 there and
keeps screen 1, so the touch screen stays on the device. One display
takes screens at a time; a screen bound for another stays attached.
Each display also carries its size, for the menu's display picker.

* samples: a touch through a second output's layout

A touch on a second display's surface, mapped through the layout drawn
there, lands in the touched screen's part of the packed frame: one
screen on a 1240x1080 panel, two screens sharing it, and screen 0 on a
monitor; the letterbox reads off-screen when asked.

* samples: pin the attached screen, picker and pillar cases

The plan keeps a screen other than 1 attached with screen 0 in its
mode, and falls back to Attached with no settings. The picker refuses a
private display but takes one that is off, and Auto never takes an
entry without an ordinal. A touch in a pillar beside two screens, not
reported off-screen, is held to the nearer one.

* video: route the screens' windows through a platform's display list

A platform publishes its displays from its UI thread, latest wins,
through three slots as the window layouts are handed over. Each frame
takes the newest list, routes the Separate screens with
video_windows_route() and plans with the screen it keeps attached; a
screen with nowhere to go stays in the main window. All screens bound
for one display share one window, led by the lowest one, and the frame
says which screens each window shows. Taking a list restores the screens
a failed window sent back, and video_driver_attach_windows() lets a
renderer send them back from any thread. A request names its display,
and Android gets a native window kind.

A screenshot or GPU recording through the threaded wrapper draws the
cached frame again on the video thread. That redraw takes no list: it
keeps the app thread's last route, as it reads the forced and restore
state, so a renderer keeps the second display's window and the capture
shows the right screens. The route is written only when it changes, so
Win32, which routes nothing and whose modal redraw runs beside the app
thread, never writes it while it is read.

The video_screens harness redraws the cached frame through the real
threaded wrapper and checks that it keeps the frame's route and plan.

* video: let a platform take the secondary windows back

video_driver_windows_retire() has the renderer let go of every
secondary window, through a new optional poke hook. It is for a
platform command such as Android's second surface going away, never a
frame. With the threaded wrapper up it is queued to the video thread
behind the frame being drawn, and nothing waits for it: a hook the
caller passes runs there just before and just after, for the platform
to hold its own requests back and to hear when it is done. Without the
wrapper all of it runs before the call returns. One is queued at a
time; false says the video thread cannot take it yet.

video_screens checks that a retire queued behind a frame being drawn
returns at once, and runs after the frame, between the hook's calls.

* video: one slot hand-over for the window layouts and displays

The window layouts and the display list are handed over the same way,
through three slots. Two small helpers publish and take a slot for both,
with the same memory orders. No behaviour change.

The harness adds two display list checks: a longer list keeps its first
VIDEO_WINDOW_DISPLAYS_MAX, and a get of capacity 1 fills only the first
entry.

* android: native side of the second display

The Java controller hands native code the display list and the second
display's surface through three natives. The list goes to
video_driver_publish_window_displays(). A surface becomes a node holding
one ANativeWindow reference, exchanged in for the video thread to take,
with a generation the touches carry. A size change of the same window
keeps the generation; a different window first gives up the old one.
Giving one up uses the main window's ticket handoff: the UI thread waits
while the app thread drops the node and queues the renderer's letting go
of its windows. The thread that lets go of them acknowledges once it is
done, so the UI thread is not left waiting for the app thread's next
poll; a retire the video thread cannot take yet is tried again from the
input poll. Native code asks Java to show or hide the display through
setSecondDisplay(), which posts and never waits, and asks nothing while
the renderer is letting go of a surface. The app state gets a 64-record
ring for the display's touches.

The natives run on the UI thread and read its own app state,
g_android_early, which android_app_free() clears unless a re-created
activity has already replaced it, so a callback after onDestroy finds
none. A display name GetStringUTFChars cannot read leaves no exception
pending for the next JNI call.

* android: second display windows for the gl and vulkan contexts

Both Android contexts give the renderer the secondary-window table.
Creating a window asks Java to show the display it is routed to and
returns unconfigured. Until Java publishes its first display list a
Separate screen resolves to display 0, which names no display, so its
window is refused and the renderer keeps the screen attached until a
list arrives. An update takes only the request's settings; another
screen or request generation is a new window, the renderer's to make.
Its poll hands it the newest surface native code received, raising
GEOMETRY and publishing which window and surface the input poll maps
touches through. The EGL context makes the window's surface on the main
context the first time it is bound after a surface arrives, at swap
interval 0, with the native visual id queried once at init, since the
query logs on failure and binding sits on the present path; the Vulkan
context gives the renderer the ANativeWindow and leaves the surface to
it.

Each request native code sends Java carries a number, and each surface
comes back with the number of the request it was made for. Destroying
the window drops the surface it held, then asks Java to hide the
display. When it was the last window, that hide gives up every surface
made for an earlier request, including one that reaches native code
after it: no window takes one, and its surfaceDestroyed frees what is
left and returns without the ticket handoff. A hide native code starts,
for closing or switching content or reinitialising the video driver,
never holds the UI thread.

* android: touches on the second display reach the core

Each touch on the second display crosses from the UI thread in the ring
with the generation of the surface it was on. nativeTouch sits with the
other second display natives and reads the UI thread's own app state,
which android_app_free() clears. A down or a move goes in only when it
leaves room for an up of every finger the input driver tracks, and an up
only when its down went in, so a full ring never drops a lift and leaves
a finger pressed for the core. MAX_TOUCH moves to platform_unix.h for
the native and the input driver to share.

The input poll drops the touches of any surface but the one the renderer
shows, maps the rest through that window's published layout into the
core's frame, and keeps the fingers after the main display's in the
core's pointer list. They never move the mouse, count as a quick tap or
reach the frontend's own screen pointer, so the menu and overlay stay on
the main display.

* input: the second display's fingers on a device of their own

With an overlay's pointer on, or one of its buttons held, the frontend
answers RETRO_DEVICE_POINTER from the overlay or not at all, and a
windowed overlay polls that device as its own touches. So the second
display's fingers cannot share it: the Android driver answers them on a
frontend-only RARCH_DEVICE_POINTER_SECOND and keeps RETRO_DEVICE_POINTER
to the main display. The frontend lists them for the core after the main
list's pointers, whoever answers for those: the overlay, none while the
pointer is blocked, or the driver. A driver with no second list answers
with its main list alone.

The pointer_second harness checks those answers against a stand-in
driver with both lists, through input_state_device() and the overlay's
pointer answer extracted from input_driver.c.

* android: a Presentation for the second display

SecondDisplayController lists the displays for native code - whether
each is the Activity's own, valid, on, private, a real panel or monitor
(product info, API 31+), its connection, name and size - and shows a
Presentation with a SurfaceView on the display native code asks for. The
window is not focusable and not touch modal and the view is not
focusable, so touching it neither pauses the core nor takes the gamepad;
it keeps the screen on and, on API 29+, keeps system gestures off it. It
follows the Activity from onStart to onStop, holding a request made
while stopped, and a frame after a display or configuration change
publishes the list again when it changed. A display it cannot show on
stays invalid until the list changes, so the display events a
Presentation coming or going may fire do not retry the failing show in a
loop. Each Presentation carries the number of the show request it was
made for and hands its surfaces to native code with it; touches go with
the surface's generation.

* android: second display failures and Presentations Android dismisses

Every display a show failed on stays invalid until the list changes or
the Activity starts again. A single failed id would not do: of two
displays that both refuse a Presentation, marking one failed makes the
other valid again, native code routes back to it, and the two take turns
failing forever. Starting again retries a one-off failure when the app
returns.

Android cancels a Presentation itself when its display's metrics change
(API 17-29). A dismiss we did not make clears it, so the dead
Presentation does not leave its display counted as shown. While native
code still wants that display, it is shown on again if the dismissed
Presentation's view had a surface. One cancelled before that cancels
itself, for example on stale display metrics, and rebuilding it would
loop at message rate; it counts as a failed show, so its display is
published invalid until the list changes or the Activity starts again.

* video: which screen windows a renderer makes and lets go each frame

video_windows_steps() tells a renderer, from one frame's snapshot,
which of its windows to let go of (no screens now, or another
display) and which to make. A window that closed or failed is not
made again until the restore count moves, so frames already queued
do not bring it straight back. Renderers share the rule instead of
each keeping its own.

* gl2: views draw into a given output; the preset's targets can grow

gl2_views_render() takes the layout, size and canvas it draws with
instead of reading the main window's, and the loop that grows the
preset's targets on a resize becomes a function of its own. Nothing
changes for the main window; a screen's own window will draw through
both.

* gl2: draw the screens' own windows

With a context that can make windows of their own for screens, gl2
reports GFX_CTX_FLAGS_VIDEO_WINDOWS while it may draw views. Each frame
it makes and lets go of the windows the frame's snapshot asks for and
draws each one's screens through the views compositor, with the preset's
passes where one is loaded. A window's pass targets grow only once its
surface is bound, so a failed bind has nothing to undo. A window is
drawn after the main window's frame and swapped at interval 0 before the
main swap, which stays the only one that can wait. The menu, OSD,
widgets and overlay stay in the main window.

A window that cannot be made or drawn hands its screens back to the main
window until the next restore and logs once. A window its context closes
hands them back too, and a restore that lands in the same frame does not
make it again; one closed with no screens left is not marked failed, so
it can be made again when its screens come back before the next restore.
The poke's windows_retire lets go of every window at once, for a
platform that takes a surface back.

The gl2_pbo_readback and gl_last_pass_fbo samples include gl2.c; neither
draws windows, so their shared stubs answer the frontend's window calls
with no-ops.

* samples: views test core logs a second finger, and asks for GLES 3

With a finger on each of two displays the second display's finger is
pointer 1. The core reads, maps and logs pointers 0 and 1 through one
helper, each with its own last state; pointer 0's lines and crosshair
are unchanged. Android's gl driver only gives GLES contexts, so a gles3
value of video_views_test_hw lets the core render through hardware there
too. gles3 with the checker or noise pattern logs an error at load:
those patterns upload a BGRA texture, which GLES 3.0 does not take.

* vulkan: the window-system surface switch as a helper

vulkan_surface_create() made the VkSurfaceKHR for each window system
in line. The switch moves into vulkan_wsi_surface_create(), which
takes the instance and returns the surface, so a second output on the
same device can make its own surface the same way. VULKAN_WSI_DISPLAY
stays where it was, since it needs the context's GPU. No behaviour
change.

* vulkan: a second output on the context's device

A vulkan_output_t is a surface of its own with a swapchain on the
context's device and queue, for a screen shown on another display. It
never waits in a frame. The acquire takes no time and uses a fence,
never a semaphore, so nothing the second display's presentation engine
signals ever enters the queue the main output's frames share. The fence
is polled, and an image not yet free is kept for the next frame. Out of
date remakes the swapchain at once; suboptimal remakes it once and then
keeps it, as the main chain does. On Android the swapchain takes the
size the window reports, since the surface's currentExtent can lag an
in-place resize and Android scales any extent to the window. A lost
surface is dropped. A swapchain let go of is destroyed once a fence
queued behind its last present has signalled. Only teardown waits, on
the output's own fences.

The harness runs the real code against a fake device and checks that no
frame-path call waits, that only completed objects are destroyed, and
that everything made is freed.

* vulkan: draw screens in windows of their own

Where the context has a windows table (Android's second display), a
screen the plan sends to a window of its own is drawn there with its
views' chains, sized for the window, and the frame's own view copies.
The window is drawn in the frame's command buffer, after the main pass
and before the chains end their frame. Its image comes from a fence
already signalled, so the frame's submission waits on nothing new; it
signals the window's semaphore, and the window presents after the
submission and before the main present, which stays the only one that
waits. The menu, OSD, widgets and overlay stay in the main window.

A window that can't be made, drawn or kept puts its screens back in
the main window until the next restore. One whose output is not done
yet is let go of on a later frame; the poke's windows_retire and the
driver's teardown wait for it, on the output's own fences.

* video: separate screen windows on by default on Android

On Android a Separate screen goes to a second display, which is what a
device with two panels is for, so the switch starts on there. Desktop
windows stay opt-in. The settings table records the Android default;
the harness checks that an override carries the switch and a screen's
mode.

* menu: a window mode and a display for each screen from 2 on

Each screen past the first gets two settings rows: Attached, Separate or
Hidden, and the display it goes to when it is Separate. The display
picker offers Auto and the displays the platform has published, shown as
the monitor index is ("2 (Screen-2 1240x1080)"), and keeps a display set
elsewhere until it is changed. It widens only up to the most displays
that can be published, so a hand-edited value in the thousands cannot
build a list that long on the main thread. The row's sublabel says that
the display RetroArch is on keeps the screen attached. Each screen's
rows have their own enum, as the per-port input rows do, so the dropdown
and the sublabel find the right one. A change to any of the rows gives a
screen the renderer sent back to the main window another try.

The settings harness checks the rows, the left key's clamp, and that a
change to any of them restores closed windows.

* menu: show the screens' window rows where the driver has windows

Stereo 3D & Screens shows Separate Screen Windows when the video
driver reports secondary windows. While it is on, it also shows the
mode and display of screen 2 and of each further screen the core has.
On Android the switch reads "Show secondary screens on another
display". A screen row's dropdown takes that row's label as its
title.

* android: show the second display again after its metrics change

On Android 10, a density or size change on the second display left it
empty until the app restarted. Android cancels a Presentation when its
display's metrics change, and the one made to replace it got the
display's resources from a cache keyed by display id, still holding the
old metrics. Presentation.onStart() found them stale and cancelled it
before it drew, so the display was marked failed and both screens stayed
on the main display.

Bring the new Presentation's resources up to the display's current
metrics before showing it. The refresh runs only before Android 12; from
then on a Presentation has a window context of its own.

* android: read input queue keys and sticks through their fields

The key and stick helpers of the input queue take the few fields
they read instead of the event, and the default loop's slot lookup
and key path become functions of their own. Nothing changes for the
queue; the second display's window can then hand its keys and stick
moves to the same code.

* android: a focus rule for the Activity and the second display's window

Once the second display's window can take focus, RetroArch's focus
comes from two windows. Where each display has its own focus, the
second window keeps its display's focus while the keys go elsewhere,
so it counts only while in use: after a key, stick move or touch from
it, a focus it gained while the Activity had none, or a hand-off of
the one focus from the Activity. Any other change of the Activity's
focus ends that, so a notification shade still pauses as before.

The rule is a header without NDK types; a host test replays the
sequences the AYN Thor's focus modes and a one-focus system produce.

* android: keys and sticks from the second display's window

Keys and joystick moves the second display's window receives cross
from the UI thread in a ring of their fields; the input poll handles
them with the input queue's own code, so mapping, device ids, axes,
hats and hotplug behave the same. The window's focus comes in as two
commands, and the focus of both windows is acted on once per poll by
the focus rule, only when it changes.

A paused app thread sleeps in its looper, so writing to either of the
second display's rings wakes it, and the poll reads both rings after
the looper so the woken poll acts on what woke it. The app thread
keeps a reference on its looper for those wakes until the activity is
freed.

* android: the second display's window takes focus and passes keys on

The second display's Presentation can take focus, so a tap on it or a
focus mode that locks the gamepad to its display (the AYN Thor's Bottom)
gives it the pad. Its keys and joystick moves go to native input as the
Activity's do. Each key gets native code's answer, so Android makes no
fallback key (BUTTON_B as BACK, BUTTON_MODE as HOME) of a key RetroArch
uses. It is not cancelable, so BACK stays with native code, and it never
becomes the soft keyboard's target. The IME refuses the Activity's text
field while the second display's window holds the focus, so the Activity
asks for the soft keyboard again when it regains focus.

When RetroArch dismisses the Presentation while it holds the focus, it
first brings its own task to the front, which takes REORDER_TASKS
(granted at install). Android keeps one focus for all displays, so it
would otherwise pass to that display's launcher and leave RetroArch
without focus until the next tap on the main display.

* android: hand the second display's surface back without waiting

surfaceDestroyed and a window change on the second display return at
once. The renderer's own ANativeWindow references keep the window
alive; a frame still drawing into it gets an error from the abandoned
queue, and the next frame's poll finds the surface gone, drops it and
lets the EGL surface or Vulkan output go, the output on later frames
while the presentation engine still holds an image. Nothing waits on
the video thread or the GPU, so the UI thread can't stall into an ANR
behind a slow frame, and the window keeps its display for the next
surface. The going surface also gives the windows another try, so a
failure it caused is not held against the next.

vulkan_output_finish() is the driver's teardown only. An acquire it
gave up on keeps its fence, which is never destroyed while it may still
be signalled.

* video: build the screens' own windows only where a platform has them

HAVE_VIDEO_WINDOWS gates the secondary windows: their state in the
video driver and the threaded wrapper's slots, the drivers' window code
and its per-frame calls, the second output and EGL window surfaces,
the input layer's second pointer list and window places, and the
per-screen settings with their menu rows and config keys. Android
defines it; elsewhere ./configure --enable-video_windows does, and the
CI job that runs video_screens_test builds that way. A build without it
compiles none of this and carries none of its state, which is about
15 KB of the video driver's state alone.

* video: a window failure counts only for the restore it was drawn under

A renderer reports a window it could not make or keep with the restore
count of the frame it was drawing. Failures travel in one word, the
screens beside that count, and the main thread takes them only when
the count is still its own. Under threaded video a frame laid out
before a display or surface change can fail after that change; its
failure no longer keeps the screen on the main display until some
later event.

* android: the jelly-bean package may reorder tasks too

It builds the same second display controller, which brings RetroArch's
task back to the front when it dismisses the second display's window on
a one-focus system; without REORDER_TASKS that call throws and the focus
stays on that display's launcher.

---------

Co-authored-by: libretroadmin <[email protected]>
2026-10-24 10:41:49 +02:00
Codex 7a3bcbdc28 media: route image and movie activation to the internal viewer
Open supported external media in the fullscreen menu viewer and keep image confirmation there across all four menu drivers. Disable the legacy imageviewer core in normal build configurations while preserving its sources and Griffin guard.
2026-10-24 01:00:53 +02:00
LibretroAdmin 001fe040f5 android: size the window to the cutout safe area again
Revert ed3187ec8e. On Android 15 the window manager rejects a main
window that is not translucent asking to fit the display cutout insets
("Illegal attributes ... trying to fit insets"), so the activity never
resumed. The window is placed in the safe area by hand once more when
drawing over the notch is off.
2026-10-19 20:00:50 +02:00
LibretroAdmin b8e8f1ae92 audio: add AAudio driver for Android 8.0 and up
AAudio is resolved from libaaudio.so at runtime, so the build needs no
API 26 headers, the library gains no dependency on it, and the binary
still runs on older releases. Below API 26 the frontend takes the
driver out of the list at startup, before the configuration or the
menu reads it; OpenSL stays the default.

The stream is a low-latency callback stream at the device's own rate,
fed from a mirrored retro_spsc ring the writer parks on through the
eventcount. The device stage is two bursts and the ring holds the rest
of the latency setting. The driver provides the lend pair,
wait_writable, frames consumed on the device's clock and underruns.
Layouts wider than stereo open as a channel mask on API 32 and as a
count with Android's canonical positions before it. Outputs can be
picked from AudioManager's list. A disconnect frees the writer and
asks the frontend to reopen, and a late report from a closed stream is
dropped.

A wider layout whose output takes fewer PCM channels than asked, but
lists AC-3 among its encodings, goes as AC-3 over IEC 61937 (API 34),
encoded with rac3 as WASAPI does. Underruns are filled with pause
bursts, and a burst is written whole or not at all. Android.mk gains
HAVE_RAC3 for it.

samples/audio/aaudio runs the driver against a scripted libaaudio
behind dylib_load, on Linux and with mingw-w64.
2026-10-19 07:03:11 +02:00
LibretroAdmin ed3187ec8e android: fit the display cutout insets instead of resizing the window
With 'Enable fullscreen over notch' off on Android 15+, ask the window
manager to fit the display cutout insets rather than sizing the window
to the safe area by hand. The window manager then lays the window out
in the safe area in the same pass that rotates the display, so an
orientation change no longer shows a frame drawn under the cutout.
2026-10-19 01:26:14 +02:00
Craig 2958b3716b openxr: retry refresh rate, add supported headsets on quest, fix washed out picture in immersive (#19844)
* openxr: allow GLES 3.1/3.2 contexts, add supported headsets on quest

* openxr: fix washed out picture in immersive mode
2026-10-15 22:01:04 +02:00
Roman VasilyevandLibretroAdmin 74d02ceaaa menu: preserve and select existing text when editing (#19809)
* menu: add an option to preserve text when editing

Prefill string settings and playlist names before opening built-in or native keyboards. Expose the behavior in Menu Controls, enabled by default, with a persisted menu_input_preserve_text setting.

* menu: distinguish new text input from editing

Use an explicit dialog type to preserve existing values when editing. New-entry, search and authentication dialogs start empty. Remove the menu option and configuration setting.

* input: select existing text when opening an editor

Typing replaces the initial selection, while arrow keys collapse it for editing. Draw the selection in each menu driver and select the initial text in native Android and iOS fields.

* text entry: no stored passwords on screen, and native buffers stay put

A password setting opens its editor empty rather than with the stored
secret selected in plain text.

The Android and iOS keyboards put a fixed 512-byte buffer under the
line. Opening it now sets the line's capacity to that buffer and marks
it fixed, so an append from a hardware or on-screen key stops at its
end instead of writing past it or moving it from under the keyboard.

frontend_overhead's text selection lane opens a string setting and a
password setting, types over a selection, and appends to a fixed line.

---------

Co-authored-by: LibretroAdmin <[email protected]>
2026-10-14 20:41:07 +02:00
5103a232df OpenXR: one core for the desktop and Quest headsets, with head tracking, controllers, settings and desktop OpenGL (#19811)
* menu: the cursor stays on Headset Output when Stereo Mode comes and goes

Stereo 3D & Screens lists Stereo Mode above Headset Output only while
the headset is off. Turning it on moved Headset Output up a row and
left the cursor on the row below it, Headset Refresh Rate, so a second
OK meant to turn the headset off opened the rate list instead.

The cursor follows the row, as it does for Mapped Port when Device Type
comes and goes. samples/menu/stereo_headset checks it for OK, right
and Start.

Co-authored-by: Craig <[email protected]>

* openxr: a session without focus is not logged as controllers not read

xrSyncActions answers XR_SESSION_NOT_FOCUSED, a success code, while the
runtime's dashboard or another application has focus. It was logged as
a warning each time focus went; only a failure is now.

The test layer answers a sync with a scripted result (sync <n>), and
the headset suite gains input-sync-unfocused.

Co-authored-by: Craig <[email protected]>

* libretro: a TRACKED view map, and HMD is how a core finds a headset

A map's shape could not tell a head-tracked eye pair from a
stereoscopic screen sent alone. The new flag says which. HMD now means
tracking works, and a core learns of a headset from it only.
REQUEST_FLAT is a map without the flag, and OpenXR always gives real
eye poses, so the flag and the IPD hint go; nothing read either.

Co-authored-by: Craig <[email protected]>

* openxr: the runtime, session, swapchains and frame loop are shared units

The desktop Vulkan host's instance and headset, its session state
machine, its swapchain slots and its frame loop with pacing move into
gfx/common/openxr_*, which name no graphics API, so every headset host
can use them. vulkan_openxr keeps only what is Vulkan's.

Co-authored-by: Craig <[email protected]>

* video: the headset's host says how it is, and answers the pose calls

One host interface reports the headset's state on the desktop and the
Quest alike. HMD comes with PRESENTS and STEREO, and only while the
session runs. The pose calls go to the host, and a view map sent again
unchanged is not applied again.

Co-authored-by: Craig <[email protected]>

* openxr: a TRACKED map's eyes are the desktop headset's projection

Tracked content gets a space of its own, LOCAL or STAGE as the core
asks, and the poses sampled in it. The eyes the core draws are
submitted as a projection layer with the poses it was given, and the
screens a headset leaves to that projection are hidden. A recenter
moves a seated world with the screens; a standing one stays where the
room puts it, and the core is told RECENTERED. A paused core's tracked
world comes back after a driver reinit. The views test core can draw a
world-locked grid, for checks made in a headset.

Co-authored-by: Craig <[email protected]>

* vulkan: the Quest's headset runs on the shared OpenXR core

The runtime starts Android's loader and tells the instance of it. A
headset frame can be begun and ended inside the video frame, so
vulkan_openxr runs in-line for a headset's own context, which says so
in its flags. The units that name no graphics API build without Vulkan,
and a GLES binding is ready for the GL host. A space a core asks for
again is kept, so a cached context keeps its paused world.

Co-authored-by: Craig <[email protected]>

* gl: the Quest's headset runs on the shared OpenXR core

The Quest's ES3 context is its own in-line host: gl2 draws into a
window the context owns, shown as a screen in the room, and copies a
tracked map's eyes into the headset's projection. The window shows
until the first projection. The kept view pair lives in openxr_host, so
the GL host keeps a paused world across a reinit as Vulkan does. The
quest flavour builds with GLES=3. A stand-in runtime test runs the
in-line host.

Co-authored-by: Craig <[email protected]>

* input: one headset controller module for the desktop and the Quest

The headset input's hooks belong to the frame, so any host attaches the
module, and the Quest's own controller module goes. Touch's menu button
is Start from its press, and opens the RetroArch menu when let go after
a second. The menu's laser can point at a window shown whole, mapped
through the area the core is drawn in. The stand-in test runs the
shared module.

Co-authored-by: Craig <[email protected]>

* video: headset settings for every headset driver, and Screen Placement

The headset's rows show for every driver with a headset path. Recenter
works in the menu and reaches the Quest's GL host, and a seated
recenter moves a paused world with the screens in the same headset
frame. Screens are placed from an anchor of their own: Fixed in Room,
Follow Head or Lazy Follow. The Quest asks for a refresh rate that fits
the content and repeats each core frame for whole headset frames.

Co-authored-by: Craig <[email protected]>

* input: the headset's controllers are a controller

They take joypad slots with built-in profiles, so hotkeys, port binds,
remaps and rumble use the normal input path on the desktop and the
Quest. They take the next free slot like any pad, and keep their port
when a gamepad is plugged in, without saving that exchange. A saved
"Meta Quest Touch Plus Controller" profile and its remaps no longer
apply.

Co-authored-by: Craig <[email protected]>

* gl: headset output for the desktop OpenGL drivers

glcore and gl show a headset on Linux. The XR thread holds a context in
the window's share group, made by the X11, X11 EGL and Wayland context
drivers, and the session binds it through XR_KHR_opengl_enable or
XR_MNDX_egl_enable. The video thread draws each image into shared
textures and hands it over with a fence through a triple buffer; the XR
thread copies the newest into the swapchain and shows it with the quads
or the eye poses it was drawn with. glcore draws the screens and the menu
through chains of its own and a tracked map's eyes as the projection, as
Vulkan does. gl shows its window, whole or cut into an untracked map's
screens, as the Quest does, and tracked eyes as the projection. Both
follow the headset's pacing: the XR thread wakes the core for its next
frame after taking the last, so none is shown a headset frame early.
OpenXR configures with OpenGL alone, without Vulkan. The test layer
captures OpenGL swapchain images, and the headset end-to-end runner has
lanes for both drivers on GLX and EGL.

Co-authored-by: Craig <[email protected]>

* docs: OpenXR headset output, and how a headset or graphics API is added

What the headset path does and how to build and turn it on, what a core
sees, which units are shared and what each of the three hosts adds, and
the steps for another headset, graphics API or platform.

Co-authored-by: Craig <[email protected]>

* audio: pass the reinit flag to MMDevice watchers

XAudio2 and DirectSound must give mmdevice_thread the frontend atomic flag. Passing the driver object overwrites its watcher handle when an endpoint changes. Exercise both thread backends against the extracted production spawn functions.

* test: reject video backend fallback in the threaded harness

Fail early when HARNESS_VIDEO_DRIVER differs from the initialized backend so a missing driver cannot qualify another backend's lanes.

* Animated previews: keep high-bitrate audio fed across loops

Size the audio lookahead from the preview bitrate, prime it before playback, and refill the full window on rewind. The audio demuxer walks interleaved video packets too; a fixed 2 MiB window stalled on large H.264 packets and left high-bitrate previews silent or delayed after looping.

Add a real feeder/mixer regression using generated oversized video packets. Verify that audio continues through three complete loops; the previous feeder fails this test. Validate both reported 4K clips locally on Windows and Linux.

* wdmks: bound threaded writes by the device fragment

A small write can exhaust its request-sized wait budget before a healthy WaveRT pin's next notification. Size the threaded ring waits from rt_ahead instead, and cover writable waits and blocking writes against a 64 ms fragment.

* CI: views end to end on glcore and Vulkan under Xvfb

run.py gains RA_E2E_XVFB (Xvfb in place of gamescope, llvmpipe and
lavapipe) and RA_E2E_ONLY (a case filter). The validation job runs a
subset of the cases.

* Views: one compositor for gl3 and Vulkan

gfx/video_views_draw.h holds the chain pick, the placement loop with
the interlaced row shift, and the canvas and UI target lifetime. Each
driver includes it with its own calls bound in by macros, so nothing
goes through a function pointer. The UI layer's predicate, per-eye
test and dims swap move to video_views.h. Output is unchanged.

* Threaded video harness: the frame on screen is the core's newest

A frame lag lane checks, end to end, that what the driver draws is the
frame the core pushed last: the harness core stamps each frame with its
number in eight full-height bands that survive scaling and filtering,
and the lane reads back the driver's own upload (the cached frame
dropped first) and decodes it. Copied, lent and lent as a cropped
window, with threaded video off and on: with nothing in flight it must
be the newest pushed frame, and with frames still in the wrapper's ring
at most one behind. A zero-copy path that draws a buffer the core has
since rewritten, or skips an upload, or a wrapper that holds frames
back, fails it.

It runs on vulkan, gl and glcore, desktop and GLES 3, in the existing
harness jobs (release, ASan+UBSan, TSan, the validation layer).

* Fix MSVC preprocessing of video views UI checks

Move widget conditionals outside VIDEO_VIEWS_UI_WANTED calls in the GL3 and Vulkan drivers. The traditional MSVC preprocessor treats directives inside macro arguments as tokens, breaking the Windows build.

* Provide audio state in the DirectSound pump harness

The device notification thread now uses the audio state reinit request. Supply a persistent harness-owned state through audio_state_get_ptr so the standalone MinGW test links.

* Preserve RetroArch when building the threaded video harness

Request the link command with make -W instead of deleting the frontend executables. The later video views end-to-end tests need the existing RetroArch binary to launch.

* ATRAC3plus: accept implicit master code-table indices

Master channel code-table mode 3 has no payload and uses zero indices. Return before consuming the optional value count bit, rather than rejecting valid frames and losing bit alignment. Add a synthetic regression covering float, s16 and reset decoding.

* Expose hardware H.264 decoding with CPU planes to cores

Provide an experimental versioned frontend decoder interface with Vulkan and D3D11 I420 readback. Reserve private video queues through Vulkan device negotiation v2, and retain the decoder backend with cached devices across video driver reinitialization. Keep software fallback for unavailable or disabled hardware decoding and cover readback in decoder and GPU tests.

* ATRAC3plus: distinguish coded and declared subband flags

Use coded subbands for stereo swap and negation flags and declared subbands for window shapes. Add a synthetic audible stereo regression to detect parser misalignment when fewer subbands carry spectra.

* D3D11: views

The driver draws a core's views through video_views_draw.h, straight
from the frame as the stock chain draws it: placed in each eye's area,
blended through a canvas for anaglyph and interlaced, with the UI drawn
once into a layer composited into each eye. With a preset or HDR output
the frame is drawn whole.

Windows-wine-views.yml runs the views end to end on it: run.py gains
RA_E2E_WINE, for retroarch.exe under Wine with vkd3d's shader compiler,
and the test core builds as a DLL.

* D3D12: stock pipes made for the swap chain's format

The pipes were made before the swap chain, whose formats were still
unset, so every stock draw had an UNKNOWN target format: Windows drivers
draw it anyway, vkd3d (Wine) draws nothing. They are made after it now.

* D3D12: views

The driver draws a core's views through video_views_draw.h as the
D3D11 driver does: straight from the frame, through a canvas for
anaglyph and interlaced, with the UI drawn once into a layer composited
into each eye. Its SDR pipes keep coverage in alpha, which the layer is
composited by. With a preset or HDR output the frame is drawn whole.

Windows-wine-views.yml runs the views end to end on it as well, on
Wine's vkd3d over lavapipe.

* openxr: apply unified headset support to current master

Apply PR #19811 at 3d4ad57 to master at 204f03cfec, resolving its
conflicts with the shared views compositor and hardware video decoder.

Retain master's compositor and GL frame-memory interfaces while
preserving the PR's headset UI sizing, shader-chain cleanup, tracking
and controller support. Keep both sets of regression tests and the
hardware decoder callbacks alongside the OpenXR pose helper.

---------

Co-authored-by: Craig <[email protected]>
Co-authored-by: Codex <[email protected]>
Co-authored-by: libretroadmin <[email protected]>
2026-10-12 18:33:10 +02:00
Codex 5fbf22321f android: resolve startup storage access on return from Settings
Launch Settings in its own task and check storage access after pause/resume, preserving pending requests across activity recreation. Settings redirects can otherwise release startup before the user responds.

Keep the API 16 input listener behind a version guard for Android 2.x, skip unsupported startup work, and reuse core scan resources and preferences rather than fetching them for every directory and library.
2026-10-12 13:43:49 +02:00
Codex c8bd052dc3 Android: queue cached pulses for keypress haptics
Send keypress feedback as a short default-amplitude pulse instead of relying on view feedback or a predefined click. Cache the immutable effect, touch attributes and vibrator capability. Reuse one UI callback and coalesce pending posts, keeping the native caller free of vibrator service work.

Read the system touch-feedback setting at dispatch time, reject unfocused feedback, and remove queued work on activity destruction. Keep guarded legacy API paths and other view haptic effects.

Candidate follow-up for #19791; Galaxy A07 physical validation remains required.
2026-10-11 19:53:22 +02:00
Codex a4afa5a6b8 Android: add a touch haptic vibrator fallback
Use virtual-key feedback for overlay presses and respect system touch settings. If view feedback is unavailable, request a click through the device vibrator with touch attributes, using a short pulse on older APIs.

The fallback follows the effect selection in PadKit 1.0.0-beta1, used by Lemuroid. This is a candidate fix for #19791; Galaxy A07 verification is still required.
2026-10-11 14:17:57 +02:00
Craig 4f1610462e quest: add var if meta build so can show core downloader, fixUp xml (#19805) 2026-10-11 10:57:19 +02:00
libretroadmin 8042f59dbc camera: frames reach the core as planar YCbCr, in place
RETRO_CAMERA_BUFFER_PLANAR hands a core the capture device's own 4:2:0
planes, untouched, for the duration of the callback; the frame
callback sits past the end of retro_camera_callback and is read only
when its caps bit is set, so older cores are never read past.

The Android driver picks the cheapest delivery the core accepts: the
SurfaceTexture for a GL hardware context, the planes for a planar
core, and XRGB8888 converted once by image_yuv_blit only for a core
that takes nothing else. Planes come from Camera2 through the NDK
(resolved at runtime, the HAL buffer mapped where it lies) and fall
back to the legacy Camera's NV21 preview buffers, rotated between the
camera and the reader without a copy. No permission is declared: a
port that declares CAMERA gets the prompt, others get no camera.
2026-10-10 19:44:44 +02:00
LibretroAdmin 1df1eabad7 openxr: the loader lives in deps/openxr and holds no lock
The vendored loader moves to deps/openxr without the hex and handle
helpers (handles cast through XR_HANDLE_TO_U64/XR_U64_TO_HANDLE and print
with snprintf and PRIx64 from retro_common_api.h), platform_exports.h,
the CMake common_config and GLAD branches, and the CMake header list.

Its runtime, active instance, recorder table, messenger table and
property set are published by pointer and replaced by compare-exchange.
Creating, destroying and reinitializing take a one-word instance claim
and are refused, never made to wait, while another call holds it; that
also keeps xrInitializeLoaderKHR from unloading the runtime under a
create. An unloaded runtime is retired and freed once no enumeration can
still hold it. samples/openxr/loader_threads drives all of it from
several threads against a stand-in runtime, under TSan and ASan.
2026-10-08 06:02:03 +02:00
Craigandlibretroadmin ebfc5c7652 VR support - Quest to begin with (#19726)
* deps: add openxr-sdk

* openxr: initial implementation

* openxr: build the vendored loader from the tree, drop the CMake step

The OpenXR loader is compiled like every other vendored dependency
instead of through a cmake invocation run from a $(shell) at Android.mk
parse time against one hardcoded NDK path: griffin_cpp pulls in the
loader, jsoncpp and (on Android) the jnipp/android-jni-wrappers broker
sources, griffin.c picks up the generated C dispatch table, and the
loader links statically into retroarch-activity, so the prebuilt
libopenxr_loader.so module and its packaging disappear. The loader is
C++17, so the quest build appends -std=gnu++17 after the global gnu++11.

Makefile.common gains an equivalent HAVE_OPENXR block (platform-neutral
sources only) and qb grows a disabled-by-default HAVE_OPENXR switch, so
a future desktop OpenXR backend builds the same loader through configure
rather than growing a second build path.

* openxr: move the vendored loader onto libretro-common threading

The loader's C++11 threading (std::mutex, std::shared_timed_mutex, and
jnipp's std::atomic_bool) is replaced with rthreads slocks and
retro_atomic. Cold paths - the global loader lock, the property
overrides, the debug-messenger map - keep plain mutual exclusion as
lazily-created slocks published with a CAS. The two read-mostly
structures become lock-free: the runtime dispatch table is a single
acquire/release-published slot, since the loader only ever has one live
runtime instance, and the logger's recorder list is an immutable
snapshot that the LogMessage paths walk wait-free while writers rebuild
and republish it under a cold slock, retiring superseded snapshots and
removed recorders until teardown so a reader never touches freed
memory. One behavioural delta, noted in loader_logger.hpp: a removal no
longer waits for in-flight readers, so a log call racing it can reach
the removed recorder once more before it drops out of view.

The per-frame xr* dispatch (the generated trampolines through the
cached dispatch table) was already lock-free and is unchanged; this
puts the whole tree on one threading layer and drops the libstdc++
thread machinery from the loader. Verified with per-TU and
griffin-shape amalgamated C++17 builds of every touched TU, plus a
TSan and ASan/UBSan/LeakSan harness driving concurrent LogMessage
readers against recorder add/remove churn and concurrent first-use
slock creation.

* openxr: route Vulkan instance/device creation by context, not build

On a HAVE_OPENXR build every VkInstance and VkDevice went through
xrCreateVulkanInstanceKHR/xrCreateVulkanDeviceKHR unconditionally, so
the flat vk_android fallback the driver takes when a session is
unavailable could never create a working context, and the queue-family
probe still queried surface support with a null surface after computing
the headless answer. Creation now routes through OpenXR only when the
live context is android_vk_openxr - everything else keeps
vkCreateInstance/vkCreateDevice - and vulkan_common reaches the XR
instance and system id through accessors on the context driver instead
of another TU's static, which only linked because of griffin include
order. The headless surface check is a single call that never passes
VK_NULL_HANDLE.

In vulkan.c the swap-interval emulation and BFI frame-duping recursion
are skipped under an XR session: the compositor owns pacing, and
re-entering vulkan_frame would issue a second xrBeginFrame before
xrEndFrame (the context's begin_frame also guards against re-entry
now). The HW fence wait keeps one shared condition with XR added inside
a single #ifdef so the non-XR build preprocesses exactly as before, the
dangling HAVE_OPENXR_2D probe is gone, and the eye loop and
vulkan_frame_eye declarations are C89.

* openxr: fix quest input regressions, make shared state cross-thread safe

android_joypad_state's axis-to-button path required port 0 under
HAVE_OPENXR, so physical pads on ports 1+ lost all analog input on the
quest build; it now goes through android_joypad_axis, which merges the
XR sticks for port 0 and falls through to the plain Android state for
every other port. Window motion events are only suppressed while an
OpenXR session is actually live instead of being compiled out (the
unreachable duplicate source check is gone), the never-defined HAVE_XR
gates are removed so the file matches the behaviour the quest build has
actually been tested with, and the INIT_WINDOW reinit check is braced
with its declaration at block top for the C89 lanes.

The shared OpenXR input state (buttons, axes, attach flag, menu long
press) is written by openxr_input_sync on the frame loop's thread -
the video thread under threaded video - and read by the input drivers
on the main thread, so it moves onto retro_atomic with a swap-consumed
long-press flag. The shared readers also lose their android_vk_ prefix
(they serve the GL path too) and live in openxr_input.h; the long-press
hotkey check in input_driver.c gains the ANDROID guard its definition
has always had.

* openxr: report VR availability honestly, decouple GL path from Vulkan

gl2's set_vr_content_info returned true unconditionally, so a core
asking for VR on the flat fallback context was told a session existed
and got a 0x0 recommended eye size; it now reports unavailable unless
the OpenXR context owns a live session, video_driver refuses and
unwinds a request when no eye target size is available, and the Vulkan
driver treats a NULL info as the documented disable instead of a probe,
clearing its stereo state. The unused RUNLOOP_FLAG_CORE_VR_CONTENT is
dropped: it sat on the deliberately retired bit 31 (1 << 31 in a signed
enum besides), and the bool the code actually uses stays, with its
comment naming the real environment calls.

griffin no longer nests the GL OpenXR context, the shared OpenXR input
TU and the loader dispatch table under HAVE_VULKAN, so a GLES-only
HAVE_OPENXR build links instead of failing on prototypes. The GL
context's end_frame declarations move to block top for the C89 lanes,
the stray C++ comment is gone, and the magenta debug clear on the eye
buffer is black.

* android: keep quest manifest entries out of the shared manifest

The Oculus app id meta-data and the installLocation flip to auto landed
in the manifest every Android build ships. The app id moves to the
quest flavor's overlay manifest where it belongs, and installLocation
returns to internalOnly, which is set deliberately: external-storage
installs have broken content paths and services before. If the quest
build needs auto, it can set it in its own overlay.

* deps/openxr: trim the vendored SDK to what the tree builds

The full SDK checkout came with its own CI configs, CMake build system,
gradle/prefab/vcxproj packaging, jsoncpp's test suites, examples,
docs and devtools, jnipp's project files, and the unused non-core
dispatch table. All of it goes: what stays is the OpenXR headers, the
loader and common sources the tree compiles, the lib_json sources and
headers, jnipp and the android-jni-wrappers, the generated core
dispatch table, and every license file (LICENSE, COPYING.adoc, the
LICENSES directory and REUSE sidecars of kept files, jsoncpp's and
jnipp's licenses). README.md stays as provenance for the vendored
version.

* ci/android: carry the quest flavor's debug apk as an artifact

assembleDebug already builds every flavor, so the quest build compiles
on each CI run now that the loader builds through ndk-build with no
host cmake step; uploading its apk makes a quest regression visible in
the artifact list and gives testers a build to sideload. openxr_input
also includes verbosity.h itself instead of leaning on griffin include
order for RARCH_ERR.

* openxr: parse manifests with libretro-common rjson, drop jsoncpp

The loader's JSON use is three manifest/broker walks over a small value
tree, so a ~250-line Json::Value subset backed by rjson (loader_json)
replaces the vendored jsoncpp - a megabyte of C++ the binary already
duplicated, since rjson is compiled into every RetroArch build. The
shim keeps jsoncpp's names and the exact semantics the loader relies
on: missing members read as a shared null, mutable indexing creates
members, extension_version accepts both its string and integer
spellings, and the parser takes comments, trailing data and a UTF-8
BOM like the CharReaderBuilder defaults did, so nothing previously
loadable becomes unloadable.

Verified against the old implementation as an oracle: a harness driving
RuntimeManifestFile and ApiLayerManifestFile over valid, relative-path,
malformed, wrong-version, field-missing, trailing-data and commented
manifests produces byte-identical results on both, runs clean under
ASan/UBSan/LeakSan, and the quest, GLES-only and OpenXR-off NDK builds
all still link.

* android: declare the OpenXR runtime brokers and their permissions

The vendored loader discovers the active runtime by querying the
ContentProviders at org.khronos.openxr.runtime_broker and
org.khronos.openxr.system_runtime_broker (android_utilities.cpp). The
quest flavor targets API 34, where package-visibility filtering hides
any provider authority the app does not declare, and the manifest
carried no queries element at all, so broker resolution was working
only at the OS vendor's grace. Declare the two authorities the loader
actually queries, together with the Khronos broker permissions - both
normal protection level, granted silently at install with no user
prompt - so the generic installable broker also serves the app, not
just Meta's unenforcing one.

Everything lives in the quest overlay, where the loader ships: the
headtracking uses-feature in particular must never migrate to the
shared manifest, since required=true there would delist the app for
every non-VR device on the Play store; a comment now says so.

* jni: cleanroom rjni layer in libretro-common, used tree-wide

All JNI plumbing moves behind one cleanroom libretro-common layer:
rjni registers the process JavaVM once (CAS, first wins), caches each
thread's JNIEnv in destructor-capable TLS - GetEnv tells threads the
VM already owns, which are left alone, from threads rjni attaches,
which are detached again when they exit - and provides lock-free
once-caches for class global references, method IDs and field IDs,
the VM crossings that make JNI call sites expensive. The exception
guard switches to ExceptionCheck, which creates no local reference.
The call wrapper macros move here verbatim from platform_unix.h, so
every JNI user in the tree shares one header.

platform_unix keeps jni_thread_getenv as the entry-point name but it
is rjni_env now, with the local key machinery deleted, and the
KeyCharacterMap path resolves through process-lifetime rjni caches,
so a driver restart re-resolves nothing. Verified with a fake-VM
harness under TSan and ASan/UBSan/LeakSan (8 threads, attach/detach
balance asserted), a real OpenJDK 21 VM exercising the caches from
four native threads, the C89 lane, NDK arm64 at -Werror, and MinGW.

* openxr: drop jnipp and android-jni-wrappers, query the broker through rjni

The loader's two remaining vendored C++ dependencies existed only to
make JNI calls, which the tree now does through libretro-common's
rjni, so android_utilities is rewritten on raw JNI with an rjni
once-cache for every broker class, method and field - same URIs, same
log lines, same installable-then-system fallback - and
loader_init_data reads the asset manager and nativeLibraryDir the same
way (a dead getApplicationContext call did not survive the rewrite).
With jnipp gone this also deletes the only exception-throwing code in
the loader, so the Android path is safe under -fno-exceptions
unconditionally, and the last C++11 threading construct, jnipp's
thread_local, leaves the tree with it.

jnipp and android-jni-wrappers are removed from deps, griffin and the
Android include paths. Both rewritten files pass standalone NDK arm64
syntax checks, the quest ndk-build links, the GLES-only and plain
shapes still build, and the Windows-shape loader amalgam still
compiles under MinGW.

* openxr: filesystem code on libretro-common file_path/retro_dirent

filesystem_utils keeps its interface but its three implementation
branches - C++17 <filesystem>, experimental, and the per-platform
fallbacks - are replaced by one body over libretro-common's
file_path, path_stat and retro_dirent, matching the std::filesystem
semantics this build shipped (parent/trailing-slash decomposition,
absolute-child replacement in CombinePaths, the Windows canonical
no-op). stdfs_conditions.h goes with the branches. Three behavioural
deltas, each for the better: FindFilesInPath and GetCanonicalPath
return false on a missing path where directory_iterator and
std::filesystem::canonical threw (the loader builds -fno-exceptions
elsewhere), and ParsePathList no longer corrupts the middle entries
of XR_API_LAYER_PATH lists with three or more paths, an upstream
substr count bug present in every branch.

Verified by a function-level oracle, old implementation against new
over a fixture tree (files, subdirs, symlinks, dangling links, path
lists): byte-identical except the two corrupted-list lines, now
correct; ASan/UBSan/LeakSan clean; and the manifest-file oracle from
the rjson conversion rerun on the new filesystem, byte-identical
including relative library_path resolution. Quest ndk-build links
and the Windows-shape loader amalgam compiles under MinGW.

* openxr: remove the loader's exception handling

Nothing in the loader throws any more - jsoncpp, jnipp and the
std::filesystem calls went in earlier changes - so the exception
machinery guarding the C ABI goes too: exception_handling.hpp and the
71 XRLOADER_ABI_TRY/CATCH wrappers around the entry points in
loader_core and xr_generated_loader, the empty catch blocks in the
ApiLayerInterface and RuntimeInterface destructors, and the dead
jsoncpp exception-mode guard in manifest_file. The whole loader now
compiles under -fno-exceptions, which the compiler itself enforces as
proof that no try, catch or throw remains; griffin keeps building it
with exceptions enabled only because glslang shares the TU. The one
reachable delta is allocation failure inside an entry point, which
terminates instead of unwinding to XR_ERROR_OUT_OF_MEMORY - the
loader is statically linked into the frontend, where that is already
the outcome everywhere else in the process.

Verified with -fno-exceptions compiles of the full loader in Linux,
MinGW and NDK arm64 shapes, the manifest-file oracle byte-identical
under ASan/UBSan/LeakSan, and quest plus plain ndk-builds linking on
the PR's rebased head.

* openxr: filesystem_utils on direct libretro-common calls where one fits

Every function here is now a direct libretro-common call where one
with std::filesystem's semantics exists: PathExists is
path_is_valid, CombinePaths joins through
fill_pathname_join_special, and the hand-rolled separator macros
give way to PATH_CHAR_IS_SLASH and PATH_DEFAULT_SLASH_C; only the
env path-list separator keeps a local define, since libretro-common
has no such constant. What stays hand-written is documented in
place as a deliberate semantic difference from libretro-common's
contracts: GetParentPath (fill_pathname_basedir fabricates "./"
for a bare filename and keeps trailing slashes), GetAbsolutePath
(path_resolve_realpath without symlink resolution would normalise
dots, which std::filesystem::absolute does not), ParsePathList
(string_split's strtok collapses empty entries), and
FindFilesInPath wants bare names where dir_list_new returns full
paths, so the retro_dirent primitives are the fit.

FileSysUtilsGetCurrentPath had no caller anywhere in the loader and
leaves the interface, surviving only as the static getcwd helper
behind GetAbsolutePath. The last two <stdexcept> references go too:
manifest_file's include was dead since the exception removal, and
vulkan_debug_object_namer.hpp was referenced by nothing in the
tree. Both oracles rerun byte-identical - the function-level one
over the fixture tree and the manifest-file one under
ASan/UBSan/LeakSan - and the loader still compiles -fno-exceptions
in Linux, MinGW and NDK shapes, with quest and plain ndk-builds
linking.

* openxr: guard the context drivers' XR platform defines

The Android build also passes XR_USE_PLATFORM_ANDROID on the command
line, so the bare defines the two OpenXR context drivers carry for
standalone compilation redefine it - and the graphics-API macros
alongside it are one -D away from the same collision. Wrap all of
them in #ifndef so either source of the define wins quietly,
whichever compiles the file first in the griffin TU.

---------

Co-authored-by: libretroadmin <[email protected]>
2026-10-07 16:24:05 +02:00
LibretroAdmin d580ffc2f7 android: the legacy NativeActivity project builds rzstd
HAVE_RZSTD joins HAVE_RCHD so zstd-compressed CHD hunks decode and the
rzstd transform stream is available, as in Android.mk.
2026-10-06 05:39:32 +02:00
LibretroAdmin 7ac3f16060 Move the remaining platforms to rchd
Emscripten, RS90, Android (Android.mk and the legacy NativeActivity
project) and UWP define HAVE_RCHD with HAVE_CHD, so every build reads
CHD images through formats/chd/rchd.c and griffin leaves libchdr out.
Each platform already builds the codecs rchd calls for the formats it
had before.
2026-10-06 05:29:23 +02:00
libretroadmin 967c05df29 android: show the display's EDID under Display Information
Display Information > EDID is built on Android and reads the block the
kernel driver keeps in sysfs: the DRM connector node through the reader
the Linux servers share, then the Amlogic vendor HDMI driver's rawedid,
which most Android TV boxes ship and which prints the block as hex
text. Android has no API for the raw block, so devices whose kernel or
SELinux policy keeps both nodes out of reach show the existing
explanation for a display with no EDID rather than a block assembled
from guessed timings.

The Android build turns on the modeline engine to carry the EDID
reader. Its display server advertises no modeline capability, so the
CRT SwitchRes menu stays hidden there.
2026-10-02 22:19:03 +02:00
LibretroAdminandf4mrfaux cf61e93c6c android: report a controller the OS has removed as disconnected
Android tells the activity when an input device goes away - a pad
switched off, out of range or unplugged - but nothing passed that on
to the input driver, and no input event marks a removal. A pad
therefore stayed connected for as long as the app ran:

- its port kept its device name, so an overlay hidden while a
  controller is connected never came back;
- whatever it was holding stayed held, since no release arrives from a
  device that is gone;
- no disconnect notification was shown, and pausing on disconnect
  could not happen.

RetroActivityCommon.onInputDeviceRemoved now hands the device id to
the driver. It is left in a small lock-free mailbox and taken out at
the next input poll, which clears the pad's key, axis and hat state
and reports the disconnect through input_autoconfigure_disconnect, as
the other joypad drivers do.

The pad's slot keeps its name and is marked removed instead of being
emptied, so the device gets its old port back when it returns, under
whatever id Android gives it; its return is reported as a connect. A
removed slot is matched before a live one of the same name, so of two
identical controllers the one that went away is not taken for the one
still connected.

A removal is read at the next poll, which can be long after it
happened if the app was in the background. If the id resolves again by
then, the device has come back under it and nothing is reported.

This is the device-removal part of PR #19077, reworked. That version
emptied the slot's name, which the driver uses to find a returning
device's port: every reconnect would have moved the pad to the next
free port.

Compiled with the NDK through the Android unity build. Not run on a
device.

Co-authored-by: f4mrfaux <[email protected]>
2026-10-02 07:12:54 +02:00
libretroadmin f516093bc4 deps: drop the bundled mbedtls, BearSSL and libsmb2; library backends by option
Every build uses the built-in TLS, SMB and NFS clients, so the
bundled deps/mbedtls, deps/bearssl-0.6 and deps/libsmb2 are removed.
A system library can still stand in for a built-in client, off by
default: --enable-mbedtls and --enable-bearssl replace the TLS client
with a system mbedTLS or BearSSL, and --enable-libsmb the SMB client
with a system libsmb2; a library asked for and not found is a
configure error, and two TLS libraries at once are refused. The
older builtin/system switches go, as do the platform files' bundled
selections, and the libsmb2 backend follows the library's current
share-enumeration API.

Linux-library-net-backends.yml replaces the legacy lane with one lane
per library: each configures with its option, builds RetroArch to
the link, checks the library's backend is linked and the built-in
client is not, and operates it - the cheevos login transport over a
local HTTPS server for mbedTLS and BearSSL, the SMB VFS tests against
a local smbd for libsmb2 (built from upstream; Ubuntu has no package).
The cheevos_login sample links the system BearSSL, the smb_backup
sample carries stand-in libsmb2 headers for its fake share, and the
samples workflow installs libbearssl-dev.
2026-09-30 05:01:22 +02:00
libretroadmin db9e44da97 android: the cleanroom network stack in place of mbedtls and libsmb2
The APK still had griffin pull in the bundled mbedtls for TLS and
libsmb2 for SMB. Android.mk now sets HAVE_CRYPTO, HAVE_KEYCHAIN,
HAVE_RETROSSL and HAVE_RETROSMB as the desktop builds do, so the APK
gets the same TLS 1.2/1.3 client with certificate verification, the
encrypted keychain, and the SMB2/3 client with signing, sealing,
Kerberos and the read-ahead window, and neither bundled library is
compiled in any more; the libsmb2 configuration defines and include
paths go with it. The mbedtls path stays selectable by turning
HAVE_RETROSSL off.

The compile matrix gains a lane per translation unit of the stack
compiled by clang for aarch64 with the cross headers standing in for
the NDK's: the same compiler family and target the APK is built
with, which is where the AES/PMULL path takes its +crypto target
attribute and which the gcc lanes never exercised. The matrix job
installs clang and the aarch64 cross toolchain for it.
2026-09-30 01:08:28 +02:00
libretroadmin b5de27786d android: build the NFS client and the nfs:// backend
HAVE_RETRONFS in the Android makefile: the built-in NFSv3/v4 client
and its backend, which griffin already carries under HAVE_NFSCLIENT,
need nothing but sockets, so the APK gets nfs:// beside the smb:// it
had. The compile matrix compiles the client, the backend and the
prefetcher through the Android platform stubs.
2026-09-30 00:59:13 +02:00
libretroadmin 6d536cff0f video: Use Display Peak, and the display's peak on Windows and Android
Use Display Peak, off by default, makes the peak luminance the display
reports stand in for Peak Brightness wherever a peak is used: what
cores are told through GET_HDR_MAX_NITS, and the HDR metadata the
drivers send - DXGI's for D3D11 and D3D12, Vulkan's vkSetHdrMetadataEXT
(which reaches the compositor under Wayland), and the KMS blob. Off, or
with no reported peak, every value stays as it was: the setting for
cores, each driver's fixed 1000 nits for its metadata.

Windows reports the panel's peak through DXGI_OUTPUT_DESC1, read where
the HDR check already reads it and passed on only when it changes, and
Android through HdrCapabilities (API 24), queried once by its display
server; Wayland and KMS already did. Apple's EDR APIs give a headroom
over the current SDR white, never an absolute peak, so macOS and iOS
report none.
2026-09-25 03:21:58 +02:00
LibretroAdmin f6e7ece621 (Android) Post keypress haptic feedback to the UI thread
doHapticFeedback() is called from the native app thread, and
View.performHapticFeedback() is not documented as thread safe. Drop the
per-keypress log line with it: it runs on an interactive path.
2026-09-20 19:08:17 +02:00
LibretroAdmin 9516f454b8 The reference Zstandard library goes; the built-in codec is the one
With every platform build on HAVE_RZSTD, HAVE_ZSTD had no user: the
configure option, the deps/zstd tree it built, griffin's inclusion
of it, the Makefile.common objects, the UWP and Android include
paths, and the other branch of every site that could take either
backend - the input replays, the RVZ hashing, the .zst archive and
zip-member readers, the CHD hunk decoder - are gone, and each site
is the one path. Every gate that read 'either backend' reads
HAVE_RZSTD.
2026-09-09 10:55:28 +02:00
libretroadmin 6e2b5d5318 android: run the startup core symlink pass off the UI thread
RetroActivityCommon.onCreate ran cleanupSymlinks() and updateSymlinks()
synchronously on the UI thread. cleanupSymlinks() lists the cores
directory and unlinks every symlink in it; on a Play Store build
updateSymlinks() then walks the whole app data and native library
trees looking for Dynamic Feature Module cores to relink. Both are
disk I/O, and on a slow flash device with many installed modules the
walk is long enough to stall the first frame and count towards an ANR.

Run the pass on a worker thread instead and fold it into the startup
gate the native thread already waits on: the gate is now released by
whichever of the storage permission outcome and the symlink pass lands
last, so cores are still linked before native core enumeration runs,
and the native upcall only ever fires after super.onCreate has loaded
the library. The pass is serialized against the one PlayCoreManager
runs after a module install so two walks never delete and recreate
the same links at once, and cleanupSymlinks() no longer dereferences a
null listFiles() result when the cores directory cannot be read.

Fixes #18332
2026-09-08 01:39:43 +02:00