From 8f0f080ef473313137da5003f0979115d55de995 Mon Sep 17 00:00:00 2001 From: rizukirr Date: Sun, 14 Jun 2026 22:40:16 +0700 Subject: [PATCH 1/4] chore: update .gitignore --- .gitignore | 10 +++++----- examples/truecolor.c | 28 ++++++++++++---------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 47b57f2..c8b8f50 100644 --- a/.gitignore +++ b/.gitignore @@ -22,11 +22,11 @@ cmake_install.cmake Makefile compile_commands.json external/ -/.codex -/.omx -/package-lock.json -/docs -/.claude +.codex/ +.omx/ +package-lock.json +docs/ +.claude/ CLAUDE.md AGENTS.md Testing/ diff --git a/examples/truecolor.c b/examples/truecolor.c index 1feabab..1d20261 100644 --- a/examples/truecolor.c +++ b/examples/truecolor.c @@ -13,11 +13,6 @@ #include -static void puts_at(int x, int y, const char *s, lt_attr fg, lt_attr bg) { - for (int i = 0; s[i]; i++) - lt_set_cell(x + i, y, (lt_uchar)s[i], fg, bg); -} - int main(void) { int rc = lt_init(); if (rc != LT_OK) { @@ -35,11 +30,11 @@ int main(void) { char title[128]; snprintf(title, sizeof title, "libterm truecolor demo [%s] (press q or Esc to quit)", depth_note); - puts_at(2, 1, title, LT_DEFAULT, LT_DEFAULT); + lt_print(2, 1, LT_DEFAULT, LT_DEFAULT, title); /* 24-bit RGB gradient: blue -> red across the row, drawn as cell * backgrounds (space glyphs). Each column is a distinct true color. */ - puts_at(2, 3, "24-bit gradient:", LT_DEFAULT, LT_DEFAULT); + lt_print(2, 3, LT_DEFAULT, LT_DEFAULT, "24-bit gradient:"); for (int i = 0; i < 36; i++) { int r = i * 7; int b = 255 - i * 7; @@ -52,21 +47,22 @@ int main(void) { /* Named LT_RGB swatches: a label drawn on its own background color. The * orange swatch uses LT_RGB(0,0,0)|LT_HI_BLACK for genuinely-black text. */ - puts_at(2, 6, "LT_RGB swatches:", LT_DEFAULT, LT_DEFAULT); - puts_at(2, 7, " orange ", LT_RGB(0, 0, 0) | LT_HI_BLACK, LT_RGB(255, 128, 0)); - puts_at(11, 7, " teal ", LT_RGB(255, 255, 255), LT_RGB(0, 128, 128)); - puts_at(18, 7, " purple ", LT_RGB(255, 255, 255), LT_RGB(128, 0, 128)); + lt_print(2, 6, LT_DEFAULT, LT_DEFAULT, "LT_RGB swatches:"); + lt_print(2, 7, LT_RGB(0, 0, 0) | LT_HI_BLACK, LT_RGB(255, 128, 0), + " orange "); + lt_print(11, 7, LT_RGB(255, 255, 255), LT_RGB(0, 128, 128), " teal "); + lt_print(18, 7, LT_RGB(255, 255, 255), LT_RGB(128, 0, 128), " purple "); /* LT_HI_BLACK sentinel: terminal default vs real black. The left cell uses * the terminal's default background; the right forces RGB(0,0,0). */ - puts_at(2, 9, "HI_BLACK vs default:", LT_DEFAULT, LT_DEFAULT); - puts_at(2, 10, " default-bg ", LT_DEFAULT, LT_DEFAULT); - puts_at(15, 10, " real-black ", LT_RGB(255, 255, 255), - LT_RGB(0, 0, 0) | LT_HI_BLACK); + lt_print(2, 9, LT_DEFAULT, LT_DEFAULT, "HI_BLACK vs default:"); + lt_print(2, 10, LT_DEFAULT, LT_DEFAULT, " default-bg "); + lt_print(15, 10, LT_RGB(255, 255, 255), LT_RGB(0, 0, 0) | LT_HI_BLACK, + " real-black "); /* truecolor color composed with an attribute (bold) — proves attrs still * work after their bits moved above the 24-bit color field. */ - puts_at(2, 12, "truecolor + bold", LT_RGB(255, 96, 0) | LT_BOLD, LT_DEFAULT); + lt_print(2, 12, LT_RGB(255, 96, 0) | LT_BOLD, LT_DEFAULT, "truecolor + bold"); lt_present(); From b44ba4c8fb126850a3f8db2fc68ecac66b7cb301 Mon Sep 17 00:00:00 2001 From: rizukirr Date: Sun, 14 Jun 2026 22:53:40 +0700 Subject: [PATCH 2/4] feat: add LT_HEX(0xRRGGBB) color-packing macro --- include/libterm/libterm.h | 5 +++++ tests/CMakeLists.txt | 1 + tests/test_color_macros.c | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 tests/test_color_macros.c diff --git a/include/libterm/libterm.h b/include/libterm/libterm.h index 2402d9c..83da053 100644 --- a/include/libterm/libterm.h +++ b/include/libterm/libterm.h @@ -214,6 +214,11 @@ extern "C" { ((lt_attr)((((lt_attr)(r) & 0xFFu) << 16) | (((lt_attr)(g) & 0xFFu) << 8) | \ ((lt_attr)(b) & 0xFFu))) +/* Pack a 0xRRGGBB hex color into bits 0-23 for LT_OUTPUT_TRUECOLOR. High bits + * (attribute flags + the LT_HI_BLACK sentinel) are masked off, so LT_HEX is a + * pure color packer — equivalent to LT_RGB((rgb)>>16, (rgb)>>8, (rgb)). */ +#define LT_HEX(rgb) ((lt_attr)((rgb) & 0xFFFFFFu)) + /* ---- types ---- */ typedef uint32_t lt_uchar; typedef uint32_t lt_attr; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 19eafcb..3176683 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,6 +34,7 @@ libterm_add_test(test_simd_diff) libterm_add_test(test_keymap) libterm_add_test(test_detect_color_depth) libterm_add_test(test_color_parse) +libterm_add_test(test_color_macros) # compat/termbox2.h drop-in layer: compile+link proof that every aliased symbol # resolves. Platform-agnostic (no terminal init), so it runs everywhere. add_executable(compat_smoke compat_smoke.c) diff --git a/tests/test_color_macros.c b/tests/test_color_macros.c new file mode 100644 index 0000000..81198ce --- /dev/null +++ b/tests/test_color_macros.c @@ -0,0 +1,27 @@ +/* LT_HEX color-packing macro: proves hex packing is identical to LT_RGB and + * that the high bits (the 7 attribute flags + the LT_HI_BLACK sentinel) are + * masked off, so LT_HEX can never leak an attribute. Pure compile-time macro + * checks via _Static_assert — no terminal init, runs everywhere, immune to + * NDEBUG. */ +#include "libterm/libterm.h" + +/* Packing parity: a 0xRRGGBB hex literal equals the three-channel LT_RGB form. + */ +_Static_assert(LT_HEX(0xFF8000) == LT_RGB(255, 128, 0), + "LT_HEX packing must match LT_RGB"); + +/* High bits masked: a value with every bit set yields pure white, with no + * attribute or LT_HI_BLACK bits leaking through. */ +_Static_assert(LT_HEX(0xFFFFFFFF) == LT_RGB(255, 255, 255), + "LT_HEX must mask bits above the 24-bit color field"); +_Static_assert((LT_HEX(0xFFFFFFFF) & 0xFF000000u) == 0u, + "LT_HEX must not set any attribute/sentinel bit"); + +/* Default-vs-black semantics match LT_RGB: a zero color is the terminal + * default; OR in LT_HI_BLACK for real black. */ +_Static_assert(LT_HEX(0x000000) == LT_DEFAULT, + "LT_HEX(0) must equal the terminal default"); +_Static_assert((LT_HEX(0) | LT_HI_BLACK) == LT_HI_BLACK, + "LT_HEX(0) | LT_HI_BLACK must select real black"); + +int main(void) { return 0; } From 6ba06adfb3ff6bad8e7e03e73e75a8fe01e80871 Mon Sep 17 00:00:00 2001 From: rizukirr Date: Sun, 14 Jun 2026 22:55:57 +0700 Subject: [PATCH 3/4] docs: note LT_HEX in ROADMAP and CHANGELOG --- CHANGELOG.md | 5 +++++ ROADMAP.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d87cf0a..0280659 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ may contain breaking API changes; patch versions never do). ## [Unreleased] +### Added +- **`LT_HEX(rgb)`** — pack a `0xRRGGBB` hex color into an `lt_attr`, complementing + `LT_RGB(r,g,b)`. High bits (attribute flags + the `LT_HI_BLACK` sentinel) are + masked, so it is a pure color packer; `LT_HEX(0xRRGGBB) == LT_RGB(0xRR,0xGG,0xBB)`. + ## [0.1.1] - 2026-06-13 Tooling + compatibility release. The core library API and ABI are unchanged diff --git a/ROADMAP.md b/ROADMAP.md index cc457bd..7d46cf0 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -162,7 +162,7 @@ On **kitty-capable terminals** (negotiated by default), POSIX now delivers `LT_M ### Colors (`TB_DEFAULT/BLACK/RED/…` → `LT_DEFAULT/BLACK/RED/…`) -Declared: `LT_DEFAULT`, `LT_BLACK`, `LT_RED`, `LT_GREEN`, `LT_YELLOW`, `LT_BLUE`, `LT_MAGENTA`, `LT_CYAN`, `LT_WHITE`, plus `LT_RGB(r,g,b)` (24-bit pack) and the `LT_HI_BLACK` sentinel. Emitted on **both platforms** via mode-aware SGR in the shared `lt__emit_sgr` (`src/shared/sgr.c`) — named (NORMAL), 8-bit palette index (256/216/grayscale), and 24-bit RGB (TRUECOLOR), with `LT_HI_BLACK` distinguishing the terminal default from real black. Emitted bytes asserted on POSIX in `tests/test_posix_sgr_output.c`; Windows runs the same shared code (real-terminal/byte verification pending). +Declared: `LT_DEFAULT`, `LT_BLACK`, `LT_RED`, `LT_GREEN`, `LT_YELLOW`, `LT_BLUE`, `LT_MAGENTA`, `LT_CYAN`, `LT_WHITE`, plus `LT_RGB(r,g,b)` (24-bit pack), `LT_HEX(0xRRGGBB)` (hex pack, high bits masked), and the `LT_HI_BLACK` sentinel. Emitted on **both platforms** via mode-aware SGR in the shared `lt__emit_sgr` (`src/shared/sgr.c`) — named (NORMAL), 8-bit palette index (256/216/grayscale), and 24-bit RGB (TRUECOLOR), with `LT_HI_BLACK` distinguishing the terminal default from real black. Emitted bytes asserted on POSIX in `tests/test_posix_sgr_output.c`; Windows runs the same shared code (real-terminal/byte verification pending). ### Attributes (`TB_BOLD/UNDERLINE/…` → `LT_BOLD/UNDERLINE/…`) From 70a3b898e54a270c08464e93e2da034d1fc0f537 Mon Sep 17 00:00:00 2001 From: rizukirr Date: Sun, 14 Jun 2026 23:08:34 +0700 Subject: [PATCH 4/4] docs: clarify LT_HEX equivalence comment --- include/libterm/libterm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/libterm/libterm.h b/include/libterm/libterm.h index 83da053..5334fae 100644 --- a/include/libterm/libterm.h +++ b/include/libterm/libterm.h @@ -216,7 +216,8 @@ extern "C" { /* Pack a 0xRRGGBB hex color into bits 0-23 for LT_OUTPUT_TRUECOLOR. High bits * (attribute flags + the LT_HI_BLACK sentinel) are masked off, so LT_HEX is a - * pure color packer — equivalent to LT_RGB((rgb)>>16, (rgb)>>8, (rgb)). */ + * pure color packer: LT_HEX(0xRRGGBB) packs the same bits as + * LT_RGB(0xRR, 0xGG, 0xBB). */ #define LT_HEX(rgb) ((lt_attr)((rgb) & 0xFFFFFFu)) /* ---- types ---- */