ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
client/main.c
Go to the documentation of this file.
1
66#include "main.h"
67#include "../main.h" // Global exit API
68#include "server.h"
69#include "protocol.h"
70#include "crypto.h"
71#include "display.h"
72#include "capture.h"
73#include "audio.h"
74#include <ascii-chat/ui/splash.h>
75#include <ascii-chat/log/interactive_grep.h>
76#include <ascii-chat/session/session_log_buffer.h>
77#include <ascii-chat/session/client_like.h>
78#include <ascii-chat/audio/analysis.h>
79#include <ascii-chat/video/webcam/webcam.h>
80#include <ascii-chat/network/mdns/discovery_tui.h>
81#include <ascii-chat/network/mdns/discovery.h>
82
83#include <ascii-chat/platform/abstraction.h>
84#include <ascii-chat/platform/init.h>
85#include <ascii-chat/platform/terminal.h>
86#include <ascii-chat/platform/symbols.h>
87#include <ascii-chat/platform/system.h>
88#include <ascii-chat/common.h>
89#include <ascii-chat/common/buffer_sizes.h>
90#include <ascii-chat/log/logging.h>
91#include <ascii-chat/log/json.h>
92#include <ascii-chat/options/options.h>
93#include <ascii-chat/options/rcu.h> // For RCU-based options access
94#include <ascii-chat/util/time.h> // For time macros
95#include <ascii-chat/util/url.h> // For URL parsing and WebSocket detection
96#include <ascii-chat/buffer_pool.h>
97#include <ascii-chat/video/palette.h>
98#include <ascii-chat/network/network.h>
99#include <ascii-chat/network/tcp/client.h>
100#include <ascii-chat/network/client.h>
101#include <ascii-chat/network/acip/acds_client.h>
102#include <ascii-chat/util/ip.h>
103#include <ascii-chat/network/acip/acds.h>
104#include <ascii-chat/network/acip/client.h>
105#include "connection_state.h"
106#include <ascii-chat/util/path.h>
107
108#ifndef NDEBUG
109#include <ascii-chat/debug/lock.h>
110#ifdef DEBUG_MEMORY
111#include <ascii-chat/debug/memory.h>
112#endif
113#endif
114
115#include <signal.h>
116#include <stdlib.h>
117#include <stdio.h>
118#include <string.h>
119#include <stdatomic.h>
120#ifndef _WIN32
121#include <unistd.h>
122#endif
123#include <fcntl.h>
124
125/* ============================================================================
126 * Global State Variables
127 * ============================================================================ */
128
139thread_pool_t *g_client_worker_pool = NULL;
140
151app_client_t *g_client = NULL;
152
160
172typedef struct {
173 connection_attempt_context_t connection_ctx; // Fallback connection context (embedded)
174 const char *discovered_address; // From LAN/session discovery
175 int discovered_port; // From LAN/session discovery
176 int reconnect_attempt; // Current reconnection attempt number
177 bool has_ever_connected; // Track if connection ever succeeded
179
180static client_session_state_t g_client_session = {0};
181
182/* Signal handling is now centralized in src/main.c via setup_signal_handlers()
183 * Client mode uses set_interrupt_callback(server_connection_shutdown) to register
184 * its network shutdown as the interrupt handler. SIGWINCH is still client-specific.
185 */
186
196#ifndef _WIN32
197static void client_handle_sigwinch(int sigwinch) {
198 (void)(sigwinch);
199
200 // Terminal was resized, update dimensions and recalculate aspect ratio
201 // ONLY if both width and height are auto (not manually set)
202 if (GET_OPTION(auto_width) && GET_OPTION(auto_height)) {
203 // Get terminal size and update via proper RCU setters
204 unsigned short int term_width, term_height;
205 if (get_terminal_size(&term_width, &term_height) == ASCIICHAT_OK) {
206 options_set_int("width", (int)term_width);
207 options_set_int("height", (int)term_height);
208 }
209
210 // Send new size to server if connected
212 if (threaded_send_terminal_size_with_auto_detect(GET_OPTION(width), GET_OPTION(height)) < 0) {
213 log_warn("Failed to send terminal capabilities to server: %s", network_error_string());
214 } else {
217 }
218 }
219 }
220}
221#else
222// Windows-compatible signal handler (no-op implementation)
223static void client_handle_sigwinch(int sigwinch) {
224 (void)(sigwinch);
225 log_console(LOG_DEBUG, "SIGWINCH received (Windows no-op implementation)");
226}
227#endif
228
238static void shutdown_client() {
239 // Guard against double cleanup (can be called explicitly + via atexit)
240 static bool shutdown_done = false;
241 if (shutdown_done) {
242 return;
243 }
244 shutdown_done = true;
245
246 // Set global shutdown flag to stop all threads
247 signal_exit();
248
249 // IMPORTANT: Stop all protocol threads BEFORE cleaning up resources
250 // protocol_stop_connection() shuts down the socket to interrupt blocking recv(),
251 // then waits for the data reception thread and capture thread to exit.
252 // This prevents race conditions where threads access freed resources.
254
255 // Destroy client worker thread pool (all threads already stopped by protocol_stop_connection)
259 }
260
261 // Destroy application client context
262 if (g_client) {
264 log_debug("Application client context destroyed successfully");
265 }
266
267 // Now safe to cleanup server connection (socket already closed by protocol_stop_connection)
268 // Legacy cleanup - will be removed after full migration to tcp_client
270
271 // Cleanup capture subsystems (capture thread already stopped by protocol_stop_connection)
273
274 // Print audio analysis report if enabled
275 if (GET_OPTION(audio_analysis_enabled)) {
278 }
279
281
282#ifndef NDEBUG
283 // Stop lock debug thread BEFORE display_cleanup() because the debug thread uses
284 // _kbhit()/_getch() on Windows which interact with the console. If we close the
285 // CON handle first, the debug thread can hang on console I/O, blocking process exit.
287#endif
288
289 // Cleanup display and terminal state
291
292 // Cleanup core systems
294
295 // Disable keepawake mode (re-allow OS to sleep)
297
298 // Clean up symbol cache (before log_destroy)
299 // This must be called BEFORE log_destroy() as symbol_cache_destroy() uses log_debug()
300 // Safe to call even if atexit() runs - it's idempotent (checks g_symbol_cache_initialized)
301 // Also called via platform_destroy() atexit handler, but explicit call ensures proper ordering
303
304 // Clean up binary path cache explicitly
305 // Note: This is also called by platform_destroy() via atexit(), but it's idempotent
307
308 // Clean up errno context (allocated strings, backtrace symbols)
310
311 // Clean up RCU-based options state
313
314 log_debug("Client shutdown complete");
315 log_destroy();
316
317#ifndef NDEBUG
318 // Join the debug thread as the very last thing (after log_destroy since thread may log)
320#endif
321}
322
323#ifndef NDEBUG
324#endif
325
335static int initialize_client_systems(void) {
336 // All shared subsystem initialization (timer, logging, platform, buffer pool)
337 // is now done by asciichat_shared_init() in src/main.c BEFORE options_init()
338
339 // Initialize client worker thread pool (always needed, even if shared init done)
341 g_client_worker_pool = thread_pool_create("client_workers");
343 log_fatal("Failed to create client worker thread pool");
344 return ERROR_THREAD;
345 }
346 }
347
348 // Ensure logging output is available for connection attempts (unless it was disabled with --quiet)
349 if (!GET_OPTION(quiet)) {
351 }
353
354 // Display subsystem is now initialized by session_client_like_run()
355 // No need to call display_init() here
356
357 // Initialize application client context
358 if (!g_client) {
360 if (!g_client) {
361 log_fatal("Failed to create application client context");
362 return ERROR_NETWORK;
363 }
364 log_debug("Application client context created successfully");
365 }
366
367 // Initialize server connection management (legacy - will be migrated to tcp_client)
368 if (server_connection_init() != 0) {
369 log_fatal("Failed to initialize server connection");
370 return ERROR_NETWORK;
371 }
372
373 // Initialize capture subsystems
374 int capture_result = capture_init();
375 if (capture_result != 0) {
376 log_fatal("Failed to initialize capture subsystem");
377 return capture_result;
378 }
379
380 // Initialize audio if enabled
381 if (GET_OPTION(audio_enabled)) {
382 if (audio_client_init() != 0) {
383 log_warn("Failed to initialize audio system");
384 // Continue without audio instead of crashing (e.g., ARM systems with audio device incompatibility)
385 }
386
387 // Initialize audio analysis if requested
388 if (GET_OPTION(audio_analysis_enabled)) {
389 if (audio_analysis_init() != 0) {
390 log_warn("Failed to initialize audio analysis");
391 }
392 }
393 }
394
395 return 0;
396}
397
409/* ============================================================================
410 * Client Connection/Reconnection Loop (run_fn for session_client_like)
411 * ============================================================================
412 *
413 * This callback is executed after shared initialization (media/audio/display)
414 * is complete. It manages the entire client connection lifecycle:
415 * 1. Connect to server (with fallback stages: TCP, WebRTC+STUN, WebRTC+TURN)
416 * 2. Exchange media/audio with server
417 * 3. On disconnection, attempt reconnection based on policy
418 * 4. Exit on user request or max reconnection limit
419 */
420
432static bool client_should_reconnect(asciichat_error_t last_error, int attempt_number, void *user_data) {
433 (void)last_error;
434 (void)attempt_number;
435 (void)user_data;
436
437 // In snapshot mode, don't reconnect - exit after first failure
438 if (GET_OPTION(snapshot_mode)) {
439 log_error("Connection lost in snapshot mode - not retrying");
440 return false;
441 }
442
443 // Otherwise, allow reconnection (framework handles max_reconnect_attempts limit)
444 return true;
445}
446
463static asciichat_error_t client_run(session_capture_ctx_t *capture, session_display_ctx_t *display, void *user_data) {
464 (void)capture; // Provided by session_client_like, used by protocol threads
465 (void)user_data; // Unused
466
467 // Make the framework-created display context available to protocol threads
468 display_set_context(display);
469
470 // Get the render loop's should_exit callback for monitoring
471 bool (*render_should_exit)(void *) = session_client_like_get_render_should_exit();
472 if (!render_should_exit) {
473 return SET_ERRNO(ERROR_INVALID_STATE, "Render should_exit callback not initialized");
474 }
475
476 // Attempt connection with fallback stages (TCP, WebRTC+STUN, WebRTC+TURN)
477 // Get pre-created TCP client from framework if available
478 tcp_client_t *framework_tcp_client = session_client_like_get_tcp_client();
479 asciichat_error_t connection_result = connection_attempt_tcp(
480 &g_client_session.connection_ctx,
481 g_client_session.discovered_address != NULL ? g_client_session.discovered_address : "",
482 (uint16_t)(g_client_session.discovered_port > 0 ? g_client_session.discovered_port : 27224),
483 framework_tcp_client);
484
485 // Check if shutdown was requested during connection attempt
486 if (should_exit()) {
487 log_info("Shutdown requested during connection attempt");
488 return ERROR_NETWORK;
489 }
490
491 if (connection_result != ASCIICHAT_OK) {
492 // Connection failed - framework will handle retry based on config
493 log_error("Connection attempt failed");
494 return connection_result;
495 }
496
497 // Connection successful - integrate transport into server layer
498 if (g_client_session.connection_ctx.active_transport) {
500 g_client_session.connection_ctx.active_transport = NULL;
501 } else {
502 log_error("Connection succeeded but no active transport");
503 return ERROR_NETWORK;
504 }
505
506 // Log connection status
507 if (!g_client_session.has_ever_connected) {
508 log_info("Connected");
509 g_client_session.has_ever_connected = true;
510 } else {
511 log_info("Reconnected");
512 }
513
514 // Start protocol worker threads for this connection
515 if (protocol_start_connection() != 0) {
516 log_error("Failed to start connection protocols");
519 return ERROR_NETWORK;
520 }
521
522 // Monitor connection until it breaks or shutdown is requested
525 log_debug("Connection lost detected");
526 break;
527 }
528 platform_sleep_us(100 * US_PER_MS_INT);
529 }
530
531 if (should_exit()) {
532 log_debug("Shutdown requested, cleaning up connection");
533 } else {
534 log_debug("Connection lost, preparing for reconnection attempt");
535 }
536
537 // Clean up this connection for potential reconnection
540
541 // Recreate thread pool for clean reconnection
545 }
546 g_client_worker_pool = thread_pool_create("client_reconnect");
548 log_error("Failed to recreate worker thread pool");
549 return ERROR_THREAD;
550 }
551
552 // Reset connection context for next attempt
554 memset(&g_client_session.connection_ctx, 0, sizeof(g_client_session.connection_ctx));
555 if (connection_context_init(&g_client_session.connection_ctx) != ASCIICHAT_OK) {
556 log_error("Failed to re-initialize connection context");
557 return ERROR_NETWORK;
558 }
559
560 // Signal reconnection attempt to the user (splash screen will show logs)
561 if (g_client_session.has_ever_connected && !GET_OPTION(quiet)) {
562 splash_intro_start(NULL);
563 }
564
565 // Return error to signal reconnection needed (framework handles the retry)
566 return ERROR_NETWORK;
567}
568
569int client_main(void) {
570 log_debug("client_main() starting");
571
572 // Initialize client-specific systems (NOT shared with session_client_like)
573 // This includes: thread pool, display layer, app client context, server connection
574 int init_result = initialize_client_systems();
575 if (init_result != 0) {
576#ifndef NDEBUG
577 // Debug builds: automatically fall back to test pattern if webcam is in use
578 if (init_result == ERROR_WEBCAM_IN_USE && !GET_OPTION(test_pattern)) {
579 log_warn("Webcam is in use - automatically falling back to test pattern mode (debug build only)");
580
581 // Enable test pattern mode via RCU update
582 asciichat_error_t update_result = options_set_bool("test_pattern", true);
583 if (update_result != ASCIICHAT_OK) {
584 log_error("Failed to update options for test pattern fallback");
585 FATAL(init_result, "%s", asciichat_error_string(init_result));
586 }
587
588 // Retry initialization with test pattern enabled
589 init_result = initialize_client_systems();
590 if (init_result != 0) {
591 log_error("Failed to initialize even with test pattern fallback");
592 webcam_print_init_error_help(init_result);
593 FATAL(init_result, "%s", asciichat_error_string(init_result));
594 }
595 log_debug("Successfully initialized with test pattern fallback");
596
597 // Clear the error state since we successfully recovered
598 CLEAR_ERRNO();
599 } else
600#endif
601 {
602 // Release builds or other errors: print help and exit
603 if (init_result == ERROR_WEBCAM || init_result == ERROR_WEBCAM_IN_USE || init_result == ERROR_WEBCAM_PERMISSION) {
604 webcam_print_init_error_help(init_result);
605 FATAL(init_result, "%s", asciichat_error_string(init_result));
606 }
607 // For other errors, just exit with the error code
608 return init_result;
609 }
610 }
611
612 // Register cleanup function for graceful shutdown
613 (void)atexit(shutdown_client);
614
615 // Register client interrupt callback (socket shutdown on SIGTERM/Ctrl+C)
616 // Global signal handlers (SIGTERM, SIGPIPE, Ctrl+C) are set up in setup_signal_handlers() in src/main.c
618
619#ifndef _WIN32
620 // Register SIGWINCH for terminal resize handling (client-specific, not in framework)
621 platform_signal(SIGWINCH, client_handle_sigwinch);
622#endif
623
624 /* ========================================================================
625 * Client-Specific: LAN/Session Discovery
626 * ========================================================================
627 * This phase discovers the server to connect to via:
628 * - LAN discovery (mDNS)
629 * - Session string lookup (ACDS)
630 * - Direct address/port
631 */
632
633 // LAN Discovery: If --scan flag is set, discover servers on local network
634 const options_t *opts = options_get();
635 if (opts && opts->lan_discovery &&
636 (opts->address[0] == '\0' || is_localhost_ipv4(opts->address) || strcmp(opts->address, "localhost") == 0)) {
637 log_debug("LAN discovery: --scan flag set, querying for available servers");
638
639 discovery_tui_config_t lan_config;
640 memset(&lan_config, 0, sizeof(lan_config));
641 lan_config.timeout_ms = 2 * MS_PER_SEC_INT; // Wait up to 2 seconds for responses
642 lan_config.max_servers = 20; // Support up to 20 servers on LAN
643 lan_config.quiet = true; // Quiet during discovery, TUI will show status
644
645 int discovered_count = 0;
646 discovery_tui_server_t *discovered_servers = discovery_tui_query(&lan_config, &discovered_count);
647
648 // Use TUI for server selection
649 int selected_index = discovery_tui_select(discovered_servers, discovered_count);
650
651 if (selected_index < 0) {
652 // User cancelled or no servers found
653 if (discovered_count == 0) {
654 // No servers found - log message and prevent any further output
655 // Lock the terminal so other threads can't write and our error
656 // will be the last message
658
659 // Log single message with embedded newlines to prevent multiple log entries
660 log_error("No ascii-chat servers found on the local network.\nUse 'ascii-chat client <address>' to connect "
661 "manually.");
662
663 // Exit without cleanup
664 platform_force_exit(1);
665 }
666 // User cancelled (had servers to choose from but pressed cancel)
667 log_debug("LAN discovery: User cancelled server selection");
668 if (discovered_servers) {
669 discovery_tui_free_results(discovered_servers);
670 }
671 return 1; // User cancelled
672 }
673
674 // Update options with discovered server's address and port
675 discovery_tui_server_t *selected = &discovered_servers[selected_index];
676 const char *selected_address = discovery_tui_get_best_address(selected);
677
678 // We need to modify options, but they're immutable via RCU
679 // Create a new options copy with updated address/port
680 options_t *opts_new = SAFE_MALLOC(sizeof(options_t), options_t *);
681 if (opts_new) {
682 memcpy(opts_new, opts, sizeof(options_t));
683 SAFE_STRNCPY(opts_new->address, selected_address, sizeof(opts_new->address));
684
685 opts_new->port = (int)selected->port;
686
687 log_debug("LAN discovery: Selected server '%s' at %s:%d", selected->name, opts_new->address, opts_new->port);
688
689 // Note: In a real scenario, we'd update the global options via RCU
690 // For now, we'll use the updated values directly for connection
691 // This is a known limitation - proper RCU update requires more infrastructure
692 }
693
694 discovery_tui_free_results(discovered_servers);
695 }
696
697 // =========================================================================
698 // Client-Specific: Server Address Resolution
699 // =========================================================================
700 // Client mode supports:
701 // 1. Direct address/port (--address HOST --port PORT) - handled by options
702 // 2. LAN discovery (--scan) - handled above
703 // 3. WebSocket URL (direct ws:// or wss:// connection string)
704 //
705 // Note: Session string discovery via ACDS is handled by discovery mode only.
706 // Client mode does NOT use ACDS or session strings.
707
708 const char *discovered_address = NULL;
709 int discovered_port = 0;
710
711 // Check if user provided a WebSocket URL as the server address
712 const options_t *opts_websocket = options_get();
713 const char *provided_address = opts_websocket && opts_websocket->address[0] != '\0' ? opts_websocket->address : NULL;
714
715 if (provided_address && url_is_websocket(provided_address)) {
716 // Direct WebSocket connection
717 log_debug("Client: Direct WebSocket URL: %s", provided_address);
718 discovered_address = provided_address;
719 discovered_port = 0; // Port is embedded in the URL
720 }
721
722 log_debug("Client: discovered_address=%s, discovered_port=%d", discovered_address ? discovered_address : "NULL",
723 discovered_port);
724
725 // Store discovered address/port in session state for client_run() callback
726 static char address_storage[BUFFER_SIZE_SMALL];
727 if (discovered_address) {
728 SAFE_STRNCPY(address_storage, discovered_address, sizeof(address_storage));
729 g_client_session.discovered_address = address_storage;
730 g_client_session.discovered_port = discovered_port;
731 } else {
732 const options_t *opts_fallback = options_get();
733 if (opts_fallback && opts_fallback->address[0] != '\0') {
734 SAFE_STRNCPY(address_storage, opts_fallback->address, sizeof(address_storage));
735 g_client_session.discovered_address = address_storage;
736 g_client_session.discovered_port = opts_fallback->port;
737 } else {
738 g_client_session.discovered_address = "localhost";
739 g_client_session.discovered_port = 27224;
740 }
741 }
742
743 // Initialize connection context for first attempt
744 asciichat_error_t ctx_init_result = connection_context_init(&g_client_session.connection_ctx);
745 if (ctx_init_result != ASCIICHAT_OK) {
746 log_error("Failed to initialize connection context");
747 return 1;
748 }
749
750 /* ========================================================================
751 * Configure and Run Shared Client-Like Session Framework
752 * ========================================================================
753 * session_client_like_run() handles all shared initialization:
754 * - Terminal output management (force stderr if piped)
755 * - Keepawake system (platform sleep prevention)
756 * - Splash screen lifecycle
757 * - Media source selection (webcam, file, URL, test pattern)
758 * - FPS probing for media files
759 * - Audio initialization and lifecycle
760 * - Display context creation
761 * - Proper cleanup ordering (critical for PortAudio)
762 *
763 * Client mode provides:
764 * - client_run() callback: connection loop, protocol startup, monitoring
765 * - client_should_reconnect() callback: reconnection policy
766 * - Reconnection configuration: attempts, delay, callbacks
767 */
768
769 // Get reconnect attempts setting (-1 = unlimited, 0 = no retry, >0 = retry N times)
770 int reconnect_attempts = GET_OPTION(reconnect_attempts);
771
772 // Configure session_client_like with client-specific settings
773 session_client_like_config_t config = {
774 .run_fn = client_run,
775 .run_user_data = NULL,
776 .tcp_client = NULL,
777 .websocket_client = NULL,
778 .discovery = NULL,
779 .custom_should_exit = NULL,
780 .exit_user_data = NULL,
781 .keyboard_handler = NULL, // Client mode: server drives display
782 .max_reconnect_attempts = reconnect_attempts,
783 .should_reconnect_callback = client_should_reconnect,
784 .reconnect_user_data = NULL,
785 .reconnect_delay_ms = 1000, // 1 second delay between reconnection attempts
786 .print_newline_on_tty_exit = false, // Server/client manages cursor
787 };
788
789 log_debug("Client: calling session_client_like_run() with %d max reconnection attempts", reconnect_attempts);
790 asciichat_error_t session_result = session_client_like_run(&config);
791 log_debug("Client: session_client_like_run() returned %d", session_result);
792
793 // Cleanup connection context
795
796 // Cleanup session log buffer (used by splash screen in session_client_like)
798
799 log_debug("ascii-chat client shutting down");
800
801 // IMPORTANT: Stop worker threads and join them BEFORE memory report
802 // atexit(shutdown_client) won't run if interrupted by SIGTERM, so call explicitly
803 shutdown_client();
804
805 // Cleanup remaining shared subsystems (buffer pool, platform, etc.)
806 // Note: atexit(asciichat_shared_destroy) is registered in main.c,
807 // but won't run if interrupted by signals (SIGTERM from timeout/killall)
809
810 return (session_result == ASCIICHAT_OK) ? 0 : 1;
811}
int audio_analysis_init(void)
Definition analysis.c:114
void audio_analysis_print_report(void)
Definition analysis.c:513
void audio_analysis_destroy(void)
Definition analysis.c:875
void asciichat_errno_destroy(void)
void buffer_pool_cleanup_global(void)
ascii-chat Client Media Capture Management Interface
app_client_t * g_client
Global client application context.
int client_main(void)
Client mode entry point for unified binary.
struct webrtc_peer_manager * g_peer_manager
Global WebRTC peer manager (legacy compatibility)
thread_pool_t * g_client_worker_pool
Global client worker thread pool.
asciichat_error_t session_client_like_run(const session_client_like_config_t *config)
Definition client_like.c:96
bool should_exit(void)
Definition main.c:90
tcp_client_t * session_client_like_get_tcp_client(void)
Definition client_like.c:54
bool(*)(void *) session_client_like_get_render_should_exit(void)
Definition client_like.c:50
void asciichat_shared_destroy(void)
Clean up all shared library subsystems.
Definition common.c:164
asciichat_error_t connection_context_init(connection_attempt_context_t *ctx)
Initialize connection attempt context.
void connection_context_cleanup(connection_attempt_context_t *ctx)
Cleanup connection attempt context.
asciichat_error_t connection_attempt_tcp(connection_attempt_context_t *ctx, const char *server_address, uint16_t server_port, struct tcp_client *pre_created_tcp_client)
Attempt direct TCP connection.
Connection state machine for TCP client connections.
void discovery_tui_free_results(discovery_tui_server_t *servers)
Free results from mDNS discovery.
discovery_tui_server_t * discovery_tui_query(const discovery_tui_config_t *config, int *out_count)
TUI wrapper around core mDNS discovery.
int discovery_tui_select(const discovery_tui_server_t *servers, int count)
TUI-based server selection with formatted display.
const char * discovery_tui_get_best_address(const discovery_tui_server_t *server)
Get best address for a server.
ascii-chat Client Display Management Interface
int audio_client_init()
Initialize audio subsystem.
void audio_cleanup()
Cleanup audio subsystem.
int capture_init()
Initialize capture subsystem.
void capture_cleanup()
Cleanup capture subsystem.
void server_connection_close()
Close the server connection gracefully.
bool server_connection_is_active()
Check if server connection is currently active.
void server_connection_shutdown()
Emergency connection shutdown for signal handlers.
asciichat_error_t threaded_send_terminal_size_with_auto_detect(unsigned short width, unsigned short height)
Thread-safe terminal size packet transmission with auto-detection.
void server_connection_set_transport(acip_transport_t *transport)
Set ACIP transport instance from connection fallback.
int server_connection_init()
Initialize the server connection management subsystem.
void server_connection_cleanup()
Cleanup connection management subsystem.
void display_set_context(session_display_ctx_t *display_ctx)
Set the display context created by session_client_like framework.
void display_full_reset()
Perform full display reset.
void display_cleanup()
Cleanup display subsystem.
void protocol_stop_connection()
Stop protocol connection handling.
int protocol_start_connection()
Start protocol connection handling.
bool protocol_connection_lost()
Check if connection has been lost.
int is_localhost_ipv4(const char *ip)
Definition ip.c:1299
void app_client_destroy(app_client_t **client_ptr)
Destroy client application context and free all resources.
app_client_t * app_client_create(void)
Create and initialize client application context.
void platform_disable_keepawake(void)
void lock_debug_cleanup_thread(void)
Definition lock.c:1371
void lock_debug_destroy(void)
Definition lock.c:1370
void log_truncate_if_large(void)
void log_destroy(void)
bool log_lock_terminal(void)
void log_set_terminal_output(bool enabled)
void signal_exit(void)
Definition main.c:94
void set_interrupt_callback(void(*cb)(void))
Definition main.c:102
const char * network_error_string()
Get human-readable error string for network errors.
ASCIICHAT_API bool auto_width
ASCIICHAT_API bool auto_height
asciichat_error_t get_terminal_size(unsigned short int *width, unsigned short int *height)
void platform_sleep_us(unsigned int us)
asciichat_error_t options_set_int(const char *field_name, int value)
Definition rcu.c:449
void options_state_destroy(void)
Definition rcu.c:309
const options_t * options_get(void)
Definition rcu.c:347
asciichat_error_t options_set_bool(const char *field_name, bool value)
Definition rcu.c:562
Server cryptographic operations and per-client handshake management.
ascii-chat Server Mode Entry Point Header
Server packet processing and protocol implementation.
void session_log_buffer_destroy(void)
int splash_intro_start(session_display_ctx_t *ctx)
Definition splash.c:482
ascii-chat Client Audio Processing Management Interface
#define bool
Definition stdbool.h:22
Client connection session state for session_client_like integration.
const char * discovered_address
connection_attempt_context_t connection_ctx
Context for TCP connection attempts.
acip_transport_t * active_transport
Currently active transport.
Internal session display context structure.
WebRTC peer manager structure.
void symbol_cache_destroy(void)
Definition symbols.c:292
void platform_cleanup_binary_path_cache(void)
Cleanup the binary PATH cache.
Definition system.c:304
void thread_pool_destroy(thread_pool_t *pool)
Definition thread_pool.c:48
thread_pool_t * thread_pool_create(const char *pool_name)
Definition thread_pool.c:17
bool url_is_websocket(const char *url)
Definition url.c:307
void webcam_print_init_error_help(asciichat_error_t error_code)