ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
colorscheme.c File Reference

Color scheme management implementation and early initialization. More...

Go to the source code of this file.

Macros

#define NUM_BUILTIN_SCHEMES   (sizeof(BUILTIN_SCHEMES) / sizeof(BUILTIN_SCHEMES[0]))
 

Functions

asciichat_error_t colorscheme_init (void)
 
void colorscheme_cleanup_compiled (compiled_color_scheme_t *compiled)
 
void colorscheme_destroy (void)
 
const color_scheme_t * colorscheme_get_active_scheme (void)
 
asciichat_error_t colorscheme_set_active_scheme (const char *name)
 
asciichat_error_t colorscheme_load_builtin (const char *name, color_scheme_t *scheme)
 
asciichat_error_t colorscheme_load_from_file (const char *path, color_scheme_t *scheme)
 
asciichat_error_t parse_hex_color (const char *hex, uint8_t *r, uint8_t *g, uint8_t *b)
 
void rgb_to_truecolor_ansi (uint8_t r, uint8_t g, uint8_t b, char *buf, size_t size)
 
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)
 
asciichat_error_t colorscheme_export_scheme (const char *scheme_name, const char *file_path)
 
terminal_background_t detect_terminal_background (void)
 
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 = PTHREAD_MUTEX_INITIALIZER
 

Detailed Description

Color scheme management implementation and early initialization.

Implements color scheme selection, loading, and compilation for ascii-chat. Supports built-in themes, TOML configuration files, and early initialization before logging starts.

Definition in file colorscheme.c.

Macro Definition Documentation

◆ NUM_BUILTIN_SCHEMES

#define NUM_BUILTIN_SCHEMES   (sizeof(BUILTIN_SCHEMES) / sizeof(BUILTIN_SCHEMES[0]))

Definition at line 247 of file colorscheme.c.

Function Documentation

◆ colorscheme_cleanup_compiled()

void colorscheme_cleanup_compiled ( compiled_color_scheme_t *  compiled)

Definition at line 313 of file colorscheme.c.

313 {
314 if (!compiled) {
315 return;
316 }
317
318 /* Free allocated color code strings */
319 for (int i = 0; i < 8; i++) {
320 char *str_16 = (char *)compiled->codes_16[i];
321 char *str_256 = (char *)compiled->codes_256[i];
322 char *str_truecolor = (char *)compiled->codes_truecolor[i];
323 /* Only free non-NULL pointers - protects against uninitialized data */
324 if (str_16 != NULL) {
325 SAFE_FREE(str_16);
326 }
327 if (str_256 != NULL) {
328 SAFE_FREE(str_256);
329 }
330 if (str_truecolor != NULL) {
331 SAFE_FREE(str_truecolor);
332 }
333 }
334 memset(compiled, 0, sizeof(compiled_color_scheme_t));
335}

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 
)

Definition at line 539 of file colorscheme.c.

