# Double Buffer Architecture Research v11
## Focus: v5 可行性 — PaintText 跳過 screen capture

---

## 目標

消除 PaintText 中的 `BitBlt(s_hdcPaintBuf ← hdcScreen)`。
這是穩定狀態下的冗餘操作（v3 分析：自己抄自己）。

## 前提條件

跳過 capture 的前提：s_hdcPaintBuf 已有有效內容（上一幀的 ASCII + UDA）。

### 何時 buffer 有效？
1. ✅ 上一幀成功完成 v4 流程（PaintText + ScanAndDrawUda + writeback）
2. ❌ 首次啟動（buffer 剛建立，全黑）
3. ❌ Resize 後（buffer 重建，全黑）
4. ❓ Alt+Tab 回來（螢幕可能被其他視窗覆蓋，但 buffer 仍有效！）
5. ❓ 其他視窗覆蓋後移開（同上，buffer 有效）
6. ❓ WM_DISPLAYCHANGE（螢幕解析度/色深變化，DC 可能失效）

### 關鍵洞察

**Alt+Tab 和視窗覆蓋不影響 buffer！**

因為 buffer 是 memory DC（off-screen），不受螢幕覆蓋影響。
Windows 送 WM_PAINT 時，PaintText 只需從 buffer（有效內容）重繪到螢幕。
**不需要 capture！**

唯一需要 capture 的情境：
1. **Buffer 剛建立**（首次或 resize 後）
2. **WM_DISPLAYCHANGE**（色深變化，需重建所有 DC）
3. **Hook 剛啟用**（buffer 可能有 hook 前的舊內容）

## 實作設計

```c
// In usrfont_c.c:
int g_bBufferValid = 0;  /* 1 = buffer has valid content from last successful paint */

// In gtwvt.c PaintText, before capture:
extern int g_bBufferValid;
extern int g_bSharedPaint;

if( g_bSharedPaint && g_bBufferValid )
{
   /* v5: skip capture — buffer already has valid content from last frame.
    * PaintText will overwrite dirty cells with new ASCII.
    * Non-dirty cells retain correct content from last frame. */
   hdc = s_hdcPaintBuf;
   bUseMemDC = 2;
}
else if( g_bSharedPaint )
{
   /* Shared mode but buffer not valid — need initial capture */
   BitBlt( s_hdcPaintBuf ← hdcScreen );  /* capture screen → buffer */
   hdc = s_hdcPaintBuf;
   bUseMemDC = 2;
}
else
{
   /* Original path */
   ...
}

// After successful writeback in ScanAndDrawUda:
extern int g_bBufferValid;
if( g_hPaintBuf )
   g_bBufferValid = 1;  /* buffer is now valid for next frame */

// On resize (buffer recreated):
g_bBufferValid = 0;

// On WM_DISPLAYCHANGE:
g_bBufferValid = 0;
// Also recreate all cached DCs
```

## 風險分析

| 情境 | capture 需要？ | g_bBufferValid | 安全？ |
|------|---------------|----------------|--------|
| 首次啟動 | ✅ 需要 | 0（初始） | ✅ 安全 |
| 正常操作 | ❌ 不需 | 1 | ✅ 安全 |
| Resize | ✅ 需要 | 0（重設） | ✅ 安全 |
| Alt+Tab | ❌ 不需 | 1（buffer 有效） | ✅ 安全 |
| 視窗覆蓋 | ❌ 不需 | 1（buffer 有效） | ✅ 安全 |
| WM_DISPLAYCHANGE | ✅ 需要 | 0（重設） | ✅ 安全 |
| Hook 剛啟用 | ✅ 需要 | 0（初始） | ✅ 安全 |
| DWM composition 變化 | ❓ | 1 | ⚠️ 可能需要重設 |

## 效能收益

- 每次 WM_PAINT 省一次 dirty-region BitBlt（約 0.1-0.5ms）
- 在快速輸入/捲動時（每秒 30+ WM_PAINT），累計可觀

## 需要注意的邊界情境

### 1. ps.rcPaint 只覆蓋部分 row
- PaintText 只重繪 ps.rcPaint 內的 rows
- 跳過 capture 時，buffer 保留上一幀在這些 rows 的內容
- 如果這些 rows 的內容沒變 → 正確（buffer 有效內容）
- 如果這些 rows 的 Harbour 螢幕 buffer 已變 → PaintText TextOut 會覆蓋 → 正確
- **安全**

### 2. Margin 區域
- Maximized 時有 margin（MarginLeft/Top > 0）
- PaintText 的 FillRect 填充 margin → 畫到 buffer
- 跳過 capture 時，buffer 的 margin 保留上一幀的填充 → 正確（margin 不變）
- **安全**

### 3. 色彩主題變化
- 使用者切換色彩主題 → 所有 cell 的 color 改變
- Harbour 標記所有 cell dirty → ps.rcPaint = 全螢幕
- PaintText 重繪所有 rows → buffer 完全覆蓋
- **安全**（但 capture 也能處理，差異不大）

## 結論

**v5 跳過 capture 是安全的。** 用 `g_bBufferValid` flag 追蹤 buffer 有效性，在首次/resize/displaychange 時重設。穩定操作時省一次 BitBlt。

## 實作優先順序

1. ✅ v4.1 — 跳過 s_hdcDblBuf 建立（已完成）
2. ✅ v4.3 — SetStretchBltMode on s_hdcPaintBuf（已完成）
3. 🔲 v5 — 跳過 capture（本報告分析完成，可實作）
4. 🔲 v6 — 理想架構（需修正 ShowCaret 時序，高風險）

## v5 實作步驟

1. usrfont_c.c: 新增 `int g_bBufferValid = 0`
2. gtwvt.c PaintText: `g_bSharedPaint && g_bBufferValid` 時跳過 capture
3. usrfont_scan.c: writeback 成功後設 `g_bBufferValid = 1`
4. gtwvt.c PaintText: buffer 重建時設 `g_bBufferValid = 0`
5. usrfont_wndproc.c: 考慮 WM_DISPLAYCHANGE 時重設（如果需要）

---

## 附錄：v4 早期返回 Bug（發現於測試中）

### 症狀
BUYSELL 表身區域顯示大片藍色，無資料內容

### 根因
ScanAndDrawUda 有兩個早期返回點（無 UDA 時 return）：
1. Line 86: 第一次 pre-scan（畫面無任何 UDA）
2. Line 191: 第二次 pre-scan（dirty rows 無 UDA）

在 v4 模式下，PaintText 已跳過螢幕寫入。如果 ScanAndDrawUda 早期返回，
**沒有任何東西寫螢幕** → 螢幕保持舊內容 → 大片藍色

### 修正
兩個早期返回點加入 writeback：
```c
if( g_hPaintBuf )
{
   BitBlt( hdc, 0, wbTop, rc.right, wbBot-wbTop,
           g_hPaintBuf, 0, wbTop, SRCCOPY );
}
```

### 教訓
**v4 模式下，PaintText 和 ScanAndDrawUda 形成責任鏈。
ScanAndDrawUda 是唯一的螢幕寫入者，任何 return 路徑都必須確保 writeback。**
