ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
platform/terminal.c
Go to the documentation of this file.
1
11#include <ascii-chat/common.h>
12#include <ascii-chat/log/log.h>
13#ifndef _WIN32
14#include <unistd.h>
15#endif
16#include <string.h>
17
32 // Check color_mode setting - if explicitly set to NONE, disable colors
33 int color_mode = GET_OPTION(color_mode);
34 if (color_mode == COLOR_MODE_NONE) {
35 return false; // Color mode explicitly disabled
36 }
37
38 // Get current color setting (auto/true/false)
39 int color_setting = GET_OPTION(color);
40
41 // Priority 1: --color=true - Force colors ON (overrides everything)
42 if (color_setting == COLOR_SETTING_TRUE) {
43 return true; // ALWAYS colorize
44 }
45
46 // Priority 2: --color=false - Force colors OFF (overrides everything)
47 if (color_setting == COLOR_SETTING_FALSE) {
48 return false; // NEVER colorize
49 }
50
51 // Priority 3: --color=auto (default) - Smart detection
52 // Special case: Show colors for --help and --version even if not a TTY
53 // Check for help/version in global argv (set by main.c early)
54 if (g_argc > 1 && g_argv) {
55 for (int i = 1; i < g_argc; i++) {
56 if (strcmp(g_argv[i], "--help") == 0 || strcmp(g_argv[i], "-h") == 0 || strcmp(g_argv[i], "--version") == 0 ||
57 strcmp(g_argv[i], "-v") == 0 || strcmp(g_argv[i], "--show-capabilities") == 0) {
58 // For help/version/show-capabilities, use colors only if output is a TTY
59 // (respect piping convention: no colors unless --color=true)
60 int is_tty = platform_isatty(fd);
61 if (!is_tty) {
62 // Piping detected - fall through to check environment overrides
63 break;
64 }
65
66 // It's a TTY - show colors for help. Check environment overrides.
67 if (SAFE_GETENV("ASCII_CHAT_COLOR")) {
68 return true; // ASCII_CHAT_COLOR env var forces colors
69 }
70
71 // CLAUDECODE environment variable (LLM automation) - disable colors
72 if (SAFE_GETENV("CLAUDECODE")) {
73 return false; // NO colors in Claude Code environment
74 }
75
76 // For help output to TTY, show colors by default
77 return true;
78 }
79 }
80 }
81
82 // Check environment variable overrides
83 if (SAFE_GETENV("ASCII_CHAT_COLOR")) {
84 return true; // ASCII_CHAT_COLOR env var forces colors
85 }
86
87 // CLAUDECODE environment variable (LLM automation) - disable colors
88 if (SAFE_GETENV("CLAUDECODE")) {
89 return false; // NO colors in Claude Code environment
90 }
91
92 // Check if output is a TTY (pipe detection)
93 // Default: Use colors only if we have TTY and no environment overrides
94 return platform_isatty(fd);
95}
96
108 // If colors are disabled entirely by terminal_should_color_output(), return NONE
109 if (!terminal_should_color_output(STDOUT_FILENO)) {
110 return TERM_COLOR_NONE;
111 }
112
113 // If --color-mode is set to specific value, use it
114 terminal_color_mode_t color_mode = GET_OPTION(color_mode);
115 if (color_mode != TERM_COLOR_AUTO) {
116 return color_mode;
117 }
118
119 // Otherwise auto-detect from terminal capabilities
120 // For now, return the detected color mode from terminal capabilities
121 // This will be used by apply_color_mode_override() if needed
122 return TERM_COLOR_AUTO; // Let the calling code handle auto-detection
123}
124
125/* ============================================================================
126 * Interactive Mode and TTY State Detection
127 * ============================================================================ */
128
130 return platform_isatty(STDIN_FILENO) != 0;
131}
132
134 return platform_isatty(STDOUT_FILENO) != 0;
135}
136
138 return platform_isatty(STDERR_FILENO) != 0;
139}
140
144
153unsigned short int terminal_get_effective_width(void) {
154 int width = GET_OPTION(width);
155
156 // If width option is explicitly set to non-zero, use it
157 if (width > 0) {
158 return (unsigned short int)width;
159 }
160
161 // Try to detect actual terminal width
162 terminal_size_t size;
163 if (terminal_get_size(&size) == ASCIICHAT_OK && size.cols > 0) {
164 return (unsigned short int)size.cols;
165 }
166
167 // Fallback to default
168 return OPT_WIDTH_DEFAULT;
169}
170
179unsigned short int terminal_get_effective_height(void) {
180 int height = GET_OPTION(height);
181
182 // If height option is explicitly set to non-zero, use it
183 if (height > 0) {
184 return (unsigned short int)height;
185 }
186
187 // Try to detect actual terminal height
188 terminal_size_t size;
189 if (terminal_get_size(&size) == ASCIICHAT_OK && size.rows > 0) {
190 return (unsigned short int)size.rows;
191 }
192
193 // Fallback to default
194 return OPT_HEIGHT_DEFAULT;
195}
196
198 return !terminal_is_stdout_tty();
199}
200
202 // If stdout is piped/redirected, force logs to stderr to avoid corruption
204 // Unless in TESTING environment where test framework may capture stdout
205 const char *testing = SAFE_GETENV("TESTING");
206 if (testing && strcmp(testing, "1") == 0) {
207 // NOLINTNEXTLINE(readability-simplify-boolean-expr) - Intentional logic for test environment override
208 return false;
209 }
210 return true; // Force stderr when piped
211 }
212 return false; // stdout is TTY, no need to force stderr
213}
214
216 // Must be fully interactive (stdin and stdout are TTYs)
218 return false;
219 }
220
221 // Must not be in snapshot mode (non-interactive capture)
222 if (GET_OPTION(snapshot_mode)) {
223 return false;
224 }
225
226 // Must not have automated prompt responses configured
227 const char *auto_response = SAFE_GETENV("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
228 if (auto_response && *auto_response != '\0') {
229 // NOLINTNEXTLINE(readability-simplify-boolean-expr) - Intentional check for configured automated responses
230 return false;
231 }
232
233 // All checks passed, interactive prompts are appropriate
234 return true;
235}
236
238 // When force_stderr is enabled (client mode), send ALL logs to stderr
239 // When terminal is NOT interactive (piped/redirected), send ALL logs to stderr
240 // to keep stdout clean for piped data (JSON, frames, etc.)
242 return STDERR_FILENO;
243 }
244
245 // In interactive mode, route based on level:
246 // WARN and above (ERROR, FATAL) go to stderr
247 // Others (DEV, DEBUG, INFO) go to stdout
248 if (level >= LOG_WARN) {
249 return STDERR_FILENO;
250 }
251
252 return STDOUT_FILENO;
253}
254
255/* ============================================================================
256 * Theme-Aware Renderer Color Selection
257 * ============================================================================ */
258
269void terminal_get_default_foreground_color(int theme, uint8_t *out_r, uint8_t *out_g, uint8_t *out_b) {
270 if (theme == 1) { // TERM_RENDERER_THEME_LIGHT
274 } else { // TERM_RENDERER_THEME_DARK or TERM_RENDERER_THEME_AUTO
278 }
279}
280
291void terminal_get_default_background_color(int theme, uint8_t *out_r, uint8_t *out_g, uint8_t *out_b) {
292 if (theme == 1) { // TERM_RENDERER_THEME_LIGHT
296 } else { // TERM_RENDERER_THEME_DARK or TERM_RENDERER_THEME_AUTO
300 }
301}
302
316 if (fd < 0) {
317 return false;
318 }
319 if (GET_OPTION(snapshot_mode)) {
320 return false;
321 }
322 const char *testing_env = SAFE_GETENV("TESTING");
323 if (testing_env != NULL) {
324 return false;
325 }
326 return platform_isatty(fd) != 0;
327}
Platform abstraction layer umbrella header providing unified cross-platform API.
⚙️ Common definitions, error codes, macros, and types shared throughout the application
char ** g_argv
Global argv for early argument inspection (e.g., –color flag detection) Set by main() for access from...
Definition common.c:54
int g_argc
Global argc for early argument inspection (e.g., –color flag detection) Set by main() for access from...
Definition common.c:53
#define SAFE_GETENV(name)
Definition common.h:434
unsigned char uint8_t
Definition common.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
bool log_get_force_stderr(void)
Get current force_stderr setting.
Definition log/log.c:725
log_level_t
Logging levels enumeration.
Definition types.h:29
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
#define COLOR_MODE_NONE
Monochrome mode.
#define OPT_HEIGHT_DEFAULT
Default terminal height in characters.
#define OPT_WIDTH_DEFAULT
Default terminal width in characters.
@ COLOR_SETTING_TRUE
Force colors ON: always colorize regardless of TTY/piping/CLAUDECODE.
@ COLOR_SETTING_FALSE
Force colors OFF: disable all colors and logging colors.
#define TERMINAL_COLOR_THEME_LIGHT_BG_R
Default background color for light theme (RGB)
Definition terminal.h:135
#define TERMINAL_COLOR_THEME_DARK_FG_B
Definition terminal.h:128
#define TERMINAL_COLOR_THEME_LIGHT_FG_G
Definition terminal.h:117
#define TERMINAL_COLOR_THEME_DARK_BG_G
Definition terminal.h:145
#define TERMINAL_COLOR_THEME_DARK_FG_R
Default text color for dark theme (RGB)
Definition terminal.h:126
#define TERMINAL_COLOR_THEME_LIGHT_BG_B
Definition terminal.h:137
asciichat_error_t terminal_get_size(terminal_size_t *size)
Get terminal size.
#define TERMINAL_COLOR_THEME_DARK_BG_B
Definition terminal.h:146
#define TERMINAL_COLOR_THEME_DARK_BG_R
Default background color for dark theme (RGB)
Definition terminal.h:144
#define TERMINAL_COLOR_THEME_DARK_FG_G
Definition terminal.h:127
#define TERMINAL_COLOR_THEME_LIGHT_FG_B
Definition terminal.h:118
#define TERMINAL_COLOR_THEME_LIGHT_FG_R
Platform-specific getopt include.
Definition terminal.h:116
#define TERMINAL_COLOR_THEME_LIGHT_BG_G
Definition terminal.h:136
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
📝 Logging API with multiple log levels and terminal output control
bool terminal_can_prompt_user(void)
Determine if interactive user prompts are appropriate.
unsigned short int terminal_get_effective_width(void)
Get effective terminal width with fallback priority.
void terminal_get_default_background_color(int theme, uint8_t *out_r, uint8_t *out_g, uint8_t *out_b)
Get theme-aware default background color for the renderer.
bool terminal_is_interactive(void)
Check if the session is fully interactive.
bool terminal_is_stdin_tty(void)
Check if stdin is connected to a TTY.
int terminal_choose_log_fd(log_level_t level)
Choose output file descriptor for logging based on level and interactivity.
bool terminal_is_stderr_tty(void)
Check if stderr is connected to a TTY.
bool terminal_is_stdout_tty(void)
Check if stdout is connected to a TTY.
bool terminal_should_force_stderr(void)
Determine if logs should be forced to stderr.
unsigned short int terminal_get_effective_height(void)
Get effective terminal height with fallback priority.
bool terminal_is_piped_output(void)
Check if stdout is piped or redirected.
terminal_color_mode_t terminal_get_effective_color_mode(void)
Get current color mode considering all overrides.
bool terminal_should_use_control_sequences(int fd)
Check if terminal control sequences should be used for the given fd.
bool terminal_should_color_output(int fd)
Determine if color output should be used.
void terminal_get_default_foreground_color(int theme, uint8_t *out_r, uint8_t *out_g, uint8_t *out_b)
Get theme-aware default foreground color for the renderer.
Terminal size structure.
Definition terminal.h:163
int cols
Number of columns (width) in terminal.
Definition terminal.h:165
int rows
Number of rows (height) in terminal.
Definition terminal.h:164
🖥️ Cross-platform terminal interface for ascii-chat
terminal_color_mode_t
Terminal color support levels.
Definition terminal.h:578
@ TERM_COLOR_NONE
No color support (monochrome terminal)
Definition terminal.h:582
@ TERM_COLOR_AUTO
Auto-detect color support from terminal capabilities.
Definition terminal.h:580
#define LOG_WARN
Definition types.h:41
int platform_isatty(int fd)
Check if a file descriptor is a terminal.
Definition util.c:63