Files
RetroArch/command.h
T
Davey Hughesandlibretroadmin 6ebf66b643 OpenXR: per-screen placement for headset screens, and Edit Screens to arrange them by hand (#19945)
* video: per-screen geometry for the headset's floating screens

Each floating screen, by the core's logical screen id, gets its own
offsets, yaw, pitch, roll, scale and visibility (video_screen_xr_t),
applied on top of where the layout puts it. video_views_xr_reset gives
the defaults and video_views_xr_normalize clamps each value to its
range: offsets to 10 m either way, angles to 180 degrees, scale from 0.1
to 10. A planner given no screens uses the defaults, so nothing moves
until a setting does.

* configuration: save per-screen headset geometry

video_screen<N>_xr_x, _y, _z, _yaw, _pitch, _roll, _scale and _visible
keep each floating screen's adjustments for screen ids 1 to 8, and
survive configuration overrides like the other video settings. A minimal
save writes only the values that differ from the defaults.

The video_screens settings sample checks the round trip and the override
cases, and runs in the OpenXR input and Wine views jobs. The defaults
fixture now links video_views.c, which the reset uses.

* menu: per-screen controls for the headset's floating screens

Stereo 3D & Screens gains Screen, which picks a floating screen by its
logical id among those in the current frame's map, and for it
Horizontal, Vertical and Depth Offset, Yaw, Pitch, Roll, Scale and
Visible, with Reset Screen and Reset All Screens. The rows apply live
and are hidden where they cannot apply, such as a tracked projection's
eyes. Stepping between screens reads one snapshot of the map and is
bounded, so a tracking change mid-step cannot leave it walking an empty
map.

docs/openxr.md describes the controls; the video_screens sample drives
them through the shipping menu actions.

* video: apply per-screen headset geometry to the frame snapshots

Each XR planner, Vulkan's and GL's, takes the normalized screen settings
with the frame snapshot, so a screen moves, turns, scales or hides as
its rows change, while paused too; recentering and tracked projections
are unaffected. Screen images are kept between frames, so shader
feedback survives a placement change.

A retained XR target that cannot be allocated is skipped quietly,
remembered by its size so it is not retried every frame; only the
desktop UI and canvas targets log the failure.

Covered by openxr_core_test, the video_screens sample, the e2e runner's
feedback shader case and the new vulkan_views_target sample.

* android: keep the OpenXR EGL context C89

gl_xr_egl_init declared its extension string after statements.

* video: pose inverse, layout spots and grab maths for headset edit mode

Each item's layout spot comes from one helper that placement uses and
the quad set records per slot, so a shown pose turns back into the
settings that placed it. Grab maths: the item follows the aim, level
unless tilting, offsets clamped to the settings' range; the stick pushes
along the ray and resizes. Edit picking sees every item and takes the
menu by a handle bar under it.

* video: a frame without a view map is screen 1 in the headset

A core without a views map (SNES, say) had no screen to select, so its
frame could not be moved. Its whole frame now takes screen 1's offsets,
turn, scale and visibility, and the selector lists Screen 1. gl's window
stays as laid out while the menu is open over it.

* video: the headset's menu in front of screen 1, or where it was put

The menu sat at a fixed spot, so moving screen 1 closer left the menu
behind it. It now floats 10 cm in front of screen 1, facing as it does,
at its default angular size and never nearer than 0.4 m, while screen 1
floats and shows, and is still submitted after the screens. A menu place
(video_openxr_menu_xr_* with video_openxr_menu_placed) overrides that;
it is saved like the per-screen keys, and Reset All Screens clears it.

* input: Edit Headset Screens mode for the OpenXR controllers

Edit Screens starts from a hotkey, a headset action, the
HEADSET_EDIT_SCREENS command or a Stereo 3D & Screens row, and stops on
the same, on opening the menu, on a video reinit or when the session
ends. It syncs its own action set in place of the game's, points the
lasers at every item whatever the Laser Pointer setting, keeps the
menu's quad up while editing, and with While Editing Screens set to
Pause pauses only content that was running. A runtime that refuses its
bindings keeps the game's and leaves edit mode unavailable.

The setting reaches the headset's poll with the others, and the pause
and resume are asked of the frontend as platform requests, so the input
driver issues no command and takes no settings itself. A Laser Pointer
value past its choices, which a hand-edited config can hold, reads as
Auto rather than as edit mode's.

* input: grab, tilt, push and resize headset screens in edit mode

A grip on an item takes it in the screens' frame: it keeps its pose
relative to the hand, stays level unless the trigger is held, and the
stick pushes it along the laser and resizes it, past a deadzone. Each
poll writes the item's offsets, turn and scale where the menu's rows
write them, through named frontend calls, clamped so it waits at a limit
and resumes from there. The menu is taken by its handle; a menu that
follows screen 1 is placed as large as it shows. The main button resets
a free item as Reset Screen does, or puts the menu back in front of
screen 1. A recenter, a screen leaving the map and leaving edit mode let
go, counted for the frame snapshot; an untracked hand holds its item
still.

* openxr: held screens follow the hand at the headset's rate

The main thread publishes what each hand holds through a seqlock; each
headset frame re-places a held item, both eyes of a stereo screen, from
that frame's own aim, so it no longer waits for the next video frame. An
item let go stays where the hand left it until a quad set planned at or
after its release (an edit count carried in the frame snapshot) shows
it, even once the hand holds another item, so nothing steps back. Lazy
Follow holds its glide while anything is held.

Edit mode outlines every item, brightly the one under a laser, and draws
the menu's handle bar under it, from the laser's image, now an atlas of
the dot, two outlines and two handle bars. They take only the layers the
screens leave after the dots.

* openxr: edit mode end-to-end cases and documentation

The test layer's aim can roll, and the e2e runner drives edit mode
against Monado: grabs that follow the hand on every headset frame and
stay where they are let go, level and tilt, the stick, resets of a
screen and of the menu, the menu taken by its handle and following
Screen 1 at its size, outlines and the handle, a no-map core, a views
core's fallback frame, Follow Head, Lazy Follow, recentering, two hands,
pausing, and what starts and ends edit mode, including the headset's
hotkey bits with no command interface. The cases whose work is a host's
or driver's own run on glcore and gl too. docs/openxr.md explains
editing screens in the headset and where the menu goes.

* openxr: a laser beam from each controller to its dot

A thin strip runs from each hand's aim to its dot, or 1.5 m into
nothing, turned about its own axis each headset frame to face the head
so it never shows edge on. It comes from the laser's image, so it needs
no new swapchain, and uses layers left after the dots and edit mode's
outlines, so it is the first to go when the runtime has few. The new
Laser Beam setting shows it in edit mode only (the default), always, or
never; outside edit mode, Laser Pointer and its hotkey hide it with the
dot.

* openxr: edit mode outlines in three bands, outside the item

A light band between two dark ones shows on white, black and busy
borders alike. The rim now sits wholly outside its item, 5 mm clear of
it even after filtering, so it never covers the picture; its region
grows to keep two texels a band at about the old thickness. Screens'
outlines are submitted before the screens, so a neighbour placed flush
covers the part of a rim over its picture, while the menu's outline and
handle bar stay on top. The handle bar moves 5 mm below the menu's
outline, which it would otherwise cover, and gets the same bands, faint
and bright.

* input: choose a headset edit item with Select Next and Previous

Edit mode steps from the laser through the screens it can move and the
menu. A chosen item is the one lit, either hand's grip takes it from
wherever the hand is, and Reset resets it, so a screen hidden behind
another can be moved. Whatever a hand holds stays lit.

A ray on the menu's handle bar takes the menu before any screen in front
of it. Within 3 cm of the bar, a ray that came from the bar keeps the
menu even over a screen, and one that came from elsewhere takes it only
where no screen is hit.

* openxr: snap a held headset screen to the screens near it

Snap to Screens, on by default, turns a held screen parallel or square
to a nearby screen and lines up its plane, edges or centre. Each pull is
exact within 3 cm or 5 degrees and fades out over as far again, so the
screen catches up with the hand rather than jumping, and snapping fades
in over the first 5 cm or 10 degrees after a grab, so taking a screen
never moves it. With several screens near, the snap that leaves the
least error wins, so a near screen's exact pull beats a far one's fading
pull whatever their order.

One stateless function inside the grab serves the main thread and the
headset's thread, so a screen is saved where it was shown. The holding
hand buzzes as a snap takes hold, once, and not again until the screen
has left that pull.

* input: turn a held headset item with the free hand's stick

A quarter turn of a held screen needed the controller turned a quarter
turn, which swung the screen round the hand on its long reach, so a
square snap could not be reached. Now the stick of the hand holding
nothing turns what the other hand holds about the vertical through its
middle, faster the further it is pushed, and snapping still catches it
parallel or square.

A carried item keeps its pitch and roll and its angle to the way the
hand points across the floor, so raising, lowering or twisting the hand
no longer tips or swivels it; the trigger lets it ride the hand rigidly.

* openxr: smooth the controller aim in edit mode

A screen held about 2 m out on the laser magnified every tremor of the
hand. In edit mode each hand's aim now goes through a One Euro filter,
which smooths a slow hand hard and a fast one little, with its rate
taken from the headset frame's own time step. The headset's thread
filters once a frame and hands the result to the main thread, which
grabs, moves and lets go from it, so a screen is saved where it was
shown. The new Edit Smoothing setting picks Off, Low, Medium (the
default) or High; outside edit mode the lasers are never smoothed.

* openxr: name the chosen edit item beside the controller

Edit mode now shows a faint tag just above the hand that last pressed
Select Next or Previous, reading Laser, Screen N or Menu, so what the
grip will take is plain without a notice. The words are drawn once into
the laser's image in the default font and the interface language, light
over a dark halo like the outlines so they read on any background, and
the tag is one more headset layer; when the runtime is short of layers
the beams are dropped before it.

On a runtime whose swapchains are too narrow for the words, the laser's
image keeps its old width and there is no tag.

* input: tilt a held headset item with the free hand's stick

In edit mode the free hand's stick already turns the item the other hand
holds. Its up and down now tilt that item too, about the item's own
left-right line through its middle, at the turn's rate and with no
limit: up tips the top back, as when pushing a monitor's top back. It
works whether the item is carried or rides the hand with the trigger
held, and snaps as the turn does.

Within about 25 degrees of left or right the stick only turns, and
within about 25 degrees of up or down it only tilts, so a push meant for
one does not drift into the other. Edit mode's starting notice lists the
turn and tilt and stays about 8 seconds.

* input: snap a turned or tilted headset item to angle steps

While the free hand's stick turns or tilts a held item in edit mode, the
item clicks into steps of the new Snap to Angles setting (15 degrees by
default; 30, 45, 90 or Off), so it is easy to set straight. The turn
counts from facing the user's head across the floor, the tilt from
upright. Within about 2 degrees of a step the item holds there and the
holding hand buzzes once; the stick's own angle keeps adding up, so
pushing on leaves the step.

Only the stick steps: carrying an item or tilting it with the trigger
never clicks, and a snap to another screen that would engage wins over a
step.

* openxr: show the back of a headset screen turned away

Runtimes draw only the front of a quad layer, so a screen or the menu
turned away from the head vanished. When the head is behind a quad's
plane, the frame now submits a plain dark panel in that quad's place: a
solid grey cell of the laser's image, at the quad's size, turned to
face the head. It takes the quad's own layer, so the layer count and
cap are unchanged. Edit mode picks either face, so an item turned away
stays grabbable, and its outline, handle and dot turn with its back.

* input: keep a screen snap's angle once the free stick lets go

A snap to another screen shows a held screen square or parallel to it,
but the item's own angle stays where the stick or the hand took it, up
to several degrees off. The next turn with the free hand's stick started
from that hidden angle, so the screen left the snap early on one side,
late on the other, and caught up with a lurch.

Now, as the free stick returns to its deadzone and as a push starts, a
snap engaged on the turn becomes the item's own angle: the carried turn,
or the orientation the trigger tilts. A snap still fading in never does,
since that would hop the screen into it. The place is left to the hand,
and carrying alone never keeps the angle, so pointing still pulls a
screen out of a snap. Letting go of the grip leaves the item where it
shows, as before.

* openxr: hinge a held headset screen to a neighbour's edge

Snap to Screens now also hinges a held screen to another along a
shared edge at any angle between them, so a DS or 3DS pair can be
opened at a slant: an edge within 5 degrees of parallel to one of the
other's, within 3 cm of its line and sharing some length with it,
turns exactly parallel and moves onto it, centres or ends level, its
angle left free. The pull fades in over the first 3 cm of shared
length, so a screen slides off the end of the other's edge without a
jump, and a diagonal neighbour never hinges. Screens that would
overlap do not hinge; the pull is whole from flat to square and fades
out folded shut.

Flat and square snap as before and report a hinge at 0 and 90. A turn
to the same screen pivots on the shared edge, so the edge stays on the
line as the screen leaves flat or square. The snap still keeps nothing
between calls; its result carries a hinge bit, and video_xr_snap_hinge
also says which edge and line, for the stick to swing about.

* input: swing a hinged headset screen about the shared edge

While a held screen is hinged to another, the free hand's stick axis
that turns nearest the shared edge, the tilt for a top or bottom edge
and the turn for a side one, now swings it about that edge, so it stays
attached; the other axis still turns or tilts it and can pull it off.
Where the swing leaves it, angle and place, it stays as the stick lets
go. Snap to Angles counts a hinged screen's steps from flat with the
other screen.

A hinge counts only while the screen's own edge is within its range,
not when other snaps alone hold it on the line, so a swing never
carries a screen round a line it is far from.

A square corner is a side hinge at 90 degrees, so the turn stick there
now swings the screen about the corner's edge. Main thread only;
nothing new reaches the frame loop, which still shows the grab through
video_xr_grab_pose.

* openxr: leave a gap between hinged headset screens

Edges that meet in edit mode, side by side, at a corner or hinged at any
angle, now click to a set gap from within 1 cm and otherwise keep the
gap the hand gives them, still hinged, up to 10 cm (letting go by 13).
Screens no longer have to touch, which made them overlap. Each grab
carries the gap, so the edit poll and the frame loop snap alike; until a
setting sets it, it is 0.

Pushed past touching, a hinged edge stops there for the first centimetre
of the push, the stop fading out by 3 cm, and ticks once per push.

A swing pivots on the held screen's own edge, so the gap keeps through
it, and a commit keeps the hand's gap unless the click holds it. After a
swing lets go past square, that stick axis and its steps keep pivoting
on the same edge until the edge leaves the hinge's reach, so a flick
past square and back does not creep.

Of two neighbours within the 5 degree turn snap, the nearer edge wins
the turn and the hinge, by one measure for every screen, so a screen
stacked behind, one off the ends of the edge or a side wall at a corner
never outranks a hinge the turn holds.

* menu: add Hinge Gap for headset screens

Hinge Gap (0, 1, 2, 3, 5 or 10 cm, default 2) sets the gap edit mode's
Snap to Screens leaves between screens that meet edge to edge, side by
side, at a corner or hinged. Edit mode takes it as it starts and gives
it to each grab; the click to it buzzes like the other snaps.

* menu: put Edit Screens under Headset Output

Stereo 3D & Screens now lists Edit Screens and its snaps right under the
switch that reveals them, then the headset's own setup, then where the
screens go, with the per-screen rows last and Visible first among them.

* openxr: track each held screen's hinge on the frame loop

The frame loop already re-snaps each held screen every frame. It now
also keeps, per hand, the hinge that snap holds whole, until the edge
leaves the hinge's 4.5 cm reach, where the frame draws it, so edit mode
can mark it. A kept hinge whose target no longer matches is dropped.

video_xr gains the hinge line's ends, along the overlap of the two real
edges and midway across the gap, an outline cut that drops one edge's
rim, and a green strip in the laser's image, before the tags.

* openxr: mark a hinged headset screen with a green line

While a held screen's hinge holds, the two edges that meet drop their
outline rim and a 6 mm green line runs along the hinge where they
overlap, midway across the gap, so it is clear when the hinge has taken
hold.

The line is drawn as the beam is, a centimetre nearer the eye than the
edges, since SteamVR does not keep the order of overlapping quad layers,
and comes after the outlines in the layer budget; hidden rims cost no
layer.

* input: buzz longer as a headset screen hinges

A hinge taking hold now gets one 150 ms buzz at 0.8 on the hand carrying
the screen, against the 20 ms snap tick, so the attach can be felt. It
fires again only after the edge leaves the hinge's reach, hinges
elsewhere, or the other hand takes the screen it hinges on, so a swing
past square and back does not repeat it. Ticks due while it plays are
skipped rather than cutting it short.

* openxr: find headset screens resting in a hinge

Two screens no hand holds rest in a hinge when their poses pass the
test that engages a carried screen's hinge, with either one carried,
and the hinge, held whole with nothing else pulling, leaves the carried
one where it is. So a hinge found at rest stays hinged when its screen
is taken. The engaged test moves into its own function so both use the
same one, and one target can pull whole whatever the grab's fade.

* openxr: keep a headset screen's hinge shown at rest

In Edit Screens, two screens no hand holds that rest in a hinge keep
the green line and their hidden rims, whether let go hinged or loaded
that way. The main thread counts them only when a resting screen
changes and sends them with the hands' record, so a release and its
resting pair arrive together; the frame loop places each line from
the screens it shows. A screen hinged at rest on two adjacent edges
still keeps one of those rims.

The hands' record now outgrows the quads, so the seqlock's copy is
sized to the larger of the two.

* openxr: pick up a hinged headset screen still hinged

Taking up a screen that rests in a hinge starts the carry on that hinge,
whole from the first poll, with no buzz and no jump, until it leaves the
hinge's reach, another hinge takes hold, or the other hand takes the
screen it hinges on. Until the grab's own snapping has faded in, the
kept hinge's target still pulls whole, so a straight pull out does not
hop.

The grab carries the hinge so both threads hold it the same; of several,
the one the snap ranks best carries over, and only where holding it
moves nothing. While the other screens fade in, the hinge's screen also
leads the turn, so a nearer one still fading does not take it.

* openxr: end a headset screen's hinge where a push passes the stop

Pushing a hinged screen into its neighbour holds the edge at touching
for 3 cm of push, fading out by 5.5 cm. Where it stops holding, the
hinge ends: the line goes, the rims come back, and the lid and buzz are
cleared, so screens pushed into each other show no hinge that a let-go
there would not rest in. Pulled back, the hinge takes hold again 5 mm
short of where it ended, with the long buzz, so a tremor there does not
flicker it.

The grab carries that through flag, and the frame loop keeps its line
until the main thread sets it, so the two threads never disagree for
more than a poll.

At Hinge Gap 1 a pair let go touching rests hinged and is taken up
hinged, as at every other gap. A screen swung into a square corner gets
the long buzz on arrival, and the other hand taking the target does not
hop the carried screen by the push's depth.

* input: let go of a headset screen where it was before the grip opened

Opening the grip twitches the hand, and the poll that sees the grip
open would move the screen by that twitched aim. Half a degree of pitch
moves a screen 1.8 m out by 1.6 cm, enough to knock it off its hinge's
gap or out of level, so it would not rest hinged.

Each hand keeps a short trail of where, turned and as large as its
screen showed, entries at least 10 ms apart, and a grip let go puts the
screen as it showed 80 ms before (INPUT_OPENXR_EDIT_REWIND_US), or as it
was taken if held for less. Edit mode's end and a recenter let go where
the screen shows.

* openxr: find where a let-go headset screen settles in its hinge

A screen let go while its hinge shows should land exactly in that hinge,
so the hinge still shows at rest and a pick-up is hinged.
video_xr_hinge_settle holds the hinge whole from the hand's pose, then
moves the edge to the set gap from within 3 cm of it (touching stays
where the stop holds it) and levels it along the edge within the level
pull's reach. Its result must pass the rest test, and it turns no
further than a grab's hinge engages, which shares that test.

A pair resting inside a settle band but off its point is no longer
listed at rest, since a pick-up and let-go would move it. A touching
rest is taken 5 mm deeper into the push stop, at every Hinge Gap, so a
hand that drifts less than that apart before letting go settles touching
rather than at the gap.

* openxr: settle a let-go headset screen into its hinge

A screen let go while its hinge shows now goes exactly into that hinge:
to the set gap from within 3 cm of it, touching from the stop's hold,
and level along the edge from within the level pull's reach. It then
rests there, listed and green, in the same publish, and is saved
there. Elsewhere it stays where it shows, as before.

Each trail entry also records where the hand put the screen with no
snap and whether a let-go there settles. At the let-go the rewound
entry is settled again against the screens as they are now, at that
entry's size, and put in one go.

* openxr: let a hinged headset screen rest at any fold

Snap to Screens' turn to flat, and its turn to square at a corner, is
whole within 5 degrees and fades out by 10, so a hinged screen held
there shows at twice its angle off less 10. No fold shown in that fade
read as a rest: the pair was not listed, a let-go there did not settle
into its hinge, and taking it up would have moved it.

Where a hand takes a rest, it now holds the screen at the angle the
turn's fade shows it from, the inverse of the turn's pull, so every
fold rests, is listed and is taken up unmoved. Held snapping is
unchanged.

* input: keep a headset screen's hinge through a shaking hand

Edit Screens' hinge buzz, lid and push stop followed the snap's own
engage and reach on every poll. A hand shaking half a degree (1.6 cm at
the screen) about a hinge's edge flipped them many times a second, each
flip a long buzz.

Each hold now keeps one mark: the hinge a let-go would settle into. It
takes hold at once, with the long buzz, and lets go only once polls have
seen the hand out of where a let-go would settle for 150 ms, each poll
counting at most 50 ms, so a stalled main thread or a spell untracked
never lets it go alone. The other hand taking its target, hinged
elsewhere or not, the target leaving and the let-go end it at once. The
lid waits for the mark to go, the push stop follows it, and a let-go
while it shows settles into it from where the hand last would have, edit
mode's end and a recenter included.

* openxr: draw a held screen's hinge as the main thread marks it

The grab carries the main thread's hinge mark, and the frame loop draws
a held screen's green line and cuts its rims from that mark alone,
through video_xr_grab_marked, while it snaps the screen every frame. A
frame loop taking the hinge by itself between polls would flicker the
line for a hand shaking at the edge of the hinge's range while the mark
held. A mark whose target the frame does not show cuts no rim.

The other hand taking the mark's target ends it, and the lid, on any
poll, tracked or not, so a lost hand's line does not come back when that
hand lets go.

video_xr_grab_near_hinge loses its past argument, which only the frame
loop's keep used.

* openxr: keep edit mode's hinge layers in place

SteamVR does not keep submission order for overlapping quad layers, so
a layer added, dropped or moved to another index can pop the edit
outlines in front of the screens, as a wobble at a hinge would do to
its line's layer.

The hinge lines are fixed slots: one for each hand holding a
screen, hand 0 first, then one for each pair at rest. A slot with no
line to show is drawn in place from its screen's middle, at rows of
the laser image that are empty either way up. The layer count and
every index change only at a grab, a let-go or a re-count of the pairs
at rest; a mark only changes crops and which rows a slot shows.

* input: date a let-go's trail by frame and stop it at a stall

Each trail entry is dated by the frame its aims are from, one a frame
and per hand, and the release poll records a new frame's aims first.
The rewind stops at a stall, entries more than 2.5 frames apart, a
frame counted as at least 20 ms so a fast headset's close entries never
read as one. Under load a stall of 0.1 to 0.3 s can come just before
the release; the entry from 80 ms back would then hold the pose from
before it and step the let-go back across all the stall's motion.

With Edit Smoothing on, which damps the twitch, a let-go that does not
settle into a hinge stays where and as large as it shows: a rewind there
would step back by what the smoothed aim still lags. The hinge decision
and the settle use the trail either way.

* openxr: cut edit outlines from cells instead of turning them

Cutting a top or bottom rim from the one outline image turns both
outline quads a quarter turn and swaps their width and height, and on
SteamVR that can blink every edit outline as the hinge takes hold or
lets go; side cuts, which never turn, do not.

The laser's image carries twelve pre-cut outline cells after the
tags, faint and bright, for a cut top, bottom, left, right, top and
bottom, or left and right, so a cut only picks a cell and the quads keep
their pose and size. The image is then 5582 texels wide. A runtime whose
swapchains are narrower, or that does not say how wide they can be,
keeps the turned and cropped outline at 2498 or 1208 wide. One log line
at the laser's creation gives the width chosen and the runtime's limit.
GL writes the top and bottom cells swapped for its bottom-up rows, and
Android's copy of the image is allocated rather than static.

The edit-hinge-3ds-narrow e2e case runs the turned cut at
RA_XR_LAYER_MAX_WIDTH=4096, on Vulkan and the GL EGL lanes.

* video: draw headset screens straight into the runtime's images

Each headset screen was drawn into an image of the driver's own and
copied from it into the runtime's on every new frame, Vulkan with a
render pass and GL3 with a blit. Screens are now drawn into the
runtime's image itself, as before per-screen placement, with no image
of the driver's and no copy.

A screen drawn again with no new frame - resized, made again or moved
while paused - replays the last frame's final pass from the chain:
build_viewport_pass_replay draws it at the new size from what it read
then, the passes' outputs, their feedback and the history that frame
saw, with that frame's count. Nothing else runs, feedback does not
swap and history stays, so a feedback or history preset holds still
while paused. A chain made to replay keeps one more history image, so
the frame's end writes where the last frame did not read; the window's
chains keep what they did. This also has movie frames reach the
headset, which the copy held at the first one, and draws a screen
resized while paused at its new size rather than stretching the old
image.

* openxr: leave what users who never edit see as it was

Screens seen from behind turn to their grey backs only while Edit
Screens is on, where screens are turned around; outside it nothing
changes and no head is located for them. The grabs carry whether
editing is on.

The menu opens at its own spot, straight ahead of the anchor, as it
did. Menu in Front of Screen 1, off by default, keeps it just in front
of screen 1 and moving with it.

The frame loop copies the main thread's grabs only once their stamp
moves, so outside edit mode no frame copies them. A new session clears
the frame loop's own copy and passes over what was published before
it, rather than publishing itself: the main thread stays the grabs'
one writer.

On Touch, the left menu button that opens the menu from edit mode is
spent until let go: held on as edit mode ends, it is no Start and does
not toggle the menu again after a second.

---------

Co-authored-by: libretroadmin <[email protected]>
2026-10-30 05:40:25 +01:00

1446 lines
69 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2016 - Daniel De Matteis
* Copyright (C) 2021 - David G.F.
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMAND_H__
#define COMMAND_H__
#include <stdint.h>
#include <boolean.h>
#include <retro_common_api.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <streams/interface_stream.h>
#include "tasks/task_notify.h"
#include "retroarch_types.h"
#include "input/input_defines.h"
#include "configuration.h"
#define MAX_CMD_DRIVERS 4
#define DEFAULT_NETWORK_CMD_PORT 55355
RETRO_BEGIN_DECLS
enum event_command
{
CMD_SPECIAL = -1,
CMD_EVENT_NONE = 0,
/* Resets RetroArch. */
CMD_EVENT_RESET,
CMD_EVENT_SET_PER_GAME_RESOLUTION,
CMD_EVENT_SET_FRAME_LIMIT,
/* Loads core. */
CMD_EVENT_LOAD_CORE,
CMD_EVENT_LOAD_CORE_PERSIST,
#if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
CMD_EVENT_LOAD_SECOND_CORE,
#endif
CMD_EVENT_UNLOAD_CORE,
/* Closes content. */
CMD_EVENT_CLOSE_CONTENT,
/* Swaps the current state with what's on the undo load buffer. */
CMD_EVENT_UNDO_LOAD_STATE,
/* Rewrites a savestate on disk. */
CMD_EVENT_UNDO_SAVE_STATE,
/* Save state hotkeys. */
CMD_EVENT_LOAD_STATE,
CMD_EVENT_SAVE_STATE,
CMD_EVENT_SAVE_STATE_DECREMENT,
CMD_EVENT_SAVE_STATE_INCREMENT,
/* Replay hotkeys. RECORD_REPLAY and the checkpoint events take a
* task_notify_t, told when their work is through, or NULL. */
CMD_EVENT_PLAY_REPLAY,
CMD_EVENT_RECORD_REPLAY,
CMD_EVENT_HALT_REPLAY,
CMD_EVENT_SAVE_REPLAY_CHECKPOINT,
CMD_EVENT_PREV_REPLAY_CHECKPOINT,
CMD_EVENT_NEXT_REPLAY_CHECKPOINT,
CMD_EVENT_REPLAY_DECREMENT,
CMD_EVENT_REPLAY_INCREMENT,
/* Save state actions. */
CMD_EVENT_SAVE_STATE_TO_RAM,
CMD_EVENT_LOAD_STATE_FROM_RAM,
CMD_EVENT_RAM_STATE_TO_FILE,
/* Takes screenshot. */
CMD_EVENT_TAKE_SCREENSHOT,
/* Quits RetroArch. */
CMD_EVENT_QUIT,
/* Reinitialize all drivers. */
CMD_EVENT_REINIT_FROM_TOGGLE,
/* Re-evaluate the on-screen notification (gfx_widgets) system to
* match current settings, without a full driver reinit. */
CMD_EVENT_OSD_NOTIFICATION_TOGGLE,
/* Reinitialize all drivers. */
CMD_EVENT_REINIT,
/* Toggles cheevos hardcore mode. */
CMD_EVENT_CHEEVOS_HARDCORE_MODE_TOGGLE,
/* Deinitialize rewind. */
CMD_EVENT_REWIND_DEINIT,
/* Initializes rewind. */
CMD_EVENT_REWIND_INIT,
/* Reinitializes rewind (primarily if the state size changes). */
CMD_EVENT_REWIND_REINIT,
/* Toggles rewind. */
CMD_EVENT_REWIND_TOGGLE,
/* Initializes autosave. */
CMD_EVENT_AUTOSAVE_INIT,
/* Stops audio. */
CMD_EVENT_AUDIO_STOP,
/* Starts audio. */
CMD_EVENT_AUDIO_START,
/* Mutes audio. */
CMD_EVENT_AUDIO_MUTE_TOGGLE,
/* Volume adjustments. */
CMD_EVENT_VOLUME_UP,
CMD_EVENT_VOLUME_DOWN,
CMD_EVENT_MIXER_VOLUME_UP,
CMD_EVENT_MIXER_VOLUME_DOWN,
/* Toggles Video Filter*/
CMD_VIDEO_FILTER_TOGGLE,
/* Toggles FPS counter. */
CMD_EVENT_FPS_TOGGLE,
/* Toggles statistics display. */
CMD_EVENT_STATISTICS_TOGGLE,
/* Initializes video filter. */
CMD_EVENT_VIDEO_FILTER_INIT,
/* Initializes overlay. */
CMD_EVENT_OVERLAY_INIT,
/* Frees or caches overlay. */
CMD_EVENT_OVERLAY_UNLOAD,
/* Sets current scale factor for overlay. */
CMD_EVENT_OVERLAY_SET_SCALE_FACTOR,
/* Sets current alpha modulation for overlay. */
CMD_EVENT_OVERLAY_SET_ALPHA_MOD,
/* Sets diagonal sensitivities of overlay eightway areas. */
CMD_EVENT_OVERLAY_SET_EIGHTWAY_DIAGONAL_SENSITIVITY,
/* Deinitializes overlay. */
CMD_EVENT_DSP_FILTER_INIT,
/* Initializes recording system. */
CMD_EVENT_RECORD_INIT,
/* Deinitializes recording system. */
CMD_EVENT_RECORD_DEINIT,
/* Deinitializes history playlist. */
CMD_EVENT_HISTORY_DEINIT,
/* Initializes history playlist. */
CMD_EVENT_HISTORY_INIT,
/* Deinitializes core information. */
CMD_EVENT_CORE_INFO_DEINIT,
/* Initializes core information. */
CMD_EVENT_CORE_INFO_INIT,
/* Deinitializes core. */
CMD_EVENT_CORE_DEINIT,
/* Initializes core. */
CMD_EVENT_CORE_INIT,
/* Apply video state changes. */
CMD_EVENT_VIDEO_APPLY_STATE_CHANGES,
/* Set video blocking state. */
CMD_EVENT_VIDEO_SET_BLOCKING_STATE,
/* Sets current aspect ratio index. */
CMD_EVENT_VIDEO_SET_ASPECT_RATIO,
/* Restarts RetroArch. */
CMD_EVENT_RESTART_RETROARCH,
#ifdef HAVE_CLOUDSYNC
/* Trigger cloud sync */
CMD_EVENT_CLOUD_SYNC,
/* Resolve cloud sync conflicts by keeping local files */
CMD_EVENT_CLOUD_SYNC_RESOLVE_KEEP_LOCAL,
/* Resolve cloud sync conflicts by keeping server files */
CMD_EVENT_CLOUD_SYNC_RESOLVE_KEEP_SERVER,
#endif
/* Shutdown the OS */
CMD_EVENT_SHUTDOWN,
/* Reboot the OS */
CMD_EVENT_REBOOT,
/* Resume RetroArch when in menu. */
CMD_EVENT_RESUME,
/* Add a playlist entry to favorites. */
CMD_EVENT_ADD_TO_FAVORITES,
/* Reset playlist entry associated core to DETECT */
CMD_EVENT_RESET_CORE_ASSOCIATION,
/* Toggles pause. */
CMD_EVENT_PAUSE_TOGGLE,
/* Pauses RetroArch. */
CMD_EVENT_MENU_PAUSE_LIBRETRO,
CMD_EVENT_PAUSE,
/* Unpauses RetroArch. */
CMD_EVENT_UNPAUSE,
/* Toggles menu on/off. */
CMD_EVENT_MENU_TOGGLE,
/* Configuration saving. */
CMD_EVENT_MENU_RESET_TO_DEFAULT_CONFIG,
CMD_EVENT_MENU_SAVE_CONFIG,
CMD_EVENT_MENU_SAVE_AS_CONFIG,
CMD_EVENT_MENU_SAVE_MAIN_CONFIG,
CMD_EVENT_MENU_SAVE_CURRENT_CONFIG,
CMD_EVENT_MENU_SAVE_CURRENT_CONFIG_OVERRIDE_CORE,
CMD_EVENT_MENU_SAVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR,
CMD_EVENT_MENU_SAVE_CURRENT_CONFIG_OVERRIDE_GAME,
CMD_EVENT_MENU_REMOVE_CURRENT_CONFIG_OVERRIDE_CORE,
CMD_EVENT_MENU_REMOVE_CURRENT_CONFIG_OVERRIDE_CONTENT_DIR,
CMD_EVENT_MENU_REMOVE_CURRENT_CONFIG_OVERRIDE_GAME,
/* Applies shader changes. */
CMD_EVENT_SHADERS_APPLY_CHANGES,
/* A new shader preset has been loaded */
CMD_EVENT_SHADER_PRESET_LOADED,
/* Shader hotkeys. */
CMD_EVENT_SHADER_NEXT,
CMD_EVENT_SHADER_PREV,
CMD_EVENT_SHADER_TOGGLE,
/* Apply cheats. */
CMD_EVENT_CHEATS_APPLY,
/* The codec compressed saves are written with, from the setting. */
CMD_EVENT_SAVE_COMPRESSION_CODEC_APPLY,
/* Cheat hotkeys. */
CMD_EVENT_CHEAT_TOGGLE,
CMD_EVENT_CHEAT_INDEX_PLUS,
CMD_EVENT_CHEAT_INDEX_MINUS,
/* Initializes network system. */
CMD_EVENT_NETWORK_INIT,
/* Initializes netplay system with a string or no host specified. */
CMD_EVENT_NETPLAY_INIT,
/* Initializes netplay system with a direct host specified. */
CMD_EVENT_NETPLAY_INIT_DIRECT,
/* Initializes netplay system with a direct host specified after loading content. */
CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED,
/* Deinitializes netplay system. */
CMD_EVENT_NETPLAY_DEINIT,
/* Switch between netplay gaming and watching. */
CMD_EVENT_NETPLAY_GAME_WATCH,
/* Open a netplay chat input menu. */
CMD_EVENT_NETPLAY_PLAYER_CHAT,
/* Toggle chat fading. */
CMD_EVENT_NETPLAY_FADE_CHAT_TOGGLE,
/* Start hosting netplay. */
CMD_EVENT_NETPLAY_ENABLE_HOST,
/* Disconnect from the netplay host. */
CMD_EVENT_NETPLAY_DISCONNECT,
/* Toggle ping counter. */
CMD_EVENT_NETPLAY_PING_TOGGLE,
/* Toggles netplay hosting. */
CMD_EVENT_NETPLAY_HOST_TOGGLE,
/* Reinitializes audio driver. */
CMD_EVENT_AUDIO_REINIT,
/* Resizes the window to the queued scale. */
CMD_EVENT_RESIZE_WINDOWED_SCALE,
/* Applies the configured windowed scale. */
CMD_EVENT_APPLY_WINDOWED_SCALE,
/* Toggles disk eject. */
CMD_EVENT_DISK_EJECT_TOGGLE,
/* Cycle to next disk. */
CMD_EVENT_DISK_NEXT,
/* Cycle to previous disk. */
CMD_EVENT_DISK_PREV,
/* Switch to specified disk index */
CMD_EVENT_DISK_INDEX,
/* Appends disk image to disk image list. */
CMD_EVENT_DISK_APPEND_IMAGE,
/* Stops rumbling. */
CMD_EVENT_RUMBLE_STOP,
/* Toggles turbo fire. */
CMD_EVENT_TURBO_FIRE_TOGGLE,
/* Toggles mouse grab. */
CMD_EVENT_GRAB_MOUSE_TOGGLE,
/* Toggles game focus. */
CMD_EVENT_GAME_FOCUS_TOGGLE,
/* Toggles desktop menu. */
CMD_EVENT_UI_COMPANION_TOGGLE,
/* Toggles fullscreen mode. */
CMD_EVENT_FULLSCREEN_TOGGLE,
CMD_EVENT_FULLSCREEN_APPLY,
/* Toggle recording. */
CMD_EVENT_RECORDING_TOGGLE,
/* Toggle streaming. */
CMD_EVENT_STREAMING_TOGGLE,
/* Toggle Run-Ahead. */
CMD_EVENT_RUNAHEAD_TOGGLE,
/* Toggle Preemtive Frames. */
CMD_EVENT_PREEMPT_TOGGLE,
/* Deinitialize or Reinitialize Preemptive Frames. */
CMD_EVENT_PREEMPT_UPDATE,
/* Force Preemptive Frames to refill its state buffer. */
CMD_EVENT_PREEMPT_RESET_BUFFER,
/* Toggle VRR runloop. */
CMD_EVENT_VRR_RUNLOOP_TOGGLE,
/* Place the headset's screens in front of where it looks now. */
CMD_EVENT_HEADSET_RECENTER,
/* Switch the headset's Laser Pointer between Off and its last mode. */
CMD_EVENT_LASER_POINTER_TOGGLE,
/* Cycle what a core is told the headset displays: immersive, stereoscopic, flat. */
CMD_EVENT_HEADSET_VIEW_TOGGLE,
/* Start or stop moving the headset's screens with its controllers. */
CMD_EVENT_HEADSET_EDIT_SCREENS,
/* AI service. */
CMD_EVENT_AI_SERVICE_TOGGLE,
CMD_EVENT_AI_SERVICE_CALL,
/* Misc. */
CMD_EVENT_SAVE_FILES,
CMD_EVENT_LOAD_FILES,
CMD_EVENT_CONTROLLER_INIT,
CMD_EVENT_DISCORD_INIT,
CMD_EVENT_PRESENCE_UPDATE,
/* camera_allow changed: a running camera the user just disallowed
* is stopped here, so the frame path tests only its frame_work bit */
CMD_EVENT_CAMERA_ALLOW_APPLY,
CMD_EVENT_OVERLAY_NEXT,
CMD_EVENT_OSK_TOGGLE,
CMD_EVENT_RELOAD_CONFIG,
#ifdef HAVE_MICROPHONE
/* Stops all enabled microphones. */
CMD_EVENT_MICROPHONE_STOP,
/* Starts all enabled microphones */
CMD_EVENT_MICROPHONE_START,
/* Reinitializes microphone driver. */
CMD_EVENT_MICROPHONE_REINIT,
#endif
/* Add a playlist entry to another playlist. */
CMD_EVENT_ADD_TO_PLAYLIST
};
enum cmd_source_t
{
CMD_NONE = 0,
CMD_STDIN,
CMD_NETWORK
};
/* What a command does, for interfaces that describe the commands to a
* client: HELP, and tool annotations for clients that ask before they
* run anything. */
enum cmd_info_flags
{
/* Only reports; changes nothing. */
CMD_INFO_READ_ONLY = (1 << 0),
/* May lose unsaved progress or data, or loads code from a path:
* quits, resets, loads or overwrites states and saves, writes core
* memory, loads or unloads cores and content. */
CMD_INFO_DESTRUCTIVE = (1 << 1),
/* A line command that is not offered as a tool: another command
* covers it or takes its value outright, or it steps a selection
* nothing reports. It stays a command on every line interface. */
CMD_INFO_NO_TOOL = (1 << 2)
};
/* A hotkey command: sending it presses the hotkey for one frame. */
struct cmd_map
{
const char *str;
unsigned id;
const char *desc;
unsigned flags; /* enum cmd_info_flags */
};
struct command_handler;
typedef void (*command_poller_t)(struct command_handler *cmd);
typedef void (*command_replier_t)(struct command_handler *cmd, const char * data, size_t len);
typedef void (*command_destructor_t)(struct command_handler *cmd);
typedef void *(*command_reply_dest_t)(struct command_handler *cmd);
typedef void (*command_reply_to_t)(struct command_handler *cmd,
void *dest, const char *data, size_t len);
/* As reply_to, with an image alongside the text: @image is base64 of
* @mime */
typedef void (*command_reply_image_to_t)(struct command_handler *cmd,
void *dest, const char *text, size_t len, const char *mime,
const char *image, size_t image_len);
struct command_handler
{
/* Interface to poll the driver */
command_poller_t poll;
/* Interface to reply */
command_replier_t replier;
/* Where the reply to the command being handled goes, for a reply
* sent after the handler has returned: a heap copy, released with
* free(). NULL on an interface whose replies all go one way. */
command_reply_dest_t reply_dest;
/* Sends to a destination reply_dest returned. */
command_reply_to_t reply_to;
/* Sends a reply carrying an image, where the interface can; NULL on
* the others, which are sent the text alone */
command_reply_image_to_t reply_image_to;
/* Interface to delete the underlying command */
command_destructor_t destroy;
/* Underlying command storage */
void *userptr;
/* State received */
bool state[RARCH_BIND_LIST_END];
/* Requests are structured (MCP), not lines: given the replies a
* line-based client never had */
bool structured;
/* The reply being sent reports a failure, for the interface sending
* it to read: what a line client reads as text a structured one is
* told outright. Set beside the reply by whatever answers - a
* command, a deferred reply, command_reply_error() - and cleared
* when a command starts and once the reply is away. */
bool error;
};
typedef struct command_handler command_t;
typedef struct command_handle
{
command_t *handle;
unsigned id;
} command_handle_t;
struct rarch_state;
/**
* command_event:
* @cmd : Command index.
*
* Performs RetroArch command with index @cmd.
*
* Returns: true (1) on success, otherwise false (0).
**/
bool command_event(enum event_command action, void *data);
/* @action, with @arg (or NULL) as its int argument, carried out on the
* main thread when it next retires finished tasks: for a thread that
* is not the main one - the video thread - asking for what only the
* main thread may do, such as starting the drivers again. Callable
* from any thread. False if it could not be asked for. */
bool command_event_later(enum event_command action, const int *arg);
/* Constructors for the supported drivers */
#ifdef HAVE_NETWORK_CMD
/* bind_address may be NULL or empty to listen on every interface, or an
* IPv4 address such as "127.0.0.1" to restrict the interface to it. */
command_t* command_network_new(uint16_t port, const char *bind_address);
bool command_network_send(const char *cmd_);
#endif
#ifdef HAVE_STDIN_CMD
command_t* command_stdin_new(void);
#endif
#ifdef HAVE_LAKKA
command_t* command_uds_new(void);
#endif
#ifdef __EMSCRIPTEN__
command_t* command_emscripten_new(void);
#endif
void command_event_set_mixer_volume(
settings_t *settings,
float gain);
bool command_event_resize_windowed_scale(settings_t *settings,
unsigned window_scale);
size_t command_event_save_auto_state(void);
/**
* event_set_volume:
* @gain : amount of gain to be applied to current volume level.
*
* Adjusts the current audio volume level.
*
**/
void command_event_set_volume(
settings_t *settings,
float gain,
bool widgets_active,
bool audio_driver_mute_enable);
/**
* command_event_init_controllers:
*
* Initialize libretro controllers.
**/
void command_event_init_controllers(rarch_system_info_t *info,
settings_t *settings, unsigned num_active_users);
bool command_event_load_entry_state(settings_t *settings);
bool command_event_load_auto_state(void);
void command_event_set_savestate_auto_index(
settings_t *settings);
int command_event_get_next_savestate_auto_index(
settings_t *settings);
void command_event_set_replay_auto_index(
settings_t *settings);
void command_event_set_replay_garbage_collect(
unsigned max_to_keep,
bool show_hidden_files
);
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
bool command_set_shader(command_t *cmd, const char *arg);
#endif
#ifdef HAVE_CHEATS
void command_event_init_cheats(
bool apply_cheats_after_load,
const char *path_cheat_db,
void *bsv_movie_data);
#endif
#if defined(HAVE_COMMAND)
enum cmd_param_type
{
/* text, as the command takes it */
CMD_PARAM_STRING = 0,
/* a whole number; a structured client sends a JSON number */
CMD_PARAM_INT
};
/* One of a command's parameters, for interfaces that take a request
* apart rather than a line: a tool schema is generated from these, and
* what a client sends by name is put back together into the line the
* handlers read.
*
* A space joins them in that line, and nothing precedes the first, so
* @lead is only for a command whose own form uses something else -
* LOAD_CONTENT's "|" - and a command's grammar is described here
* instead of being parsed by hand at both ends. @values, where set, is the "|"-separated set the
* parameter accepts, which the schema offers as an enum.
*
* @def stands in for an optional parameter the client left out, so the
* line form says it rather than leaving the handler to guess: what
* GET_PLAYLIST reads as a trailing entry number cannot be told from a
* playlist whose name ends in one, and a default settles it.
*
* An array of these ends with a row whose @name is NULL. */
struct cmd_param
{
const char *name;
const char *desc;
enum cmd_param_type type;
bool required;
const char *lead;
const char *values;
const char *def;
};
/* One column of a reply that is rows: the name a field on the line is
* given by an interface that answers in fields rather than in text. */
struct cmd_column
{
const char *name;
const char *desc;
};
/* A line among a reply's rows that is not one of them: a word, and a
* number ending the line. "MORE 200" says a list was cut short and where to
* resume, "NEXT 48" where GET_LOG reached, "SKIPPED 12" how many log
* lines were gone before the first it could give.
*
* @flag is a field the word alone sets true, for a word that can come
* without a number - a bare "MORE", from a reply with no resume point.
* @name is the field its number is answered as. Either may be NULL.
* @first is a word that opens the reply instead of closing it. @text
* makes the rest of the line, on whichever line it comes, the field's
* text: "MESSAGE Press again to quit" rather than a number. */
struct cmd_trailer
{
const char *word;
const char *flag;
const char *name;
const char *desc;
bool first;
bool text;
};
/* The shape of a reply that is rows of named columns - the lines of
* tab-separated fields the lists and GET_PLAYLIST answer with - so an
* interface can hand a client the same reply taken apart rather than
* leaving every client to split the text.
*
* @slack is the one column whose field may hold a tab itself, a game's
* name among them: the columns before it are read from the start of the
* line and those after it from the end, so that tab takes the slack
* instead of shifting the row.
*
* A line the command opens with its own name - "LIST_CORES none",
* "GET_PLAYLIST ERROR ..." - is not a row, and neither is one of
* @trailers, which may be that same line: "RUN_FRAMES 3".
*
* @required is how many of the columns every row has, where a reply
* ends short of them: GET_CORE_OPTION gives an option's key, value and
* description, and the values it takes only when asked for that option
* by key. Zero means every column, which is what the rest answer.
*
* @columns ends with a row whose @name is NULL, @trailers - when there
* are any - with one whose @word is. @columns itself is NULL for a
* reply whose lines are prose and whose trailers are all a client
* wants: GET_LOG's answer is the log, and the line it reached. */
struct cmd_rows
{
const struct cmd_column *columns;
const struct cmd_trailer *trailers;
unsigned slack;
unsigned required;
/* Set, the reply is one record rather than rows: the line the
* command opens with its own name, split into @columns by these
* characters in turn - the last column takes what is left - and
* answered as fields of their own. */
const char *seps;
/* The reply may carry a "PREVIOUS ..." line: how a queued STEPS no
* reply has told of ended. */
bool previous;
};
/* A command with its own handler; @arg_desc names the argument, or
* "No argument", and @params describes it for a structured client, or
* is NULL on one that takes none. @rows is the shape of its reply where
* that reply is rows, and NULL where it is prose. */
struct cmd_action_map
{
const char *str;
bool (*action)(command_t* cmd, const char *arg);
const char *arg_desc;
const char *desc;
unsigned flags; /* enum cmd_info_flags */
const struct cmd_param *params;
const struct cmd_rows *rows;
};
#if defined(HAVE_SCREENSHOTS) && defined(HAVE_RPNG)
/* GET_FRAME [size]: the frame on screen, as image content where the
* interface carries one; a PNG, built in memory */
#define COMMAND_FRAME_SIZE 320
#define COMMAND_FRAME_SIZE_MIN 64
#define COMMAND_FRAME_SIZE_MAX 640
bool command_get_frame(command_t *cmd, const char *arg);
#endif
/* GET_OPTION [option] / SET_OPTION <option> on|off */
bool command_get_option(command_t *cmd, const char *arg);
bool command_set_option(command_t *cmd, const char *arg);
#ifdef HAVE_MCP
/* GET_LOG [since] */
bool command_get_log(command_t *cmd, const char *arg);
#endif
/* RUN_FRAMES <count>. The runloop calls: tick after a frame a job ran;
* free_frame after one with no job; between before a frame, with the
* core off the stack, for a STEPS checkpoint; cancel when the content
* closes. forget ends a queued STEPS once the state it was planned
* from is gone: a state loaded, a reset, a close. */
#define COMMAND_RUN_FRAMES_MAX 36000
bool command_run_frames(command_t *cmd, const char *arg);
void command_run_frames_tick(void);
void command_run_frames_between(void);
#ifdef HAVE_MENU
/* MENU: the runloop hands action the menu's own action for a frame,
* before the menu runs it, and calls iterated once it has. */
unsigned command_menu_action(unsigned action);
void command_menu_iterated(void);
void command_menu_closed(void);
#endif
void command_run_frames_cancel(const char *why);
void command_run_frames_forget(void);
/* Achievements in hardcore mode are the player's own, as they forbid
* rewind and cheats: tools_refused is why an assistant's tool calls are
* refused now, or NULL; hardcore_entered stops what one left running -
* a frame job, a queue, held input - as hardcore mode takes effect. */
const char *command_tools_refused(void);
void command_hardcore_entered(void);
/* On the main thread each frame, before commands are read: what other
* threads left for it, and a MENU request whose menu closed. */
void command_frame_start(void);
void command_run_frames_free_frame(void);
/* SET_INPUT <player> <control> <value> / RELEASE_INPUT [player], and
* GET_INPUT [player] to look without changing anything. */
bool command_set_input(command_t *cmd, const char *arg);
bool command_release_input(command_t *cmd, const char *arg);
bool command_get_input(command_t *cmd, const char *arg);
/* The most frames a request runs, which a structured interface lowers
* to nine seconds of the content's frames to answer inside its
* ten-second reply deadline. PRESS keeps to it everywhere, since it
* holds input for every frame it runs. */
#define COMMAND_FRAMES_REPLY_MAX 600
/* PRESS <player> <control> <frames> [until <condition>]: hold, run and
* let go, then run on until the condition holds, in one. */
bool command_press(command_t *cmd, const char *arg);
/* RUN_UNTIL <address>[:<address>] <condition> <value> [max frames]: run
* the content until a byte, or two read as one value, says to stop. The
* conditions it takes are command.c's, which is all that compares them. */
bool command_run_until(command_t *cmd, const char *arg);
/* STEPS <step>[; <step>...][|on]: run, press, until and checkpoint
* steps, and a stop, as one request; with on, queued behind those
* still running, answered at once. */
bool command_steps(command_t *cmd, const char *arg);
/* WATCH [<label> <address|off>]...: the bytes RUN_FRAMES, PRESS and
* RUN_UNTIL carry along with their own answer, looked up afresh each
* time. Cleared when content closes: the next content's addresses mean
* something else. */
bool command_watch(command_t *cmd, const char *arg);
void command_watch_clear(void);
/* GET_CORE_OPTION [key] / SET_CORE_OPTION <key> <value> */
bool command_get_core_option(command_t *cmd, const char *arg);
bool command_set_core_option(command_t *cmd, const char *arg);
/* Replies with a failure and why, and marks the reply as one. The text
* opens with @name and ERROR, as a deferred reply's does. */
void command_reply_error(command_t *cmd, const char *name,
const char *fmt, ...);
/* HELP [command]: the commands, their arguments and what they do. */
bool command_help(command_t *cmd, const char* arg);
/* Queries for clients that browse and launch content. */
bool command_list_cores(command_t *cmd, const char* arg);
bool command_list_playlists(command_t *cmd, const char* arg);
bool command_get_playlist(command_t *cmd, const char* arg);
/* Runs one command by name; see command.c. */
bool command_run(command_t *handle, const char *name, const char *arg);
/* True once a command has started a content load: requests an
* interface has yet to take wait for the next poll, which comes once
* the load is through. */
bool command_interfaces_held(void);
/* Every command, for interfaces that describe them to a client. */
const struct cmd_action_map *command_action_list(size_t *count);
const struct cmd_map *command_hotkey_list(size_t *count);
bool command_version(command_t *cmd, const char* arg);
bool command_get_status(command_t *cmd, const char* arg);
bool command_get_config_param(command_t *cmd, const char* arg);
bool command_show_osd_msg(command_t *cmd, const char* arg);
bool command_load_state_slot(command_t *cmd, const char* arg);
bool command_save_state_slot(command_t* cmd, const char* arg);
bool command_play_replay_slot(command_t *cmd, const char* arg);
bool command_seek_replay(command_t *cmd, const char *arg);
bool command_save_savefiles(command_t *cmd, const char* arg);
bool command_load_savefiles(command_t *cmd, const char* arg);
#ifdef HAVE_CHEEVOS
bool command_read_ram(command_t *cmd, const char *arg);
bool command_write_ram(command_t *cmd, const char *arg);
#endif
bool command_read_memory(command_t *cmd, const char *arg);
bool command_write_memory(command_t *cmd, const char *arg);
bool command_get_memory_map(command_t *cmd, const char *arg);
bool command_load_core(command_t *cmd, const char* arg);
bool command_start_core(command_t *cmd, const char* arg);
bool command_load_content(command_t *cmd, const char* arg);
bool command_close_content(command_t *cmd, const char* arg);
bool command_unload_core(command_t *cmd, const char* arg);
bool command_video_reinit(command_t *cmd, const char* arg);
bool command_audio_reinit(command_t *cmd, const char* arg);
bool command_drivers_reinit(command_t *cmd, const char* arg);
/* MENU <button>, DISK <action>, REINIT <driver>, STATE <action> <slot>,
* REPLAY <action> [slot|frame]: the hotkeys and commands of one family
* behind one name, as one tool. */
#define COMMAND_MENU_ACTIONS "show|open|close|select|up|down|left|right|a|b|type"
/* the entries MENU answers with at once */
#define COMMAND_MENU_PAGE 50
#define COMMAND_DISK_ACTIONS "eject|next|prev"
#define COMMAND_REINIT_DRIVERS "video|audio|all"
#define COMMAND_STATE_ACTIONS "save|load|checkpoint|rollback"
#define COMMAND_REPLAY_ACTIONS "play|seek|record|halt|checkpoint"
#define COMMAND_CORE_ACTIONS "load|start|unload"
#define COMMAND_INPUT_ACTIONS "set|release|show"
#define COMMAND_OPTION_ACTIONS "show|set"
bool command_menu(command_t *cmd, const char *arg);
bool command_disk(command_t *cmd, const char *arg);
bool command_reinit(command_t *cmd, const char *arg);
bool command_state(command_t *cmd, const char *arg);
/* CHECKPOINT_STATE and ROLLBACK_STATE: one state kept in memory for
* the content's life, which command_checkpoint_clear() drops. */
bool command_checkpoint_save(command_t *cmd, const char *arg);
/* RESET [on|off]: the hotkey's reset, paused after it with on. */
bool command_reset(command_t *cmd, const char *arg);
bool command_checkpoint_rollback(command_t *cmd, const char *arg);
void command_checkpoint_clear(void);
bool command_core(command_t *cmd, const char *arg);
bool command_replay(command_t *cmd, const char *arg);
/* INPUT, OPTION and CORE_OPTION: SET_INPUT, RELEASE_INPUT, GET_INPUT,
* GET_OPTION, SET_OPTION, GET_CORE_OPTION and SET_CORE_OPTION */
bool command_input(command_t *cmd, const char *arg);
bool command_option(command_t *cmd, const char *arg);
bool command_core_option(command_t *cmd, const char *arg);
/* The parameters of the commands that take any. Several commands take
* the same ones; what tells READ_CORE_RAM from READ_CORE_MEMORY is the
* command's own description, not its parameters'. */
static const struct cmd_param cmd_params_help[] = {
{ "command", "A command name. Left out, every command.",
CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_preset[] = {
{ "preset", "Path to a shader preset, absolute or under the shader "
"directory. Empty turns the shader off.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_option[] = {
{ "option", "An option name, as GET_OPTION with no argument lists.",
CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_option_set[] = {
{ "option", "An option name, as GET_OPTION with no argument lists.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "state", "The state to put it in.",
CMD_PARAM_STRING, true, NULL, "on|off", NULL },
{ NULL }
};
#ifdef HAVE_MCP
static const struct cmd_param cmd_params_log[] = {
{ "since", "The first line wanted, as next gave it; 0 when left out.",
CMD_PARAM_INT, false, NULL, NULL, "0" },
{ NULL }
};
#endif
static const struct cmd_param cmd_params_run_frames[] = {
{ "count", "How many frames to run; ask again to run more.", CMD_PARAM_INT, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_input[] = {
{ "player", "The player, from 1.", CMD_PARAM_INT, true, NULL, NULL, NULL },
/* No enum: a combination is not one of the names, and a client that
* held the schema to one would refuse to send it. */
{ "control", "A RetroPad button or stick axis, or several joined by "
"\"+\" to hold together: b, y, select, start, up, down, left, "
"right, a, x, l, r, l2, r2, l3, r3, left_x, left_y, right_x, "
"right_y. \"right+a\" runs and jumps in one request.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "value", "1 holds a button and 0 lets it go; an axis takes -32768 "
"to 32767, 0 letting it go.", CMD_PARAM_INT, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_input_family[] = {
{ "action", "set holds or lets go of controls; release lets go of all "
"a player holds; show reads it.", CMD_PARAM_STRING, true, NULL,
COMMAND_INPUT_ACTIONS, NULL },
{ "player", "From 1. Left out, release frees every player and show "
"reads the first.", CMD_PARAM_INT, false, NULL, NULL, NULL },
/* No enum: a combination is not one of the names, and a client that
* held the schema to one would refuse to send it. */
{ "control", "For set: a button or stick axis, or several joined by "
"\"+\": b, y, select, start, up, down, left, right, a, x, l, r, l2, "
"r2, l3, r3, left_x, left_y, right_x, right_y.",
CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ "value", "For set: 1 holds a button, 0 lets go; an axis takes "
"-32768 to 32767.", CMD_PARAM_INT, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_option_family[] = {
{ "action", "show one option, or all; set one.", CMD_PARAM_STRING,
true, NULL, COMMAND_OPTION_ACTIONS, NULL },
{ "option", "An option name, as show lists it.", CMD_PARAM_STRING,
false, NULL, NULL, NULL },
{ "state", "For set.", CMD_PARAM_STRING, false, NULL, "on|off", NULL },
{ NULL }
};
static const struct cmd_param cmd_params_core_option_family[] = {
{ "action", "show the core's options, or one with the values it "
"takes; set one.", CMD_PARAM_STRING, true, NULL,
COMMAND_OPTION_ACTIONS, NULL },
{ "option", "A key, as show lists it.", CMD_PARAM_STRING, false, NULL,
NULL, NULL },
{ "value", "For set: one of the values show lists for the key.",
CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_watch[] = {
{ "watches", "Label and hexadecimal address pairs, as GET_MEMORY_MAP "
"gives them: \"x 86 mode 770\"; \"x 6d:86\" reads two bytes as one "
"value. A label watched again is replaced; off as its address stops "
"it. 16 at most.",
CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_until[] = {
{ "address", "Hexadecimal, as GET_MEMORY_MAP gives it; high:low "
"(6d:86) reads two bytes as one value; & 0x40 after it compares "
"only those bits.", CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "condition", "How it compares with the value.",
CMD_PARAM_STRING, true, NULL, "==|!=|<|>|<=|>=", NULL },
{ "value", "0 to 255, or 65535 for high:low.",
CMD_PARAM_INT, true, NULL, NULL, NULL },
{ "frames", "Frames to wait at most; a request's nine seconds when "
"left out.", CMD_PARAM_INT, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_press[] = {
{ "player", "From 1.", CMD_PARAM_INT, true, NULL, NULL, NULL },
{ "control", "Buttons as INPUT names them, joined by \"+\": "
"\"right+a\" runs and jumps.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "frames", "Frames to hold them: a tap is 2 to 4. Right after a "
"press it lets go for a frame first.",
CMD_PARAM_INT, true, NULL, NULL, NULL },
{ "until", "Then run until this holds, as RUN_UNTIL's: \"1d == 0\" "
"lands a jump.",
CMD_PARAM_STRING, false, " until ", NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_steps[] = {
{ "steps", "In order, by \";\": run <frames>, press <player> <controls> "
"<frames>, until <condition> as RUN_UNTIL's, checkpoint, and up to "
"four stop <condition> that end it: \"until 86 >= 200; "
"press 1 a 20; until 1d == 0; stop e == 11\". 32 at most. A press "
"after a press lets go for a frame first.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "queue", "on: answer now, run after those queued, pause when all "
"have (idle: ms since). After a queued list ends the next reply says "
"how (previous), and a queued call drops its steps (discarded). "
"Empty steps asks; a call not queued ends the queue.",
CMD_PARAM_STRING, false, "|", "on|off", NULL },
{ NULL }
};
static const struct cmd_param cmd_params_input_release[] = {
{ "player", "The player, from 1; every player when left out.",
CMD_PARAM_INT, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_input_get[] = {
{ "player", "The player, from 1; the first when left out.",
CMD_PARAM_INT, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_core_option[] = {
{ "option", "A core option key, as GET_CORE_OPTION with no argument "
"lists it.", CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_core_option_set[] = {
{ "option", "A core option key, as GET_CORE_OPTION with no argument "
"lists it.", CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "value", "One of the values GET_CORE_OPTION with the key lists.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_config[] = {
{ "parameter", "A configuration setting's name.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_playlist[] = {
{ "playlist", "A playlist name as LIST_PLAYLISTS gives it, \".lpl\" "
"and all.", CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "first", "The entry to start at; the first when left out.",
CMD_PARAM_INT, false, NULL, NULL, "0" },
{ NULL }
};
#if defined(HAVE_SCREENSHOTS) && defined(HAVE_RPNG)
static const struct cmd_param cmd_params_get_frame[] = {
{ "size", "The longest side at most, 64 to 640; 320 when left out.",
CMD_PARAM_INT, false, NULL, NULL, "320" },
{ NULL }
};
#endif
static const struct cmd_param cmd_params_message[] = {
{ "message", "The text.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_read[] = {
{ "address", "The address to read from, in hexadecimal.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "bytes", "How many.",
CMD_PARAM_INT, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_write[] = {
{ "address", "The address to write to, in hexadecimal.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "bytes", "The bytes to write, in hexadecimal, separated by spaces.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_slot[] = {
{ "slot", "The slot number.", CMD_PARAM_INT, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_menu[] = {
{ "action", "select moves to index; left and right change a value, a "
"confirms, b goes back, type answers a keyboard.",
CMD_PARAM_STRING, true, NULL, COMMAND_MENU_ACTIONS, NULL },
{ "text", "For type: the text, typed and confirmed.",
CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ "index", "An entry: select, left, right and a act on it.",
CMD_PARAM_INT, false, NULL, NULL, NULL },
{ "from", "The first entry listed; else the page with the selection.",
CMD_PARAM_INT, false, " from ", NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_disk[] = {
{ "action", "eject ejects or inserts it.", CMD_PARAM_STRING, true,
NULL, COMMAND_DISK_ACTIONS, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_reinit[] = {
{ "driver", "Which driver, or all of them.", CMD_PARAM_STRING, true,
NULL, COMMAND_REINIT_DRIVERS, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_state[] = {
{ "action", "checkpoint keeps a state in memory until the content "
"closes; rollback returns to it, the quick retry after a mistake.",
CMD_PARAM_STRING, true, NULL,
COMMAND_STATE_ACTIONS, NULL },
{ "slot", "For save and load.", CMD_PARAM_INT, false,
NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_reset[] = {
{ "paused", "on: pause once it has reset, before a frame runs; the "
"game has not set up its RAM yet.", CMD_PARAM_STRING, false, NULL,
"on|off", NULL },
{ NULL }
};
static const struct cmd_param cmd_params_core_family[] = {
{ "action", "unload closes the content too.",
CMD_PARAM_STRING, true, NULL, COMMAND_CORE_ACTIONS, NULL },
{ "core", "load: the path, as LIST_CORES gives it.", CMD_PARAM_STRING,
false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_replay[] = {
{ "action", "seek moves the playing replay to a frame; record uses "
"the current slot; checkpoint marks the one recording.",
CMD_PARAM_STRING, true,
NULL, COMMAND_REPLAY_ACTIONS, NULL },
{ "number", "The slot to play, or the frame to seek to.",
CMD_PARAM_INT, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_frame[] = {
{ "frame", "The frame to seek to.", CMD_PARAM_INT, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_cores[] = {
{ "content", "Only the cores that support this content path.",
CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_core[] = {
{ "core", "Path to a core library, as LIST_CORES gives it.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_content[] = {
{ "core", "A core's path, as LIST_CORES and GET_PLAYLIST give it; "
"DETECT picks one that supports the content.",
CMD_PARAM_STRING, false, NULL, NULL, "DETECT" },
{ "content", "Path to the content, as GET_PLAYLIST gives it.",
CMD_PARAM_STRING, true, "|", NULL, NULL },
{ "paused", "on: pause once it has loaded. The core has run a frame "
"or two; the game may not have set up its RAM yet.",
CMD_PARAM_STRING, false,
"|", "on|off", NULL },
{ NULL }
};
/* The replies that are rows of named columns, in the order the line
* gives the fields. */
static const struct cmd_column cmd_cols_option[] = {
{ "option", "The option's name, as OPTION set takes it." },
{ "value", "Its state - on or off - or, for one that only reports, "
"what it reports." },
{ NULL }
};
static const struct cmd_column cmd_cols_core[] = {
{ "name", "The core's display name." },
{ "path", "The core's path, as LOAD_CONTENT takes it." },
{ NULL }
};
static const struct cmd_column cmd_cols_playlist[] = {
{ "playlist", "The playlist's name, as GET_PLAYLIST takes it." },
{ NULL }
};
static const struct cmd_column cmd_cols_entry[] = {
{ "index", "The entry's number in the playlist." },
{ "label", "The entry's name." },
{ "content", "The content's path, as LOAD_CONTENT takes it." },
{ "core", "The core's path, as LOAD_CONTENT takes it; empty where the "
"playlist names none." },
{ NULL }
};
static const struct cmd_column cmd_cols_core_option[] = {
{ "key", "The option's key, as CORE_OPTION set takes it." },
{ "value", "The value it has now." },
{ "description", "What the option is called in the menu." },
{ "values", "The values it takes, separated by \"|\"; given only "
"where the option was asked for by key." },
{ NULL }
};
static const struct cmd_column cmd_cols_memory[] = {
{ "start", "The region's first address." },
{ "length", "How many bytes it covers." },
{ "select", "Which address bits name the region." },
{ "disconnect", "Which address bits it ignores." },
{ "kind", "system, save, video or other." },
{ "access", "rw, ro, or none where the core exposes none." },
{ "order", "big or little." },
{ "space", "What the core calls the address space, or empty." },
{ NULL }
};
static const struct cmd_column cmd_cols_watch[] = {
{ "label", "What the client called it." },
{ "address", "Where it is, in hexadecimal." },
{ "value", "What it holds, in decimal: 0 to 255, or to 65535 for "
"high:low; none where nothing maps it now." },
{ NULL }
};
static const struct cmd_column cmd_cols_input[] = {
{ "player", "The player the controls are held for, from 1." },
{ "buttons", "The buttons held, separated by spaces, or none." },
{ "axes", "The axes held, as name=value separated by spaces, or none." },
{ NULL }
};
/* A list long enough to be cut short, with nowhere to resume from. */
static const struct cmd_trailer cmd_trailers_cut[] = {
{ "MORE", "more", NULL, NULL },
{ NULL }
};
/* A list cut short with the entry to resume from. */
static const struct cmd_trailer cmd_trailers_page[] = {
{ "MORE", "more", "next",
"The entry the next page starts at, as GET_PLAYLIST's first "
"takes it." },
{ NULL }
};
/* RUN_UNTIL opens with one of the first two, whichever happened. */
static const struct cmd_trailer cmd_trailers_until[] = {
{ "MET", NULL, "met",
"The frames run until the condition held.", true },
{ "WAITING", NULL, "waiting",
"The frames run without it meeting it; ask again to wait more.",
true },
{ "VALUE", NULL, "value",
"What the memory waited on holds now." },
{ NULL }
};
/* the step's own line: the frames it ran end it */
static const struct cmd_trailer cmd_trailers_frames[] = {
{ "RUN_FRAMES", NULL, "frames", "The frames run.", true },
{ NULL }
};
static const struct cmd_trailer cmd_trailers_press[] = {
{ "PRESS", NULL, "frames", "The frames run.", true },
{ "MET", NULL, "met", "With until: frames run until it held.", true },
{ "WAITING", NULL, "waiting", "With until: frames run without.", true },
{ "VALUE", NULL, "value", "With until: what it read." },
{ NULL }
};
static const struct cmd_trailer cmd_trailers_steps[] = {
{ "QUEUED", NULL, "queued", "Steps left.", true },
{ "DISCARDED", NULL, "discarded", "Steps dropped.", true },
{ "DONE", NULL, "done", "Frames run.", true },
{ "WAITING", NULL, "waiting", "Frames run; ask again.", true },
{ "STOPPED", NULL, "stopped", "Frames run.", true },
{ "IDLE", NULL, "idle", "Ms paused, run dry." },
{ "STOP", NULL, "stop", NULL },
{ "STEP", NULL, "step", NULL },
{ NULL }
};
static const struct cmd_trailer cmd_trailers_log[] = {
{ "NEXT", NULL, "next",
"The line to ask for next, as GET_LOG's since takes it." },
{ "SKIPPED", NULL, "skipped",
"Lines that were asked for but had already left the log.", true },
{ NULL }
};
static const struct cmd_rows cmd_rows_option = { cmd_cols_option, NULL, 1, 0 };
static const struct cmd_rows cmd_rows_cores = { cmd_cols_core, cmd_trailers_cut, 0, 0 };
static const struct cmd_rows cmd_rows_playlists = { cmd_cols_playlist, cmd_trailers_cut, 0, 0 };
/* the values, where given, are last and take whatever is left */
static const struct cmd_rows cmd_rows_core_option =
{ cmd_cols_core_option, NULL, 3, 3 };
static const struct cmd_rows cmd_rows_memory = { cmd_cols_memory, NULL, 7, 0 };
static const struct cmd_rows cmd_rows_entries = { cmd_cols_entry, cmd_trailers_page, 1, 0 };
static const struct cmd_rows cmd_rows_input = { cmd_cols_input, NULL, 0, 0 };
static const struct cmd_rows cmd_rows_watch = { cmd_cols_watch, NULL, 0, 0 };
static const struct cmd_rows cmd_rows_frames = { cmd_cols_watch, cmd_trailers_frames, 0, 0, NULL, true };
static const struct cmd_rows cmd_rows_press = { cmd_cols_watch, cmd_trailers_press, 0, 0, NULL, true };
static const struct cmd_rows cmd_rows_until = { cmd_cols_watch, cmd_trailers_until, 0, 0, NULL, true };
static const struct cmd_rows cmd_rows_steps = { cmd_cols_watch, cmd_trailers_steps, 0, 0, NULL, true };
/* No columns: the lines are the text, and splitting a 60k page into a
* row apiece would answer with it twice over. What a client cannot read
* off the text is how far the log got, which is what the trailers say. */
static const struct cmd_rows cmd_rows_log = { NULL, cmd_trailers_log, 0, 0 };
/* "GET_STATUS PLAYING nes,Super Mario Bros.": a comma in the game's
* name stays in it, as the last field takes the rest */
static const struct cmd_column cmd_cols_status[] = {
{ "state", "PLAYING, PAUSED or CONTENTLESS." },
{ "system", "The content's system, or the core's name." },
{ "content", "The content's file name." },
{ NULL }
};
static const struct cmd_rows cmd_rows_status = { cmd_cols_status, NULL, 0, 1, " ," };
static const struct cmd_column cmd_cols_read[] = {
{ "address", "Where the bytes were read, in hexadecimal." },
{ "bytes", "The bytes, in hexadecimal, separated by spaces." },
{ NULL }
};
static const struct cmd_trailer cmd_trailers_read[] = {
{ "TRUNCATED", "truncated", "read", "Bytes read, short of those asked." },
{ NULL }
};
static const struct cmd_rows cmd_rows_read = { cmd_cols_read, cmd_trailers_read, 0, 0, " " };
/* "MENU Main Menu", the entries, then which is selected of how many */
static const struct cmd_column cmd_cols_menu[] = {
{ "index", NULL },
{ "label", NULL },
{ "value", "\"...\" opens a list; \"checked\" is the choice in use." },
{ NULL }
};
static const struct cmd_trailer cmd_trailers_menu[] = {
{ "MENU", NULL, "title", NULL, false, true },
{ "CLOSED", "closed", NULL, "The menu is closed.", true },
{ "KEYBOARD", NULL, "keyboard", "Asks for text: type, or b.",
false, true },
{ "MESSAGE", NULL, "message", "Over the list: a confirms, b dismisses.",
false, true },
{ "PATH", NULL, "path", "The lists above it.", false, true },
{ "SELECTED", NULL, "selected", NULL },
{ "COUNT", NULL, "count", NULL },
{ "MORE", "more", "next", "from for the next page." },
{ NULL }
};
static const struct cmd_rows cmd_rows_menu = { cmd_cols_menu, cmd_trailers_menu, 1, 0, NULL, false };
static const struct cmd_action_map action_map[] = {
/* HELP and VERSION are not tools: a structured interface lists the
* commands itself and names its version in every answer. */
{ "HELP", command_help, "[command]", "List the commands, or describe one.", CMD_INFO_READ_ONLY | CMD_INFO_NO_TOOL, cmd_params_help },
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
{ "SET_SHADER", command_set_shader, "<shader path>", "Load the shader preset at the given path.", 0, cmd_params_preset },
#endif
{ "VERSION", command_version, "No argument", "Report the RetroArch version.", CMD_INFO_READ_ONLY | CMD_INFO_NO_TOOL, NULL },
{ "GET_STATUS", command_get_status, "No argument", "PLAYING, PAUSED or CONTENTLESS, with the system and the content's file name.", CMD_INFO_READ_ONLY, NULL, &cmd_rows_status },
#if defined(HAVE_SCREENSHOTS) && defined(HAVE_RPNG)
{ "GET_FRAME", command_get_frame, "[size]", "The content's frame on screen now, as an image scaled down to fit size; nothing is written. For what memory cannot say - a game whose RAM is not charted, or checking it.", CMD_INFO_READ_ONLY, cmd_params_get_frame },
#endif
{ "GET_OPTION", command_get_option, "[option]", "Report an option's value, or list every option with its value.", CMD_INFO_READ_ONLY | CMD_INFO_NO_TOOL, cmd_params_option, &cmd_rows_option },
{ "SET_OPTION", command_set_option, "<option> on|off", "Turn an option on or off. Asking for the state it is already in changes nothing.", CMD_INFO_NO_TOOL, cmd_params_option_set, &cmd_rows_option },
/* GET_OPTION reports these and more; this stays for the clients that
* have always asked it. */
{ "GET_CONFIG_PARAM", command_get_config_param, "<param name>", "Report the value of a configuration setting.", CMD_INFO_READ_ONLY | CMD_INFO_NO_TOOL, cmd_params_config },
#ifdef HAVE_MCP
{ "GET_LOG", command_get_log, "[since]", "The lines RetroArch has logged since a line, where to ask from next (next), and how many asked for were gone (skipped).", CMD_INFO_READ_ONLY, cmd_params_log, &cmd_rows_log },
#endif
{ "RUN_FRAMES", command_run_frames, "<count>", "Pause and advance the content some frames; it finishes paused. One request runs nine seconds of frames at most (540 at 60fps), as PRESS, RUN_UNTIL and STEPS do.", 0, cmd_params_run_frames, &cmd_rows_frames },
{ "SET_INPUT", command_set_input, "<player> <control>[+<control>...] <value>", "Hold RetroPad buttons or stick axes for the core, over the player's own input, until set again or released. The core is given exactly what is held: opposing directions are not cleaned and the D-Pad is not turned with the picture. Under netplay the player is this machine's, and what is held is sent like a press. Answers with the player, the buttons held and the axes held, none for either where nothing is.", CMD_INFO_NO_TOOL, cmd_params_input, &cmd_rows_input },
{ "PRESS", command_press, "<player> <control>[+<control>...] <frames> [until <condition>]", "Press buttons for some frames and let go; with until, run on until a condition holds. What INPUT holds stays held. Answers with what WATCH watches; finishes paused.", 0, cmd_params_press, &cmd_rows_press },
{ "RUN_UNTIL", command_run_until, "<address>[:<address>] <condition> <value> [max frames]", "Run until memory meets a condition. Answers met or waiting (ask again) with the frames run, what WATCH watches and the value.", 0, cmd_params_until, &cmd_rows_until },
{ "STEPS", command_steps, "<step>[; <step>...][|on]", "Play a stretch in one request, or queued to play on unpaused. Answers with how it ended and what WATCH watches.", 0, cmd_params_steps, &cmd_rows_steps },
{ "WATCH", command_watch, "[<label> <address|off>]...", "Watch bytes under labels: every step answers with them, so needs no read after. With none, answers each value, in decimal. Cleared when content closes.", 0, cmd_params_watch, &cmd_rows_watch },
{ "RELEASE_INPUT", command_release_input, "[player]", "Let go of everything SET_INPUT holds, for one player, or every player when given none. Answers as GET_INPUT does, for that player or the first.", CMD_INFO_NO_TOOL, cmd_params_input_release, &cmd_rows_input },
{ "GET_INPUT", command_get_input, "[player]", "Report what a player holds, without changing it: SET_INPUT has to set something to answer and RELEASE_INPUT lets go before it does.", CMD_INFO_READ_ONLY | CMD_INFO_NO_TOOL, cmd_params_input_get, &cmd_rows_input },
{ "GET_CORE_OPTION", command_get_core_option, "[key]", "List the running core's options: key, value and description; with a key, that option with the values it takes.", CMD_INFO_READ_ONLY | CMD_INFO_NO_TOOL, cmd_params_core_option, &cmd_rows_core_option },
{ "SET_CORE_OPTION", command_set_core_option, "<key> <value>", "Set a core option to one of its values. Asking for the value it already has changes nothing.", CMD_INFO_NO_TOOL, cmd_params_core_option_set },
{ "LIST_CORES", command_list_cores, "[content path]", "The installed cores: name, and the path LOAD_CONTENT takes.", CMD_INFO_READ_ONLY, cmd_params_cores, &cmd_rows_cores },
{ "LIST_PLAYLISTS", command_list_playlists, "No argument", "List the playlists, as GET_PLAYLIST takes them.", CMD_INFO_READ_ONLY, NULL, &cmd_rows_playlists },
{ "GET_PLAYLIST", command_get_playlist, "<playlist> [first entry]", "A playlist's entries, 200 at a time: index, label, content and core paths, and next where there are more.", CMD_INFO_READ_ONLY, cmd_params_playlist, &cmd_rows_entries },
{ "SHOW_MSG", command_show_osd_msg, "<message>", "Show a message on screen.", 0, cmd_params_message },
#if defined(HAVE_CHEEVOS)
/* These functions use achievement addresses and only work if a game with achievements is
* loaded. READ_CORE_MEMORY and WRITE_CORE_MEMORY are preferred and use system addresses. */
{ "READ_CORE_RAM", command_read_ram, "<address> <number of bytes>", "Read bytes from core memory at an achievement address (needs a game with achievements).", CMD_INFO_READ_ONLY | CMD_INFO_NO_TOOL, cmd_params_read },
{ "WRITE_CORE_RAM", command_write_ram, "<address> <byte1> <byte2> ...", "Write bytes to core memory at an achievement address (needs a game with achievements).", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, cmd_params_write },
#endif
{ "READ_CORE_MEMORY", command_read_memory, "<address> <number of bytes>", "Read bytes from core memory at a system address. Answers with the address and the bytes, in hexadecimal.", CMD_INFO_READ_ONLY, cmd_params_read, &cmd_rows_read },
{ "GET_MEMORY_MAP", command_get_memory_map, "No argument", "The core's memory map: start, length, select and disconnect in hexadecimal, as READ_CORE_MEMORY takes them. A core with none lists the RAM it hands over, in space core: system RAM from 0, save RAM after.", CMD_INFO_READ_ONLY, NULL, &cmd_rows_memory },
{ "WRITE_CORE_MEMORY",command_write_memory, "<address> <byte1> <byte2> ...", "Write bytes to core memory at a system address. Answers with the address and how many bytes were written.", CMD_INFO_DESTRUCTIVE, cmd_params_write },
{ "LOAD_STATE_SLOT",command_load_state_slot, "<slot number>", "Load the save state in the given slot, replacing the current game state.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, cmd_params_slot },
{ "SAVE_STATE_SLOT",command_save_state_slot, "<slot number>", "Save a state to the given slot, overwriting what it held.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, cmd_params_slot },
{ "CHECKPOINT_STATE", command_checkpoint_save, "No argument", "Keep a state in memory, for ROLLBACK_STATE, until the content closes.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, NULL },
{ "ROLLBACK_STATE", command_checkpoint_rollback, "No argument", "Restore the state CHECKPOINT_STATE kept.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, NULL },
{ "STATE", command_state, "<save|load|checkpoint|rollback> [slot]", "Save a state to a slot, or load the one in it; or checkpoint one in memory and roll back to it.", CMD_INFO_DESTRUCTIVE, cmd_params_state },
{ "PLAY_REPLAY_SLOT",command_play_replay_slot, "<slot number>", "Play back the replay in the given slot.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, cmd_params_slot },
{ "SEEK_REPLAY",command_seek_replay, "<frame number>", "Seek the playing replay to the given frame.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, cmd_params_frame },
{ "REPLAY", command_replay, "<play|seek|record|halt|checkpoint> [slot|frame]", "Play, seek, record, halt or checkpoint a replay.", CMD_INFO_DESTRUCTIVE, cmd_params_replay },
{ "SAVE_FILES", command_save_savefiles, "No argument", "Write the core's save files (SRAM) to disk.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, NULL },
{ "LOAD_FILES", command_load_savefiles, "No argument", "Reload the core's save files (SRAM) from disk.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, NULL },
{ "LOAD_CORE", command_load_core, "<core path>", "Load the core library at the given path.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, cmd_params_core },
{ "START_CORE", command_start_core, "No argument", "Start the loaded core without content.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, NULL },
{ "INPUT", command_input, "<set|release|show> [player] [control] [value]", "Hold RetroPad buttons or stick axes over a player's own input until set again or released, or show what is held. The core gets exactly what is held: opposites are not cleaned nor the D-Pad turned; under netplay the player is this machine's. Answers player, buttons and axes, tab separated, none where nothing is held.", 0, cmd_params_input_family, &cmd_rows_input },
{ "OPTION", command_option, "<show|set> [option] [on|off]", "Show RetroArch's options - pause, fast-forward, the frame count, the slots and more - or turn one on or off.", 0, cmd_params_option_family, &cmd_rows_option },
{ "CORE_OPTION", command_core_option, "<show|set> [key] [value]", "Show the running core's options - key, value and description - or set one to one of its values.", 0, cmd_params_core_option_family, &cmd_rows_core_option },
{ "CORE", command_core, "<load|start|unload> [core path]", "Load a core library, start it without content, or unload it.", CMD_INFO_DESTRUCTIVE, cmd_params_core_family },
{ "LOAD_CONTENT", command_load_content, "<core path>|<content path>[|on]", "Load content with a core, or with one that supports it where core is DETECT or empty.", CMD_INFO_DESTRUCTIVE, cmd_params_content },
{ "RESET", command_reset, "[on|off]", "Reset the running content; with paused on, pause once it has.", CMD_INFO_DESTRUCTIVE, cmd_params_reset },
{ "CLOSE_CONTENT", command_close_content, "No argument", "Close the running content.", CMD_INFO_DESTRUCTIVE, NULL },
{ "UNLOAD_CORE", command_unload_core, "No argument", "Close the content and unload the core.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, NULL },
{ "VIDEO_REINIT", command_video_reinit, "No argument", "Restart the video driver.", CMD_INFO_NO_TOOL, NULL },
{ "AUDIO_REINIT", command_audio_reinit, "No argument", "Restart the audio driver.", CMD_INFO_NO_TOOL, NULL },
{ "DRIVERS_REINIT", command_drivers_reinit, "No argument", "Restart all drivers.", CMD_INFO_NO_TOOL, NULL },
{ "REINIT", command_reinit, "<video|audio|all>", "Restart the video or audio driver, or all drivers.", 0, cmd_params_reinit },
{ "MENU", command_menu, "<action> [index] [from <entry>]", "RetroArch's own menu, not a game's. Answers with what it shows after the action: title, a page of entries, the selection. Ozone: left reaches the tabs, up and down pick one, right returns. XMB: left and right switch tabs.", CMD_INFO_DESTRUCTIVE, cmd_params_menu, &cmd_rows_menu },
{ "DISK", command_disk, "<eject|next|prev>", "Eject or insert the virtual disc, or select the next or previous disc image.", 0, cmd_params_disk },
};
static const struct cmd_map map[] = {
{ "MENU_TOGGLE", RARCH_MENU_TOGGLE, "Open or close the menu.", CMD_INFO_NO_TOOL },
{ "QUIT", RARCH_QUIT_KEY, "Quit RetroArch.", CMD_INFO_DESTRUCTIVE },
/* the tool is the action RESET, which takes paused */
{ "RESET", RARCH_RESET, "Reset the running content.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL },
{ "FAST_FORWARD", RARCH_FAST_FORWARD_KEY, "Toggle fast-forward.", CMD_INFO_NO_TOOL },
{ "FAST_FORWARD_HOLD", RARCH_FAST_FORWARD_HOLD_KEY, "Fast-forward while held (one frame when sent as a command).", CMD_INFO_NO_TOOL },
{ "SLOWMOTION", RARCH_SLOWMOTION_KEY, "Toggle slow motion.", CMD_INFO_NO_TOOL },
{ "SLOWMOTION_HOLD", RARCH_SLOWMOTION_HOLD_KEY, "Slow motion while held (one frame when sent as a command).", CMD_INFO_NO_TOOL },
{ "REWIND", RARCH_REWIND, "Rewind while held (one frame when sent as a command).", CMD_INFO_NO_TOOL },
{ "PAUSE_TOGGLE", RARCH_PAUSE_TOGGLE, "Pause or resume the content.", CMD_INFO_NO_TOOL },
{ "FRAMEADVANCE", RARCH_FRAMEADVANCE, "Advance one frame while paused.", CMD_INFO_NO_TOOL },
{ "MUTE", RARCH_MUTE, "Mute or unmute audio.", CMD_INFO_NO_TOOL },
{ "VOLUME_UP", RARCH_VOLUME_UP, "Raise the volume.", CMD_INFO_NO_TOOL },
{ "VOLUME_DOWN", RARCH_VOLUME_DOWN, "Lower the volume.", CMD_INFO_NO_TOOL },
{ "LOAD_STATE", RARCH_LOAD_STATE_KEY, "Load the save state in the current slot.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL },
{ "SAVE_STATE", RARCH_SAVE_STATE_KEY, "Save a state to the current slot.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL },
{ "STATE_SLOT_PLUS", RARCH_STATE_SLOT_PLUS, "Select the next save state slot.", CMD_INFO_NO_TOOL },
{ "STATE_SLOT_MINUS", RARCH_STATE_SLOT_MINUS, "Select the previous save state slot.", CMD_INFO_NO_TOOL },
{ "PLAY_REPLAY", RARCH_PLAY_REPLAY_KEY, "Play back the replay in the current slot.", CMD_INFO_NO_TOOL },
{ "RECORD_REPLAY", RARCH_RECORD_REPLAY_KEY, "Start recording a replay to the current slot.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL },
{ "HALT_REPLAY", RARCH_HALT_REPLAY_KEY, "Stop playing or recording the replay.", CMD_INFO_NO_TOOL },
{ "SAVE_REPLAY_CHECKPOINT", RARCH_SAVE_REPLAY_CHECKPOINT_KEY, "Add a checkpoint to the replay being recorded.", CMD_INFO_NO_TOOL },
{ "PREV_REPLAY_CHECKPOINT", RARCH_PREV_REPLAY_CHECKPOINT_KEY, "Seek the replay back to the previous checkpoint.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL },
{ "NEXT_REPLAY_CHECKPOINT", RARCH_NEXT_REPLAY_CHECKPOINT_KEY, "Seek the replay forward to the next checkpoint.", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL },
{ "REPLAY_SLOT_PLUS", RARCH_REPLAY_SLOT_PLUS, "Select the next replay slot.", CMD_INFO_NO_TOOL },
{ "REPLAY_SLOT_MINUS", RARCH_REPLAY_SLOT_MINUS, "Select the previous replay slot.", CMD_INFO_NO_TOOL },
{ "DISK_EJECT_TOGGLE", RARCH_DISK_EJECT_TOGGLE, "Eject or insert the virtual disc.", CMD_INFO_NO_TOOL },
{ "DISK_NEXT", RARCH_DISK_NEXT, "Select the next disc image.", CMD_INFO_NO_TOOL },
{ "DISK_PREV", RARCH_DISK_PREV, "Select the previous disc image.", CMD_INFO_NO_TOOL },
{ "SHADER_TOGGLE", RARCH_SHADER_TOGGLE, "Turn the shader on or off.", CMD_INFO_NO_TOOL },
{ "SHADER_HOLD", RARCH_SHADER_HOLD, "Turn the shader off while held (one frame when sent as a command).", CMD_INFO_NO_TOOL },
{ "SHADER_NEXT", RARCH_SHADER_NEXT, "Load the next shader preset in its directory.", CMD_INFO_NO_TOOL },
{ "SHADER_PREV", RARCH_SHADER_PREV, "Load the previous shader preset in its directory.", CMD_INFO_NO_TOOL },
{ "CHEAT_TOGGLE", RARCH_CHEAT_TOGGLE, "Turn the selected cheat on or off.", CMD_INFO_NO_TOOL },
{ "CHEAT_INDEX_PLUS", RARCH_CHEAT_INDEX_PLUS, "Select the next cheat.", CMD_INFO_NO_TOOL },
{ "CHEAT_INDEX_MINUS", RARCH_CHEAT_INDEX_MINUS, "Select the previous cheat.", CMD_INFO_NO_TOOL },
{ "SCREENSHOT", RARCH_SCREENSHOT, "Take a screenshot.", 0 },
{ "RECORDING_TOGGLE", RARCH_RECORDING_TOGGLE, "Start or stop recording video.", CMD_INFO_NO_TOOL },
{ "STREAMING_TOGGLE", RARCH_STREAMING_TOGGLE, "Start or stop streaming.", CMD_INFO_NO_TOOL },
{ "TURBO_FIRE_TOGGLE", RARCH_TURBO_FIRE_TOGGLE, "Turn turbo fire on or off.", CMD_INFO_NO_TOOL },
{ "GRAB_MOUSE_TOGGLE", RARCH_GRAB_MOUSE_TOGGLE, "Grab or release the mouse.", CMD_INFO_NO_TOOL },
{ "GAME_FOCUS_TOGGLE", RARCH_GAME_FOCUS_TOGGLE, "Give the game all keyboard input, or hand it back to hotkeys.", CMD_INFO_NO_TOOL },
{ "FULLSCREEN_TOGGLE", RARCH_FULLSCREEN_TOGGLE_KEY, "Switch between fullscreen and windowed.", CMD_INFO_NO_TOOL },
{ "UI_COMPANION_TOGGLE", RARCH_UI_COMPANION_TOGGLE, "Show or hide the desktop companion window.", CMD_INFO_NO_TOOL },
{ "VRR_RUNLOOP_TOGGLE", RARCH_VRR_RUNLOOP_TOGGLE, "Turn sync to exact content framerate on or off.", CMD_INFO_NO_TOOL },
{ "RUNAHEAD_TOGGLE", RARCH_RUNAHEAD_TOGGLE, "Turn run-ahead on or off.", CMD_INFO_NO_TOOL },
{ "PREEMPT_TOGGLE", RARCH_PREEMPT_TOGGLE, "Turn preemptive frames on or off.", CMD_INFO_NO_TOOL },
{ "VIDEO_FILTER_TOGGLE", RARCH_VIDEO_FILTER_TOGGLE, "Turn the video filter on or off.", CMD_INFO_NO_TOOL },
{ "HEADSET_RECENTER", RARCH_HEADSET_RECENTER, "Recenter the headset's screens in front of where it looks.", CMD_INFO_NO_TOOL },
{ "LASER_POINTER_TOGGLE", RARCH_LASER_POINTER_TOGGLE, "Show or hide the headset's laser pointer.", CMD_INFO_NO_TOOL },
{ "HEADSET_VIEW_TOGGLE", RARCH_HEADSET_VIEW_TOGGLE, "Cycle the headset view between immersive, stereoscopic and flat.", CMD_INFO_NO_TOOL },
{ "HEADSET_EDIT_SCREENS", RARCH_HEADSET_EDIT_SCREENS, "Start or stop moving the headset's screens and menu with its controllers.", CMD_INFO_NO_TOOL },
{ "FPS_TOGGLE", RARCH_FPS_TOGGLE, "Show or hide the framerate.", CMD_INFO_NO_TOOL },
{ "STATISTICS_TOGGLE", RARCH_STATISTICS_TOGGLE, "Show or hide the technical statistics.", CMD_INFO_NO_TOOL },
{ "AI_SERVICE", RARCH_AI_SERVICE, "Run the AI service (translate or narrate the screen).", CMD_INFO_NO_TOOL },
{ "NETPLAY_PING_TOGGLE", RARCH_NETPLAY_PING_TOGGLE, "Show or hide the netplay ping.", CMD_INFO_NO_TOOL },
{ "NETPLAY_HOST_TOGGLE", RARCH_NETPLAY_HOST_TOGGLE, "Start or stop hosting netplay.", CMD_INFO_NO_TOOL },
{ "NETPLAY_GAME_WATCH", RARCH_NETPLAY_GAME_WATCH, "Switch netplay between playing and spectating.", CMD_INFO_NO_TOOL },
{ "NETPLAY_PLAYER_CHAT", RARCH_NETPLAY_PLAYER_CHAT, "Send a netplay chat message.", CMD_INFO_NO_TOOL },
{ "NETPLAY_FADE_CHAT_TOGGLE", RARCH_NETPLAY_FADE_CHAT_TOGGLE, "Turn fading of netplay chat on or off.", CMD_INFO_NO_TOOL },
{ "MENU_UP", RETRO_DEVICE_ID_JOYPAD_UP, "Menu: move up.", CMD_INFO_NO_TOOL },
{ "MENU_DOWN", RETRO_DEVICE_ID_JOYPAD_DOWN, "Menu: move down.", CMD_INFO_NO_TOOL },
{ "MENU_LEFT", RETRO_DEVICE_ID_JOYPAD_LEFT, "Menu: move left.", CMD_INFO_NO_TOOL },
{ "MENU_RIGHT", RETRO_DEVICE_ID_JOYPAD_RIGHT, "Menu: move right.", CMD_INFO_NO_TOOL },
{ "MENU_A", RETRO_DEVICE_ID_JOYPAD_A, "Menu: confirm (A).", CMD_INFO_NO_TOOL },
{ "MENU_B", RETRO_DEVICE_ID_JOYPAD_B, "Menu: back (B).", CMD_INFO_NO_TOOL },
{ "OVERLAY_NEXT", RARCH_OVERLAY_NEXT, "Switch to the next overlay.", CMD_INFO_NO_TOOL },
{ "OSK", RARCH_OSK, "Show or hide the on-screen keyboard.", CMD_INFO_NO_TOOL },
#if 0
/* Deprecated */
{ "SEND_DEBUG_INFO", RARCH_SEND_DEBUG_INFO, "Deprecated.", 0 },
#endif
};
#endif
#ifdef HAVE_CONFIGFILE
/**
* command_event_save_core_config:
*
* Saves a new (core) configuration to a file. Filename is based
* on heuristics to avoid typing.
*
* Returns: true (1) on success, otherwise false (0).
**/
bool command_event_save_core_config(
const char *dir_menu_config,
const char *rarch_path_config);
/**
* command_event_save_current_config:
*
* Saves current configuration file to disk, and (optionally)
* autosave state.
**/
void command_event_save_current_config(enum override_type type);
/**
* command_event_remove_current_config:
*
* Removes current configuration file from disk.
**/
void command_event_remove_current_config(enum override_type type);
#endif
/**
* command_event_disk_control_append_image:
* @path : Path to disk image.
*
* Appends disk image to disk image list.
**/
bool command_event_disk_control_append_image(const char *path);
/* Whether the drivers were started over: not while a load is under
* way that starts them anyway. */
bool command_event_reinit(const int flags);
bool command_event_main_state(unsigned cmd);
RETRO_END_DECLS
#endif