ascii-chat 0.11.33
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...
 

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 }
 Terminal theme detection result. More...
 

Functions

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.
 

Typedef Documentation

◆ 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

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 50 of file colorscheme.h.

50 {
terminal_background_t
Terminal theme detection result.
Definition colorscheme.h:50
@ TERM_BACKGROUND_DARK
Definition colorscheme.h:53
@ TERM_BACKGROUND_UNKNOWN
Definition colorscheme.h:51
@ TERM_BACKGROUND_LIGHT
Definition colorscheme.h:52

Function Documentation

◆ 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 316 of file colorscheme.c.

316 {
317 if (!compiled) {
318 return;
319 }
320
321 /* Free allocated color code strings */
322 for (int i = 0; i < 8; i++) {
323 char *str_16 = (char *)compiled->codes_16[i];
324 char *str_256 = (char *)compiled->codes_256[i];
325 char *str_truecolor = (char *)compiled->codes_truecolor[i];
326 /* Only free non-NULL pointers - protects against uninitialized data */
327 if (str_16 != NULL) {
328 SAFE_FREE(str_16);
329 }
330 if (str_256 != NULL) {
331 SAFE_FREE(str_256);
332 }
333 if (str_truecolor != NULL) {
334 SAFE_FREE(str_truecolor);
335 }
336 }
337 memset(compiled, 0, sizeof(compiled_color_scheme_t));
338}
#define SAFE_FREE(ptr)
Definition common.h:376
Compiled ANSI escape codes for a color scheme.
Definition colorscheme.h:85
const char * codes_16[8]
Definition colorscheme.h:86
const char * codes_256[8]
Definition colorscheme.h:87
const char * codes_truecolor[8]
Definition colorscheme.h:88

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(), and log_cleanup_colors().

◆ 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 545 of file colorscheme.c.

546 {
547 if (!scheme || !compiled) {
548 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL scheme or compiled pointer");
549 }
550
551 /* Note: mode parameter reserved for future use */
552 (void)mode;
553
554 /* Free any previously compiled strings before recompiling */
555 /* This prevents memory leaks when the color scheme is recompiled */
556 /* Only cleanup if we've actually compiled before (codes_16[0] != NULL means already compiled) */
557 if (compiled->codes_16[0] != NULL) {
559 } else {
560 }
561
562 /* Select color array based on background */
563 const rgb_pixel_t *colors = (background == TERM_BACKGROUND_LIGHT && scheme->has_light_variant)
564 ? scheme->log_colors_light
565 : scheme->log_colors_dark;
566 /* Helper: allocate and format a color code string */
567 char temp_buf[128];
568
569 /* Compile for 16-color mode */
570 for (int i = 0; i < 8; i++) {
571 if (i == 7) {
572 /* RESET */
573 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
574 } else {
575 uint8_t color_idx = rgb_to_16color(colors[i].r, colors[i].g, colors[i].b);
576 /* ANSI color codes: 30-37 for normal, 90-97 for bright */
577 if (color_idx < 8) {
578 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[%dm", 30 + color_idx);
579 } else {
580 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[%dm", 90 + (color_idx - 8));
581 }
582 }
583 /* Allocate string with SAFE_MALLOC and copy */
584 size_t len = strlen(temp_buf) + 1;
585 char *allocated = SAFE_MALLOC(len, char *);
586 if (!allocated) {
587 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
588 }
589 memcpy(allocated, temp_buf, len);
590 compiled->codes_16[i] = allocated;
591 }
592 /* Compile for 256-color mode */
593 for (int i = 0; i < 8; i++) {
594 if (i == 7) {
595 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
596 } else {
597 uint8_t color_idx = rgb_to_256color(colors[i].r, colors[i].g, colors[i].b);
598 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[38;5;%dm", color_idx);
599 }
600 /* Allocate string with SAFE_MALLOC and copy */
601 size_t len = strlen(temp_buf) + 1;
602 char *allocated = SAFE_MALLOC(len, char *);
603 if (!allocated) {
604 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
605 }
606 memcpy(allocated, temp_buf, len);
607 compiled->codes_256[i] = allocated;
608 }
609
610 /* Compile for truecolor mode */
611 for (int i = 0; i < 8; i++) {
612 if (i == 7) {
613 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
614 } else {
615 rgb_to_truecolor_ansi(colors[i].r, colors[i].g, colors[i].b, temp_buf, sizeof(temp_buf));
616 }
617 /* Allocate string with SAFE_MALLOC and copy */
618 size_t len = strlen(temp_buf) + 1;
619 char *allocated = SAFE_MALLOC(len, char *);
620 if (!allocated) {
621 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
622 }
623 memcpy(allocated, temp_buf, len);
624 compiled->codes_truecolor[i] = allocated;
625 }
626
627 return ASCIICHAT_OK;
628}
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.
uint8_t rgb_to_16color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:437
uint8_t rgb_to_256color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:360
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_MALLOC(size, cast)
Definition common.h:264
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
rgb_pixel_t log_colors_light[8]
Definition colorscheme.h:71
bool has_light_variant
Definition colorscheme.h:70
RGB pixel structure.

References ASCIICHAT_OK, compiled_color_scheme_t::codes_16, compiled_color_scheme_t::codes_256, compiled_color_scheme_t::codes_truecolor, colorscheme_cleanup_compiled(), ERROR_INVALID_PARAM, ERROR_MEMORY, color_scheme_t::has_light_variant, color_scheme_t::log_colors_dark, color_scheme_t::log_colors_light, rgb_to_16color(), rgb_to_256color(), rgb_to_truecolor_ansi(), SAFE_MALLOC, safe_snprintf(), SAFE_STRNCPY, SET_ERRNO, and TERM_BACKGROUND_LIGHT.

Referenced by log_init_colors(), and log_set_color_scheme().

◆ colorscheme_destroy()

void colorscheme_destroy ( void  )

Shutdown the color system.

Frees all allocated resources. Call at program shutdown.

Definition at line 340 of file colorscheme.c.

340 {
341 if (!lifecycle_is_initialized(&g_colorscheme_lifecycle)) {
342 return;
343 }
344
346 memset(&g_active_scheme, 0, sizeof(color_scheme_t));
347
348 /* Mark as uninitialized */
349 lifecycle_shutdown(&g_colorscheme_lifecycle);
350
352
353 /* NOTE: Do NOT call mutex_destroy() on native POSIX because the mutex is statically
354 * initialized with PTHREAD_MUTEX_INITIALIZER. Destroying a statically-initialized
355 * mutex is undefined behavior. On Windows and Emscripten, we must destroy the mutex
356 * because it was explicitly initialized in colorscheme_init(). */
357#if defined(_WIN32) || defined(__EMSCRIPTEN__)
359#endif
360}
mutex_t g_colorscheme_mutex
Shared mutex for color scheme compilation.
Definition colorscheme.c:39
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
Definition threading.c:22
bool lifecycle_is_initialized(const lifecycle_t *lc)
Definition lifecycle.c:155
bool lifecycle_shutdown(lifecycle_t *lc)
Definition lifecycle.c:108
Color scheme definition.
Definition colorscheme.h:62

References g_colorscheme_mutex, lifecycle_is_initialized(), lifecycle_shutdown(), mutex_destroy(), mutex_lock, and mutex_unlock.

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 634 of file colorscheme.c.

634 {
635 if (!scheme_name) {
636 return SET_ERRNO(ERROR_INVALID_PARAM, "Scheme name is NULL");
637 }
638
639 color_scheme_t scheme = {0};
640 asciichat_error_t result = colorscheme_load_builtin(scheme_name, &scheme);
641 if (result != ASCIICHAT_OK) {
642 return result;
643 }
644
645 /* Generate TOML content */
646 char toml_content[8192] = {0};
647 size_t offset = 0;
648
649 /* Scheme header section */
650 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset,
651 "[scheme]\n"
652 "name = \"%s\"\n"
653 "description = \"%s\"\n\n",
654 scheme.name, scheme.description);
655
656 /* Dark mode colors */
657 const char *color_names[] = {"dev", "debug", "warn", "info", "error", "fatal", "grey", "reset"};
658 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "[colors.dark]\n");
659
660 for (int i = 0; i < 8; i++) {
661 offset +=
662 safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "%s = \"#%02X%02X%02X\"\n", color_names[i],
663 scheme.log_colors_dark[i].r, scheme.log_colors_dark[i].g, scheme.log_colors_dark[i].b);
664 }
665
666 /* Light mode colors if available */
667 if (scheme.has_light_variant) {
668 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "\n[colors.light]\n");
669 for (int i = 0; i < 8; i++) {
670 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "%s = \"#%02X%02X%02X\"\n",
671 color_names[i], scheme.log_colors_light[i].r, scheme.log_colors_light[i].g,
672 scheme.log_colors_light[i].b);
673 }
674 }
675
676 /* Write to file or stdout */
677 if (file_path) {
678 FILE *fp = platform_fopen("file_stream", file_path, "w");
679 if (!fp) {
680 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Cannot open %s for writing", file_path);
681 }
682 if (fputs(toml_content, fp) < 0) {
683 fclose(fp);
684 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Failed to write to %s", file_path);
685 }
686 fclose(fp);
687 } else {
688 /* Write to stdout */
689 if (fputs(toml_content, stdout) < 0) {
690 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Failed to write to stdout");
691 }
692 /* Flush to ensure piped output is written immediately */
693 (void)fflush(stdout);
694 }
695
696 return ASCIICHAT_OK;
697}
asciichat_error_t colorscheme_load_builtin(const char *name, color_scheme_t *scheme)
Load a built-in color scheme.
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_FILE_OPERATION
FILE * platform_fopen(const char *name, const char *filename, const char *mode)
Safe file open stream (fopen replacement)
char file_path[PLATFORM_MAX_PATH_LENGTH]
Definition mmap.c:39
char description[256]
Definition colorscheme.h:64
rgb_pixel_t log_colors_dark[8]
Definition colorscheme.h:67
char name[64]
Definition colorscheme.h:63
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, 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, platform_fopen(), 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 366 of file colorscheme.c.

