ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
colorscheme.h File Reference

Go to the source code of this file.

Data Structures

struct  color_scheme_t
 Color scheme definition. More...
struct  compiled_color_scheme_t
 Compiled ANSI escape codes for a color scheme. More...
struct  colorscheme_palette_t

Macros

#define OPTIONS_COLORSCHEME_NAME_BUFFER_SIZE   64
#define OPTIONS_COLORSCHEME_DESCRIPTION_BUFFER_SIZE   256
#define OPTIONS_COLORSCHEME_FOREGROUND_16_BUFFER_SIZE   16
#define OPTIONS_COLORSCHEME_FOREGROUND_256_BUFFER_SIZE   24
#define OPTIONS_COLORSCHEME_FOREGROUND_TRUECOLOR_BUFFER_SIZE   32
#define COLORSCHEME_LOG_COLOR_COUNT   8
#define COLORSCHEME_SGR_BUFFER_SIZE   32

Typedefs

typedef struct color_scheme_t color_scheme_t
 Color scheme definition.

Enumerations

enum  terminal_background_t { TERM_BACKGROUND_UNKNOWN = 0 , TERM_BACKGROUND_LIGHT = 1 , TERM_BACKGROUND_DARK = 2 }
 Color scheme definition. More...

Functions

void colorscheme_current_palette (colorscheme_palette_t *palette, terminal_color_mode_t mode)
terminal_background_t colorscheme_current_background (void)
uint8_t colorscheme_highlight_background (void)
void colorscheme_apply (const color_scheme_t *scheme)
asciichat_error_t colorscheme_init (void)
 Initialize the color system.
void colorscheme_destroy (void)
 Shutdown the color system.
const color_scheme_t * colorscheme_get_active_scheme (void)
 Get currently active color scheme.
asciichat_error_t colorscheme_set_active_scheme (const char *name)
 Set the active color scheme.
asciichat_error_t colorscheme_load_builtin (const char *name, color_scheme_t *scheme)
 Load a built-in color scheme.
asciichat_error_t colorscheme_load_from_file (const char *path, color_scheme_t *scheme)
 Load a color scheme from a TOML file.
asciichat_error_t colorscheme_compile_scheme (const color_scheme_t *scheme, terminal_color_mode_t mode, terminal_background_t background, compiled_color_scheme_t *compiled)
 Compile a color scheme to ANSI codes.
void colorscheme_cleanup_compiled (compiled_color_scheme_t *compiled)
 Clean up allocated strings in a compiled color scheme.
asciichat_error_t colorscheme_export_scheme (const char *scheme_name, const char *file_path)
 Export a color scheme to TOML format.
asciichat_error_t parse_hex_color (const char *hex, uint8_t *r, uint8_t *g, uint8_t *b)
 Parse a hex color string.
void rgb_to_truecolor_ansi (uint8_t r, uint8_t g, uint8_t b, char *buf, size_t size)
 Generate truecolor ANSI escape code.
terminal_background_t detect_terminal_background (void)
 Detect terminal background (light or dark).
asciichat_error_t options_colorscheme_init_early (int argc, const char *const argv[])
 Initialize color scheme early, before logging.

Variables

mutex_t g_colorscheme_mutex
 Shared mutex for color scheme compilation.

Macro Definition Documentation

◆ COLORSCHEME_LOG_COLOR_COUNT

#define COLORSCHEME_LOG_COLOR_COUNT   8

Definition at line 40 of file colorscheme.h.

Referenced by colorscheme_current_palette().

◆ COLORSCHEME_SGR_BUFFER_SIZE

#define COLORSCHEME_SGR_BUFFER_SIZE   32

Definition at line 41 of file colorscheme.h.

Referenced by colorscheme_current_palette().

◆ OPTIONS_COLORSCHEME_DESCRIPTION_BUFFER_SIZE

#define OPTIONS_COLORSCHEME_DESCRIPTION_BUFFER_SIZE   256

Definition at line 34 of file colorscheme.h.

◆ OPTIONS_COLORSCHEME_FOREGROUND_16_BUFFER_SIZE

#define OPTIONS_COLORSCHEME_FOREGROUND_16_BUFFER_SIZE   16

Definition at line 35 of file colorscheme.h.

◆ OPTIONS_COLORSCHEME_FOREGROUND_256_BUFFER_SIZE

#define OPTIONS_COLORSCHEME_FOREGROUND_256_BUFFER_SIZE   24

Definition at line 36 of file colorscheme.h.

◆ OPTIONS_COLORSCHEME_FOREGROUND_TRUECOLOR_BUFFER_SIZE

#define OPTIONS_COLORSCHEME_FOREGROUND_TRUECOLOR_BUFFER_SIZE   32

Definition at line 37 of file colorscheme.h.

◆ OPTIONS_COLORSCHEME_NAME_BUFFER_SIZE

#define OPTIONS_COLORSCHEME_NAME_BUFFER_SIZE   64

Definition at line 33 of file colorscheme.h.

Typedef Documentation

◆ color_scheme_t

typedef struct color_scheme_t color_scheme_t

Color scheme definition.

Defines a color scheme with RGB colors for logging levels. Supports both dark and light mode variants.

Enumeration Type Documentation

◆ terminal_background_t

Color scheme definition.

Compiled ANSI escape codes for a color scheme

Terminal theme detection result

Represents the detected or selected terminal theme (color background preference). Used to choose appropriate color schemes and contrast levels.

Enumerator
TERM_BACKGROUND_UNKNOWN 

Theme not detected or auto-detect disabled

TERM_BACKGROUND_LIGHT 

