ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
status.c
Go to the documentation of this file.
1
5
15#include <ascii-chat/log/grep.h>
16#include <libplatform/compiler.h>
17#include <libplatform/system.h>
18#include <libplatform/terminal.h>
19#include <libplatform/system.h>
20#include <libplatform/terminal.h>
24#include <ascii-chat/util/ip.h>
27#include <ascii-chat/common.h>
28#include <stdio.h>
29#include <string.h>
30#include <time.h>
31#include <unistd.h>
32#include <libplatform/atomic.h>
33
34const char *ui_status_log_controls(void) {
35 static _Thread_local char controls[512];
36 snprintf(controls, sizeof(controls),
37 "%s / %s: close | %s: search logs | %s: keyboard controls | %s / %s: one log | "
38 "%s / %s: page | %s / %s: oldest/newest",
44 return controls;
45}
46
48 // Initialize the shared terminal screen log buffer
50 if (buf) {
52 }
53}
54
56 // Unregister from logger before destroying
58 // Cleanup the shared terminal screen log buffer
60}
61
63 // Delegate to terminal_screen log abstraction
65}
66
67// ============================================================================
68// Status Display
69// ============================================================================
70
71asciichat_error_t ui_status_gather(network_server_t *server, const char *session_string, const char *ipv4_address,
72 const char *ipv6_address, uint16_t port, time_t start_time, const char *mode_name,
73 bool session_is_mdns_only, ui_status_t *out_status) {
74 if (!server || !out_status || !mode_name) {
75 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
76 }
77
78 memset(out_status, 0, sizeof(*out_status));
79
80 // Copy session string
81 if (session_string) {
82 SAFE_STRNCPY(out_status->session_string, session_string, sizeof(out_status->session_string));
83 }
84
85 // Format IPv4 address
86 out_status->ipv4_bound = (ipv4_address && ipv4_address[0] != '\0');
87 if (out_status->ipv4_bound) {
88 snprintf(out_status->ipv4_address, sizeof(out_status->ipv4_address), "%s:%u", ipv4_address, port);
89 }
90
91 // Format IPv6 address
92 out_status->ipv6_bound = (ipv6_address && ipv6_address[0] != '\0');
93 if (out_status->ipv6_bound) {
94 snprintf(out_status->ipv6_address, sizeof(out_status->ipv6_address), "[%s]:%u", ipv6_address, port);
95 }
96
97 out_status->port = port;
98 out_status->start_time = start_time;
99 out_status->mode_name = mode_name;
100 out_status->session_is_mdns_only = session_is_mdns_only;
102
103 return ASCIICHAT_OK;
104}
105
106void ui_status_display(const ui_status_t *status) {
107 if (!status) {
108 return;
109 }
110
111 // Only render the status screen if:
112 // 1. Terminal is interactive (default behavior), OR
113 // 2. User explicitly set --status-screen on command line (force it regardless of terminal)
114 // In non-interactive mode without explicit flag, logs flow to stdout/stderr normally
115 if (!terminal_is_interactive() && !GET_OPTION(status_screen_explicitly_set)) {
116 return;
117 }
118
119 // Redirect status screen to stderr when stdout is piped (not a TTY).
120 // This keeps stdout clean for frame data while status goes to stderr.
121 // Uses platform_isatty() so non-desktop platforms (iOS, WASM) can override.
122 if (!platform_isatty(STDOUT_FILENO)) {
123 terminal_screen_set_output_fd(STDERR_FILENO);
124 }
125
126 // If --grep pattern was provided, enter interactive grep mode with it pre-populated
127 // Only do this once (check if not already entering)
128 static bool grep_mode_entered = false;
129 if (!grep_mode_entered && grep_get_last_pattern() && grep_get_last_pattern()[0] != '\0') {
131 grep_mode_entered = true;
132 }
133
134 // Use terminal_screen abstraction for rendering
135 ui_status_t rendered = *status;
136 if (!rendered.detail)
137 rendered.detail = ui_status_log_controls();
138 terminal_screen_config_t config = {
139 .screen = UI_SCREEN_STATUS,
140 .output_fd = platform_isatty(STDOUT_FILENO) ? STDOUT_FILENO : STDERR_FILENO,
141 .user_data_size = sizeof(*status),
142 .minimum_cols = 20,
143 .fixed_header_lines = 4,
144 .render_header = ui_status_render_header,
145 .user_data = &rendered,
146 .show_logs = true,
147 };
148
149 terminal_screen_render(&config);
150}
151
157 if (!status) {
158 return true;
159 }
160
161 // Only render the status screen if:
162 // 1. Terminal is interactive (default behavior), OR
163 // 2. User explicitly set --status-screen on command line (force it regardless of terminal)
164 // In non-interactive mode without explicit flag, logs flow to stdout/stderr normally
165 if (!terminal_is_interactive() && !GET_OPTION(status_screen_explicitly_set)) {
166 return true;
167 }
168
169 // If --grep pattern was provided, enter interactive grep mode with it pre-populated
170 static bool grep_mode_entered = false;
171 if (!grep_mode_entered && grep_get_last_pattern() && grep_get_last_pattern()[0] != '\0') {
173 grep_mode_entered = true;
174 }
175
176 // Status may use stderr when stdout is redirected. Check the descriptors
177 // used by this screen instead of requiring stdout to be a terminal.
178 int output_fd = platform_isatty(STDOUT_FILENO) ? STDOUT_FILENO : STDERR_FILENO;
179 bool keyboard_enabled = false;
180 if (platform_isatty(STDIN_FILENO) && platform_isatty(output_fd) && keyboard_init() == ASCIICHAT_OK) {
181 keyboard_enabled = true;
182 }
183
184 // Use terminal_screen abstraction for rendering
185 ui_status_t rendered = *status;
186 if (!rendered.detail)
187 rendered.detail = ui_status_log_controls();
188 terminal_screen_config_t config = {
189 .screen = UI_SCREEN_STATUS,
190 .output_fd = output_fd,
191 .user_data_size = sizeof(*status),
192 .minimum_cols = 20,
193 .fixed_header_lines = 4,
194 .render_header = ui_status_render_header,
195 .user_data = &rendered,
196 .show_logs = true,
197 };
198
199 terminal_screen_render(&config);
200
201 // Poll keyboard for Escape to exit or for interactive grep
202 bool should_exit_status = false;
203 if (keyboard_enabled) {
206 return true;
207 } else if (key == KEY_BACKTICK) {
209 } else if (key == KEY_ESCAPE) {
210 // Escape key: cancel grep if active, otherwise exit status screen
211 if (log_search_is_active()) {
212 log_search_exit_mode(false); // Cancel grep without applying
213 } else {
214 should_exit_status = true; // Exit status screen
215 }
216 } else if (key != KEY_NONE && log_search_should_handle(key)) {
218 }
219 }
220
221 // Keyboard ownership lasts until session shutdown, including between status frames.
222
223 return !should_exit_status; // Return false if user wants to exit
224}
225
226void ui_status_update(network_server_t *server, const char *session_string, const char *ipv4_address,
227 const char *ipv6_address, uint16_t port, time_t start_time, const char *mode_name,
228 bool session_is_mdns_only, uint64_t *last_update_ns) {
229 if (!server || !last_update_ns) {
230 return;
231 }
232
233 // Update at FPS rate (60 Hz = 16.67ms by default)
234 uint32_t fps = GET_OPTION(fps);
235 if (fps == 0) {
236 fps = 60; // Default
237 }
238
239 // Calculate frame interval in microseconds
240 uint64_t frame_interval_us = US_PER_SEC_INT / fps;
241
242 // Get current time in microseconds using platform abstraction
244
245 // Check if enough time has passed
246 if ((now_us - *last_update_ns) < frame_interval_us) {
247 return; // Too soon, skip frame
248 }
249
250 ui_status_t status;
251 if (ui_status_gather(server, session_string, ipv4_address, ipv6_address, port, start_time, mode_name,
252 session_is_mdns_only, &status) == ASCIICHAT_OK) {
253 ui_status_display(&status);
254 *last_update_ns = now_us;
255 }
256}
#define SET_ERRNO(code,...)
⚙️ Common definitions, error codes, macros, and types shared throughout the application
void ui_errno_stacks_toggle(void)
bool ui_errno_stacks_handle_key(keyboard_key_t key)
Buffered terminal frame rendering.
const char * grep_get_last_pattern(void)
Get the last (most recent) filter pattern string.
Definition grep.c:1135
unsigned short uint16_t
Definition common.h:61
unsigned int uint32_t
Definition common.h:62
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
unsigned long long uint64_t
Definition common.h:63
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
struct session_log_buffer session_log_buffer_t
Thread-safe fixed-size history ring for session-screen log lines.
void log_clear_session_log_buffer(void)
Unregister the session log buffer.
Definition log.c:2057
void log_set_session_log_buffer(session_log_buffer_t *buf)
Register a session log buffer with the logger.
Definition log.c:2048
@ LOG_COLOR_WARN
Definition log/log.h:151
#define US_PER_SEC_INT
Definition time.h:173
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
asciichat_error_t keyboard_init(void)
Initialize keyboard input system.
uint64_t platform_get_monotonic_time_us(void)
Get monotonic time in microseconds.
keyboard_key_t
Options for interactive line editing.
Definition keyboard.h:53
@ KEY_BACKTICK
'`' key - print lock state (debug builds)
Definition keyboard.h:84
@ KEY_ESCAPE
Escape key (ESC).
Definition keyboard.h:55
@ KEY_NONE
No key pressed or no input available.
Definition keyboard.h:54
const char * colored_string(log_color_t color, const char *text)
Build a colored string for terminal output.
Definition string.c:284
struct network_server network_server_t
🖥️ Display Width and Terminal Centering Utilities
🌍 IP Address Parsing and Formatting Utilities
size_t network_server_connection_count(network_server_t *s)
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
void log_search_enter_mode(void)
Enter search mode (user pressed '/').
Definition search.c:226
bool log_search_is_active(void)
Check if filtering is active.
Definition search.c:413
void log_search_exit_mode(bool accept)
Exit search mode.
Definition search.c:303
bool log_search_should_handle(int key)
Check if a key should be handled by grep module.
Definition search.c:422
asciichat_error_t log_search_handle_key(keyboard_key_t key)
Process keyboard input for grep.
Definition search.c:508
Interactive search filtering for terminal screens.
Thread-safe circular log buffer for session screens (status, splash).
void ui_status_render_header(frame_buffer_t *buf, terminal_size_t term_size, void *user_data)
void ui_status_log_clear(void)
Clear all log messages from status screen buffer.
Definition status.c:62
asciichat_error_t ui_status_gather(network_server_t *server, const char *session_string, const char *ipv4_address, const char *ipv6_address, uint16_t port, time_t start_time, const char *mode_name, bool session_is_mdns_only, ui_status_t *out_status)
Gather current status information.
Definition status.c:71
void ui_status_update(network_server_t *server, const char *session_string, const char *ipv4_address, const char *ipv6_address, uint16_t port, time_t start_time, const char *mode_name, bool session_is_mdns_only, uint64_t *last_update_ns)
Periodically update status display with live logs at FPS rate.
Definition status.c:226
void ui_status_display(const ui_status_t *status)
Display status screen.
Definition status.c:106
const char * ui_status_log_controls(void)
Definition status.c:34
void ui_status_log_destroy(void)
Cleanup status screen log capture system.
Definition status.c:55
void ui_status_log_init(void)
Initialize status screen log capture system.
Definition status.c:47
bool ui_status_display_interactive(const ui_status_t *status)
Display status screen with keyboard input support Returns true if status screen should continue,...
Definition status.c:156
📊 Status screen display and management for server/discovery service modes
🔤 String Manipulation and Shell Escaping Utilities
Configuration for terminal screen rendering.
Status information for server/discovery service modes.
Definition status.h:38
bool ipv4_bound
Whether IPv4 socket is bound.
Definition status.h:44
char ipv4_address[256]
Formatted IPv4 bind address with port.
Definition status.h:40
const char * detail
Optional client controls in the address row.
Definition status.h:48
bool ipv6_bound
Whether IPv6 socket is bound.
Definition status.h:45
uint16_t port
TCP listen port.
Definition status.h:42
char ipv6_address[256]
Formatted IPv6 bind address with port.
Definition status.h:41
char session_string[64]
Memorable session string (e.g., "happy-sunset-ocean").
Definition status.h:39
const char * mode_name
Mode name (e.g., "Server", "Discovery Service").
Definition status.h:49
time_t start_time
Server start time (for uptime calculation).
Definition status.h:47
bool session_is_mdns_only
Whether session is mDNS-only or ACDS.
Definition status.h:46
size_t connected_count
Number of connected clients/servers.
Definition status.h:43
void terminal_screen_log_destroy(void)
Standard log cleanup for terminal screens.
void terminal_screen_render(const terminal_screen_config_t *config)
Render a terminal screen with fixed header and scrolling logs.
void terminal_screen_log_clear(void)
Clear buffered logs for terminal screens.
session_log_buffer_t * terminal_screen_log_init(void)
Initialize session log buffer for terminal screens.
void terminal_screen_set_output_fd(int fd)
Set output file descriptor for terminal screens (splash/status).
Reusable "fixed header + scrolling logs" terminal screen abstraction.
⏱️ High-precision timing utilities using sokol_time.h and uthash
@ UI_SCREEN_STATUS
keyboard_key_t ui_input_read_key(ui_screen_t screen)
Definition tui/input.c:353