366 {
367 if (!lifecycle_is_initialized(&g_colorscheme_lifecycle)) {
368 /* Lazy initialization of color system */
370 }
371
372 return &g_active_scheme;
373}
asciichat_error_t colorscheme_init(void)
Initialize the color system.

References colorscheme_init(), and lifecycle_is_initialized().

Referenced by log_init_colors(), and options_init().

◆ 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 279 of file colorscheme.c.

279 {
280 if (lifecycle_is_initialized(&g_colorscheme_lifecycle)) {
281 return ASCIICHAT_OK;
282 }
283
284 /* NOTE: Mutex is already statically initialized on POSIX with PTHREAD_MUTEX_INITIALIZER.
285 * On Windows and Emscripten/WASM, we initialize it here. Do NOT call mutex_init() on native POSIX
286 * because double-initialization of pthread_mutex_t causes undefined behavior and deadlocks.
287 * Emscripten with pthreads requires explicit initialization because PTHREAD_MUTEX_INITIALIZER
288 * doesn't work correctly in threaded WASM builds. */
289#if defined(_WIN32) || defined(__EMSCRIPTEN__)
290 static bool mutex_initialized = false;
291 if (!mutex_initialized) {
292#ifdef __EMSCRIPTEN__
293#else
294#endif
295 mutex_init(&g_colorscheme_mutex, "colorscheme");
296 mutex_initialized = true;
297 } else {
298 }
299#else
300#endif
301
302 /* Load default scheme */
303 const color_scheme_t *pastel = find_builtin_scheme("pastel");
304 if (!pastel) {
305 return SET_ERRNO(ERROR_CONFIG, "Failed to load default pastel scheme");
306 }
307
308 memcpy(&g_active_scheme, pastel, sizeof(color_scheme_t));
309
310 /* Mark as initialized */
311 lifecycle_init(&g_colorscheme_lifecycle, "colorscheme");
312
313 return ASCIICHAT_OK;
314}
@ ERROR_CONFIG
Definition error_codes.h:57
int mutex_init(mutex_t *mutex, const char *name)
Initialize a mutex with a name.
Definition threading.c:16
bool lifecycle_init(lifecycle_t *lc, const char *name)
Definition lifecycle.c:26