Light theme: light/white background, use dark text colors

TERM_BACKGROUND_DARK 

Dark theme: dark/black background, use light text colors

Definition at line 64 of file colorscheme.h.

64 {
terminal_background_t
Color scheme definition.
Definition colorscheme.h:64
@ TERM_BACKGROUND_DARK
Definition colorscheme.h:67
@ TERM_BACKGROUND_UNKNOWN
Definition colorscheme.h:65
@ TERM_BACKGROUND_LIGHT
Definition colorscheme.h:66

Function Documentation

◆ colorscheme_apply()

void colorscheme_apply ( const color_scheme_t * scheme)

Publish a complete scheme for all logging and UI consumers.

Definition at line 982 of file colorscheme.c.

982 {
983 if (!scheme)
984 return;
986 mutex_lock(&g_colorscheme_mutex);
987 memmove(&g_active_scheme, scheme, sizeof(g_active_scheme));
988 atomic_fetch_add(&g_scheme_revision, 1);
989 mutex_unlock(&g_colorscheme_mutex);
990}
const color_scheme_t * colorscheme_get_active_scheme(void)
Get currently active color scheme.
mutex_t g_colorscheme_mutex
Shared mutex for color scheme compilation.
Definition colorscheme.c:50

References colorscheme_get_active_scheme(), and g_colorscheme_mutex.

Referenced by log_set_color_scheme().

◆ colorscheme_cleanup_compiled()

void colorscheme_cleanup_compiled ( compiled_color_scheme_t * compiled)

Clean up allocated strings in a compiled color scheme.

Parameters
compiledCompiled color scheme to clean (NULL-safe)

Frees all allocated color code strings and zeros the structure.

Definition at line 332 of file colorscheme.c.

332 {
333 if (!compiled) {
334 return;
335 }
336
337 /* Free allocated color code strings */
338 for (int i = 0; i < 8; i++) {
339 char *str_16 = (char *)compiled->codes_16[i];
340 char *str_256 = (char *)compiled->codes_256[i];
341 char *str_truecolor = (char *)compiled->codes_truecolor[i];
342 /* Only free non-NULL pointers - protects against uninitialized data */
343 if (str_16 != NULL) {
344 SAFE_FREE(str_16);
345 }
346 if (str_256 != NULL) {
347 SAFE_FREE(str_256);
348 }
349 if (str_truecolor != NULL) {
350 SAFE_FREE(str_truecolor);
351 }
352 }
353 memset(compiled, 0, sizeof(compiled_color_scheme_t));
354}
#define SAFE_FREE(ptr)
Definition common.h:377
Compiled ANSI escape codes for a color scheme.
Definition colorscheme.h:99
const char * codes_16[8]
const char * codes_256[8]
const char * codes_truecolor[8]

References compiled_color_scheme_t::codes_16, compiled_color_scheme_t::codes_256, compiled_color_scheme_t::codes_truecolor, and SAFE_FREE.

Referenced by colorscheme_compile_scheme().

◆ colorscheme_compile_scheme()

asciichat_error_t colorscheme_compile_scheme ( const color_scheme_t * scheme,
terminal_color_mode_t mode,
terminal_background_t background,
compiled_color_scheme_t * compiled )

Compile a color scheme to ANSI codes.

Parameters
schemeSource scheme (must not be NULL)
modeTerminal color mode (16, 256, or truecolor)
backgroundTerminal background (light or dark)
compiledOutput compiled codes (must not be NULL)
Returns
ASCIICHAT_OK on success, error code on failure

Compiles RGB colors to ANSI escape codes for the specified terminal mode and background. Applies background-appropriate color variant (light/dark).

Definition at line 577 of file colorscheme.c.

578 {
579 if (!scheme || !compiled) {
580 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL scheme or compiled pointer");
581 }
582
583 /* Note: mode parameter reserved for future use */
584 (void)mode;
585
586 /* Free any previously compiled strings before recompiling */
587 /* This prevents memory leaks when the color scheme is recompiled */
588 /* Only cleanup if we've actually compiled before (codes_16[0] != NULL means already compiled) */
589 if (compiled->codes_16[0] != NULL) {
591 } else {
592 }
593
594 /* Select color array based on background */
595 const rgb_pixel_t *colors = (background == TERM_BACKGROUND_LIGHT && scheme->has_light_variant)
596 ? scheme->log_colors_light
597 : scheme->log_colors_dark;
598 // Slot 7 stores the scheme's neutral RGB value, although log entries compile
599 // that slot as an ANSI reset. Notices need its actual foreground color.
600 rgb_pixel_t foreground = colors[7];
601 uint8_t foreground_index = rgb_to_16color(foreground.r, foreground.g, foreground.b);
602 safe_snprintf(compiled->foreground_16, sizeof(compiled->foreground_16), "\033[%dm",
603 foreground_index < 8 ? 30 + foreground_index : 90 + foreground_index - 8);
604 safe_snprintf(compiled->foreground_256, sizeof(compiled->foreground_256), "\033[38;5;%dm",
605 rgb_to_256color(foreground.r, foreground.g, foreground.b));
606 rgb_to_truecolor_ansi(foreground.r, foreground.g, foreground.b, compiled->foreground_truecolor,
607 sizeof(compiled->foreground_truecolor));
608 /* Helper: allocate and format a color code string */
610
611 /* Compile for 16-color mode */
612 for (int i = 0; i < 8; i++) {
613 if (i == 7) {
614 /* RESET */
615 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
616 } else {
617 uint8_t color_idx = rgb_to_16color(colors[i].r, colors[i].g, colors[i].b);
618 /* ANSI color codes: 30-37 for normal, 90-97 for bright */
619 if (color_idx < 8) {
620 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[%dm", 30 + color_idx);
621 } else {
622 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[%dm", 90 + (color_idx - 8));
623 }
624 }
625 /* Allocate string with SAFE_MALLOC and copy */
626 size_t len = strlen(temp_buf) + 1;
627 char *allocated = SAFE_MALLOC(len, char *);
628 if (!allocated) {
629 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
630 }
631 memcpy(allocated, temp_buf, len);
632 compiled->codes_16[i] = allocated;
633 }
634 /* Compile for 256-color mode */
635 for (int i = 0; i < 8; i++) {
636 if (i == 7) {
637 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
638 } else {
639 uint8_t color_idx = rgb_to_256color(colors[i].r, colors[i].g, colors[i].b);
640 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[38;5;%dm", color_idx);
641 }
642 /* Allocate string with SAFE_MALLOC and copy */
643 size_t len = strlen(temp_buf) + 1;
644 char *allocated = SAFE_MALLOC(len, char *);
645 if (!allocated) {
646 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
647 }
648 memcpy(allocated, temp_buf, len);
649 compiled->codes_256[i] = allocated;
650 }
651
652 /* Compile for truecolor mode */
653 for (int i = 0; i < 8; i++) {
654 if (i == 7) {
655 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
656 } else {
657 rgb_to_truecolor_ansi(colors[i].r, colors[i].g, colors[i].b, temp_buf, sizeof(temp_buf));
658 }
659 /* Allocate string with SAFE_MALLOC and copy */
660 size_t len = strlen(temp_buf) + 1;
661 char *allocated = SAFE_MALLOC(len, char *);
662 if (!allocated) {
663 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
664 }
665 memcpy(allocated, temp_buf, len);
666 compiled->codes_truecolor[i] = allocated;
667 }
668
669 return ASCIICHAT_OK;
670}
#define SET_ERRNO(code,...)
#define OPTIONS_COLORSCHEME_TEMP_BUFFER_SIZE
Definition colorscheme.c:12
void rgb_to_truecolor_ansi(uint8_t r, uint8_t g, uint8_t b, char *buf, size_t size)
Generate truecolor ANSI escape code.
void colorscheme_cleanup_compiled(compiled_color_scheme_t *compiled)
Clean up allocated strings in a compiled color scheme.
unsigned char colors[(512 *256 *2) *3]
Definition fft.c:22
uint8_t rgb_to_16color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:354
uint8_t rgb_to_256color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:251
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_MALLOC(size, cast)
Definition common.h:265
unsigned char uint8_t
Definition common.h:60
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
rgb_pixel_t log_colors_light[8]
Definition colorscheme.h:85
bool has_light_variant
Definition colorscheme.h:84
uint8_t b
Blue color component (0-255).
uint8_t g
Green color component (0-255).
uint8_t r
Red color component (0-255).

References ASCIICHAT_OK, rgb_pixel_t::b, compiled_color_scheme_t::codes_16, compiled_color_scheme_t::codes_256, compiled_color_scheme_t::codes_truecolor, colors, colorscheme_cleanup_compiled(), ERROR_INVALID_PARAM, ERROR_MEMORY, compiled_color_scheme_t::foreground_16, compiled_color_scheme_t::foreground_256, compiled_color_scheme_t::foreground_truecolor, rgb_pixel_t::g, color_scheme_t::has_light_variant, color_scheme_t::log_colors_dark, color_scheme_t::log_colors_light, OPTIONS_COLORSCHEME_TEMP_BUFFER_SIZE, rgb_pixel_t::r, rgb_to_16color(), rgb_to_256color(), rgb_to_truecolor_ansi(), SAFE_MALLOC, safe_snprintf(), SAFE_STRNCPY, SET_ERRNO, and TERM_BACKGROUND_LIGHT.

◆ colorscheme_current_background()

terminal_background_t colorscheme_current_background ( void )

Definition at line 920 of file colorscheme.c.

920 {
921 return current_background();
922}

Referenced by colorscheme_highlight_background(), and render_file_create().

◆ colorscheme_current_palette()

void colorscheme_current_palette ( colorscheme_palette_t * palette,
terminal_color_mode_t mode )

Resolve the active scheme and current terminal theme/depth. Queries are asynchronous and coalesced across threads for 250ms. The returned palette uses the latest decoded reply and never waits for input. Terminal replies and ordinary keyboard input share the keyboard decoder.

Definition at line 924 of file colorscheme.c.

924 {
925 if (!palette)
926 return;
927 if (g_resolving_palette)
928 return;
929 memset(palette, 0, sizeof(*palette));
930 g_resolving_palette = true;
931 palette->background = current_background();
932 static _Thread_local colorscheme_palette_t cached;
933 static _Thread_local terminal_color_mode_t cached_mode;
934 static _Thread_local uint64_t cached_revision;
935 uint64_t revision = atomic_load(&g_scheme_revision);
936 if (cached_revision == revision && cached_mode == mode && cached.background == palette->background) {
937 *palette = cached;
938 g_resolving_palette = false;
939 return;
940 }
943 memcpy(colors,
945 : active->log_colors_dark,
946 sizeof(colors));
947 for (int i = 0; i < 9; ++i) {
948 char *code = i == 8 ? palette->foreground : palette->codes[i];
949 rgb_pixel_t rgb = colors[i == 8 ? 7 : i];
950 if (mode == TERM_COLOR_NONE)
951 continue;
952 if (i < 8) {
953 char *background = palette->background_codes[i];
954 if (i == 7)
955 safe_snprintf(background, COLORSCHEME_SGR_BUFFER_SIZE, "\033[49m");
956 else if (mode >= TERM_COLOR_TRUECOLOR)
957 safe_snprintf(background, COLORSCHEME_SGR_BUFFER_SIZE, "\033[48;2;%u;%u;%um", rgb.r, rgb.g, rgb.b);
958 else if (mode >= TERM_COLOR_256)
959 safe_snprintf(background, COLORSCHEME_SGR_BUFFER_SIZE, "\033[48;5;%dm", rgb_to_256color(rgb.r, rgb.g, rgb.b));
960 else {
961 unsigned index = rgb_to_16color(rgb.r, rgb.g, rgb.b);
962 safe_snprintf(background, COLORSCHEME_SGR_BUFFER_SIZE, "\033[%um", index < 8 ? 40 + index : 100 + index - 8);
963 }
964 }
965 if (i == 7)
967 else if (mode >= TERM_COLOR_TRUECOLOR)
968 rgb_to_truecolor_ansi(rgb.r, rgb.g, rgb.b, code, 32);
969 else if (mode >= TERM_COLOR_256)
970 safe_snprintf(code, COLORSCHEME_SGR_BUFFER_SIZE, "\033[38;5;%dm", rgb_to_256color(rgb.r, rgb.g, rgb.b));
971 else {
972 unsigned index = rgb_to_16color(rgb.r, rgb.g, rgb.b);
973 safe_snprintf(code, COLORSCHEME_SGR_BUFFER_SIZE, "\033[%um", index < 8 ? 30 + index : 90 + index - 8);
974 }
975 }
976 cached = *palette;
977 cached_mode = mode;
978 cached_revision = revision;
979 g_resolving_palette = false;
980}
#define COLORSCHEME_SGR_BUFFER_SIZE
Definition colorscheme.h:41
#define COLORSCHEME_LOG_COLOR_COUNT
Definition colorscheme.h:40
unsigned long long uint64_t
Definition common.h:63
static bool size_t index
Definition queries.h:8
Color scheme definition.
Definition colorscheme.h:76
rgb_pixel_t log_colors_dark[8]
Definition colorscheme.h:81
char background_codes[8][32]
terminal_background_t background

References rgb_pixel_t::b, colorscheme_palette_t::background, colorscheme_palette_t::background_codes, colorscheme_palette_t::codes, colors, colorscheme_get_active_scheme(), COLORSCHEME_LOG_COLOR_COUNT, COLORSCHEME_SGR_BUFFER_SIZE, colorscheme_palette_t::foreground, rgb_pixel_t::g, color_scheme_t::has_light_variant, index, color_scheme_t::log_colors_dark, color_scheme_t::log_colors_light, rgb_pixel_t::r, rgb_to_16color(), rgb_to_256color(), rgb_to_truecolor_ansi(), safe_snprintf(), and TERM_BACKGROUND_LIGHT.

Referenced by log_get_color_array(), log_level_color_for_output(), and ui_buttons_render().

◆ colorscheme_destroy()

void colorscheme_destroy ( void )

Shutdown the color system.

Frees all allocated resources. Call at program shutdown.

Definition at line 356 of file colorscheme.c.

356 {
357 if (!lifecycle_is_initialized(&g_colorscheme_lifecycle)) {
358 return;
359 }
360
361 mutex_lock(&g_colorscheme_mutex);
362 memset(&g_active_scheme, 0, sizeof(color_scheme_t));
363 atomic_fetch_add(&g_scheme_revision, 1);
364
365 /* Mark as uninitialized */
366 lifecycle_shutdown(&g_colorscheme_lifecycle);
367
368 mutex_unlock(&g_colorscheme_mutex);
369
370 /* NOTE: Do NOT call mutex_destroy() on native POSIX because the mutex is statically
371 * initialized with PTHREAD_MUTEX_INITIALIZER. Destroying a statically-initialized
372 * mutex is undefined behavior. On Windows and Emscripten, we must destroy the mutex
373 * because it was explicitly initialized in colorscheme_init(). */
374#if defined(_WIN32) || defined(__EMSCRIPTEN__)
375 mutex_destroy(&g_colorscheme_mutex);
376#endif
377}

References g_colorscheme_mutex.

Referenced by asciichat_shared_destroy().

◆ colorscheme_export_scheme()

asciichat_error_t colorscheme_export_scheme ( const char * scheme_name,
const char * file_path )

Export a color scheme to TOML format.

Parameters
scheme_nameName of scheme to export
file_pathOutput file path (NULL = stdout)
Returns
ASCIICHAT_OK on success, error code on failure

Exports a color scheme to TOML format. If file_path is NULL, writes to stdout.

Definition at line 676 of file colorscheme.c.

676 {
677 if (!scheme_name) {
678 return SET_ERRNO(ERROR_INVALID_PARAM, "Scheme name is NULL");
679 }
680
681 color_scheme_t scheme = {0};
682 asciichat_error_t result = colorscheme_load_builtin(scheme_name, &scheme);
683 if (result != ASCIICHAT_OK) {
684 return result;
685 }
686
687 /* Generate TOML content */
688 char toml_content[OPTIONS_COLORSCHEME_TOML_CONTENT_BUFFER_SIZE] = {0};
689 size_t offset = 0;
690
691 /* Scheme header section */
692 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset,
693 "[scheme]\n"
694 "name = \"%s\"\n"
695 "description = \"%s\"\n\n",
696 scheme.name, scheme.description);
697
698 /* Dark mode colors */
699 const char *color_names[] = {"dev", "debug", "info", "warn", "error", "fatal", "grey", "reset"};
700 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "[colors.dark]\n");
701
702 for (int i = 0; i < 8; i++) {
703 offset +=
704 safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "%s = \"#%02X%02X%02X\"\n", color_names[i],
705 scheme.log_colors_dark[i].r, scheme.log_colors_dark[i].g, scheme.log_colors_dark[i].b);
706 }
707
708 /* Light mode colors if available */
709 if (scheme.has_light_variant) {
710 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "\n[colors.light]\n");
711 for (int i = 0; i < 8; i++) {
712 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "%s = \"#%02X%02X%02X\"\n",
713 color_names[i], scheme.log_colors_light[i].r, scheme.log_colors_light[i].g,
714 scheme.log_colors_light[i].b);
715 }
716 }
717
718 /* Write to file or stdout */
719 if (file_path) {
720 FILE *fp = platform_fopen("file_stream", file_path, "w");
721 if (!fp) {
722 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Cannot open %s for writing", file_path);
723 }
724 defer(fclose(fp));
725 if (fputs(toml_content, fp) < 0) {
726 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Failed to write to %s", file_path);
727 }
728 } else {
729 /* Write to stdout */
730 if (fputs(toml_content, stdout) < 0) {
731 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Failed to write to stdout");
732 }
733 /* Flush to ensure piped output is written immediately */
734 (void)fflush(stdout);
735 }
736
737 return ASCIICHAT_OK;
738}
#define SET_ERRNO_SYS(code,...)
asciichat_error_t colorscheme_load_builtin(const char *name, color_scheme_t *scheme)
Load a built-in color scheme.
#define OPTIONS_COLORSCHEME_TOML_CONTENT_BUFFER_SIZE
Definition colorscheme.c:13
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_FILE_OPERATION
char file_path[4096]
Definition mmap.c:42
char description[256]
Definition colorscheme.h:78
char name[64]
Definition colorscheme.h:77

