# GTWVT 底層流暢化計畫（v2 — 兩版 gtwvt.c 完整比對後定案）

> 版本：v2（2026-08-10 依兄弟版實碼比對重寫，取代 v1 推測方案）
> 狀態：**計畫定案，待同意後動工**
> 我方正式版：`E:\ConvertFW\HBSTK\gtwvt_patch\gtwvt.c`（6242 行，已合併 usrfont，安裝為 gtwvt.lib）
> 兄弟版：`E:\ConvertFW\HBSTK\TMP\gtwvt_patch\gtwvt_patch\gtwvt.c`（5590 行，STO87C 系列）

---

## 一、訴求

1. GTWVT 不如 HyperDos 流暢，根因＝每個 `@say` 都觸發同步 WM_PAINT（同步重繪稅）。
2. 模組要開放給**不會用 AI 的同業**：底層處理好 → 同業不必逐支優化 PRG 就流暢，
   否則「叫好不叫座」。
3. 兄弟已實作並除錯完成此優化（含 caret 消失坑），**直接移植他的解法，不重造輪子**。

---

## 二、兩版 gtwvt.c 比對結論（實際 diff 過）

### 2.1 兩版已嚴重分岔，禁止整檔覆蓋
| | 我方正式版 | 兄弟版 |
|---|---|---|
| 行數 | 6242 | 5590 |
| diff | 獨有 1647 行 | 獨有 995 行 |
| usrfont 合併（g_bHookPaint/ScanAndDrawUda） | ✅ 4 處 | ❌ 無（不同支系）|
| STO87C 標記 | 0 | 24 處 |

**我方獨有**（覆蓋會全毀）：usrfont hook、剪貼簿影像貼上（ClipboardImageToFile/Paste/OCR 鏈）、
WriteConChar、HandleWideChar、自訂 GTI（InfoCustom/InfoFont）、系統選單√X、選取反白、
DefineBoxChar 等。
**兄弟獨有**：★FLUSHDELAY 節流（本計畫目標）、框線伴隨字型（HB_GTI_BOXFACENAME 204、
W5A_box.ttf）、IME 組字視窗跟隨 caret（SetImeCompositionPos）、AddWideCharToInputQueue。

→ **結論：只精準移植 FLUSHDELAY 一項**。框線字型與 IME 定位是另外兩個獨立題目，
本次不動（列入未來候選）。

### 2.2 兄弟的節流設計（STO87C 2026-08-07，已驗證實碼）
比我 v1 計畫的「Redraw 累積 dirty region」**更聰明**：

```
HB_GTSUPER_REFRESH 照常跑（RedrawDiff → InvalidateRect 照常標記 update region）
     ↓
Refresh 末端的「pump」(WM_MY_UPDATE_CARET + ProcessMessages) 才節流：
   iFlushDelay==0        → 每次 pump（＝原行為，一 byte 不差）
   距上次 pump < 延遲值  → 跳過 pump
     ↓
InvalidateRect 只「標記」不畫；不 pump 期間 Windows 原生 update region 自動累積合併
     ↓
下一次 pump 一次畫出合併區塊 → 畫面「整塊出現」而非逐欄爬出來
```

- 不需自己 UnionRect——**讓 Windows 幫你累積**，程式碼極小。
- `GetTickCount()` 用 unsigned 減法，49.7 天 wrap 天生安全。
- 設定介面＝`hb_gtInfo(HB_GTI_FLUSHDELAY=203, ms)`，0~500 夾限。
- **Bonus**：對 203 賦值會「強制 pump 一次」→ PRG 長運算前可把「請稍候」訊息
  保證刷上畫面（兄弟案例：UY_PACK DBFEncrypt、uy_save SetOutput 迴圈）。

### 2.3 ★caret 消失坑——兄弟踩過、解法已在碼裡（照抄）
他的註解白紙黑字：節流後「GET 的 SetPos 之後那次 Refresh 被跳過 → 沒人告訴視窗
caret 去哪 → GET/READ 游標消失」。