References ASCIICHAT_OK, ERROR_CONFIG, g_colorscheme_mutex, lifecycle_init(), lifecycle_is_initialized(), mutex_init(), 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 410 of file colorscheme.c.

410 {
411 if (!name || !scheme) {
412 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL name or scheme pointer");
413 }
414
415 const color_scheme_t *builtin = find_builtin_scheme(name);
416 if (!builtin) {
417 return SET_ERRNO(ERROR_CONFIG, "Unknown built-in color scheme: %s", name);
418 }
419
420 memcpy(scheme, builtin, sizeof(color_scheme_t));
421 return ASCIICHAT_OK;
422}

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 424 of file colorscheme.c.

424 {
425 if (!path || !scheme) {
426 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL path or scheme pointer");
427 }
428
429 /* Check if file exists and is readable */
430 struct stat sb;
431 if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode)) {
432 return SET_ERRNO(ERROR_FILE_NOT_FOUND, "Color scheme file not found or not readable: %s", path);
433 }
434
435 /* Parse TOML file */
436 toml_result_t result = toml_parse_file_ex(path);
437 if (!result.ok) {
438 toml_free(result);
439 return SET_ERRNO(ERROR_CONFIG, "Failed to parse color scheme file '%s': %s", path, result.errmsg);
440 }
441
442 /* Extract scheme information */
443 memset(scheme, 0, sizeof(color_scheme_t));
444
445 /* Get scheme section */
446 toml_datum_t scheme_section = toml_get(result.toptab, "scheme");
447 if (scheme_section.type == TOML_TABLE) {
448 /* Get scheme name */
449 toml_datum_t name_datum = toml_get(scheme_section, "name");
450 if (name_datum.type == TOML_STRING) {
451 SAFE_STRNCPY(scheme->name, name_datum.u.s, sizeof(scheme->name));
452 }
453
454 /* Get scheme description */
455 toml_datum_t desc_datum = toml_get(scheme_section, "description");
456 if (desc_datum.type == TOML_STRING) {
457 SAFE_STRNCPY(scheme->description, desc_datum.u.s, sizeof(scheme->description));
458 }
459 }
460
461 /* Get colors section */
462 toml_datum_t colors_section = toml_get(result.toptab, "colors");
463 if (colors_section.type == TOML_TABLE) {
464 /* Parse dark mode colors */
465 toml_datum_t dark_section = toml_get(colors_section, "dark");
466 if (dark_section.type == TOML_TABLE) {
467 const char *color_names[] = {"dev", "debug", "warn", "info", "error", "fatal", "grey", "reset"};
468 for (int i = 0; i < 8; i++) {
469 toml_datum_t color_value = toml_get(dark_section, color_names[i]);
470 if (color_value.type == TOML_STRING) {
471 parse_hex_color(color_value.u.s, &scheme->log_colors_dark[i].r, &scheme->log_colors_dark[i].g,
472 &scheme->log_colors_dark[i].b);
473 }
474 }
475 }
476
477 /* Parse light mode colors (optional) */
478 toml_datum_t light_section = toml_get(colors_section, "light");
479 if (light_section.type == TOML_TABLE) {
480 scheme->has_light_variant = true;
481 const char *color_names[] = {"dev", "debug", "warn", "info", "error", "fatal", "grey", "reset"};
482 for (int i = 0; i < 8; i++) {
483 toml_datum_t color_value = toml_get(light_section, color_names[i]);
484 if (color_value.type == TOML_STRING) {
485 parse_hex_color(color_value.u.s, &scheme->log_colors_light[i].r, &scheme->log_colors_light[i].g,
486 &scheme->log_colors_light[i].b);
487 }
488 }
489 }
490 }
491
492 scheme->is_builtin = false;
493 SAFE_STRNCPY(scheme->source_file, path, sizeof(scheme->source_file));
494
495 toml_free(result);
496 return ASCIICHAT_OK;
497}
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:75

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 375 of file colorscheme.c.