References ASCIICHAT_OK, rgb_pixel_t::b, colorscheme_load_builtin(), color_scheme_t::description, ERROR_FILE_OPERATION, ERROR_INVALID_PARAM, file_path, rgb_pixel_t::g, color_scheme_t::has_light_variant, color_scheme_t::log_colors_dark, color_scheme_t::log_colors_light, color_scheme_t::name, OPTIONS_COLORSCHEME_TOML_CONTENT_BUFFER_SIZE, rgb_pixel_t::r, safe_snprintf(), SET_ERRNO, and SET_ERRNO_SYS.

◆ colorscheme_get_active_scheme()

const color_scheme_t * colorscheme_get_active_scheme ( void )

Get currently active color scheme.

Returns
Pointer to active color scheme (must not be freed)

Returns the currently active color scheme. Default is "pastel" if not set.

Definition at line 383 of file colorscheme.c.

383 {
384 if (!lifecycle_is_initialized(&g_colorscheme_lifecycle)) {
385 /* Lazy initialization of color system */
387 }
388
389 static _Thread_local color_scheme_t snapshot;
390 static _Thread_local uint64_t revision;
391 if (revision != atomic_load(&g_scheme_revision)) {
392 mutex_lock(&g_colorscheme_mutex);
393 memcpy(&snapshot, &g_active_scheme, sizeof(snapshot));
394 revision = atomic_load(&g_scheme_revision);
395 mutex_unlock(&g_colorscheme_mutex);
396 }
397 return &snapshot;
398}
asciichat_error_t colorscheme_init(void)
Initialize the color system.

