@@ -67,6 +67,193 @@ Additionally, **fontification runs DURING layout**. `jit-lock` calls `fontificat
6767** Category E — Hooks that expect synchronous state:**
6868` pre-redisplay-function ` (runs INSIDE ` redisplay_internal() ` ), ` fontification-functions ` (runs DURING iterator scanning), ` window-scroll-functions ` , ` post-command-hook `
6969
70+ ## Display Engine API Contract
71+
72+ The display engine sits between the Emacs C core and the rendering backend. Understanding exactly what flows in each direction is critical — the Rust replacement must satisfy this contract.
73+
74+ ```
75+ EMACS C CORE PROVIDES DISPLAY ENGINE PROVIDES BACK
76+ ═══════════════════ ═══════════════════════════
77+
78+ Buffer text (gap buffer) ──────┐ ┌──── w->window_end_pos/vpos/valid
79+ Text properties (face, │ │ w->cursor (hpos, vpos, x, y)
80+ display, invisible, │ │ w->phys_cursor (+ width/height)
81+ fontified, composition) ─────┤ │ w->mode_line_height
82+ Overlays (face, before/after │ │ /header_line_height
83+ string, invisible, │ ┌────┴──┐ /tab_line_height
84+ priority, window) ───────────┼───►│DISPLAY│───►w->current_matrix (glyph rows)
85+ Face cache (colors, fonts, │ │ENGINE │ windows_or_buffers_changed
86+ decorations) ────────────────┤ └────┬──┘ f->cursor_type_changed
87+ Window state (start, hscroll, │ │
88+ pointm, dimensions) ─────────┤ ├──── pos-visible-in-window-p()
89+ Buffer-local vars (truncate, │ │ window-end()
90+ word-wrap, tab-width, │ │ vertical-motion()
91+ bidi, line-spacing) ─────────┤ │ move-to-column()
92+ Global vars (scroll-margin, │ │ compute-motion()
93+ scroll-step, display- │ │ posn-at-point/x-y()
94+ line-numbers) ───────────────┘ │ current-column()
95+ │ line-pixel-height()
96+ fontification-functions ◄────────────────┘ format-mode-line()
97+ (callback DURING layout) (Lisp query functions)
98+ ```
99+
100+ ### Inputs: What Emacs C Core Provides to the Display Engine
101+
102+ #### Buffer Data
103+
104+ | Input | How accessed | Purpose |
105+ | -------| -------------| ---------|
106+ | Buffer text | ` BYTE_POS_ADDR ` , ` FETCH_MULTIBYTE_CHAR ` | Raw characters to display |
107+ | Narrowing bounds | ` BEGV ` , ` ZV ` | Visible region of buffer |
108+ | Point position | ` PT ` | Where cursor goes |
109+ | Multibyte flag | ` BVAR(buf, enable_multibyte_characters) ` | Character encoding mode |
110+
111+ #### Text Properties (at each position)
112+
113+ | Property | Purpose |
114+ | ----------| ---------|
115+ | ` face ` | Text styling (colors, bold, italic, underline, etc.) |
116+ | ` display ` | Override rendering (images, strings, spaces, margins, fringes) |
117+ | ` invisible ` | Hide text or show ellipsis |
118+ | ` fontified ` | Triggers lazy fontification via ` fontification-functions ` |
119+ | ` composition ` | Ligatures, combining characters, emoji sequences |
120+ | ` mouse-face ` | Hover highlighting |
121+ | ` line-height ` | Override line height |
122+ | ` raise ` | Vertical offset |
123+
124+ #### Overlays (at each position)
125+
126+ | Property | Purpose |
127+ | ----------| ---------|
128+ | ` face ` | Overlay face (merged with text property face, priority-ordered) |
129+ | ` display ` | Display overrides |
130+ | ` before-string ` / ` after-string ` | Inserted strings with their own properties |
131+ | ` invisible ` | Hide overlay region |
132+ | ` priority ` | Stacking order for face merging and string ordering |
133+ | ` window ` | Per-window overlay filtering |
134+
135+ #### Face Data
136+
137+ | Input | Purpose |
138+ | -------| ---------|
139+ | ` face_at_buffer_position() ` | Merged face at position (text prop + all overlays) |
140+ | ` FACE_FROM_ID(f, id) ` | Resolve face ID to colors, font, decorations |
141+ | ` face_for_char() ` | Font fallback for specific character (fontset system) |
142+ | ` merge_faces() ` | Combine control-char face with base face |
143+
144+ #### Window State
145+
146+ | Field | Purpose |
147+ | -------| ---------|
148+ | ` w->start ` | First visible buffer position (marker) |
149+ | ` w->pointm ` | Window's copy of point (for non-selected windows) |
150+ | ` w->hscroll ` , ` w->min_hscroll ` | Horizontal scroll offset |
151+ | ` WINDOW_PIXEL_WIDTH/HEIGHT(w) ` | Window dimensions in pixels |
152+ | ` w->contents ` | Which buffer is displayed |
153+ | Display table | ` window_display_table(w) ` — character display overrides |
154+
155+ #### Buffer-Local Variables
156+
157+ | Variable | Purpose |
158+ | ----------| ---------|
159+ | ` truncate-lines ` | Truncate long lines vs wrap |
160+ | ` word-wrap ` | Word wrapping mode |
161+ | ` tab-width ` | Tab character width in columns |
162+ | ` line-spacing ` / ` extra-line-spacing ` | Extra vertical space between lines |
163+ | ` selective-display ` | Hide lines by indentation level |
164+ | ` ctl-arrow ` | Display control chars as ` ^X ` vs ` \NNN ` |
165+ | ` bidi-display-reordering ` | Enable bidirectional text reordering |
166+ | ` bidi-paragraph-direction ` | Force paragraph direction (left-to-right / right-to-left) |
167+
168+ #### Global Variables
169+
170+ | Variable | Purpose |
171+ | ----------| ---------|
172+ | ` scroll-margin ` | Lines to keep visible around point |
173+ | ` scroll-conservatively ` | How aggressively to scroll |
174+ | ` scroll-step ` | Lines to scroll at a time |
175+ | ` hscroll-margin ` / ` hscroll-step ` | Horizontal scroll parameters |
176+ | ` truncate-partial-width-windows ` | Truncate in narrow windows |
177+ | ` display-line-numbers ` | Line number display mode (absolute/relative/visual) |
178+ | ` display-line-numbers-width ` | Width of line number column |
179+ | ` nobreak-char-display ` | Display non-breaking spaces/hyphens |
180+ | ` auto-composition-mode ` | Enable automatic character composition |
181+ | ` maximum-scroll-margin ` | Cap on scroll margin |
182+
183+ ### Outputs: What the Display Engine Must Provide Back
184+
185+ ** This is the contract the Rust replacement must satisfy.**
186+
187+ #### Window Fields (set during redisplay, read by Emacs)
188+
189+ | Field | Type | Who reads it | Purpose |
190+ | -------| ------| -------------| ---------|
191+ | ` w->window_end_pos ` | ` ptrdiff_t ` | ` window-end ` (window.c) | Buffer position of last visible char (as ` Z - end_charpos ` ) |
192+ | ` w->window_end_vpos ` | ` int ` | window.c | Matrix row number of last visible char |
193+ | ` w->window_end_valid ` | ` bool ` | ` window-end ` , ` window-line-height ` | Guard: are end fields valid? Many functions check this first. |
194+ | ` w->cursor ` | ` {x, y, hpos, vpos} ` | Cursor drawing, movement commands | Intended cursor position in matrix coordinates |
195+ | ` w->phys_cursor ` | ` {x, y, hpos, vpos} ` | neomacsterm.c, window.c | Actual cursor pixel position (window-relative) |
196+ | ` w->phys_cursor_type ` | ` enum ` | window.c | Current cursor style: box(0), bar(1), hbar(2), hollow(3) |
197+ | ` w->phys_cursor_width ` | ` int ` | neomacsterm.c, window.c | Cursor width in pixels |
198+ | ` w->phys_cursor_height ` | ` int ` | neomacsterm.c, window.c | Cursor height in pixels |
199+ | ` w->phys_cursor_ascent ` | ` int ` | window.c | Cursor ascent in pixels |
200+ | ` w->phys_cursor_on_p ` | ` bool ` | window.c, dispnew.c | Is cursor currently being displayed? |
201+ | ` w->mode_line_height ` | ` int ` | ` window-mode-line-height ` , layout | Mode-line pixel height (-1 if unknown) |
202+ | ` w->header_line_height ` | ` int ` | ` window-header-line-height ` | Header-line pixel height (-1 if unknown) |
203+ | ` w->tab_line_height ` | ` int ` | ` window-tab-line-height ` | Tab-line pixel height (-1 if unknown) |
204+ | ` w->last_cursor_vpos ` | ` int ` | xdisp.c | Previous frame's cursor vpos (detects cursor movement) |
205+
206+ #### Frame Fields
207+
208+ | Field | Purpose |
209+ | -------| ---------|
210+ | ` f->cursor_type_changed ` | Signals cursor needs redraw (set true on change, cleared after draw) |
211+ | ` f->garbaged ` | Cleared after full redraw |
212+ | ` f->updated_p ` | Frame was updated this redisplay cycle |
213+
214+ #### Global Variables
215+
216+ | Variable | Purpose |
217+ | ----------| ---------|
218+ | ` windows_or_buffers_changed ` | Dirty flag: 0 = all cached display data is fresh. Non-zero = needs redisplay. |
219+ | ` update_mode_lines ` | Mode-line dirty flag |
220+ | ` redisplaying_p ` | Re-entrancy guard: true while redisplay is running |
221+
222+ #### Current Matrix (` w->current_matrix ` )
223+
224+ The glyph matrix is read by code OUTSIDE the display engine:
225+
226+ | Consumer | What it reads | Why |
227+ | ----------| --------------| -----|
228+ | ` window-line-height ` (window.c) | ` MATRIX_ROW() ` , row height/ascent | Return line dimensions to Lisp |
229+ | ` window-cursor-info ` (window.c) | Row at ` phys_cursor.vpos ` → glyph | Get glyph under cursor for width/height |
230+ | ` pos-visible-in-window-p ` (window.c) | Calls ` pos_visible_p() ` which runs iterator | Check if position is visible |
231+ | neomacsterm.c | Entire matrix: all rows, all glyphs, all areas | Extract for GPU rendering |
232+
233+ #### Layout Query Functions (must be implemented by the display engine)
234+
235+ | Lisp Function | What it computes | How it works |
236+ | ---------------| -----------------| --------------|
237+ | ` pos-visible-in-window-p ` | Is buffer position visible? | Runs display iterator from ` w->start ` |
238+ | ` window-end ` | Last visible buffer position | Reads ` window_end_pos ` or recomputes via iterator |
239+ | ` vertical-motion ` | Move N visual lines | Runs iterator with wrapping/truncation |
240+ | ` move-to-column ` | Move to column N | Scans line handling tabs, display props, wide chars |
241+ | ` compute-motion ` | Position after hypothetical motion | Full layout computation (7 parameters) |
242+ | ` posn-at-point ` | Pixel position of point | Calls ` pos-visible-in-window-p ` internally |
243+ | ` posn-at-x-y ` | Buffer position at pixel coords | Queries glyph matrix rows and glyphs |
244+ | ` current-column ` | Column number at point | Scans from line start |
245+ | ` line-pixel-height ` | Pixel height of current line | Runs iterator for one line |
246+ | ` format-mode-line ` | Rendered mode-line text | Evaluates Lisp format specs, returns text with properties |
247+
248+ #### Callbacks During Layout
249+
250+ | Callback | When | Direction |
251+ | ----------| ------| -----------|
252+ | ` fontification-functions ` | Iterator reaches unfontified text | Display engine → Lisp (pauses layout, calls Lisp, resumes) |
253+ | ` pre-redisplay-function ` | Start of ` redisplay_internal() ` | Display engine → Lisp |
254+ | ` window-scroll-functions ` | After window scroll | Display engine → Lisp |
255+ | ` redisplay_interface ` hooks | After layout, during drawing | Display engine → rendering backend |
256+
70257## What We Keep vs Replace
71258
72259### Keep (Emacs C/Lisp)
0 commit comments