From 125d835a053a3d9a330b0dae899494ff4f851fde Mon Sep 17 00:00:00 2001 From: Cyd Date: Thu, 9 Jul 2026 19:44:45 -0500 Subject: [PATCH] Deploy: pod_deploy.ps1 cockpit window layout + focus_dosbox.ps1 (renderer topmost, DOSBox focused) Real-pod deployment launcher for the original VDB + octopus 4-head wiring: VPX_COCKPIT borderless head windows (radar/win0 800,0; 3-color MFD/win4 1440,0; 2-color MFD/win3 2080,0; all 640x480) + the GL bridge as the main out-the-window view (0,0, 800x600 via new BRIDGE_W/BRIDGE_H in live_bridge.py). Editable RECTS at the top of the script for other rigs. focus_dosbox.ps1 restores the deployment window state: renderer -> always-on-top (WS_EX_TOPMOST, game view never covered) + DOSBox-X -> 10,10 with foreground focus (keeps the emu thread at foreground priority so the RIO doesn't starve). Coexist cleanly: topmost renderer stays above the focused-but-non-topmost DOSBox. pod_deploy calls it last; re-run anytime the stack is disturbed. LAUNCH.md documents both. Co-Authored-By: Claude Fable 5 --- emulator/LAUNCH.md | 21 +++++++++ emulator/render-bridge/focus_dosbox.ps1 | 59 +++++++++++++++++++++++++ emulator/render-bridge/live_bridge.py | 6 ++- emulator/render-bridge/pod_deploy.ps1 | 56 +++++++++++++++++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 emulator/render-bridge/focus_dosbox.ps1 create mode 100644 emulator/render-bridge/pod_deploy.ps1 diff --git a/emulator/LAUNCH.md b/emulator/LAUNCH.md index 36b2e51..1205977 100644 --- a/emulator/LAUNCH.md +++ b/emulator/LAUNCH.md @@ -9,6 +9,27 @@ elevated previous instance). Boot to netnub-ready is ~4 min with sound (the SoundFont upload); add `-NoSound` to skip it. +## Real-pod DEPLOYMENT (cockpit window layout) + +Formats the windows for the original VDB + octopus 4-head wiring: borderless +VDB head windows on their VGA outputs + the GL bridge as the main out-the-window +view, then parks DOSBox at 10,10 with focus (keeps the RIO fed). + +```powershell +# BT looped drops, cockpit layout +& .\emulator\render-bridge\pod_deploy.ps1 + +# Red Planet, cockpit layout +& .\emulator\render-bridge\pod_deploy.ps1 -Conf .\emulator\net_rp.conf +``` + +Layout (edit the RECTS at the top of `pod_deploy.ps1` for this rig's outputs): +main/bridge `0,0` 800x600 · radar/win0 `800,0` · 3-color MFD/win4 `1440,0` · +2-color MFD/win3 `2080,0` (heads 640x480). Re-assert DOSBox focus anytime: +`& .\emulator\render-bridge\focus_dosbox.ps1`. TODO: borderless-fullscreen main +(BRIDGE_BORDERLESS), portrait-radar rotation in cockpit mode if this pod's radar +CRT is sideways. + ## Networked (console-driven) — the main ones ```powershell diff --git a/emulator/render-bridge/focus_dosbox.ps1 b/emulator/render-bridge/focus_dosbox.ps1 new file mode 100644 index 0000000..c9f4ffd --- /dev/null +++ b/emulator/render-bridge/focus_dosbox.ps1 @@ -0,0 +1,59 @@ +# Restore the deployment window state: +# 1. the RENDERER (GL bridge, main out-the-window view) -> always-on-top +# (WS_EX_TOPMOST), so the game view is never covered. +# 2. DOSBox-X -> parked at 10,10 with FOREGROUND FOCUS, so its emulation +# thread keeps foreground priority (else the RIO ACK deadline is missed +# and the board storms -- the RIO focus-sensitivity issue). +# A topmost renderer stays visually above the focused-but-non-topmost DOSBox, +# yet DOSBox remains the active window (priority boost). Run anytime the stack +# gets disturbed (a capture, a click, new windows): .\focus_dosbox.ps1 +param([int]$X = 10, [int]$Y = 10) +Add-Type @' +using System; +using System.Text; +using System.Runtime.InteropServices; +public class DBFocus { + delegate bool EP(IntPtr h, IntPtr l); + [DllImport("user32.dll")] static extern bool EnumWindows(EP cb, IntPtr l); + [DllImport("user32.dll")] static extern int GetWindowTextA(IntPtr h, byte[] b, int m); + [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr h); + [DllImport("user32.dll")] static extern bool SetWindowPos(IntPtr h, IntPtr a, int x,int y,int w,int hh, uint f); + [DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr h); + [DllImport("user32.dll")] static extern bool BringWindowToTop(IntPtr h); + [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr h, int c); + [DllImport("user32.dll")] static extern IntPtr GetForegroundWindow(); + [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint p); + [DllImport("user32.dll")] static extern bool AttachThreadInput(uint a, uint b, bool f); + [DllImport("kernel32.dll")] static extern uint GetCurrentThreadId(); + static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + static IntPtr Find(string needle) { + IntPtr found=IntPtr.Zero; + EnumWindows((h,l)=>{ if(!IsWindowVisible(h)) return true; + byte[] b=new byte[256]; int n=GetWindowTextA(h,b,256); + if(n>0 && Encoding.ASCII.GetString(b,0,n).Contains(needle)){ found=h; return false; } + return true; }, IntPtr.Zero); + return found; } + static IntPtr FindRenderer() { + IntPtr h=Find("dpl3-revive renderer"); if(h!=IntPtr.Zero) return h; + return Find("VelociRender"); } + public static string Apply(int x, int y) { + string s=""; + // 1) renderer -> topmost (keeps game view above everything) + IntPtr r=FindRenderer(); + if(r!=IntPtr.Zero){ SetWindowPos(r, HWND_TOPMOST, 0,0,0,0, 0x0001|0x0002); s+="renderer topmost; "; } + else s+="renderer NOT found; "; + // 2) DOSBox -> 10,10 + foreground focus (RIO priority) + IntPtr d=Find("DOSBox-X"); + if(d==IntPtr.Zero) return s+"DOSBox NOT found"; + SetWindowPos(d, IntPtr.Zero, x,y, 0,0, 0x0001|0x0004); // NOSIZE|NOZORDER + ShowWindow(d, 9); // SW_RESTORE + uint pid; uint fg=GetWindowThreadProcessId(GetForegroundWindow(), out pid); + uint me=GetCurrentThreadId(); + AttachThreadInput(me, fg, true); + BringWindowToTop(d); SetForegroundWindow(d); + AttachThreadInput(me, fg, false); + return s+"DOSBox at "+x+","+y+" focused="+(GetForegroundWindow()==d); + } +} +'@ +[DBFocus]::Apply($X, $Y) diff --git a/emulator/render-bridge/live_bridge.py b/emulator/render-bridge/live_bridge.py index 62fa130..86997de 100644 --- a/emulator/render-bridge/live_bridge.py +++ b/emulator/render-bridge/live_bridge.py @@ -18,7 +18,11 @@ path = sys.argv[1] catchup = sys.argv[2] if len(sys.argv) > 2 else None board = VirtualBoard() board.munga = False -r = Renderer(w=832, h=512, +# BRIDGE_W/BRIDGE_H size the main out-the-window render window (default the +# 832x512 debug size; pod deploy sets the main display's native res e.g. +# 800x600). BRIDGE_BORDERLESS=1 drops the title bar for a kiosk deploy. +r = Renderer(w=int(os.environ.get('BRIDGE_W', '832')), + h=int(os.environ.get('BRIDGE_H', '512')), title=f"dpl3-revive renderer (Dave) -- LIVE from our pod [{backend}]") r.fps = int(os.environ.get('BRIDGE_FPS', '60' if backend == 'GL' else '30')) # eye TRIM on top of the camera position (live; render window must be diff --git a/emulator/render-bridge/pod_deploy.ps1 b/emulator/render-bridge/pod_deploy.ps1 new file mode 100644 index 0000000..2b3991d --- /dev/null +++ b/emulator/render-bridge/pod_deploy.ps1 @@ -0,0 +1,56 @@ +# Real-pod deployment launcher: cockpit window layout for the ORIGINAL VDB + +# octopus 4-head wiring. Four VGA heads, each a borderless window on its +# physical output: +# Head 1 main out-the-window = the GL bridge window -> 0,0 +# Head 2 radar (HEAD C) = win0 (VPX_WIN0) -> 800,0 +# Head 3 3-color MFD (HEAD B) = win4 (VPX_WIN4, UL+UC+UR) -> 1440,0 +# Head 4 2-color MFD (HEAD A) = win3 (VPX_WIN3, LL+LR) -> 2080,0 +# The octopus/DB25 cable splits each MFD head's R/G/B wires to the mono MFDs. +# +# Edit the RECTS below to match this pod's extended-desktop output coordinates, +# then: .\pod_deploy.ps1 (BT, looped drops) +# .\pod_deploy.ps1 -Conf ..\net_rp.conf (Red Planet) +param( + [string]$Conf = "$PSScriptRoot\..\net_loop.conf", + [switch]$NoSound +) +$ErrorActionPreference = 'Stop' + +# ---- POD DISPLAY LAYOUT (x,y[,w,h]); edit for this rig's outputs ---------- +$MAIN = '0,0'; $MAIN_W = 800; $MAIN_H = 600 # GL bridge (out-the-window) +$RADAR = '800,0,640,480' # Head C -> win0 +$MFD3 = '1440,0,640,480' # Head B, 3-color (UL/UC/UR) -> win4 +$MFD2 = '2080,0,640,480' # Head A, 2-color (LL/LR) -> win3 +# -------------------------------------------------------------------------- + +# Cockpit window mode: borderless VDB head windows at the rects above. +# VPX_COCKPIT overrides VPX_EXPLODE (which launch_pod sets), so we get the +# 3 head windows (win0/win3/win4), not the 7-window debug spread. +$env:VPX_COCKPIT = '1' +$env:VPX_WIN0 = $RADAR +$env:VPX_WIN4 = $MFD3 +$env:VPX_WIN3 = $MFD2 +# Main render window size = the main display's native res. +$env:BRIDGE_W = "$MAIN_W" +$env:BRIDGE_H = "$MAIN_H" + +$extra = @{} +if ($NoSound) { $extra.NoSound = $true } +# The GL bridge is the main head; park it at $MAIN's origin. +& "$PSScriptRoot\launch_pod.ps1" -Conf $Conf -BridgePos $MAIN @extra + +# DOSBox to 10,10 with FOREGROUND FOCUS (keeps the emu thread at foreground +# priority so the RIO doesn't starve). Small wait so the DOSBox window exists, +# then focus it LAST so it wins over the bridge/head windows. +Start-Sleep -Seconds 3 +& "$PSScriptRoot\focus_dosbox.ps1" -X 10 -Y 10 + +Write-Host "" +Write-Host "DEPLOY layout: main(bridge)=$MAIN radar/win0=$RADAR 3col/win4=$MFD3 2col/win3=$MFD2" +Write-Host "DOSBox parked at 10,10 with focus. If focus is lost later (head/bridge" +Write-Host "windows, or a capture), re-run: .\focus_dosbox.ps1" +Write-Host "NOTE: VDB head windows are borderless 640x480. Main render window is" +Write-Host "now sized to $MAIN_W`x$MAIN_H but still has its title bar -- a true kiosk" +Write-Host "deploy wants it BORDERLESS on the main output (BRIDGE_BORDERLESS SDL" +Write-Host "change, TODO). Radar is NOT rotated in cockpit mode (explode rotates it);" +Write-Host "if this pod's radar CRT is portrait, flag it and we'll rotate win0."