540 {
541 if (!scheme || !compiled) {
542 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL scheme or compiled pointer");
543 }
544
545 /* Note: mode parameter reserved for future use */
546 (void)mode;
547
548 /* Free any previously compiled strings before recompiling */
549 /* This prevents memory leaks when the color scheme is recompiled */
550 /* Only cleanup if we've actually compiled before (codes_16[0] != NULL means already compiled) */
551 if (compiled->codes_16[0] != NULL) {
553 } else {
554 }
555
556 /* Select color array based on background */
557 const rgb_pixel_t *colors = (background == TERM_BACKGROUND_LIGHT && scheme->has_light_variant)
558 ? scheme->log_colors_light
559 : scheme->log_colors_dark;
560 /* Helper: allocate and format a color code string */
561 char temp_buf[128];
562
563 /* Compile for 16-color mode */
564 for (int i = 0; i < 8; i++) {
565 if (i == 7) {
566 /* RESET */
567 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
568 } else {
569 uint8_t color_idx = rgb_to_16color(colors[i].r, colors[i].g, colors[i].b);
570 /* ANSI color codes: 30-37 for normal, 90-97 for bright */
571 if (color_idx < 8) {
572 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[%dm", 30 + color_idx);
573 } else {
574 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[%dm", 90 + (color_idx - 8));
575 }
576 }
577 /* Allocate string with SAFE_MALLOC and copy */
578 size_t len = strlen(temp_buf) + 1;
579 char *allocated = SAFE_MALLOC(len, char *);
580 if (!allocated) {
581 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
582 }
583 memcpy(allocated, temp_buf, len);
584 compiled->codes_16[i] = allocated;
585 }
586 /* Compile for 256-color mode */
587 for (int i = 0; i < 8; i++) {
588 if (i == 7) {
589 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
590 } else {
591 uint8_t color_idx = rgb_to_256color(colors[i].r, colors[i].g, colors[i].b);
592 safe_snprintf(temp_buf, sizeof(temp_buf), "\x1b[38;5;%dm", color_idx);
593 }
594 /* Allocate string with SAFE_MALLOC and copy */
595 size_t len = strlen(temp_buf) + 1;
596 char *allocated = SAFE_MALLOC(len, char *);
597 if (!allocated) {
598 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
599 }
600 memcpy(allocated, temp_buf, len);
601 compiled->codes_256[i] = allocated;
602 }
603
604 /* Compile for truecolor mode */
605 for (int i = 0; i < 8; i++) {
606 if (i == 7) {
607 SAFE_STRNCPY(temp_buf, "\x1b[0m", sizeof(temp_buf));
608 } else {
609 rgb_to_truecolor_ansi(colors[i].r, colors[i].g, colors[i].b, temp_buf, sizeof(temp_buf));
610 }
611 /* Allocate string with SAFE_MALLOC and copy */
612 size_t len = strlen(temp_buf) + 1;
613 char *allocated = SAFE_MALLOC(len, char *);
614 if (!allocated) {
615 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate color code string");
616 }
617 memcpy(allocated, temp_buf, len);
618 compiled->codes_truecolor[i] = allocated;
619 }
620
621 return ASCIICHAT_OK;
622}
uint8_t rgb_to_16color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi_fast.c:314
uint8_t rgb_to_256color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi_fast.c:230
void rgb_to_truecolor_ansi(uint8_t r, uint8_t g, uint8_t b, char *buf, size_t size)
void colorscheme_cleanup_compiled(compiled_color_scheme_t *compiled)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456

References colorscheme_cleanup_compiled(), rgb_to_16color(), rgb_to_256color(), rgb_to_truecolor_ansi(), and safe_snprintf().

Referenced by log_init_colors(), and log_set_color_scheme().

◆ colorscheme_destroy()

void colorscheme_destroy ( void  )

Definition at line 337 of file colorscheme.c.

337 {
338 if (!g_colorscheme_initialized) {
339 return;
340 }
341
342 mutex_lock(&g_colorscheme_mutex);
343 memset(&g_active_scheme, 0, sizeof(color_scheme_t));
344 g_colorscheme_initialized = false;
345 mutex_unlock(&g_colorscheme_mutex);
346
347 /* NOTE: Do NOT call mutex_destroy() on native POSIX because the mutex is statically
348 * initialized with PTHREAD_MUTEX_INITIALIZER. Destroying a statically-initialized
349 * mutex is undefined behavior. On Windows and Emscripten, we must destroy the mutex
350 * because it was explicitly initialized in colorscheme_init(). */
351#if defined(_WIN32) || defined(__EMSCRIPTEN__)
353#endif
354}
mutex_t g_colorscheme_mutex
Definition colorscheme.c:38
int mutex_destroy(mutex_t *mutex)
Definition threading.c:21

References g_colorscheme_mutex, and mutex_destroy().

Referenced by asciichat_shared_destroy().

◆ colorscheme_export_scheme()

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

Definition at line 628 of file colorscheme.c.

628 {
629 if (!scheme_name) {
630 return SET_ERRNO(ERROR_INVALID_PARAM, "Scheme name is NULL");
631 }
632
633 color_scheme_t scheme = {0};
634 asciichat_error_t result = colorscheme_load_builtin(scheme_name, &scheme);
635 if (result != ASCIICHAT_OK) {
636 return result;
637 }
638
639 /* Generate TOML content */
640 char toml_content[8192] = {0};
641 size_t offset = 0;
642
643 /* Scheme header section */
644 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset,
645 "[scheme]\n"
646 "name = \"%s\"\n"
647 "description = \"%s\"\n\n",
648 scheme.name, scheme.description);
649
650 /* Dark mode colors */
651 const char *color_names[] = {"dev", "debug", "warn", "info", "error", "fatal", "grey", "reset"};
652 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "[colors.dark]\n");
653
654 for (int i = 0; i < 8; i++) {
655 offset +=
656 safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "%s = \"#%02X%02X%02X\"\n", color_names[i],
657 scheme.log_colors_dark[i].r, scheme.log_colors_dark[i].g, scheme.log_colors_dark[i].b);
658 }
659
660 /* Light mode colors if available */
661 if (scheme.has_light_variant) {
662 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "\n[colors.light]\n");
663 for (int i = 0; i < 8; i++) {
664 offset += safe_snprintf(toml_content + offset, sizeof(toml_content) - offset, "%s = \"#%02X%02X%02X\"\n",
665 color_names[i], scheme.log_colors_light[i].r, scheme.log_colors_light[i].g,
666 scheme.log_colors_light[i].b);
667 }
668 }
669
670 /* Write to file or stdout */
671 if (file_path) {
672 FILE *fp = platform_fopen(file_path, "w");
673 if (!fp) {
674 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Cannot open %s for writing", file_path);
675 }
676 if (fputs(toml_content, fp) < 0) {
677 fclose(fp);
678 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Failed to write to %s", file_path);
679 }
680 fclose(fp);
681 } else {
682 /* Write to stdout */
683 if (fputs(toml_content, stdout) < 0) {
684 return SET_ERRNO_SYS(ERROR_FILE_OPERATION, "Failed to write to stdout");
685 }
686 /* Flush to ensure piped output is written immediately */
687 (void)fflush(stdout);
688 }
689
690 return ASCIICHAT_OK;
691}
asciichat_error_t colorscheme_load_builtin(const char *name, color_scheme_t *scheme)
char file_path[PLATFORM_MAX_PATH_LENGTH]
Definition mmap.c:39
FILE * platform_fopen(const char *filename, const char *mode)