References colorscheme_init(), and g_colorscheme_mutex.

Referenced by colorscheme_apply(), colorscheme_current_palette(), and log_init_colors().

◆ colorscheme_highlight_background()

uint8_t colorscheme_highlight_background ( void )

Theme-adaptive neutral search highlight, with contrast against the last RGB reply.

Definition at line 992 of file colorscheme.c.

992 {
994 uint8_t grey = dark ? 70 : 200;
995#if !defined(__EMSCRIPTEN__)
996 uint8_t r, g, b;
997 if (keyboard_is_initialized() && keyboard_stream_background(&r, &g, &b) && abs((int)((r + g + b) / 3) - grey) < 40)
998 grey = dark ? 0 : 255;
999#endif
1000 return grey;
1001}
terminal_background_t colorscheme_current_background(void)
bool keyboard_stream_background(uint8_t *r, uint8_t *g, uint8_t *b)
bool keyboard_is_initialized(void)

References colorscheme_current_background(), keyboard_is_initialized(), keyboard_stream_background(), and TERM_BACKGROUND_DARK.

Referenced by ui_buttons_render().

◆ colorscheme_init()

asciichat_error_t colorscheme_init ( void )

Initialize the color system.

Returns
ASCIICHAT_OK on success, error code on failure

Must be called once at program startup before using any color functions.

