# Double Buffer Architecture Research v2
## Date: 2026-04-30 — Deep Second Pass

---

## 10 Critical Findings

### 1. EMPTY rcPaint — STALE BUFFER CONTENT (HIGH)
- BeginPaint may return empty ps.rcPaint (0,0,0,0)
- BitBlt with 0x0 size does nothing, but hdc still = s_hdcPaintBuf
- Buffer retains STALE content from previous frame
- ScanAndDrawUda draws UDA onto stale buffer → visual artifacts
- **Fix**: Check rcPaint before proceeding

### 2. RESIZE STALE CAPTURE (HIGH)
- WM_SIZE deletes/recreates s_hdcPaintBuf (empty)
- Next PaintText captures screen → new buffer
- But in v4, PaintText skipped screen write last frame → screen has OLD content
- Capture gets OLD content into NEW buffer
- **Fix**: On resize, force full-screen capture or accept one-frame stale

### 3. BUFFER UDA REMNANTS ACROSS FRAMES (MEDIUM)
- s_hdcPaintBuf is persistent (not recreated each paint)
- Frame 1: PaintText draws ASCII, ScanAndDrawUda draws UDA → buffer has both
- Frame 2: PaintText captures screen → buffer, overwrites ASCII areas
- BUT: UDA pixels from Frame 1 still exist in buffer at positions not repainted
- If UDA cell becomes ASCII in Frame 2, old UDA remnants may show through
- **Fix**: PaintText's capture from screen should overwrite UDA areas. Need to verify capture covers full dirty rows.

### 4. SetStretchBltMode NOT SET ON g_hPaintBuf (MEDIUM)
- PaintText creates s_hdcPaintBuf without SetStretchBltMode
- ScanAndDrawUda sets COLORONCOLOR on screen DC, not on g_hPaintBuf
- DrawDynUnicodeGlyph uses StretchBlt on g_hPaintBuf → default mode
- **Fix**: Add SetStretchBltMode(s_hdcPaintBuf, COLORONCOLOR) after creation

### 5. SelectClipRgn ON g_hPaintBuf — EXCEPTION UNSAFE (MEDIUM)
- DrawAscFontDirect calls SelectClipRgn(hdc, hClip) where hdc = g_hPaintBuf
- If exception between SelectClipRgn and NULL restore → clip region stuck
- Subsequent drawing clipped to old cell boundary
- **Fix**: Wrap in __try/__finally or use structured pattern

### 6. g_bSharedPaint RE-ENTRANCY — USE COUNTER (MEDIUM)
- ProcessMessages inside PaintText can trigger nested WM_PAINT
- Nested paint sets g_bSharedPaint=1, then clears to 0
- Outer paint resumes with g_bSharedPaint=0 → buffer written to screen directly
- **Fix**: Use depth counter instead of boolean flag

### 7. FONT NOT SAVED/RESTORED ON g_hPaintBuf (LOW)
- PaintText selects pWVT->hFont onto s_hdcPaintBuf
- ScanAndDrawUda draws UDA (uses temp DCs for glyph rendering, not g_hPaintBuf)
- Font remains selected on g_hPaintBuf after ScanAndDrawUda
- Not a functional problem (BitBlt doesn't care about font) but state leak

### 8. BeginPaint RETURN NOT CHECKED (LOW)
- If BeginPaint fails, ps.rcPaint contains garbage
- Subsequent BitBlt with garbage coords → undefined behavior
- Rare but no validation

### 9. DC CHAIN COMPATIBILITY (LOW)
- Multiple CreateCompatibleDC chains: screen → s_hdcPaintBuf → s_hdcMemScan
- All should be compatible (same color depth) since created from screen DC
- But if screen DC changes (display mode change), cached DCs become incompatible
- **Fix**: Detect display mode change, recreate all cached DCs

### 10. ScanAndDrawUda GetDC STILL NEEDED (DESIGN)
- Even in v4, ScanAndDrawUda calls GetDC(hWnd)
- Only used for: CreateCompatibleDC for glyph workspace, SetStretchBltMode
- Could be replaced with g_hPaintBuf for DC compatibility source
- But GetDC also used for final BitBlt writeback (screen write)
- **Cannot eliminate** — still needed for screen write
