Native Stage, Input, and Timing
Recovered projector flags, the presentation rect recompute (CenterStage/ResizeStage), window rebinding, the input dispatch pipeline, and score/timing native surfaces.
Projector startup and flags
Proj.dll parses projector settings from RT_STRING resources into a flag word. Decoded flags and bits:
| Resource | Setting | Bit |
|---|---|---|
0x57f | Animation | 0x800 |
0x580 | SystemFriendly | 0x2000 |
0x5aa | EscapeOk | 0x2 |
0x5ac | FullScreen | 0x4 |
0x5ad | UseTitleBar | 0x8 |
0x5ae | BackgroundAnimation | 0x10 |
0x5af | SwitchColorDepth | 0x20 |
0x5b0 | CenterStage | 0x40 |
0x5b1 | ResizeStage | 0x80 |
0x5bb | SingleInstance | 0x1000 |
0x5bc | SingleTaskbarEntry | 0x10000 |
Use sites: FullScreen selects fullscreen IML32 window helpers (window flag 0x20000 vs 0x10000 windowed); CenterStage calls Dirapi ordinal 52 at setup; ResizeStage is inverted into ordinal 53; SwitchColorDepth calls ordinal 161; EscapeOk is inverted into ordinal 92.
The projector drives the runtime through a small ordinal API: forward the HWND and presentation flags (ordinals 86/87/92), pump idle/event processing (93), read-and-clear runtime status (94), enter/exit active drawing state (96/98).
IML32 window and rect helpers
Confirmed ordinals (building blocks of all presentation math):
| Ordinal | Function |
|---|---|
| 1060 | Write a rect (l, t, r, b). |
| 1064 | Offset a rect by dx/dy. |
| 1068 | Rect intersection test (strict < on all four edges). |
| 1810 | Create the ImlWinCls HWND from a rect. |
| 1815 / 1818 | Focus+activate / show+hide. |
| 1820 | InvalidateRect + UpdateWindow (null rect = whole window). |
| 1826 | Render an IML backing bitmap into an HWND (BeginPaint/GetDC + BITMAPINFO draw). |
| 1832 / 1833 / 1834 | MoveWindow with child adjust / read+normalize client rect (menu-height aware) / move outer window so a client rect lands at a screen position. |
| 2031 | Obtain display/screen bounds (per display index). |
Splash/error windows demonstrate the pattern: build rect at origin (1060), get display bounds (2031), center via offset (1064), create (1810), draw (1826). Centering is rect construction before window creation, not post-hoc scaling.
The presentation record and rect recompute
The recovered stage-presentation routine (reached from CenterStage setup, FUN_680607fd in Dirapi.dll):
- Requires a live display/movie object (flag bit
0x8). - Reads display 0's rect, aggregates all displays' bounds (ordinal 2031 loop).
- Builds the presentation candidate from the presentation record (x, y, width, height).
- If resize-stage is disabled (stored at
+0x380) or the candidate is invalid, falls back to four authored shorts (+0x198...+0x19e). - If center-stage (
+0x378) is set, centers the candidate within display 0 (ordinal 1064). - If the candidate intersects no display, recenters it.
- Commits via a helper that invalidates newly exposed bottom/right strips when shrinking, then stores origin and size on the presentation record (
+0x8x,+0xcy,+0x10w,+0x14h). - Optionally moves the real window/client rect (ordinal 1834).
The architectural lesson: Director keeps three separate concepts that emulators routinely conflate:
- logical stage coordinates (what Lingo sees),
- the native presentation/window rect (the record above),
- the backing/display surface (canvas, DPI).
Native code never treats the backing canvas or CSS-like element size as the coordinate system. Resize bugs in emulators (wall/floor desync, offset avatars, misplaced loaders) trace to mixing these spaces.
Window rebinding rebuilds channels
When the projector rebinds the runtime to a new HWND (ordinal 86 route), Dirapi writes the HWND into the display/window state block and then walks all sprite/channel entries (0x170 bytes each), dispatching per-type rebuild/reset routes that clear type fields, flags, and cached rect data. Native resize handling is a shared channel-state rebuild, never per-content offsets: the strongest possible argument against per-room resize hacks in emulators.
directToStage is likewise an operation mode: its property read temporarily toggles dispatch/draw state flags around channel calls rather than choosing a different renderer.
Input dispatch pipeline
Native surfaces proved by table/descriptor evidence: eventPassMode, dontPassEvent, sendSprite, sendAllSprites, stopEvent, mouseDown/Up(Script), mouseWithin/Leave/Enter, mouseStillDown, mouseUpOutSide (note the native capital S spelling: normalize event names, do not string-match), clickLoc, clickOn, doubleClick, lastClick/Roll/Event, mouseSprite, keyboardFocusSprite, editFocusSprite, plus the modifier and selection families. Mouse state descriptors (mouseH [0x40, 0x1af], mouseV [0x40, 0x1b0], doubleClick [0x40, 0x1bb]...) ride the simple descriptor branch documented in Native Lingo internals.
Two separately traceable native routes:
- Target selection: host coordinates -> stage coordinates -> candidate sprites from the last baked frame (front to back) -> per-candidate hit policy by member type and ink -> mouse-down/hover/up target retention.
- Event execution: selected target -> primary handler -> behaviors -> cast member -> frame -> movie, honoring
pass/stopEvent/default behavior (message hierarchy).
Bug classification by layer (works for any engine):
| Layer | Typical symptom |
|---|---|
| Coordinate conversion (DPI, presentation rect) | Clicks offset after resize |
| Candidate list (order, visibility, locZ) | Front UI ignored |
| Hit policy (matte/key/text rules) | Only some pixels clickable |
| Target retention (down vs up sprite) | Buttons miss on release |
| Primary handler | Handler never fires |
| Propagation (order, pass, stopEvent) | Wrong handler consumes |
| Timing (double-click, drag capture) | Single click acts as double |
Hit-policy cases still needing native microtests: mouseUp target when press and release differ; doubleClick timing and target-identity requirements; matte hit source (coverage stream vs simplified scan); background-transparent hits vs keyed pixels; editable-field focus consumption; drag capture semantics.
Score and timing surfaces
Native tables group the expected lifecycle/timing surface: prepareMovie, beginSprite, prepareFrame, enterFrame, exitFrame, actorList, frameTempo, updateStage, and the timeout family (timeout, timeoutScript/List/Handler/Length/Lapsed/Keydown/Mouse/Play). This proves the native surface and its grouping; exact handler order and timer cadence remain to be proved by native microtests (the documented order in Event lifecycle is the working model). Open timing questions worth microtesting: whether updateStage forces immediate paint or marks dirty; whether mouse movement pumps frame work; timeout firing position relative to exitFrame; actorList mutation during iteration.