**他的雙保險（battle-tested）**：
1. **ReadKey 無條件 pump**（程式即將等待輸入的點，畫面與 caret 必須正確）＋
   pump 後 `dwLastPump = GetTickCount()` 重啟節流窗（避免 Refresh 馬上重複 pump）。
2. Refresh 的 caret 更新**故意放在 pump 分支內**：沒 pump 就不動 caret，
   避免游標停在「還沒畫」的空白/舊字上。

修正 v1 的過度絕對條款：不是「ProcessMessages 絕不能節流」，正確版是——
> **輸出路徑（Refresh）的 pump 可節流；輸入路徑（ReadKey）無條件 pump，
> 鍵盤與 caret 就永遠安全。**

### 2.4 我方版移植相容性檢查（全部通過）
| 檢查項 | 結果 |
|---|---|
| GTI id 203 衝突？ | ✅ 我方自訂用 202/210/211/213/214，**203 空著**，沿用兄弟 id |
| pump 呼叫點 | ✅ 我方全檔僅 2 點：ReadKey(4634)、Refresh(5836) — 移植面極小 |
| ReadKey 現況 | ✅ 本來就無條件 pump ＋ **直呼** UpdateCaret ＋ s_bCaretReady 記錄（比兄弟經 message 更直接）→ 只需補 `dwLastPump` 重啟一行 |
| usrfont paint 路徑（我方獨有，兄弟沒測過的面）| pump 變少 → WM_PAINT 變少 → ScanAndDrawUda（CJK 貴）跑更少次＝**加分**；paint 內 BitBlt+ShowCaret 少跑，但 ReadKey 直呼 UpdateCaret 無條件補上 → caret 有獨立命脈 |
| caret Hide/Show 計數（舊坑 feedback-caret-hidecount-imbalance）| paint 次數減少＝失衡機會減少，成對修正已在我方碼內，不受影響 |
| 兄弟欄位初始化 | alloc 歸零預設 0；我方移植時**顯式初始化**再加一道保險 |

---

## 三、移植清單（動工時的確切落點）

改動只有 5 小塊（C 面 4 塊 + PRG 面 1 塊）：

1. **gtwvt.h**（我方 `gtwvt_patch\gtwvt.h`，struct _HB_GTWVT）：
   加 `int iFlushDelay;` + `DWORD dwLastPump;`（連同兄弟的說明註解一起搬）。
2. **gtwvt.c 初始化區**（約 448 行 `pWVT->CaretExist = HB_FALSE;` 附近）：
   `pWVT->iFlushDelay = 0; pWVT->dwLastPump = 0;`（顯式，預設關）。
3. **gtwvt.c `hb_gt_wvt_Refresh`**（5819，pump 在 5834-5836）：
   包上節流分支（fPump 判斷，照抄兄弟 5278-5310 段含註解）。
4. **gtwvt.c `hb_gt_wvt_ReadKey`**（4634 pump 之後）：
   加 `pWVT->dwLastPump = GetTickCount();` 一行（無條件 pump 本來就有，不動）。
   ※ 我方 UpdateCaret 直呼與 s_bCaretReady 段**原樣保留**。
5. **gtwvt.c Info 函式**：加 `case 203`（HB_GTI_FLUSHDELAY，0~500 夾限、
   賦值強制 pump 一次，照抄兄弟 4405-4433）。

之後重編：`gtwvt_patch\build_gtwvt.bat` → 安裝 gtwvt.lib → 重編 STK32。

---

## 四、設定與止血（System.ini）

### Key 設計（單一 key 即可，兄弟語意天生含開關）
| Key | 值 | 意義 |
|---|---|---|
| `[GTWVT] FlushDelay` | `0`(預設)~`500` 毫秒 | **0＝完全原行為（止血）**；建議起始實驗值 16(≈60fps)/33(≈30fps) |

放 **System.ini**（非 stk.ini）：各站顯卡/RDP 環境不同（per-station）、同業用
INI_Editor GUI 就能自己止血、顯示類設定慣例都在這。

### 傳遞路徑（C 讀不到 PRG 的 ini）
PRG 開機讀 System.ini → `hb_gtInfo( 203, nVal )` 推給 driver。
配合 idle 簽章比對重讀（比照列印預設鈕 5 秒那套）→ **不重開 STK 就能止血/調參**。