375 {
376 if (!name) {
377 return SET_ERRNO(ERROR_INVALID_PARAM, "Scheme name is NULL");
378 }
379
380 /* Ensure color system is initialized */
381 if (!lifecycle_is_initialized(&g_colorscheme_lifecycle)) {
383 }
384
385 color_scheme_t scheme = {0};
387
388 /* Try loading as built-in scheme first */
389 const color_scheme_t *builtin = find_builtin_scheme(name);
390 if (builtin) {
391 memcpy(&scheme, builtin, sizeof(color_scheme_t));
392 } else if (strchr(name, '/') || strchr(name, '.')) {
393 /* Try loading as file path if it contains / or . */
394 result = colorscheme_load_from_file(name, &scheme);
395 if (result != ASCIICHAT_OK) {
396 return result;
397 }
398 } else {
399 return SET_ERRNO(ERROR_CONFIG, "Unknown color scheme: %s (not a built-in scheme or valid file path)", name);
400 }
401
403 memcpy(&g_active_scheme, &scheme, sizeof(color_scheme_t));
405
406 log_debug("Switched to color scheme: %s", name);
407 return ASCIICHAT_OK;
408}
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:548

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

Referenced by options_colorscheme_init_early(), and options_init().

◆ 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 715 of file colorscheme.c.

