Skip to content

Releases: rizukirr/libterm

v0.1.2

14 Jun 17:16

Choose a tag to compare

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 a 0xRRGGBB hex color into an lt_attr, complementing LT_RGB(r, g, b). LT_HEX(0xRRGGBB) packs the same bits as LT_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 the LT_HI_BLACK sentinel; it stays a pure color packer. Default-vs-black semantics match LT_RGB: LT_HEX(0x000000) is the terminal default, OR in LT_HI_BLACK for 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

13 Jun 12:34

Choose a tag to compare

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 termbox2 tb_/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) and tb_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_init keeps libterm's modern key model; call tb_set_input_mode(LT_INPUT_COMPAT) for exact termbox2 control-byte behavior. Full Porting from termbox2 guide on the wiki.
  • examples/editor.c — a minimal but real text editor (open/edit/save, cursor movement, Save As prompt, Ctrl-S/Ctrl-Q) built only against compat/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_safe label), 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-backed test_compat_get_cell, and a terminal-free test_compat_editor white-box buffer test.

Fixed (editor example)

  • Use-after-free that surfaced as "out of memory" on Enter (a row-array pointer held across realloc); now ASan-guarded by a regression test.
  • Ctrl-S / Ctrl-Q not firing — the editor now enables LT_INPUT_COMPAT so 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 editor example is POSIX-built (examples aren't built on the Windows CI lanes).

Full changelog: v0.1.0...v0.1.1

v0.1.0

04 Jun 23:02

Choose a tag to compare

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_initlt_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. No ENABLE_VIRTUAL_TERMINAL_INPUT dependency.
  • One shared core; platform code stays behind a small internal interface (lt__plat_*). No #ifdef _WIN32 in 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+A and Ctrl+I vs Tab), key press/repeat/release in ev.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_COMPAT restores 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 packed 0x00RRGGBB.
  • POSIX: a real OSC 10/11/4 round-trip — timeout-bounded, accepts rgb: and URxvt rgba: 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 in examples/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 *.o

Artifacts 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 inspector

Eight 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+I vs Tab and Shift+letter are 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/11 itself, 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).