Definition at line 290 of file colorscheme.c.

290 {
291 if (lifecycle_is_initialized(&g_colorscheme_lifecycle)) {
292 return ASCIICHAT_OK;
293 }
294
295 /* NOTE: Mutex is already statically initialized on POSIX with PTHREAD_MUTEX_INITIALIZER.
296 * On Windows and Emscripten/WASM, we initialize it here. Do NOT call mutex_init() on native POSIX
297 * because double-initialization of pthread_mutex_t causes undefined behavior and deadlocks.
298 * Emscripten with pthreads requires explicit initialization because PTHREAD_MUTEX_INITIALIZER
299 * doesn't work correctly in threaded WASM builds. */
300#if defined(_WIN32) || defined(__EMSCRIPTEN__)
301 static bool mutex_initialized = false;
302 if (!mutex_initialized) {
303#ifdef __EMSCRIPTEN__
304#else
305#endif
306 mutex_init(&g_colorscheme_mutex, "colorscheme");
307 mutex_initialized = true;
308 } else {
309 }
310#else
311#endif
312
313 /* Load default scheme */
314 const color_scheme_t *pastel = find_builtin_scheme("pastel");
315 if (!pastel) {
316 return SET_ERRNO(ERROR_CONFIG, "Failed to load default pastel scheme");
317 }
318
319 memcpy(&g_active_scheme, pastel, sizeof(color_scheme_t));
320 atomic_fetch_add(&g_scheme_revision, 1);
321
322 /* Mark as initialized */
323 lifecycle_init(&g_colorscheme_lifecycle, "colorscheme");
324#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
325 // Register the statically initialized mutex without initializing its pthread storage again.
327#endif
328
329 return ASCIICHAT_OK;
330}
#define NAMED_REGISTER_MUTEX(mutex, name, parent_ptr)
Register a mutex with automatic format specifier.
@ ERROR_CONFIG
Definition error_codes.h:52

