6#include <ascii-chat/ui/server_status.h>
7#include <ascii-chat/ui/terminal_screen.h>
8#include <ascii-chat/log/interactive_grep.h>
9#include <ascii-chat/session/session_log_buffer.h>
10#include <ascii-chat/log/grep.h>
11#include <ascii-chat/platform/abstraction.h>
12#include <ascii-chat/platform/system.h>
13#include <ascii-chat/platform/terminal.h>
14#include <ascii-chat/platform/keyboard.h>
15#include <ascii-chat/options/options.h>
16#include <ascii-chat/util/display.h>
17#include <ascii-chat/util/ip.h>
18#include <ascii-chat/util/time.h>
19#include <ascii-chat/common.h>
49asciichat_error_t
server_status_gather(tcp_server_t *server,
const char *session_string,
const char *ipv4_address,
50 const char *ipv6_address, uint16_t port, time_t start_time,
51 const char *mode_name,
bool session_is_mdns_only, server_status_t *out_status) {
52 if (!server || !out_status || !mode_name) {
53 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters for server_status_gather");
56 memset(out_status, 0,
sizeof(*out_status));
60 SAFE_STRNCPY(out_status->session_string, session_string,
sizeof(out_status->session_string));
64 out_status->ipv4_bound = (ipv4_address && ipv4_address[0] !=
'\0');
65 if (out_status->ipv4_bound) {
66 snprintf(out_status->ipv4_address,
sizeof(out_status->ipv4_address),
"%s:%u", ipv4_address, port);
70 out_status->ipv6_bound = (ipv6_address && ipv6_address[0] !=
'\0');
71 if (out_status->ipv6_bound) {
72 snprintf(out_status->ipv6_address,
sizeof(out_status->ipv6_address),
"[%s]:%u", ipv6_address, port);
75 out_status->port = port;
76 out_status->start_time = start_time;
77 out_status->mode_name = mode_name;
78 out_status->session_is_mdns_only = session_is_mdns_only;
90static void truncate_to_fit(
const char *src,
int max_display_width,
char *dst,
size_t dst_size) {
91 if (!src || !dst || max_display_width < 0) {
92 if (dst && dst_size > 0) {
100 if (width <= max_display_width) {
101 SAFE_STRNCPY(dst, src, dst_size);
106 int target_width = max_display_width - 3;
107 if (target_width <= 0) {
113 size_t src_len = strlen(src);
114 for (
size_t truncate_at = src_len; truncate_at > 0; truncate_at--) {
117 if (truncate_at >=
sizeof(test_buf) - 4) {
121 memcpy(test_buf, src, truncate_at);
122 test_buf[truncate_at] =
'\0';
127 safe_snprintf(dst, dst_size,
"%.*s...", (
int)truncate_at, src);
133 SAFE_STRNCPY(dst,
"...", dst_size);
147static void render_server_status_header(terminal_size_t term_size,
void *user_data) {
148 const server_status_t *status = (
const server_status_t *)user_data;
154 time_t now = time(NULL);
155 time_t uptime_secs = now - status->start_time;
156 int uptime_hours = uptime_secs / SEC_PER_HOUR;
157 int uptime_mins = (uptime_secs % SEC_PER_HOUR) / SEC_PER_MIN;
158 int uptime_secs_rem = uptime_secs % 60;
161 printf(
"\033[1;36m━");
162 for (
int i = 1; i < term_size.cols - 1; i++) {
168 char status_line[512];
169 char status_line_truncated[512];
170 char status_line_colored[600];
172 format_uptime_hms(uptime_hours, uptime_mins, uptime_secs_rem, uptime_str,
sizeof(uptime_str));
173 snprintf(status_line,
sizeof(status_line),
"ascii-chat %s | 👥 %zu | ⏱️ %s", status->mode_name,
174 status->connected_count, uptime_str);
177 truncate_to_fit(status_line, term_size.cols - 2, status_line_truncated,
sizeof(status_line_truncated));
178 snprintf(status_line_colored,
sizeof(status_line_colored),
"\033[1;36m%s\033[0m", status_line_truncated);
181 for (
int i = 0; i < padding; i++) {
184 printf(
"%s\n", status_line_colored);
188 char addr_line_truncated[512];
190 if (status->session_string[0] !=
'\0') {
191 pos += snprintf(addr_line + pos,
sizeof(addr_line) - pos,
"🔗 %s", status->session_string);
193 if (status->ipv4_bound && pos < (
int)
sizeof(addr_line) - 30) {
195 pos += snprintf(addr_line + pos,
sizeof(addr_line) - pos,
" | ");
201 pos += snprintf(addr_line + pos,
sizeof(addr_line) - pos,
"%s (%s)", status->ipv4_address, type);
203 pos += snprintf(addr_line + pos,
sizeof(addr_line) - pos,
"%s", status->ipv4_address);
206 if (status->ipv6_bound && pos < (
int)
sizeof(addr_line) - 30) {
208 pos += snprintf(addr_line + pos,
sizeof(addr_line) - pos,
" | ");
214 snprintf(addr_line + pos,
sizeof(addr_line) - pos,
"%s (%s)", status->ipv6_address, type);
216 snprintf(addr_line + pos,
sizeof(addr_line) - pos,
"%s", status->ipv6_address);
221 truncate_to_fit(addr_line, term_size.cols - 2, addr_line_truncated,
sizeof(addr_line_truncated));
224 for (
int i = 0; i < addr_padding; i++) {
227 printf(
"%s\n", addr_line_truncated);
230 printf(
"\033[1;36m━");
231 for (
int i = 1; i < term_size.cols - 1; i++) {
250 static bool grep_mode_entered =
false;
253 grep_mode_entered =
true;
257 terminal_screen_config_t config = {
258 .fixed_header_lines = 4,
259 .render_header = render_server_status_header,
260 .user_data = (
void *)status,
282 static bool grep_mode_entered =
false;
285 grep_mode_entered =
true;
289 bool keyboard_enabled =
false;
290 if (keyboard_init() == ASCIICHAT_OK) {
291 keyboard_enabled =
true;
295 terminal_screen_config_t config = {
296 .fixed_header_lines = 4,
297 .render_header = render_server_status_header,
298 .user_data = (
void *)status,
305 bool should_exit_status =
false;
306 if (keyboard_enabled) {
307 keyboard_key_t key = keyboard_read_nonblocking();
308 if (key == KEY_ESCAPE) {
313 should_exit_status =
true;
321 if (keyboard_enabled) {
325 return !should_exit_status;
329 const char *ipv6_address, uint16_t port, time_t start_time,
const char *mode_name,
330 bool session_is_mdns_only, uint64_t *last_update_ns) {
331 if (!server || !last_update_ns) {
336 uint32_t fps = GET_OPTION(fps);
342 uint64_t frame_interval_us = US_PER_SEC_INT / fps;
348 if ((now_us - *last_update_ns) < frame_interval_us) {
352 server_status_t status;
353 if (
server_status_gather(server, session_string, ipv4_address, ipv6_address, port, start_time, mode_name,
354 session_is_mdns_only, &status) == ASCIICHAT_OK) {
356 *last_update_ns = now_us;
const char * grep_get_last_pattern(void)
void interactive_grep_exit_mode(bool accept)
void interactive_grep_enter_mode(void)
bool interactive_grep_is_active(void)
asciichat_error_t interactive_grep_handle_key(keyboard_key_t key)
bool interactive_grep_should_handle(int key)
int extract_ip_from_address(const char *addr_with_port, char *ip_out, size_t ip_out_size)
const char * get_ip_type_string(const char *ip)
size_t tcp_server_get_client_count(tcp_server_t *server)
int display_width(const char *text)
int display_center_horizontal(const char *text, int terminal_width)
bool server_status_display_interactive(const server_status_t *status)
Display status screen with keyboard input support Returns true if status screen should continue,...
void server_status_log_destroy(void)
asciichat_error_t server_status_gather(tcp_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, server_status_t *out_status)
void server_status_log_append(const char *message)
void server_status_log_clear(void)
void server_status_display(const server_status_t *status)
void server_status_update(tcp_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)
void server_status_log_init(void)
bool session_log_buffer_init(void)
void session_log_buffer_append(const char *message)
void session_log_buffer_clear(void)
void session_log_buffer_destroy(void)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
void terminal_screen_render(const terminal_screen_config_t *config)
int format_uptime_hms(int hours, int minutes, int seconds, char *buffer, size_t buffer_size)