Skip to main content

Diff Rendering

MonoTerm uses intelligent change detection to only render what actually changed.

DiffHint: The Rendering Decision

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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

┌───────────────────────────────────────────────────────────────────────┐
│  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.