How to use it
Three steps to D47 in the headset.
1 Start SteamVR first, then D47.
Start SteamVR first, then D47, and turn on Draw in the headset.
The panel appears in the headset as an overlay beside the game.
2 Put the panel where you want it.
Say "put the panel down", "lock it to my head", "move it left" or "bring it nearer".
The panel moves; world-locked stays where you put it and head-locked follows you.
3 Set the size and the distance.
Set the distance, size and opacity in the Headset settings.
The panel changes to match, and tilts to face you when it is below eye level.
! The one that stops people.
Minimise the desktop window if you want it out of the way.
The overlay stays in the headset; only closing D47 removes it.
Why it works this way
Directive 47 drawn over Elite in your own cockpit — beside the game, never inside it.
1 It attaches. It never starts anything.
2 The panel is the same app, drawn a second time.
Point a controller at it, pull the trigger, and it comes with you — position and angle together, so it feels attached rather than dragged. Nothing turns it to face you while you hold it: a panel forced upright and square cannot be tilted to read from below or angled to sit beside you, which is most of what moving one is for.
3 Three levers that all sound like “how big”.
Scale and mini are different things, and the difference is the point: scale changes how big everything on the panel is, mini changes how much of it there is. Zooming a panel you cannot read makes it readable; switching to mini gives you less to read.
4 The captions follow the broadcast standard, not a preference.
The details
Directive 47 in your headset, drawn over Elite in your own cockpit.
It sits alongside the game rather than replacing it. It never hooks Elite, never touches its process, and never takes over the frame — it is an overlay, the same way SteamVR’s own dashboard is.
Ask for it
“is the headset working” “headset overlay off” “mini panel” “full panel”
The order does not matter
SteamVR first, Directive 47 first, SteamVR restarted halfway through the evening — none of that is something you should have to think about. Directive 47 looks for a headset every few seconds and builds everything the moment one appears.
It attaches to SteamVR rather than starting it. If SteamVR is not running, or your headset is switched off, Directive 47 waits and tells you which of the two it is waiting for:
SteamVR is not running. D47 will attach when you start it.
No headset is switched on. D47 will attach when one appears.
Nothing here starts SteamVR on your behalf. Launching it uninvited on a machine whose headset is off takes over your desktop for no reason.
What you get
Two things float in front of you.
The panel — the same panel as on the desktop, not a picture of it. It is the same app drawn a second time, so the windowed version can never do something the headset version cannot.
A Settings row that opens a window is refused, and the panel says so. Some Settings rows open a second window over the desktop panel: what Directive 47 remembers, the directions it has drafted, your notes, the logbook, the audio recorder, the Features list, your own ship cores, macros, switches, and the arrow that clears a stored key. A window cannot appear in a headset at all, so a ray press on one of those says “Not currently supported in VR” over the panel and opens nothing. Everything else on the page is pressed as usual.
Captions — everything Directive 47 says, written underneath. They place themselves and clear themselves, and there is nothing to drag them somewhere you would not see them. You choose which of two places they sit — in your view, or low in the cockpit — and Directive 47 works out the rest.
Moving it about
Say where you want it. The panel moves a step at a time, in whichever direction you name:
“move the panel left” / “move the panel right” / “move the panel up” / “move the panel down” “move the panel closer” / “move the panel further away” “turn the panel left” / “turn the panel right” “tilt the panel up” / “tilt the panel down”
One step is 5 cm, or 5 degrees for a turn or a tilt. Say it again for another step. Turning swings the panel’s face towards that side; tilting up leans it back towards your eyes, which is what a panel sitting below them wants.
It acts on whichever panel is on screen — big or mini — and each keeps its own place. If the panel was still riding your head when you asked, Directive 47 puts it down in front of you first and then moves it, exactly as picking it up with a controller used to.
Or look where you want it and say so. The panel on screen moves to the middle of your view, as far from you as it already was, turned to face you:
“place the panel here” / “put the panel here” / “panel here”
Looking down puts it lower and looking up puts it higher. Directive 47 reads which way the headset is pointing, not your eyes, so it is the direction your head faces that counts. The panel becomes world-locked and stays there; a panel that was riding your head goes at its Distance setting.
No model is needed for any of this. The phrases above are matched by name, so they work with no provider configured.
Or put it back where it started. Forgets where the on-screen panel was placed, sets it world-locked at its default distance, and lets it come to rest in front of you again:
“reset the panel” / “reset the vr panel” / “reset panel position”
Size, curvature, scale and resolution are left alone, and so is the other panel. With no headset session, the anchor and the two settings are reset all the same — the panel comes to rest the next time the headset attaches. Each placement heading on the Settings page carries the same reset as a glyph, for the panel that heading names rather than only the one on screen.
Motion controllers are on by default — see below for what turning them off costs.
Point a controller at the panel, pull the trigger, and it comes with you — position and angle together, so it feels attached rather than dragged. Let go and it stays.
While a ray is on the panel, Directive 47 asks SteamVR for the trigger and grip of that controller at overlay priority, and gives them back the moment the ray leaves. On a default SteamVR install that changes nothing you would notice: taking inputs away from other applications needs SteamVR’s Enable global input from overlays developer setting, which is off unless you turned it on, and without it the game, Virtual Desktop and the dashboard keep the trigger while Directive 47 reads it too. With it on, the hand-back is what returns the trigger — 0.48.6 meant to do that on every path and did not, because SteamVR refused the call it made and said so only in d47’s log; 0.52.2 does, and its log says when the controllers are claimed and given back, and “headset overlay off” then “headset overlay on” still frees a controller on an older build. A controller that stops answering everything some way into a session is a different fault, tracked as #1 and closed as not reproducing on 0.111.3.
Nothing turns it to face you while you hold it. A panel forced upright and square cannot be tilted to read from below or angled to sit beside you, which is most of what moving one is for.
Picking it up makes it world-locked, because picking something up and putting it down is what world-locked means. Directive 47 does not then argue with you about the setting.
If Elite’s own recenter moves your cockpit out from under a panel you had placed, put it back by switching that panel’s lock to head, which brings it to your face, and then nudging it — the first nudge sets a head-locked panel down in front of you, at knee height, before it moves. Two steps where re-anchoring was one; that command was retired in 0.94.0.
Resizing it
Say “resize the panel” and the panel on screen goes into resize mode: a coloured handle runs along each of its edges. Point a controller at an edge or a corner — the handle under the ray thickens — hold the trigger and pull. An edge changes one dimension and a corner changes both, and the opposite edges stay where they were.
“resize the panel” / “panel resize mode” “stop resizing” / “done resizing”
Resizing changes the panel’s shape, and the content reflows into it. Pull the panel wider and it gets more pixels as well as more metres, at the same density, so more room means more rows rather than bigger ones. To make what is drawn larger without moving the edges, zoom instead.
Resize mode is a mode because the panel is something you press all session, and an edge that resized whenever a ray brushed it would spoil presses near the edge. Out of the mode, a press on an edge is a press. In the mode, a press anywhere away from the handles still presses, and the grip leaves the mode rather than going back a page.
A drag stops at a smallest and a largest size, so a panel cannot be shrunk past the point where its handles can be found again. Letting go writes the new size and resolution to the settings of the panel you resized; the big panel and the mini panel each keep their own. A panel that was riding your head is put down in the room when you take hold of a handle, the same as picking it up.
Resizing needs the motion controllers. With them off, the Size and Resolution settings below change the same two things.
A ray can enter and leave without speaking, too. A square glyph beside the help mark opens resize mode on the full panel, when the motion controllers are on. Once the handles are showing, a bar along the bottom offers zoom out, reset, zoom in and done — the same four things the hotkeys and voice already reach, drawn where a ray can put a controller and inset far enough from every edge that pressing one is never mistaken for taking hold of a handle. The mini panel carries the bar too, but no entry glyph: voice and the hotkey are what open resize mode there.
Toggle the headset panel’s resize mode
Ctrl+Alt+S out of the box, and it works anywhere, including while Elite has the foreground —
so resize mode is reachable with a controller withdrawn and no model configured. Entering and
leaving are silent: the handles appearing, or leaving, is the answer. Asking to enter it with the
motion controllers off, or with no headset session, is spoken instead — from inside a headset there
is no transcript to read it from.
Each of these four headset keys takes a stick or throttle button as well: press BIND on the row and press the button, and it is bound beside the key rather than in its place. Giving it the same kind twice replaces that one, and CLEAR removes both. The button works anywhere, as the key does.
Zooming it
“zoom the panel in” / “make the panel text bigger” “zoom the panel out” / “make the panel text smaller” “reset the panel zoom”
Each step moves the panel’s Scale one rung along the ladder the desktop window zooms with, and reset puts it back to 100%. It acts on whichever panel is on screen. The panel’s edges do not move; the content is laid out again at the new size.
Zoom the headset panel in
Ctrl+Alt+= out of the box, and it works anywhere, including while Elite has the foreground —
the same as the overlay’s own keys. Steps Scale up one rung on whichever panel is on screen.
A stick button bound beside the key does the same.
Zoom the headset panel out
Ctrl+Alt+- out of the box, and it works anywhere. Steps Scale down one rung. A stick
button bound beside the key does the same.
Reset the headset panel’s zoom
Ctrl+Alt+0 out of the box, and it works anywhere. Puts Scale back to 100%. A stick
button bound beside the key does the same.
Settings
Show Directive 47 in the headset
On by default, which costs nothing if you have no headset — it looks, finds nothing, and says so. Turn it off if you use SteamVR for other things and would rather Directive 47 stayed out of it.
“headset overlay on” / “headset overlay off”
Panel content
Full or mini. Mini is the same panel showing less — the gear, the banners and the ask box go, the transcript stays. It is also drawn smaller, so it can sit at the edge of vision.
Mini out of the box. The full panel is 1.1 m across — close to fifty degrees of view — which is a lot to meet before you know it can be moved or shrunk. Switch to full whenever you want it.
Ask for either by name, in whichever words come out:
“big panel” / “large panel” / “full panel” “small panel” / “little panel” / “minimal panel” / “mini panel”
The two keep their own placements, so parking mini out to one side while the full panel stays in front of you works the way you would expect.
Mini means a minimal interface, not a fixed size. It starts at 512 by 280 pixels and 0.34 m across, and both are yours to change — with its own resolution setting, or by resizing it in the headset to whatever shape you want.
Mini carries no controls, only what they were showing you. Its starting size was chosen so the text is readable rather than so there is room — every button on it would be space taken from the thing you opened it for. So a page’s own bar goes: the checklist’s filter, its ordering, its import, its Goals switch. What stays is the list itself, including the switch beside each line, so you can still mark work done from the headset. The big panel keeps the whole bar, and that is the one headset surface where a controller can genuinely press it.
Motion controllers
On by default for new installs. An install that already had a settings file keeps whatever that file held — the default only reaches a fresh one.
What you lose with them off. Nothing on the panel can be pressed in the headset: no buttons, no toggles, no checklist ticks, no steppers or segments, no on-panel keyboard, no scrollbar dragging, no grip-to-go-back, and no Settings tab. You cannot grab the panel and carry it.
What still works. Everything by voice: moving between tabs, going back, scrolling, answering a question the panel is already asking, and placing the panel — see Moving it about above, which is the replacement for the carry.
“motion controllers on” / “motion controllers off”
Placing a surface
Six settings each, and the mini panel has its own copies of all six.
Ask for a change and it lands on the panel you are looking at. The two surfaces keep their own numbers — that is the point of mini, which exists to sit smaller and further out of the way — so “move it closer” means the one currently on screen, and the other one does not move. Both are on this page in full, so setting mini up while the big panel is in front of you is a thing you can do here whenever you like.
“Size” and “Scale” are different questions and it is worth knowing which you want. Size is how big the panel itself is, in metres across — the edges move. Scale is how big the writing and the controls on it are drawn, without the edges moving. Wanting bigger text on a panel that is already the right size is Scale.
| Setting | What it does |
|---|---|
| Locking {#panel-lock} | Head-locked follows you; world-locked stays where you put it |
| Distance {#panel-distance} | How far in front of you, in metres. Head-locked only — a surface you put down is where you put it |
| Size {#panel-size} | How wide, in metres. Height follows the panel’s proportions, so there is nothing else to set |
| Curvature {#panel-curve} | 0 is flat, 1 is wrapped around you |
| Scale {#panel-scale} | How large the content is drawn, on the same steps the desktop window zooms with |
| Resolution {#panel-resolution} | How many pixels the panel is rendered at, which also sets its shape. The big panel starts at 1024x640, the mini panel at 512x280 |
Opacity is one knob for both
How solid the panel is, from 0.1 to 1 — and one setting, not one per panel. Everything in the table above is genuinely per-surface: mini exists to be smaller and further out of the way, so its distance, size and drop have to be able to differ. How see-through the glass is does not. It is one preference about how much cockpit shows through Directive 47, and asking for it at half never means half, in one of the two modes.
It used to be two, and that is exactly how it went wrong: “set the opacity to 0.5” landed on the big panel’s copy while the mini panel was the one in front of the Commander, so the number they could see never moved and nothing was broken. A value already set on either panel is carried up to the shared one the first time this version runs, so nobody has to set it again.
Three levers, and they do different things
Size, Scale and Resolution all sound like “how big”, and keeping them apart is worth a minute.
- Resolution decides how much the image can hold. More pixels, more room for rows.
- Size decides how big it looks in the room. Metres across, at whatever distance you put it.
- Scale decides how much layout those pixels carry. It re-measures and rewraps rather than simply magnifying, so it is a density control: at 75% the default panel presents 1365x853 worth of layout and a whole ship’s slots fit; at 200% it presents 512x320 and you can read it from across the cockpit.
Every size on the offered list is the same shape as the big panel’s default, so picking one of them changes how much detail is in the panel and not its proportions. A size a resize produced keeps the shape you pulled it to, and is listed beside them. More pixels cost more to render, every frame, and past the point where the panel covers what your headset can actually resolve they buy you nothing. Pick by looking at it, not by picking the biggest number.
Head-locked is what you want for something you glance at; world-locked for something that lives in a particular corner of the cockpit.
World-locked out of the box, and put down for you the first time Directive 47 runs in your headset: about a metre ahead of wherever you are facing, low enough that the top of the panel sits around knee height, and tilted back so it faces you rather than the ceiling. A panel that follows your gaze is in the way of whatever you turned to look at, which is the one thing a companion beside a flight sim should never be.
That first position is worked out from where your head actually is — the floor comes from your room setup, and the height of the panel from its width and proportions — so it is not a number picked for somebody else’s height or somebody else’s panel size. Move it once and it is yours; Directive 47 never places it again.
Scale and mini are different things, and the difference is the point: scale changes how big everything on the panel is, mini changes how much of it there is. Zooming a panel you cannot read makes it readable; switching to mini gives you less to read.
The mini rows work the same way: locking, distance, size, curvature, scale and resolution — but not opacity, which both panels share. The mini panel defaults to 0.34 m across — it is meant to sit at the edge of vision — against 1.1 m for the full one.
Captions
They follow the broadcast closed-caption standard rather than anyone’s preference:
42 characters the most on one line
3 lines the most for one thing said
3 lines the most on screen at once
20 per second reading speed, which decides how long one stays up
5/6 s .. 7 s the shortest and longest a caption lingers
A caption stays up timed from when the speech ends, not when it starts. Nobody reads along with a voice they can hear; they catch the last line after it has gone.
A long sentence arrives as several captions, not as its last three lines. Anything that wraps past three lines is shown three at a time, each staying up for the longer of your reading speed and the time the voice takes to say it, until the sentence has been shown in full. The speaking time comes from how fast the voice has recently spoken. If Directive 47 starts saying something new while an earlier sentence is still being shown, the new one takes over: a caption still working through something the voice finished with is no longer captioning.
Sounds that mean something are captioned too. The warning tone that plays a moment before an
urgent callout is written in brackets — [interdiction alert], [attack alarm], [heat alarm] —
so a Commander reading captions gets the same head start a Commander hearing them does. The loop
tones are not captioned, because the panel already shows the loop state; ambience, music and the
thinking bed are not, because they are continuous and say nothing.
And a caption says who is talking when it is not Directive 47. Your carrier’s tower and its
captain are different people in different voices, and a crew member you address by name answers in
theirs — so those lines open with [Tower], [Carrier] or the crew member’s own name. Directive
47’s own lines carry no name: it is the voice the caption band belongs to, and putting a label on
every line would be noise. In-game messages from other Commanders are not captioned at all — they
are already written out in full, with their sender, in the Transcript’s Journal File reading.
Captions follow what is actually audible rather than what was generated, so if you say “stop” the captions stop with the voice. A caption still sitting there after a silence command is Directive 47 visibly not having stopped.
They stay level with the horizon, not with your head. Head-locked captions follow where you are looking — turn or look down and they come with you — but they ignore how far your head is tilted, so the text runs along the same line the cockpit does instead of along your eyeline. Tilt your head twenty degrees and the captions stay put and level while your view rolls around them. There is no tilt dial and deliberately so: a caption is either level or it is wrong, and an angle you have to trim by hand is a way of shipping it wrong.
If your captions look level but the cockpit does not, that is Elite’s own recenter having been taken with your head tilted — recenter again with your head straight, and both agree.
Caption position
head or world. Two positions, both worked out for you, and nothing else about where captions
go is settable — there is no distance, no curvature and nothing to reach out and drag.
head is the default. The band hangs a little below the middle of your view, 1.6 m out, and
goes where you go. It is always readable, which is the one thing a caption has to be.
world puts it in the cockpit instead, in a fixed strip between the console and your feet:
0.8 m ahead of where you sit and 0.67 m below your eyeline, so it covers the band from about 37°
to 43° down. It stays there while you look around, the way the cockpit does.
Why you might want that: a head-locked caption is the only thing in the headset that does not move when you turn your head. Your eyes report a band holding perfectly still while your inner ear reports a turn, and that disagreement is what motion sickness is made of — so a band that sits in the room instead of on your face is the more comfortable of the two for anyone it bothers. World- locked is also that little bit steadier when you move your head fast: head-locked captions are put level again every time the overlay is served, and the headset carries them rigidly in between, so a quick roll tilts them until the next frame. A strip sitting in the cockpit is not being carried, so there is nothing to catch up.
What it costs, plainly: you have to glance down for it. Head-locked is always in front of you and world-locked is somewhere you look. That trade is yours, which is why this is a row and not a ruling.
The strip is measured from your seated position, not dropped where you happened to be looking, so it cannot drift and there is nothing to put back. If it ever sits wrong, it is your seated position that has moved: use SteamVR’s own Reset Seated Position with your head straight and the band comes with it.
Text is the same size either way. The strip is nearer than the head-locked band, so it is correspondingly narrower — both cover the same 30° of view, and caption size means the same thing in both.
Caption size
small, medium or large. Three sizes rather than a number, because a caption is either
legible at a glance or it is not, and there is nothing useful between two adjacent values.
Caption background
How solid the box behind the text is.
A caption sits over a starfield, a station’s floodlights and your own instruments. Text with nothing behind it is unreadable against half of those — which is why broadcast captions have always sat on a box, and why the box is not fully solid, since one you cannot see through is a hole cut in your cockpit. The text stays fully opaque whatever you set; only the box fades.
Reading speed
Characters a second. 20 is the standard’s adult rate, 17 its children’s rate, and 12 is
there because reading speed is the one thing about a caption that is a property of the reader
rather than of the caption.
Recentre Elite’s view
Say “recentre the headset”, “recenter the headset”, “reset the headset”, “recentre the view” or
“reset vr” to press the game’s HMDReset key. It is the recentre_headset action of
control_interface, and works in the ship, the SRV and on foot. It recentres Elite’s view only;
Directive 47’s own panel keeps its place.
Headset
Not a setting but a state, shown next to the switch — because switched off and SteamVR is not running look identical from the outside unless something says which.
The tool surface, for contributors
get_headset_status
Reports whether D47 is showing in the headset, and if not, why not. Reports only. Takes no arguments.
{"type":"object","properties":{},"required":[],"additionalProperties":false}
show_in_headset
Shows D47 in the headset, or stops showing it — the same row as the switch above, reached by voice. It answers with the status rather than an acknowledgement, because switching it on is not the same as it appearing: with no runtime installed the setting takes and nothing shows.
{"type":"object","properties":{"on":{"type":"boolean","description":"True to show D47 in the headset, false to leave SteamVR alone."}},"required":["on"],"additionalProperties":false}
move_headset_panel
Moves the panel that is on screen one step at a time. A world-locked panel’s position is not in
settings.json at all — it is an anchor pose in view state, written until now only by a completed
carry — so this is a delta applied to that anchor rather than a settings row. The two placement
fields that look like the obvious answer, Drop and Pitch, are read only by the head-locked
path, and the default lock is world: rows for them would move nothing you could see.
{"type":"object","properties":{"direction":{"type":"string","description":"Which way. turn-left and turn-right swing the face of the panel towards that side; tilt-up leans it back to face the Commander.","enum":["left","right","up","down","nearer","further","turn-left","turn-right","tilt-up","tilt-down"]},"steps":{"type":"integer","description":"How many steps, 1 to 20. One step is 5 cm or 5 degrees. Defaults to one."}},"required":["direction"],"additionalProperties":false}
Up and down are the room’s vertical, not the panel’s own, and nearer and further run along the floor rather than along the tilted face — otherwise bringing a panel closer would raise it at the same time, which is one gesture doing two things.
place_headset_panel_here
Writes a new anchor for the panel on screen: centred on the headset’s forward line, pitch included,
with its face turned back at the head and no roll. The distance is from the head to the existing
anchor’s centre for a world-locked panel, and the surface’s Distance otherwise. The head pose is
read when the command runs, after transcription.
{"type":"object","properties":{},"required":[],"additionalProperties":false}
restore_headset_panel_position
Puts the panel on screen back where a fresh install puts it: removes its anchor from view state
and resets its lock and distance rows to their defaults, world and the surface’s own. It acts
even with no headset session — a world-locked panel with no anchor rests itself on the next active
tick, the same as one that has never been placed.
{"type":"object","properties":{},"required":[],"additionalProperties":false}
zoom_headset_panel
Steps vr.current.scale one rung along ZoomLadder, or back to its default, so it lands on
whichever panel is on screen.
{"type":"object","properties":{"direction":{"type":"string","description":"in draws it one step larger, out one step smaller, reset at 100%.","enum":["in","out","reset"]}},"required":["direction"],"additionalProperties":false}
resize_headset_panel
Turns resize mode on or off. The mode lives in the headset host rather than in settings, so it ends with the session. Turning it on is refused with the motion controllers off, since nothing could then take hold of a handle.
{"type":"object","properties":{"on":{"type":"boolean","description":"True to enter resize mode, false to leave it."}},"required":["on"],"additionalProperties":false}
It exists because asking to see the panel used to reach get_headset_status, that being the only
headset-shaped thing on the surface — so “show the VR panel” was answered with “the overlays
are dark, Commander”, which is true and is not what was asked. set_setting could always have
done it; nothing pointed at that.
An overlay application, never a scene one — that is what keeps D47 clear of anything resembling game injection. Elite renders through OpenVR, which is why this is OpenVR and not OpenXR.
Three states, and the difference between the first two matters. Unavailable is no runtime
installed; Connecting is a runtime present but no session; Active is up. VR_Init returns
Init_HmdNotFound while vrserver.exe is already running: SteamVR is running and a headset
is present are different facts. VR_Init also starts SteamVR when it is not running, so it
is called only once a session already exists and a headset is present — checked with the process
list and VR_IsHmdPresent, neither of which starts anything. Recovery rebuilds rather than
repairs: a session that has gone away leaves every overlay handle stale, and a stale handle fails
forever with a plausible error.
Two quads: com.dseelinger.d47.panel and com.dseelinger.d47.captions. Apparent text size is
the texture’s pixel count and the quad’s width in metres together, so mini is a genuinely
smaller image as well as less content — drawing the full panel and hanging it nearer gives text a
third of the size.
Out of the box, before anything is resized:
full 1024 x 640 px at 1.10 m wide
mini 512 x 280 px at 0.34 m wide
captions 1600 x 340 px at 0.90 m head-locked, 0.57 m world-locked
The caption quad is the same texture in both positions. The world-locked strip is nearer, so it is narrower by exactly the ratio of the two distances — which is what makes the size steps mean one thing rather than two.
1.4 m was tried first and read as enormous — close to fifty degrees of view, so the panel filled the middle and the cockpit was behind it rather than around it.
The panel is a second instantiation of the same view definition bound to the same view model,
rasterised offscreen into a plain pixel buffer and submitted with SetOverlayRaw. About 0.75 ms
a frame measured live against SteamVR, of which the rasterise is 0.30 ms, and only when something
changed.
The trigger only. A controller reports its grip and other buttons through the same channel,
so treating them all as a press means the grip that grabs the panel also clicks whatever the ray
was over. And the pointer is the only controller input an overlay gets: SteamVR takes the
controllers for its own laser and hands back mouse events.
IVRSystem.GetControllerState returns false for controllers that are connected and tracking,
silently, and an IVRInput action manifest was built twice in two separate projects, accepted by
SteamVR, and never went live.
The pixels go over as raw bytes rather than as a texture. A shared Direct3D texture was the
original route, and it left the overlays invisible in the headset while SteamVR reported them
visible — with every call returning success, the copy proven correct by test, and the rasterise
proven correct by writing a live session’s first frame to data/vr-PanelFull.png and looking at
it. SetOverlayRaw hands OpenVR a buffer and lets it do the upload, which removes the graphics
device, the adapter match and the share flag along with the fault. Two things it has to get
right, both of which come out as a wrong picture rather than an error: the buffer is RGBA where
Avalonia rasterises BGRA, and its rows carry no padding, because SetOverlayRaw derives the
stride from the width.
Caption line breaks go after punctuation, or before a conjunction or preposition, bottom-heavy where nothing else decides it; when those compete the syntactic break wins. Captions are never curved: two short lines in the middle of the view have no far edges to bring closer.