Files
CydandClaude Opus 4.8 9df87f2c01 Deploy: pod install scaffold -- supervisor entry-point, postinstall, packaging
Network-agnostic, air-gapped pod install for the two DOSBox titles (BT/RP 4.10),
fitting the existing TeslaConsole/TeslaLauncher pod-bay architecture.

emulator/DEPLOYMENT-PLAN.md
  Full design: bridge on the one NIC (pods form a source-proven P2P TCP mesh, so
  NAT/slirp is out); launcher keeps the bay IP, the DOSBox guest bridges at
  bayIP+100 (egg/mesh/game endpoint); two-edit +100 convention (postinstall +
  console DOSBox flag); static, air-gapped, <=32 pods.

emulator/pod-launch/  (C# net8 supervising entry-point)
  Creates a Job Object (KILL_ON_JOB_CLOSE), launches DOSBox-X + the render bridge
  into it and blocks -- kill this process and both die (kernel-enforced, even on
  a hard TerminateProcess of a hung session; verified). Mode dispatch: bt/rp
  wired (also serve camera + live mission-review via egg hostType); review +
  diagnostics recognized but fail clean until their DOS launch args are known.

emulator/deploy/  (install side)
  postinstall.bat (thin elevated wrapper) + configure.ps1 (NIC detect, realnic
  bind as a contiguous letter-leading GUID fragment, game IP = bayIP+100, stable
  MAC, render net_*.conf templates, stamp WATTCP.CFG my_ip) + tokenized conf
  templates + package.ps1 (assemble the zip). Render/bind/stamp + packaging
  verified against a scratch tree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:36:30 -05:00

90 lines
3.9 KiB
C#

// Window layout, folded in from focus_dosbox.ps1:
// 1. the RENDERER (GL bridge) -> always-on-top, so the out-the-window view is
// never covered by a head window.
// 2. DOSBox-X -> parked at (x,y) 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 stays the active window.
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace VwePod
{
internal static class Focus
{
public static string Apply(int x, int y)
{
var s = new StringBuilder();
// 1) renderer -> topmost
IntPtr r = FindRenderer();
if (r != IntPtr.Zero)
{
SetWindowPos(r, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
s.Append("renderer topmost; ");
}
else s.Append("renderer NOT found; ");
// 2) DOSBox -> parked + foreground
IntPtr d = FindWindow("DOSBox-X");
if (d == IntPtr.Zero) return s.Append("DOSBox NOT found").ToString();
SetWindowPos(d, IntPtr.Zero, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
ShowWindow(d, SW_RESTORE);
uint fgThread = GetWindowThreadProcessId(GetForegroundWindow(), out _);
uint me = GetCurrentThreadId();
AttachThreadInput(me, fgThread, true);
BringWindowToTop(d);
SetForegroundWindow(d);
AttachThreadInput(me, fgThread, false);
s.Append("DOSBox at ").Append(x).Append(',').Append(y)
.Append(" focused=").Append(GetForegroundWindow() == d);
return s.ToString();
}
private static IntPtr FindRenderer()
{
IntPtr h = FindWindow("dpl3-revive renderer");
return h != IntPtr.Zero ? h : FindWindow("VelociRender");
}
private static IntPtr FindWindow(string needle)
{
IntPtr found = IntPtr.Zero;
EnumWindows((h, l) =>
{
if (!IsWindowVisible(h)) return true;
var b = new byte[512];
int n = GetWindowTextA(h, b, b.Length);
if (n > 0 && Encoding.ASCII.GetString(b, 0, n).Contains(needle)) { found = h; return false; }
return true;
}, IntPtr.Zero);
return found;
}
// ---- native ----
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const uint SWP_NOSIZE = 0x0001, SWP_NOMOVE = 0x0002, SWP_NOZORDER = 0x0004;
private const int SW_RESTORE = 9;
private delegate bool EnumProc(IntPtr h, IntPtr l);
[DllImport("user32.dll")] private static extern bool EnumWindows(EnumProc cb, IntPtr l);
[DllImport("user32.dll")] private static extern int GetWindowTextA(IntPtr h, byte[] b, int max);
[DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr h);
[DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr h, IntPtr after, int x, int y, int w, int hh, uint flags);
[DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr h);
[DllImport("user32.dll")] private static extern bool BringWindowToTop(IntPtr h);
[DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr h, int cmd);
[DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
[DllImport("user32.dll")] private static extern bool AttachThreadInput(uint a, uint b, bool attach);
[DllImport("kernel32.dll")] private static extern uint GetCurrentThreadId();
}
}