References ASCIICHAT_OK, ERROR_CONFIG, g_colorscheme_mutex, NAMED_REGISTER_MUTEX, and SET_ERRNO.

Referenced by colorscheme_get_active_scheme(), colorscheme_set_active_scheme(), and options_colorscheme_init_early().

◆ colorscheme_load_builtin()

asciichat_error_t colorscheme_load_builtin ( const char * name,
color_scheme_t * scheme )

Load a built-in color scheme.

Parameters
nameScheme name
schemePointer to store loaded scheme
Returns
ASCIICHAT_OK on success, error code if not found

Loads a built-in color scheme by name. "default" is aliased to "pastel".

Definition at line 436 of file colorscheme.c.

436 {
437 if (!name || !scheme) {
438 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL name or scheme pointer");
439 }
440
441 const color_scheme_t *builtin = find_builtin_scheme(name);
442 if (!builtin) {
443 return SET_ERRNO(ERROR_CONFIG, "Unknown built-in color scheme: %s", name);
444 }
445
446 memcpy(scheme, builtin, sizeof(color_scheme_t));
447 return ASCIICHAT_OK;
448}

References ASCIICHAT_OK, ERROR_CONFIG, ERROR_INVALID_PARAM, and SET_ERRNO.

Referenced by colorscheme_export_scheme(), and main().

◆ colorscheme_load_from_file()

asciichat_error_t colorscheme_load_from_file ( const char * path,
color_scheme_t * scheme )

Load a color scheme from a TOML file.

Parameters
pathPath to TOML file
schemePointer to store loaded scheme
Returns
ASCIICHAT_OK on success, error code on failure

Loads a color scheme from a TOML configuration file. See docs/colors.toml for format specification.

Definition at line 450 of file colorscheme.c.

450 {
451 if (!path || !scheme) {
452 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL path or scheme pointer");
453 }
454
455 /* Check if file exists and is readable */
456 struct stat sb;
457 if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode)) {
458 return SET_ERRNO(ERROR_FILE_NOT_FOUND, "Color scheme file not found or not readable: %s", path);
459 }
460
461 /* Parse TOML file */
462 toml_result_t result = toml_parse_file_ex(path);
463 if (!result.ok) {
464 toml_free(result);
465 return SET_ERRNO(ERROR_CONFIG, "Failed to parse color scheme file '%s': %s", path, result.errmsg);
466 }
467
468 /* Extract scheme information */
469 memset(scheme, 0, sizeof(color_scheme_t));
470
471 /* Get scheme section */
472 toml_datum_t scheme_section = toml_get(result.toptab, "scheme");
473 if (scheme_section.type == TOML_TABLE) {
474 /* Get scheme name */
475 toml_datum_t name_datum = toml_get(scheme_section, "name");
476 if (name_datum.type == TOML_STRING) {
477 SAFE_STRNCPY(scheme->name, name_datum.u.s, sizeof(scheme->name));
478 }
479
480 /* Get scheme description */
481 toml_datum_t desc_datum = toml_get(scheme_section, "description");
482 if (desc_datum.type == TOML_STRING) {
483 SAFE_STRNCPY(scheme->description, desc_datum.u.s, sizeof(scheme->description));
484 }
485 }
486
487 /* Get colors section */
488 toml_datum_t colors_section = toml_get(result.toptab, "colors");
489 if (colors_section.type == TOML_TABLE) {
490 /* Parse dark mode colors */
491 toml_datum_t dark_section = toml_get(colors_section, "dark");
492 if (dark_section.type == TOML_TABLE) {
493 const char *color_names[] = {"dev", "debug", "info", "warn", "error", "fatal", "grey", "reset"};
494 for (int i = 0; i < 8; i++) {
495 toml_datum_t color_value = toml_get(dark_section, color_names[i]);
496 if (color_value.type == TOML_STRING) {
497 parse_hex_color(color_value.u.s, &scheme->log_colors_dark[i].r, &scheme->log_colors_dark[i].g,
498 &scheme->log_colors_dark[i].b);
499 }
500 }
501 }
502
503 /* Parse light mode colors (optional) */
504 toml_datum_t light_section = toml_get(colors_section, "light");
505 if (light_section.type == TOML_TABLE) {
506 scheme->has_light_variant = true;
507 const char *color_names[] = {"dev", "debug", "info", "warn", "error", "fatal", "grey", "reset"};
508 for (int i = 0; i < 8; i++) {
509 toml_datum_t color_value = toml_get(light_section, color_names[i]);
510 if (color_value.type == TOML_STRING) {
511 parse_hex_color(color_value.u.s, &scheme->log_colors_light[i].r, &scheme->log_colors_light[i].g,
512 &scheme->log_colors_light[i].b);
513 }
514 }
515 }
516 }
517
518 scheme->is_builtin = false;
519 SAFE_STRNCPY(scheme->source_file, path, sizeof(scheme->source_file));
520
521 toml_free(result);
522 return ASCIICHAT_OK;
523}
asciichat_error_t parse_hex_color(const char *hex, uint8_t *r, uint8_t *g, uint8_t *b)
Parse a hex color string.
@ ERROR_FILE_NOT_FOUND
char source_file[4096]
Definition colorscheme.h:89

