---
name: verify
description: Build, launch, and drive the vRIO WinForms app to verify changes at the GUI surface — screenshots via PrintWindow, input via PostMessage, no focus stealing.
---

# Verifying vRIO changes

Build + tests (tests are CI's job; run the app for verification):

```powershell
dotnet build vrio.sln -v q -nologo          # Debug
# exe: src\VRio.App\bin\Debug\net48\VRio.App.exe
# exe: src\VPlasma.App\bin\Debug\net48\VPlasma.App.exe   (companion display)
```

Everything below applies to vPLASMA too. It auto-opens **COM12** on
launch (hardwired; status in the title bar), so its serial path tests
end-to-end with no UI driving: the script just plays the game writing
**COM2** (second com0com pair; vRIO/RIOJoy use COM1⇄COM11). Double-click
the glass = self-test pages, right-click = reset.
Killing a writer mid-stream can leave stale bytes queued in the com0com
buffer — the next session's byte counter will run high; re-run clean
before trusting counts.

## Driving the GUI without touching the user's desktop

This is the user's live desktop — **never** use SetForegroundWindow +
SendKeys/mouse_event: focus-stealing prevention leaves the app behind
VS Code and your clicks land in the user's windows. Instead:

- **Screenshots**: `PrintWindow(hwnd, dc, 2)` (PW_RENDERFULLCONTENT) —
  captures the window even when occluded. CopyFromScreen captures
  whatever is on top; don't use it.
- **Canvas input** (stick drags, cell clicks): PostMessage
  `WM_LBUTTONDOWN/WM_MOUSEMOVE/WM_LBUTTONUP` straight to the canvas
  child HWND. Find it by descending `RealChildWindowFromPoint` from the
  form. Canvas geometry is compile-time constant (PanelCanvas.cs):
  CellW=66, BoxXY = x 416..496, y 6..74 (X/Y stick box).
  The **first** posted drag after launch can half-land (only the
  button-down registers); do a throwaway drag first, then the measured
  one, screenshot **while held** (spring-back recenters on up).
- **Checkboxes/buttons**: WinForms controls expose no UIA TogglePattern
  (they surface as bare Panes) and don't answer BM_GETCHECK — but
  `SendMessage(hwnd, BM_CLICK, 0, 0)` works. Get the HWND from the UIA
  element's NativeWindowHandle (find by Name).
- **Combo boxes** (COM port pickers): string-carrying messages
  (`CB_FINDSTRINGEXACT`, `CB_GETLBTEXT`) do **not** marshal across
  processes — the SendMessage hangs. Compute the item index locally
  (both apps fill from `SerialPort.GetPortNames()` sorted
  OrdinalIgnoreCase) and send index-only `CB_SETCURSEL`; the apps read
  `SelectedItem` lazily so no CBN_SELCHANGE notification is needed.
  Find the combo child HWND via EnumChildWindows + GetClassName
  containing `COMBOBOX` (UIA ClassName "ComboBox" doesn't match).
- Pattern that works: Add-Type user32 P/Invokes, Start-Process the exe,
  drive via messages, PrintWindow screenshot to the scratchpad, kill
  the process in `finally`.

## Environment gotchas

- Bindings load from `%APPDATA%\vRIO\bindings.txt` (the user's file),
  not the built-in defaults — arrow keys are bound to Hat *buttons*,
  not the joystick axis. Drive axes via the X/Y box.
- A real XInput gamepad is connected on this machine ("Controller #1
  connected"); the router only writes an axis when its composed value
  changes, so a centered pad won't fight a canvas drag.
- The wire readout (top center, green) shows `GetWireAxis` values —
  joystick Y is natively negated on the wire (stick up = −80) unless
  "Invert Y" is checked.
