# Double Buffer Architecture Research v15
## Focus: v5 失敗分析 + v4 full 為最終穩定版

---

## v5 失敗原因

### 現象
離開銷貨單回主選單時，父選單閃爍一下。從深層子選單往上回時每層都閃。

### 根因
v5 跳過 screen capture 後，buffer 殘留上一個畫面的內容。
當畫面大範圍切換（RestScreen）時：
1. RestScreen 寫新內容到 Harbour screen buffer
2. WM_PAINT 觸發，ps.rcPaint 可能只覆蓋部分區域
3. PaintText 跳過 capture（g_bBufferValid=1）
4. TextOut 只覆蓋 ps.rcPaint 範圍的 cell
5. buffer 的其他區域仍是舊畫面內容
6. writeback 顯示混合的新+舊 → 閃爍

### 為什麼 Research v12/v13 沒發現
- 模擬假設 RestScreen 觸發全螢幕 dirty（全部 cell 都變）
- 實際上 Windows 可能分多次 WM_PAINT 處理
- 或 ps.rcPaint 只覆蓋部分行（先處理部分，再處理其餘）

### v5 的根本問題
跳過 capture 的前提是「buffer 內容 = 螢幕內容」。
但在畫面切換時，buffer 有舊畫面內容，螢幕有新畫面內容。
只靠 TextOut 覆蓋 dirty cell 不夠（dirty area 可能不完整）。

### 結論
v5 需要更精確的 g_bBufferValid 判斷，例如：
- 偵測 RestScreen/SaveScreen 操作 → reset g_bBufferValid
- 偵測全螢幕 dirty（ps.rcPaint 覆蓋 > 80% 客戶區）→ force capture
- 但這增加了複雜度，收益小（省一次 BitBlt）

**決定：v5 暫時不實作。v4 full 為最終穩定版。**

---

## v4 full 最終架構

```
WM_PAINT:
  WndProc: g_bSharedPaint = 1

  PaintText:
    BeginPaint → ps.rcPaint
    BitBlt(s_hdcPaintBuf ← screen, ps.rcPaint)  ← 每次都 capture（安全）
    TextOut → s_hdcPaintBuf
    跳過 BitBlt 到螢幕
    g_hPaintBuf = s_hdcPaintBuf
    EndPaint

  ScanAndDrawUda:
    if( g_hPaintBuf ):
      hdcDraw = g_hPaintBuf（直接畫 UDA）
      writeback: g_hPaintBuf → screen（唯一螢幕寫入）
    else:
      原始路徑（capture screen → s_hdcDblBuf → writeback）

  WndProc: g_bSharedPaint = 0

Buffer 數量：1 個使用（s_hdcPaintBuf），s_hdcDblBuf 不建立（v4.1）
螢幕寫入：1 次（ScanAndDrawUda writeback）
```

## 效能特性

| 操作 | 次數 | 說明 |
|------|------|------|
| screen → buffer (capture) | 1 | PaintText，dirty region only |
| TextOut → buffer | N | N = dirty rows 的 cell 數 |
| UDA draw → buffer | M | M = dirty rows 的 UDA cell 數 |
| buffer → screen (writeback) | 1 | ScanAndDrawUda，dirty rows only |
| **總螢幕操作** | **2** | 1 read + 1 write（vs 原始 4 次） |

## 未來優化方向（需更多研究）

1. **v5 smart capture**: 只在「大面積 dirty」時 capture，小面積時跳過
2. **v6 一個 buffer**: 修正 ShowCaret 循環（v8 發現），PaintText 直接畫到共用 buffer
3. **v7 最簡架構**: 去掉 s_hdcPaintBuf 和 s_hdcDblBuf，只有 g_hSharedBuf