References ASCIICHAT_OK, rgb_pixel_t::b, color_scheme_t::description, ERROR_CONFIG, ERROR_FILE_NOT_FOUND, ERROR_INVALID_PARAM, rgb_pixel_t::g, color_scheme_t::has_light_variant, color_scheme_t::is_builtin, color_scheme_t::log_colors_dark, color_scheme_t::log_colors_light, color_scheme_t::name, parse_hex_color(), rgb_pixel_t::r, SAFE_STRNCPY, SET_ERRNO, and color_scheme_t::source_file.

Referenced by colorscheme_set_active_scheme().

◆ colorscheme_set_active_scheme()

asciichat_error_t colorscheme_set_active_scheme ( const char * name)

Set the active color scheme.

Parameters
nameScheme name (e.g., "pastel", "nord", "solarized-dark")
Returns
ASCIICHAT_OK on success, error code if scheme not found

Changes the active color scheme. Scheme must be either built-in or previously loaded from a TOML file.

Definition at line 400 of file colorscheme.c.

400 {
401 if (!name) {
402 return SET_ERRNO(ERROR_INVALID_PARAM, "Scheme name is NULL");
403 }
404
405 /* Ensure color system is initialized */
406 if (!lifecycle_is_initialized(&g_colorscheme_lifecycle)) {
408 }
409
410 color_scheme_t scheme = {0};
412
413 /* Try loading as built-in scheme first */
414 const color_scheme_t *builtin = find_builtin_scheme(name);
415 if (builtin) {
416 memcpy(&scheme, builtin, sizeof(color_scheme_t));
417 } else if (strchr(name, '/') || strchr(name, '.')) {
418 /* Try loading as file path if it contains / or . */
419 result = colorscheme_load_from_file(name, &scheme);
420 if (result != ASCIICHAT_OK) {
421 return result;
422 }
423 } else {
424 return SET_ERRNO(ERROR_CONFIG, "Unknown color scheme: %s (not a built-in scheme or valid file path)", name);
425 }
426
427 mutex_lock(&g_colorscheme_mutex);
428 memcpy(&g_active_scheme, &scheme, sizeof(color_scheme_t));
429 atomic_fetch_add(&g_scheme_revision, 1);
430 mutex_unlock(&g_colorscheme_mutex);
431
432 log_debug("Switched to color scheme: %s", name);
433 return ASCIICHAT_OK;
434}
asciichat_error_t colorscheme_load_from_file(const char *path, color_scheme_t *scheme)
Load a color scheme from a TOML file.
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595

References ASCIICHAT_OK, colorscheme_init(), colorscheme_load_from_file(), ERROR_CONFIG, ERROR_INVALID_PARAM, g_colorscheme_mutex, log_debug, and SET_ERRNO.

Referenced by options_colorscheme_init_early().

◆ detect_terminal_background()

terminal_background_t detect_terminal_background ( void )

Detect terminal background (light or dark).

Returns
Detected terminal background

Uses multiple methods to detect terminal background:

  1. TERM_BACKGROUND environment variable
  2. COLORFGBG environment variable
  3. OSC 11 terminal query (if supported)
  4. Defaults to DARK

Detect terminal background (light or dark).

Determines the terminal's color background theme to adapt color schemes accordingly. The detection uses a priority-based approach:

  1. Environment variable override (TERM_BACKGROUND="light" or "dark")
  2. Automatic detection via terminal_has_dark_background():
    • OSC 11 escape query with luminance calculation (modern terminals)
    • Environment variables and terminal type hints (fallback)
Returns
The detected theme: TERM_BACKGROUND_LIGHT or TERM_BACKGROUND_DARK

Definition at line 756 of file colorscheme.c.

756 {
757 /* Method 1: Check environment variable override (highest priority) */
758 const char *term_bg = SAFE_GETENV("TERM_BACKGROUND");
759 if (term_bg) {
760 if (platform_strcasecmp(term_bg, "light") == 0) {
762 }
763 if (platform_strcasecmp(term_bg, "dark") == 0) {
765 }
766 }
767
768 /* Method 2: Use terminal_has_dark_background() for automatic theme detection
769 * This checks OSC 11 background color, environment variables, and terminal type */
770 bool is_dark = terminal_has_dark_background();
772}
#define SAFE_GETENV(name)
Definition common.h:449

References SAFE_GETENV, TERM_BACKGROUND_DARK, and TERM_BACKGROUND_LIGHT.

◆ options_colorscheme_init_early()

asciichat_error_t options_colorscheme_init_early ( int argc,
const char *const argv[] )

Initialize color scheme early, before logging.

Parameters
argcCommand-line argument count
argvCommand-line arguments
Returns
ASCIICHAT_OK on success, error code on failure (non-fatal)

