--- name: glue-embedded-game-preview description: How the Game tab's live preview embeds and resizes the real running game window, and its zoom/resolution status bar. Triggers: GameHostView, WinformsHost, Runner_MoveWindow, BottomStatusBar, ZoomControl, CurrentDisplayInfoDto, SetParent game window. version: 1.0.0 --- # Glue Embedded Game Preview The "Game" tab does not render the game into a WPF surface. Glue launches the game as a real, separate process and reparents its native window (`SetParent`, `GameHostView.xaml.cs::EmbedHwnd`) into a WinForms `Panel` hosted by a `WindowsFormsHost` (`GameHostView.xaml` → `WinformsHost`). ## Resize flow `WinformsHost_SizeChanged`/`MainGrid_SizeChanged` → `SetGameToEmbeddedGameWindow` sends a `Runner_MoveWindow` command — not over the DTO socket, but as a raw `_eventCaller`/`ReactToPluginEvent` string handled by `CompilerPlugin.HandleEvent` (`case "Runner_MoveWindow"`), which calls `Runner.MoveWindow` → the real Win32 `MoveWindow` API directly on `gameHandle`. Every resize is the *real game process's* actual OS window changing size/position for real, in-process — there's no visual scaling on Glue's side, and whatever the game's own `AspectRatioBehavior`/`ResizeBehavior` (see below) does with that size happens the same as it would for an end user. `WinformsHost` and its underlying WinForms `Panel` (`winformsPanel`, `GameHostView` constructor) always stay full-size, filling `MainGrid` (Stretch, no explicit size/alignment) — **even in fixed-size preview mode.** Letterboxing is achieved by moving/sizing only the real embedded game window (via `Runner_MoveWindow`'s X/Y/Width/Height) smaller than `winformsPanel`, whose own dark `BackColor` (`FromArgb(30,30,30)`, set in the `GameHostView` constructor) shows through the margins as letterbox bars. **Landmine — do not center by resizing/repositioning `WinformsHost`/`winformsPanel` (the ANCESTOR) instead of the game window itself.** An earlier version of fixed-size preview did exactly that (`WinformsHost.HorizontalAlignment/VerticalAlignment = Center` + explicit `Width`/`Height`, always passing `X=0,Y=0` to `Runner_MoveWindow`). It looked correct visually but broke rectangle-select and zoom-around-cursor by exactly the centering offset: `gameHandle`'s position *relative to its own immediate parent* (`winformsPanel`) never changed, so it never received a native move notification — only its *ancestor* moved. MonoGame/SDL's cached window position (used to translate the cursor's absolute screen position into client-relative mouse coordinates) went stale by that offset. The fix was to always keep `winformsPanel` full-size/unmoved and instead pass the letterbox offset as `Runner_MoveWindow`'s `X`/`Y` — since that's a real position change to `gameHandle` relative to its own immediate parent, it generates the native move notification MonoGame/SDL needs. ## Landmine — "connected" does not mean the game can handle a DTO yet `Runner_GameStarted` fires as soon as the game process has a `MainWindowHandle` (`Runner.cs`), which is *earlier* than the game being able to act on anything. In generated `Game1.Initialize`, `GameConnectionManager` is constructed (and connects) several statements before `GlueControlManager`, with `CameraSetup.SetupCamera` between them. In that window the socket is live but `GlueControlManager.Self` is null, so nothing can dispatch an incoming DTO. Anything sent on game startup therefore needs a retry budget that outlasts graphics-device setup, not just the connection (`BorderlessRetryPolicy`). Waiting on `GameCommunication_Connected` does **not** close this — connected is precisely the state where it fails. **Retry on `Succeeded`, never on the reply's content.** The game answers that window with `GameConnectionManager.NotReadyPayload` and Glue turns it into an unsuccessful `GeneralResponse`, so `Succeeded` is the readiness signal. Most `HandleDto` overloads return `void` — `SetBorderlessDto` included — and a healthy game answers those with an *empty* payload, so a "did it send anything back?" check never becomes true no matter how long the budget is. ## Zoom / resolution status bar `BottomStatusBar.xaml` (`ZoomControl` + resolution `TextBlock`) is fed by `CurrentDisplayInfoDto`, pushed from `GlueControl.Editing.CameraLogic` (`Embedded/Editing/CameraLogic.cs`, game process only — `PushZoomLevelToEditor`) and applied on the Glue side in `CommandReceiving/CommandReceiver.cs::HandleDto(CurrentDisplayInfoDto)`, which writes `CompilerViewModel.CurrentZoomLevelDisplay`/`ResolutionDisplayText`. Zoom itself (`ChangeZoomDto`, `+`/`-`) is a separate concept from window size — it scales `Camera.Main.OrthogonalHeight` inside the game and is independent of what size the embedded OS window actually is. None of this zoom state is persisted to the project file; it resets every Glue launch. ## Landmine — `GameCommunicationPlugin.csproj` has no implicit globbing `false` means a new `.cs` file under this project (e.g. adding a class next to `GameHostView.xaml.cs`) silently compiles into nothing until it's added to the `