715 {
716 /* Method 1: Check environment variable override (highest priority) */
717 const char *term_bg = SAFE_GETENV("TERM_BACKGROUND");
718 if (term_bg) {
719 if (platform_strcasecmp(term_bg, "light") == 0) {
721 }
722 if (platform_strcasecmp(term_bg, "dark") == 0) {
724 }
725 }
726
727 /* Method 2: Use terminal_has_dark_background() for automatic theme detection
728 * This checks OSC 11 background color, environment variables, and terminal type */
729 bool is_dark = terminal_has_dark_background();
731}
#define SAFE_GETENV(name)
Definition common.h:434
int platform_strcasecmp(const char *s1, const char *s2)
Case-insensitive string comparison.
bool terminal_has_dark_background(void)
Detect if terminal theme is dark.

References platform_strcasecmp(), SAFE_GETENV, TERM_BACKGROUND_DARK, TERM_BACKGROUND_LIGHT, and terminal_has_dark_background().

Referenced by log_init_colors(), and log_set_color_scheme().

◆ 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 810 of file colorscheme.c.

810 {
811 /* Initialize the color system with defaults */
813 if (result != ASCIICHAT_OK) {
814 /* Non-fatal: use built-in defaults */
815 return ASCIICHAT_OK;
816 }
817
818 /* Step 1: Try to load from config file (~/.config/ascii-chat/colors.toml) */
819 color_scheme_t config_scheme = {0};
820 asciichat_error_t config_result = load_config_color_scheme(&config_scheme);
821 if (config_result == ASCIICHAT_OK) {
822 /* Config file loaded successfully, apply it */
823 colorscheme_set_active_scheme(config_scheme.name);
824 }
825
826 /* Step 2: CLI --color-scheme overrides config file */
827 const char *cli_scheme = find_cli_color_scheme(argc, argv);
828 if (cli_scheme) {
829 asciichat_error_t cli_result = colorscheme_set_active_scheme(cli_scheme);
830 if (cli_result != ASCIICHAT_OK) {
831 /* Invalid scheme name from CLI, continue with current scheme */
832 return cli_result;
833 }
834 }
835
836 return ASCIICHAT_OK;
837}
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 503 of file colorscheme.c.

503 {
504 if (!hex || !r || !g || !b) {
505 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL parameter");
506 }
507
508 /* Skip '#' prefix if present */
509 if (hex[0] == '#') {
510 hex++;
511 }
512
513 /* Validate hex string length */
514 if (strlen(hex) != 6) {
515 return SET_ERRNO(ERROR_CONFIG, "Invalid hex color (must be #RRGGBB): %s", hex);
516 }
517
518 /* Parse hex digits */
519 unsigned int rgb = 0;
520 if (SAFE_SSCANF(hex, "%6x", &rgb) != 1) {
521 return SET_ERRNO(ERROR_CONFIG, "Invalid hex color format: %s", hex);
522 }
523
524 *r = (rgb >> 16) & 0xFF;
525 *g = (rgb >> 8) & 0xFF;
526 *b = rgb & 0xFF;
527
528 return ASCIICHAT_OK;
529}
#define SAFE_SSCANF(str, format,...)
Definition common.h:440

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 535 of file colorscheme.c.

535 {
536 if (!buf || size < 20)
537 return;
538 safe_snprintf(buf, size, "\x1b[38;2;%d;%d;%dm", r, g, b);
539}

References safe_snprintf().

Referenced by colorscheme_compile_scheme().

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 39 of file colorscheme.c.

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

Referenced by colorscheme_destroy(), colorscheme_init(), colorscheme_set_active_scheme(), log_init_colors(), and log_set_color_scheme().