### 止血為什麼乾淨
`FlushDelay=0` 時 `if( pWVT->iFlushDelay > 0 )` 不成立 → **根本不進新分支**，
執行路徑與未改版完全相同；caret/鍵盤/usrfont 路徑一 byte 未動。
三層保險：① ini 設 0（同業自己會）② idle 重讀免重開 ③ 極端狀況重編純淨 lib。

### INI_Editor 三處同步（鐵律）
`ini_editor.py` INI_META ＋ `ini_template.json` ×3（build/dist/update_pack）。
做成下拉或數值框＋中文說明：「畫面流暢化延遲(毫秒)，0=關閉(原行為)，異常時設 0」。

---

## 五、測試策略（解決「STK32 已優化、找不到測試環境」）

1. **DBF.PRG 索引資料測試**（使用者準備中）：索引 browse/SEEK/SKIP 密集重繪
   ＋中文全形寬度變化（CreateCaret 重建）＝最會逼出 caret 坑的場景，
   同時是流暢度 demo。
2. **benchmark PRG**（故意寫成未優化樣式）：純 `@say` 流水＋GET＋大迴圈，
   模擬同業畫面；FlushDelay 0 vs 33 對照展示。
3. **已優化 STK32 當無回歸基準**：跑遍所有畫面，0 vs 33 逐畫面比對，
   caret/鍵盤/顯示完全一致即過關。
4. **量化**：暫時加 WM_PAINT 計數 log，同一操作比實際重繪次數，數字說話。

---

## 六、驗收項

### caret / 輸入（最高優先）
- [ ] GET/READ 游標不消失（兄弟踩過的原坑，ReadKey 無條件 pump 應已擋掉）。
- [ ] 中文輸入游標不消失；全形/半形切換游標尺寸正確。
- [ ] browse 上下移動游標跟手；長迴圈大量顯示後仍可輸入。
- [ ] `FlushDelay=0` 行為與未改版 100% 一致。

### 顯示正確性
- [ ] 無殘影、無漏畫（ReadKey 無條件 pump＝等待輸入前必為最新畫面）。
- [ ] usrfont 路徑（華康字、選取反白、剪貼簿貼上）全部正常——**這是兄弟版
      沒有的面，是我們唯一要自己驗的新組合**。
- [ ] 系統選單√X、滑鼠游標、視窗拖曳等我方獨有功能不受影響。

### 效能
- [ ] benchmark：FlushDelay=33 的 WM_PAINT 次數明顯下降、畫面整塊出現。
- [ ] 已優化畫面無負面（不變慢、不閃爍）。

---

## 七、回公司要拍板的點

1. FlushDelay 預設值：先 0（保守、逐站開）還是直接 16/33（產品化給同業就流暢）？
   → 建議：我方各站先 0 實測一輪，穩了改預設 33 出貨給同業。
2. 「請稍候」強制刷新（賦值 203 強制 pump）要不要在我方長運算 PRG 套用
   （對應兄弟的 UY_PACK/uy_save 案例）。
3. 兄弟版另兩項（框線伴隨字型 204、IME 組字視窗跟隨 caret）要不要排後續移植。
4. idle 重讀 System.ini 的實作點（掛在哪個 idle 迴圈）。

---

## 八、動工步驟（同意後執行）

1. 備份 `gtwvt.c` / `gtwvt.h`（`.bak_flushdelay_日期_時間`）。
2. 依第三節清單 5 小塊移植（C 註解照搬兄弟原文＋補繁中說明）。
3. `build_gtwvt.bat` 重編 lib → 重編 STK32。
4. PRG 開機讀 `[GTWVT] FlushDelay` → hb_gtInfo(203)；idle 重讀。
5. INI_Editor + template ×3 加 key。
6. 跑第五節測試（DBF.PRG 索引測試 + benchmark + 全畫面回歸）。
7. 全綠 → 功能報告 PDF、UpdateList、Memory；使用者確認後刪 .bak。

---

*依 CLAUDE.md：實際動 gtwvt.c 前仍會逐塊說明並等待同意。*
