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>
90 lines
3.9 KiB
C#
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();
|
|
}
|
|
}
|