References colorscheme_load_builtin(), file_path, platform_fopen(), and safe_snprintf().

◆ colorscheme_get_active_scheme()

const color_scheme_t * colorscheme_get_active_scheme ( void  )

Definition at line 360 of file colorscheme.c.

360 {
361 if (!g_colorscheme_initialized) {
362 /* Lazy initialization of color system */
364 }
365
366 return &g_active_scheme;
367}
asciichat_error_t colorscheme_init(void)

References colorscheme_init().

Referenced by log_init_colors(), and options_init().

◆ colorscheme_init()

asciichat_error_t colorscheme_init ( void  )

Definition at line 278 of file colorscheme.c.

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

References g_colorscheme_mutex, and mutex_init().

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 
)

Definition at line 404 of file colorscheme.c.

404 {
405 if (!name || !scheme) {
406 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL name or scheme pointer");
407 }
408
409 const color_scheme_t *builtin = find_builtin_scheme(name);
410 if (!builtin) {
411 return SET_ERRNO(ERROR_CONFIG, "Unknown built-in color scheme: %s", name);
412 }
413
414 memcpy(scheme, builtin, sizeof(color_scheme_t));
415 return ASCIICHAT_OK;
416}

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 
)

Definition at line 418 of file colorscheme.c.

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

References parse_hex_color().

Referenced by colorscheme_set_active_scheme().

◆ colorscheme_set_active_scheme()

asciichat_error_t colorscheme_set_active_scheme ( const char *  name)

Definition at line 369 of file colorscheme.c.

