---
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 `