Called from main() BEFORE log_init() to apply color scheme to logging. Scans for –color-scheme and loads ~/.config/ascii-chat/colors.toml.

Priority: –color-scheme CLI > colors.toml > built-in default

Initialize color scheme early, before logging.

Parameters
argcArgument count
argvArgument array
Returns
ASCIICHAT_OK on success, error code on failure (non-fatal)

This function is called from main() BEFORE log_init() to apply color scheme to logging before any log messages are printed.

Priority order:

  1. –color-scheme CLI argument (highest priority)
  2. ~/.config/ascii-chat/colors.toml config file
  3. Built-in "pastel" default scheme (lowest priority)

Definition at line 851 of file colorscheme.c.

851 {
852 /* Initialize the color system with defaults */
854 if (result != ASCIICHAT_OK) {
855 /* Non-fatal: use built-in defaults */
856 return ASCIICHAT_OK;
857 }
858
859 /* Step 1: Try to load from config file (~/.config/ascii-chat/colors.toml) */
860 color_scheme_t config_scheme = {0};
861 asciichat_error_t config_result = load_config_color_scheme(&config_scheme);
862 if (config_result == ASCIICHAT_OK) {
863 /* Config file loaded successfully, apply it */
864 colorscheme_set_active_scheme(config_scheme.name);
865 }
866
867 /* Step 2: CLI --color-scheme overrides config file */
868 const char *cli_scheme = find_cli_color_scheme(argc, argv);
869 if (cli_scheme) {
870 asciichat_error_t cli_result = colorscheme_set_active_scheme(cli_scheme);
871 if (cli_result != ASCIICHAT_OK) {
872 /* Invalid scheme name from CLI, continue with current scheme */
873 return cli_result;
874 }
875 }
876
877 return ASCIICHAT_OK;
878}
asciichat_error_t colorscheme_set_active_scheme(const char *name)
Set the active color scheme.

References ASCIICHAT_OK, colorscheme_init(), colorscheme_set_active_scheme(), and color_scheme_t::name.

Referenced by main().

◆ parse_hex_color()

asciichat_error_t parse_hex_color ( const char * hex,
uint8_t * r,
uint8_t * g,
uint8_t * b )

Parse a hex color string.

Parameters
hexHex color string (e.g., "#FF0000" or "FF0000")
rPointer to store red channel
gPointer to store green channel
bPointer to store blue channel
Returns
ASCIICHAT_OK on success, error code on failure

Parses hex color strings in #RRGGBB or RRGGBB format.

Definition at line 529 of file colorscheme.c.

529 {
530 if (!hex || !r || !g || !b) {
531 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL parameter");
532 }
533
534 /* Skip '#' prefix if present */
535 if (hex[0] == '#') {
536 hex++;
537 }
538
539 /* Validate hex string length */
540 if (strlen(hex) != 6) {
541 return SET_ERRNO(ERROR_CONFIG, "Invalid hex color (must be #RRGGBB): %s", hex);
542 }
543
544 for (size_t i = 0; i < 6; ++i) {
545 if (!((hex[i] >= '0' && hex[i] <= '9') || (hex[i] >= 'a' && hex[i] <= 'f') || (hex[i] >= 'A' && hex[i] <= 'F'))) {
546 return SET_ERRNO(ERROR_CONFIG, "Invalid hex color format: %s", hex);
547 }
548 }
549
550 /* Parse hex digits */
551 unsigned int rgb = 0;
552 if (SAFE_SSCANF(hex, "%6x", &rgb) != 1) {
553 return SET_ERRNO(ERROR_CONFIG, "Invalid hex color format: %s", hex);
554 }
555
556 *r = (rgb >> 16) & 0xFF;
557 *g = (rgb >> 8) & 0xFF;
558 *b = rgb & 0xFF;
559
560 return ASCIICHAT_OK;
561}
#define SAFE_SSCANF(str, format,...)
Definition common.h:455

References ASCIICHAT_OK, ERROR_CONFIG, ERROR_INVALID_PARAM, SAFE_SSCANF, and SET_ERRNO.

Referenced by colorscheme_load_from_file().

◆ rgb_to_truecolor_ansi()

void rgb_to_truecolor_ansi ( uint8_t r,
uint8_t g,
uint8_t b,
char * buf,
size_t size )

Generate truecolor ANSI escape code.

Parameters
rRed channel (0-255)
gGreen channel (0-255)
bBlue channel (0-255)
bufOutput buffer for escape code
sizeBuffer size (minimum 20 bytes)

Generates ANSI truecolor escape code: \x1b[38;2;R;G;Bm

Definition at line 567 of file colorscheme.c.

567 {
568 if (!buf || size < 20)
569 return;
570 safe_snprintf(buf, size, "\x1b[38;2;%d;%d;%dm", r, g, b);
571}

References safe_snprintf().

Referenced by colorscheme_compile_scheme(), and colorscheme_current_palette().

Variable Documentation

◆ g_colorscheme_mutex

mutex_t g_colorscheme_mutex
extern

Shared mutex for color scheme compilation.

Used by lib/options/colorscheme.c and lib/log/logging.c to synchronize color scheme compilation. Defined in colorscheme.c and declared here for use by logging.c.

Definition at line 50 of file colorscheme.c.

50{.impl = PTHREAD_MUTEX_INITIALIZER, .name = NULL}; /* POSIX pthread_mutex_t */

Referenced by colorscheme_apply(), colorscheme_destroy(), colorscheme_get_active_scheme(), colorscheme_init(), and colorscheme_set_active_scheme().