369 {
370 if (!name) {
371 return SET_ERRNO(ERROR_INVALID_PARAM, "Scheme name is NULL");
372 }
373
374 /* Ensure color system is initialized */
375 if (!g_colorscheme_initialized) {
377 }
378
379 color_scheme_t scheme = {0};
380 asciichat_error_t result = ASCIICHAT_OK;
381
382 /* Try loading as built-in scheme first */
383 const color_scheme_t *builtin = find_builtin_scheme(name);
384 if (builtin) {
385 memcpy(&scheme, builtin, sizeof(color_scheme_t));
386 } else if (strchr(name, '/') || strchr(name, '.')) {
387 /* Try loading as file path if it contains / or . */
388 result = colorscheme_load_from_file(name, &scheme);
389 if (result != ASCIICHAT_OK) {
390 return result;
391 }
392 } else {
393 return SET_ERRNO(ERROR_CONFIG, "Unknown color scheme: %s (not a built-in scheme or valid file path)", name);
394 }
395
396 mutex_lock(&g_colorscheme_mutex);
397 memcpy(&g_active_scheme, &scheme, sizeof(color_scheme_t));
398 mutex_unlock(&g_colorscheme_mutex);
399
400 log_debug("Switched to color scheme: %s", name);
401 return ASCIICHAT_OK;
402}
asciichat_error_t colorscheme_load_from_file(const char *path, color_scheme_t *scheme)

References colorscheme_init(), colorscheme_load_from_file(), and g_colorscheme_mutex.

Referenced by options_colorscheme_init_early(), and options_init().

◆ detect_terminal_background()

terminal_background_t detect_terminal_background ( void  )

Definition at line 697 of file colorscheme.c.

697 {
698 /* Method 1: Check environment variable override (highest priority) */
699 const char *term_bg = SAFE_GETENV("TERM_BACKGROUND");
700 if (term_bg) {
701 if (platform_strcasecmp(term_bg, "light") == 0) {
702 return TERM_BACKGROUND_LIGHT;
703 }
704 if (platform_strcasecmp(term_bg, "dark") == 0) {
705 return TERM_BACKGROUND_DARK;
706 }
707 }
708
709 /* Method 2: Use OSC 11 query with luminance calculation + environment fallbacks
710 * This automatically queries the terminal via OSC 11, calculates luminance,
711 * and falls back to $COLORFGBG, $TERM_PROGRAM, etc. if query fails */
712 bool is_dark = terminal_has_dark_background();
713 return is_dark ? TERM_BACKGROUND_DARK : TERM_BACKGROUND_LIGHT;
714}
int platform_strcasecmp(const char *s1, const char *s2)
bool terminal_has_dark_background(void)

References platform_strcasecmp(), 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
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 793 of file colorscheme.c.

793 {
794 /* Initialize the color system with defaults */
795 asciichat_error_t result = colorscheme_init();
796 if (result != ASCIICHAT_OK) {
797 /* Non-fatal: use built-in defaults */
798 return ASCIICHAT_OK;
799 }
800
801 /* Step 1: Try to load from config file (~/.config/ascii-chat/colors.toml) */
802 color_scheme_t config_scheme = {0};
803 asciichat_error_t config_result = load_config_color_scheme(&config_scheme);
804 if (config_result == ASCIICHAT_OK) {
805 /* Config file loaded successfully, apply it */
806 colorscheme_set_active_scheme(config_scheme.name);
807 }
808
809 /* Step 2: CLI --color-scheme overrides config file */
810 const char *cli_scheme = find_cli_color_scheme(argc, argv);
811 if (cli_scheme) {
812 asciichat_error_t cli_result = colorscheme_set_active_scheme(cli_scheme);
813 if (cli_result != ASCIICHAT_OK) {
814 /* Invalid scheme name from CLI, continue with current scheme */
815 return cli_result;
816 }
817 }
818
819 return ASCIICHAT_OK;
820}
asciichat_error_t colorscheme_set_active_scheme(const char *name)

References colorscheme_init(), and colorscheme_set_active_scheme().

Referenced by main().

◆ parse_hex_color()

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

Definition at line 497 of file colorscheme.c.

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

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 
)

Definition at line 529 of file colorscheme.c.

529 {
530 if (!buf || size < 20)
531 return;
532 safe_snprintf(buf, size, "\x1b[38;2;%d;%d;%dm", r, g, b);
533}

References safe_snprintf().

Referenced by colorscheme_compile_scheme().

Variable Documentation

◆ g_colorscheme_mutex

mutex_t g_colorscheme_mutex = PTHREAD_MUTEX_INITIALIZER