diff options
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..e877a01 --- /dev/null +++ b/README.md @@ -0,0 +1,143 @@ +# term + +term is a headless terminal core: the parser, screen ring, logical-line +history store, write core, and viewport snapshots. It is a C11 library +with no dependency but libm. + +| Path | Contents | +| --- | --- | +| `include/term/` | The public headers: `term.h`, and the header-only `unicode/` | +| `src/` | The core. Its headers are private. | +| `tests/` | Unit, model, fuzz, and portability tests, and the Kitty parser suite | +| `benchmarks/` | `term-write-bench`, `term-resize-bench`, and `hist-bench` | +| `tools/` | `amalgamate.py` and `generate_unicode.py` | + +`meson setup build && meson test -C build` builds and tests it alone. + +## Embedding + +Pick one of these: + +- **Meson subproject**: `dependency('term')`, or + `subproject('term').get_variable('term_dep')`. The `arch_flag` option + sets the lowest instruction set, such as `-march=x86-64-v3`. +- **Amalgamation**: run `tools/amalgamate.py . OUT`. It writes + `OUT/term.c` and `OUT/term.h`. Compile `term.c` with any C11 compiler + and link libm. A Rust `build.rs` needs one + `cc::Build::new().file("term.c").compile("term")`. +- **Sources**: compile `src/*.c` and `src/osc/*.c` once, with `src` and + `include/term` on the include path. + +The library needs nothing else. The `*-amalgamation` tests run the term +tests against the generated `term.c`, and `term-cross-test` compiles it +for aarch64, macOS, and Windows. + +### SIMD level + +The hot paths have AVX2, AVX-512, and NEON versions. Without them, +scroll and line-feed output runs 8 to 30 times slower. The build needs +no flags to get them: + +- On x86-64 with GCC or Clang, each kernel compiles at every level with + a target attribute. The first `term_new()` picks the highest level the + CPU supports. The levels are `base` (x86-64), `avx2` (x86-64-v3), and + `avx512` (x86-64-v4 with VBMI and VBMI2). `term_isa()` names the level + in use. Set `TERM_HEADLESS_ISA` to a lower level to force it. +- `-march` sets the lowest level. The checks for that level and below + become constants, and their kernels inline into their callers. +- aarch64 always has NEON, so it needs no choice at run time. +- MSVC gets only the levels that its `/arch` flag enables. + +`simd.h` states the rules for a new kernel. + +- `term.h` is the only header a host includes. It includes + `term_input.h` and `term_types.h`. It compiles as C11 and as C++. +- Every global symbol starts with `term_`. The library builds with hidden + symbols, so a shared build exports only the `TERM_API` functions of + `term.h`. `term-symbols-test` checks both. +- Any thread can call any function. Calls on one terminal run one at a + time. `term.h` states the rules. +- The `static inline` helpers (`term_color_*`, `term_view_color`, + `term_attr_underline`) only decode documented bit layouts. A binding + generator such as bindgen skips them. Mirror them in the binding, or + use the generator's static-function wrappers. + +A host wires the terminal in four places: + +1. Create it with `term_new()`. Set `term_options.callbacks.reply` to + send replies to the program. +2. Feed program output with `term_feed()`. +3. Translate key and mouse events into `struct term_key_event` and + `struct term_mouse_event`. Encode them with `term_input_encode_key()` + and `term_input_encode_mouse()`, and send the bytes to the program. +4. Paint from a `struct term_snapshot`. The terminal gives code points, + colors, and attributes. The host shapes and draws the glyphs. + +## Files and responsibilities + +The files are in `src/`. + +| File | Responsibility | +| --- | --- | +| `term.c` | Mode queries, cursor options, mouse reports, focus events | +| `core.c`, `core.h` | Unified write core primitives (`sc_print_ascii`, `sc_linefeed`, etc.) | +| `screen.c`, `screen.h` | Screen ring buffer, line feed, editing, resize, and reflow | +| `hist.c`, `hist.h` | Logical-line history store, SIMD cell encoder, and page arena | +| `hist_view.c`, `hist_view.h` | Viewport anchor, history row lookup, and scroll offset | +| `snapshot.c` | Immutable view snapshot, row keys, and scroll movement detection | +| `selection.c` | Text selection, coordinate mapping, and UTF-8 export | +| `gstore.c`, `gstore.h` | Per-terminal grapheme cluster table | +| `text.c` | Byte stream decoder, control character dispatch, and text run routing | +| `text_run.c`, `text_run.h` | SIMD UTF-8 text run blit | +| `fastcsi.c` | Fast CSI sequence parser | +| `input.c` | Key and mouse encoding | +| `escape.c` | Parser state machine for ESC, CSI, DCS, and OSC | +| `pen.h` | SGR graphics rendition state | +| `osc/dispatch.c` | OSC command dispatcher and palette queries | +| `osc/link.c` | OSC 8 hyperlink table | +| `osc/clipboard.c` | OSC 52 clipboard reads and writes | +| `osc/misc.c` | Operating system commands: title, directory, notifications | + +## Architecture + +### Write core +All text and control sequences route through unified primitives in +`core.c`. Primitives handle character printing, line feeds, margins, +and cursor movement. + +### Screen and storage +The visible screen is a circular ring of rows (`struct screen`). +Rows that scroll off the top move into the logical-line history store +(`struct hist`). +The history store compacts lines into UTF-8 text and delta-encoded +attribute runs. + +### Viewport and snapshots +The viewport names a logical line and a wrapped segment, or the live bottom. +The anchor also keeps the cell offset before wrapping. +Output keeps the anchor on the same text. +Resize maps that cell offset to a segment at the new width. +Eviction clamps the anchor to the oldest retained line. +A line can lose its prefix when it exceeds the history byte limit. +The anchor tracks the removed cells to keep its text position. +The alternate screen has no history. + +Relative scroll walks from the anchor. +Absolute scroll uses a circular Fenwick index of line row counts. +Snapshot capture updates this index. +The write path does not update the index. +History descriptors keep row totals for the current width. +A width change starts wide lines with estimated row counts. +Snapshot capture makes the counts exact for lines in the view. +Counts above 254 rows use a separate cache. +That cache grows only when a visited line needs it. + +`term_snapshot_update` takes a mutable terminal. +It updates history query metadata before it copies the view. +Snapshot getters and history getters do not change terminal state. +Renderers do not read terminal memory directly. Renderers read an +immutable snapshot (`struct term_snapshot`). +Snapshots track row keys based on row content identities. +When output scrolls, snapshots detect the scroll distance `k`. +Renderers shift existing pixels and draw records by `k` rows and paint +only changed rows. |
