Releases: rizukirr/libterm
v0.1.2
An additive API release. The core API and ABI are unchanged from v0.1.1 — every existing lt_* symbol behaves identically (lt_version() now returns "0.1.2"). No code changes are required in callers.
Added
LT_HEX(rgb)— pack a0xRRGGBBhex color into anlt_attr, complementingLT_RGB(r, g, b).LT_HEX(0xRRGGBB)packs the same bits asLT_RGB(0xRR, 0xGG, 0xBB), so a color already held as a single hex value can be used directly. The high byte (bits 24–31) is masked off, so a value with stray high bits — e.g.0xFFFFFFFF— packs as pure white and can never accidentally set an attribute flag or theLT_HI_BLACKsentinel; it stays a pure color packer. Default-vs-black semantics matchLT_RGB:LT_HEX(0x000000)is the terminal default, OR inLT_HI_BLACKfor real black. Documented on the Colors & Attributes wiki page.
Compatibility
ABI-compatible with the 0.1.x line. LT_HEX is a new public macro (purely additive); no existing symbol, signature, or behavior changed.
Full Changelog: v0.1.1...v0.1.2
v0.1.1
A tooling + compatibility release. The core library API and ABI are unchanged from v0.1.0 — every lt_* symbol behaves identically (lt_version() now returns "0.1.1"). This release adds a drop-in termbox2 compatibility header, a worked example built on it, and substantially hardened the test infrastructure.
Added — termbox2 compatibility layer
compat/termbox2.h— a single drop-in header mapping the supported termbox2tb_/TB_API onto libterm. Replace#include "termbox2.h"and existing termbox2 code compiles and links unchanged:- Aliased: every public function with a libterm equivalent; the full
TB_KEY_*/TB_MOD_*/TB_EVENT_*/TB_OUTPUT_*/TB_INPUT_*set, colors, attributes, return codes;struct tb_cell/struct tb_event/uintattr_t. - Adapted:
tb_get_cell(snapshot copy, back-buffer only) andtb_put_cell. - Rejected at compile time with a message naming the
lt_replacement:tb_init_rwfd,tb_set_func,tb_has_truecolor,tb_cell_buffer,tb_key_i. - Input semantics:
tb_initkeeps libterm's modern key model; calltb_set_input_mode(LT_INPUT_COMPAT)for exact termbox2 control-byte behavior. Full Porting from termbox2 guide on the wiki.
- Aliased: every public function with a libterm equivalent; the full
examples/editor.c— a minimal but real text editor (open/edit/save, cursor movement, Save As prompt,Ctrl-S/Ctrl-Q) built only againstcompat/termbox2.h.
Added — testing & CI hardening
- libFuzzer harnesses for the three untrusted-input parsers — the escape-sequence decoder (
lt__key_decode), the OSC color-reply parser, and the UTF-8 decoder — with seed corpora, a per-PR smoke run, and a nightly ~1h-per-target run (-DLIBTERM_BUILD_FUZZERS=ON, clang). - valgrind memcheck CI job over the pure-unit test subset (new
valgrind_safelabel), catching uninitialized-read defects the ASan/UBSan job doesn't; fails on definite + indirect leaks. - New tests: exhaustive
compat_smoke(every alias compile-checked), pty-backedtest_compat_get_cell, and a terminal-freetest_compat_editorwhite-box buffer test.
Fixed (editor example)
- Use-after-free that surfaced as
"out of memory"on Enter (a row-array pointer held acrossrealloc); now ASan-guarded by a regression test. Ctrl-S/Ctrl-Qnot firing — the editor now enablesLT_INPUT_COMPATso its termbox2-idiom key checks match libterm's modern default.
Documentation
- New wiki page Porting from termbox2; fuzzing how-to (
tests/fuzz/README.md); README now points at the compat header, the porting guide, and the editor example.
Compatibility
- No API/ABI changes — drop-in replacement for v0.1.0. The compat header is platform-agnostic; the
editorexample is POSIX-built (examples aren't built on the Windows CI lanes).
Full changelog: v0.1.0...v0.1.1
v0.1.0
Fast. Native. Fluent in modern terminal protocols.
libterm is a small C11 terminal UI library with a termbox2-compatible API — the same code drives POSIX terminals and the native Win32 Console, with no POSIX shims and no #ifdef in your app. This is the first release.
Note: v0.1.0 was re-tagged on 2026-06-06 to fold in the packaging and consumption work that landed immediately after the original tag (FetchContent support, pkg-config, conventional artifact names). The prebuilt archives below were rebuilt from the re-tagged commit.
Why libterm
You draw into an off-screen cell buffer and call lt_present(). libterm diffs the frame against what's on screen — with SIMD-accelerated scans — and emits only the bytes that changed, inside a synchronized-update (DEC 2026) bracket, so the terminal swaps frames atomically. Flicker-free by construction.
Modern terminal capabilities are negotiated automatically and degrade gracefully on terminals that lack them. Code written against termbox2 ports with a prefix swap (tb_ → lt_); the bundled keyboard example is termbox2's own demo, ported exactly that way. termbox2 parity extends to the details: the cursor is hidden by default after lt_init — lt_set_cursor shows it and keeps it parked across frames.
Highlights
Truly cross-platform
- POSIX:
/dev/tty+ termios raw mode, alt-screen, SIGWINCH resize via self-pipe. - Windows: the real Win32 Console API (
ReadConsoleInputW, console handles) — a first-class citizen, not a port. NoENABLE_VIRTUAL_TERMINAL_INPUTdependency. - One shared core; platform code stays behind a small internal interface (
lt__plat_*). No#ifdef _WIN32in shared code.
Rendering engine
- Double-buffered cell diff with run coalescing and a cursor-position cache.
- SIMD cell scans: AVX2, AVX-512, NEON, SVE, RVV — runtime-dispatched: every backend for your architecture is compiled in and the best one is selected at startup from CPU capabilities (cpuid/hwcap), so one binary is fast on capable CPUs and never hits an illegal instruction on older ones. Each backend is guarded in CI by a correctness contract and an "actually emitted these instructions" assertion; pin a single backend with
-DLIBTERM_SIMD=<backend>. - All five output modes: normal (8 colors), 256, 216-cube, grayscale, 24-bit truecolor — plus 7 text attributes. Hand-rolled integer formatting in the hot path (no
snprintf). - Resize done right: a size change forces a full repaint (no stale content after shrinks), with exactly-once delivery, burst coalescing, and no-op suppression validated end-to-end.
⌨️ Modern input model
- The kitty keyboard protocol is negotiated by default: modifiers on every key (including
Shift+AandCtrl+IvsTab), key press/repeat/release inev.action, bare-modifier keys. - Automatic fallback to broad legacy decoding (xterm, SS3, rxvt, Linux console, vt220 tilde forms, CSI modifier suffixes) on terminals without it.
- SGR (1006) mouse reporting; UTF-8 input assembly with malformed-sequence resync; grapheme clusters (combining marks, ZWJ emoji) as first-class cells.
LT_INPUT_COMPATrestores exact termbox2 control-byte semantics for drop-in code.
Color querying & theming (beyond termbox2)
int dark = lt_is_dark_background(500);
if (dark < 0) dark = 1; /* terminal didn't answer — assume dark, carry on */lt_query_color()— the terminal's actual default fg/bg or any palette index, as packed0x00RRGGBB.- POSIX: a real
OSC 10/11/4round-trip — timeout-bounded, acceptsrgb:and URxvtrgba:reply forms, and anything you type while the query is in flight is preserved (escape sequences re-assemble correctly). - Windows: answered natively from the console color table — immediate, works on every Windows version.
lt_is_dark_background()— the question 95% of apps actually have, answered with a BT.709 luminance verdict. See it live inexamples/theme.c.
Use it your way — every consumption path is CI-guarded
CMake, installed (a release archive below, or cmake --install):
find_package(Libterm REQUIRED)
target_link_libraries(app PRIVATE Libterm::libterm)CMake, as a subproject — clean by construction: a FetchContent / add_subdirectory consumer gets exactly one target (the static library); examples, benches, tests, the shared lib, and install rules are all top-level-only and opt-in:
include(FetchContent)
FetchContent_Declare(libterm
GIT_REPOSITORY https://github.com/rizukirr/libterm
GIT_TAG v0.1.0)
FetchContent_MakeAvailable(libterm)
target_link_libraries(app PRIVATE Libterm::libterm)pkg-config (Meson, Autotools, plain Makefiles) — installed trees ship a relocatable lib/pkgconfig/libterm.pc that works wherever you extract an archive:
cc app.c $(pkg-config --cflags --libs libterm)No build system at all — libterm is plain C11 with no dependencies:
cc -std=c11 -c src/shared/*.c src/platform/posix/*.c src/intrinsics/scalar.c -Iinclude -Isrc
ar rcs libterm.a *.oArtifacts follow Unix naming convention: libterm.a / libterm.so / libterm.dll, link flag -lterm. find_package(Libterm <ver>) enforces SameMinorVersion while pre-1.0, so a breaking 0.x can never be picked up silently.
Prebuilt binaries
Install trees for 6 targets — Linux (x86_64 / aarch64 / riscv64), Windows (x86_64 MinGW), macOS (arm64 / x86_64) — with a SHA256SUMS file, built in Release mode with runtime-dispatched SIMD:
tar -xzf libterm-v0.1.0-linux-x86_64.tar.gz
sha256sum -c SHA256SUMS --ignore-missing
cmake -B build -DCMAKE_PREFIX_PATH=$PWD/libterm-v0.1.0-linux-x86_64 # find_package(Libterm)macOS note: the binaries are unsigned — xattr -d com.apple.quarantine the extracted files or build from source.
CI that executes, not just compiles
| Platform | Coverage |
|---|---|
| Linux | gcc + clang × scalar/AVX2 (full suite), AVX-512 compile, ASan/UBSan suite, clang-tidy, clang-format |
| macOS (arm64) | portable suite + pty lifecycle canary (full pty-test parity tracked in the Roadmap) |
| Windows | native MinGW build + full Windows test execution on windows-latest, plus a Linux MinGW cross-build |
| aarch64 / riscv64 | NEON, SVE, RVV backends built and tested under QEMU |
| Consumption | fetchcontent-smoke on all 3 OSes; install + find_package smoke; pkg-config consume/run incl. a moved-prefix relocation pass; the README's manual cc+ar build executed verbatim |
Getting started
cmake -B build && cmake --build build
ctest --test-dir build # run the suite
./build/examples/theme # color-query demo — try it on light & dark profiles
./build/examples/kbd # live key/modifier/event inspectorEight runnable examples ship in examples/: hello, print, colors, truecolor, keyboard (termbox2's demo, prefix-swapped), mouse, kbd, theme.
Full guides and API reference live in the wiki.
Stability
Pre-1.0 semantic versioning: minor versions (0.x) may contain breaking API changes, always listed in CHANGELOG.md; patch versions never break API or ABI (SOVERSION 0 until 1.0). The termbox2-compatible surface is the stability contract — per-symbol status in the Roadmap.
Known limitations (documented, tracked)
- Legacy-terminal modifiers: on terminals without the kitty protocol (or
modifyOtherKeys),Ctrl+IvsTabandShift+letterare inherently indistinguishable — the bytes carry no modifier information. Same ceiling termbox2 has; kitty-capable terminals get full fidelity. - macOS pty-test parity: most pty-based tests are currently Apple-gated (small kernel pty buffers + a shutdown-path interaction under diagnosis — Roadmap "Known blockers" #6). The library itself targets macOS; the portable suite + a pty canary run green there.
- tmux: answers
OSC 10/11itself, so color queries report tmux's notion of colors. - Windows palette queries above index 15 return
LT_ERR_UNSUPPORTED_TERM(the Win32 color table has 16 slots). - Mouse/resize behavior is parser-tested; live-terminal click verification across emulators is still pending.
What's next
Mode 2031 theme-change events, the kitty OSC 21 color stack, modifyOtherKeys fallback, macOS pty-test parity, and a compat/termbox2.h zero-edit drop-in header — see the Roadmap.
libterm is MIT licensed, derived from termbox2 (also MIT).