Diff Rendering
MonoTerm uses intelligent change detection to only render what actually changed.DiffHint: The Rendering Decision
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ DiffHint MODES │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ MonoTerm determines the optimal rendering mode for each update: │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │#########│ │.........│ │.........│ │ X X X X │ │
│ │#########│ │..##.....│ │.........│ │ X X X X │ │
│ │#########│ │.........│ │...._....│ │ X X X X │ │
│ │#########│ │.........│ │.........│ │ X X X X │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ Full Partial None Skip │
│ Render ALL Render SOME Cursor only Discard │
│ │
│ │
│ Full = Major screen changes (opening vim, running ls) │
│ Partial = Small changes (typing, line updates) │
│ None = Cursor only moved/blinked │
│ Skip = Invalid frame detected (glitch protection) │
│ │
└───────────────────────────────────────────────────────────────────────┘
Decision Flow
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ HOW DiffHint IS CHOSEN │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Start │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ First frame? │───── Yes ────────────────────────→ Full │
│ └────────┬─────────┘ │
│ │ No │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Size mismatch? │───── Yes ────────────────────────→ Full │
│ └────────┬─────────┘ │
│ │ No │
│ ▼ │
│ ┌──────────────────┐ │
│ │ History changed?│───── Yes ────────────────────────→ Full │
│ └────────┬─────────┘ │
│ │ No │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Glitch detected?│───── Yes ────────────────────────→ Skip │
│ └────────┬─────────┘ │
│ │ No │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Compare lines │ │
│ └────────┬─────────┘ │
│ │ │
│ ├── No changes ─────────────────────────────── None │
│ │ │
│ └── Some rows changed ──────────────────────── Partial │
│ │
└───────────────────────────────────────────────────────────────────────┘
Line Comparison: O(1) Speed
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ FAST LINE COMPARISON │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Each line has a precomputed signature for instant comparison: │
│ │
│ What's included in signature: │
│ * Character content │
│ * Foreground color │
│ * Background color │
│ * Text attributes (bold, underline, etc.) │
│ * Line wrap flag │
│ │
│ │
│ Comparison: O(1) instead of O(cols) │
│ │
│ │
│ Previous State: New State: │
│ │
│ Line 0: A ================ Line 0: A -> SAME │
│ Line 1: B ================ Line 1: B -> SAME │
│ Line 2: C =/=/=/=/=/=/=/= Line 2: D -> CHANGED (row 2) │
│ Line 3: E ================ Line 3: E -> SAME │
│ Line 4: F =/=/=/=/=/=/=/= Line 4: G -> CHANGED (row 4) │
│ │
│ Result: Partial { start_row: 2, end_row: 4 } │
│ │
├───────────────────────────────────────────────────────────────────────┤
│ PERFORMANCE GAIN │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Without fast comparison: │
│ 40 rows x 120 cols x 3 fields = 14,400 comparisons │
│ │
│ With fast comparison: │
│ 40 rows x 1 signature = 40 comparisons │
│ │
│ Speedup: 360x faster │
│ │
└───────────────────────────────────────────────────────────────────────┘
True Partial Mode
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ DATA TRANSMISSION OPTIMIZATION │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ BEFORE (Render Only Partial): │
│ ────────────────────────────── │
│ Backend: Partial detected (rows 5-10 changed) │
│ BUT sends ALL 1040 lines anyway │
│ Data: ~50KB │
│ Frontend: Creates new buffer (1080 lines) │
│ Copies ALL 1040 lines │
│ Refreshes only rows 5-10 │
│ │
│ │
│ AFTER (True Partial): │
│ ───────────────────── │
│ Backend: Partial { start_row: 5, end_row: 10 } │
│ Sends ONLY changed lines (6 lines) │
│ Data: ~3KB │
│ Frontend: Reuses existing buffer │
│ Updates only 6 lines │
│ Refreshes only rows 5-10 │
│ │
│ │
│ RESULTS: │
│ │
│ Data reduction: 99.95% (50KB -> 0.5KB) │
│ Memory allocation: 0 (reused) │
│ GC pressure: eliminated │
│ │
└───────────────────────────────────────────────────────────────────────┘
Rendering Dispatch
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ RENDERING BASED ON DIFFHINT │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Full │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ refreshRows(0, total_rows - 1) │ │
│ │ │ │
│ │ ┌─────────┐ │ │
│ │ │#########│ All rows rendered │ │
│ │ │#########│ │ │
│ │ │#########│ │ │
│ │ └─────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ Partial { start_row: 2, end_row: 4 } │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ refreshRows(start_row, end_row) │ │
│ │ │ │
│ │ ┌─────────┐ │ │
│ │ │.........│ rows 0-1: skip │ │
│ │ │#########│ rows 2-4: render (the changed range) │ │
│ │ │#########│ │ │
│ │ │.........│ rows 5+: skip │ │
│ │ └─────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ None │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ // No refresh - cursor position only │ │
│ │ │ │
│ │ ┌─────────┐ │ │
│ │ │.........│ Nothing rendered │ │
│ │ │...._....│ (cursor update only) │ │
│ │ │.........│ │ │
│ │ └─────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ Skip │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ return false; // Don't apply, don't ACK │ │
│ │ │ │
│ │ ┌─────────┐ │ │
│ │ │ X X X X │ Frame discarded │ │
│ │ │ X X X X │ Wait for valid frame │ │
│ │ │ X X X X │ │ │
│ │ └─────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
Skip Mechanism: Glitch Protection
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ THE PROBLEM: Oscillation Glitches │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ VTE sometimes produces inconsistent frames during rapid updates: │
│ │
│ Frame 1: ybase=100 (correct) │
│ Frame 2: ybase=0 (WRONG - glitch!) │
│ Frame 3: ybase=100 (correct again) │
│ │
│ If Frame 2 is applied: │
│ * All scrollback appears lost │
│ * Scroll position jumps to top │
│ * User sees flicker/glitch │
│ │
├───────────────────────────────────────────────────────────────────────┤
│ THE SOLUTION: Skip Detection │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Logic: │
│ IF history suddenly drops to 0 │
│ AND we had history before │
│ THEN this is likely a VTE glitch │
│ │
│ -> Skip the frame, don't apply it │
│ -> Wait for next valid frame │
│ │
└───────────────────────────────────────────────────────────────────────┘
Hint Comparison
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ DIFFHINT COMPARISON │
├───────────────────────────────────────────────────────────────────────┤
│ │ Full │ Partial │ None │ Skip │
│───────────────┼─────────────┼─────────────┼─────────────┼─────────────│
│ Data valid? │ Yes │ Yes │ Yes │ NO (corrupt)│
│ Apply frame? │ Yes │ Yes │ Yes │ NO │
│ Send ACK? │ Yes │ Yes │ Yes │ NO │
│ Render? │ All rows │ Dirty rows │ Nothing │ Nothing │
│ Reuse memory? │ No │ Yes │ Yes │ Keep prev │
└───────────────────────────────────────────────────────────────────────┘
Performance Summary
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ SCENARIO: H=1000, V=40, typing one character │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Without optimization: │
│ Lines sent: 1040 (all H+V) │
│ Data: ~50KB per keystroke │
│ │
│ With optimization: │
│ Lines sent: 1 (dirty only) │
│ Data: ~0.05KB per keystroke │
│ │
│ REDUCTION: 99.9% │
│ │
├───────────────────────────────────────────────────────────────────────┤
│ MODE COMPARISON │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Mode │ Lines Sent │ IPC Data │ Reuse Memory │ GC Pressure│
│───────────────┼─────────────┼─────────────┼──────────────┼────────────│
│ Full │ H + V │ ~50KB │ No │ High │
│ Partial │ dirty only │ ~0.5KB │ Yes │ None │
│ None │ 0 │ ~0.1KB │ Yes │ None │
│ │
└───────────────────────────────────────────────────────────────────────┘
Typical Use Cases
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ USE CASE ANALYSIS │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ Typing in shell: │
│ DiffHint: Partial { start_row: cursor_y, end_row: cursor_y } │
│ Data: ~0.05KB │
│ Render: 2.5% of screen │
│ │
│ Running `ls`: │
│ DiffHint: Full (new output) │
│ Data: ~50KB │
│ Render: 100% │
│ │
│ Cursor blink: │
│ DiffHint: None │
│ Data: ~0.1KB │
│ Render: 0% │
│ │
│ vim scroll: │
│ DiffHint: Full (rapid content change) │
│ Data: ~50KB │
│ Render: 100% │
│ │
└───────────────────────────────────────────────────────────────────────┘
Summary
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ DIFF RENDERING SUMMARY │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ [OK] Smart Detection: │
│ - O(1) line comparison │
│ - 360x faster than cell-by-cell │
│ │
│ [OK] True Partial Mode: │
│ - Only dirty lines transmitted │
│ - 99.95% data reduction │
│ │
│ [OK] Memory Reuse: │
│ - No allocation in Partial/None mode │
│ - Zero GC pressure │
│ │
│ [OK] Glitch Protection: │
│ - Skip detects invalid frames │
│ - Prevents visual artifacts │
│ │
└───────────────────────────────────────────────────────────────────────┘
DiffHint System
The DiffHint enum (Full, Partial, None, Skip) enables intelligent rendering decisions. Built in Rust for speed, the system achieves O(1) line comparison and 99.95% data reduction.