# Double Buffer Architecture Research v9
## Focus: 理想架構 vs 現實限制

---

## 理想架構（一個 buffer）

```
WM_PAINT:
  g_hSharedBuf = ensure_buffer(clientW, clientH)
  BeginPaint(ps)
  
  // ASCII: 直接畫到 g_hSharedBuf
  SelectObject(g_hSharedBuf, hFont)
  for each dirty row:
    ExtTextOutW(g_hSharedBuf, ...)
  
  // UDA: 直接疊加到 g_hSharedBuf
  for each UDA cell in dirty rows:
    DrawGlyph(g_hSharedBuf, ...)
  
  // 一次寫螢幕
  hdcScr = GetDC(hWnd)
  BitBlt(hdcScr ← g_hSharedBuf, dirty_region)
  ReleaseDC
  
  // Caret
  EndPaint(ps)  ← 在 BitBlt 之後？但 EndPaint 應在 BeginPaint 配對...
```

## 為什麼理想架構無法直接實現

### 限制 1：BeginPaint/EndPaint 必須配對
- BeginPaint 取得 ps.rcPaint（dirty rect）
- EndPaint 驗證 update region
- BeginPaint 和 EndPaint 之間 **不能** 做 GetDC（不同的 DC 類型衝突）
- **但**：目前的 PaintText 已經在 BeginPaint/EndPaint 之間做 GetDC，沒出問題
- **結論：這不是真正的限制**

### 限制 2：PaintText 是 GTWVT 核心代碼
- PaintText 是 Harbour 的 GTWVT contrib
- 它有自己的 row/column 迭代邏輯、font 選擇、色彩處理
- 要讓它畫到外部 buffer，需要替換 hdc 變數
- v1/v2 嘗試過（hdc = g_hPaintDC），但造成「無法輸入」
- v8 分析找到根因：ShowCaret 循環，不是 hdc 替換的問題
- **結論：hdc 替換本身是安全的，v1/v2 失敗是 caret 循環**

### 限制 3：ScanAndDrawUda 需要 screen DC
- CreateCompatibleDC 需要參考 DC
- 可以用 g_hSharedBuf 代替 screen DC？
  - CreateCompatibleDC(g_hSharedBuf) → 建立兼容的 memory DC → 應該可以
  - CreateDIBSection(g_hSharedBuf, ...) → 建立 DIB → 應該可以
- **但**：writeback BitBlt 仍需要 screen DC
- **結論：至少需要一次 GetDC 做 writeback**

### 限制 4：Margin 填充在 PaintText 內部
- Maximized 時 MarginLeft/Top > 0
- PaintText 用 FillRect 填充 margin
- 這些 FillRect 必須畫到 buffer（不是螢幕）
- v1/v2 的 hdc = g_hPaintDC 時，FillRect 正確畫到 buffer
- **結論：不是限制**

## 可行的理想架構實現

基於 v8 分析（v1/v2 失敗是 ShowCaret 循環），修正後的 v1 應該可行：

```
v1-fixed WndProc:
  s_bInsidePaint = 1
  g_hPaintDC = g_hSharedBuf  
  
  CallWindowProcW → PaintText:
    hdc = g_hPaintDC（畫到 shared buffer）
    跳過 s_hdcPaintBuf（不需要自己的 buffer）
    跳過 final BitBlt
  
  ScanAndDrawUda:
    hdcDraw = g_hPaintDC（直接畫到 shared buffer）
    跳過 capture、跳過 writeback、跳過 caret
  
  // Caret 在 s_bInsidePaint=1 之內做（防止 ShowCaret 循環）
  hdcScr = GetDC(hWnd)
  BitBlt(hdcScr ← g_hSharedBuf, dirty_region)
  ReleaseDC
  HideCaret ← 之前
  DestroyCaret + CreateCaret + SetCaretPos
  ShowCaret ← InvalidateRect 觸發 WM_PAINT，但 s_bInsidePaint=1 擋住
  
  g_hPaintDC = NULL
  s_bInsidePaint = 0  ← 此時 ShowCaret 的 WM_PAINT 才被處理
```

## 結論

**理想架構是可行的。** v1/v2 失敗的根因是 ShowCaret→InvalidateRect→WM_PAINT 循環，不是架構問題。修正 caret 操作的時序（在 s_bInsidePaint=1 內完成）就能解決。

**但 v4 已經足夠好。** 理想架構的額外收益是省一個 buffer-to-buffer 複製，效能提升 < 1ms。架構簡化的收益是去掉 s_hdcDblBuf（省 3MB 記憶體 + 少一層間接）。
