109#include <stdatomic.h>
191 static _Atomic
int ctrl_c_count = 0;
192 int count = atomic_fetch_add(&ctrl_c_count, 1) + 1;
196 TerminateProcess(GetCurrentProcess(), 1);
223static void sigwinch_handler(
int sigwinch) {
244static void sigwinch_handler(
int sigwinch) {
246 log_debug(
"SIGWINCH received (Windows no-op implementation)");
257static void shutdown_client() {
276 log_debug(
"TCP client instance destroyed successfully");
298 lock_debug_cleanup();
323 log_info(
"Client shutdown complete");
328 lock_debug_cleanup_thread();
341static int initialize_client_systems(
bool shared_init_completed) {
342 if (!shared_init_completed) {
345 (void)fprintf(stderr,
"FATAL: Failed to initialize platform\n");
355 log_error(
"Failed to apply palette configuration");
360 char *validated_log_file = NULL;
366 if (log_path_result !=
ASCIICHAT_OK || !validated_log_file || strlen(validated_log_file) == 0) {
368 (void)fprintf(stderr,
"WARNING: Invalid log file path specified, using default 'client.log'\n");
369 log_init(
"client.log", log_level,
true,
true );
371 log_init(validated_log_file, log_level,
true,
true );
375 log_init(
"client.log", log_level,
true,
true );
381 debug_memory_set_quiet_mode(quiet_mode);
383 (void)atexit(debug_memory_report);
396 log_fatal(
"Failed to initialize WebRTC library: %s", asciichat_error_string(webrtc_result));
400 log_debug(
"WebRTC library initialized successfully");
406 log_fatal(
"Failed to create client worker thread pool");
417 log_fatal(
"Failed to initialize display subsystem");
425 log_fatal(
"Failed to create TCP client instance");
428 log_debug(
"TCP client instance created successfully");
433 log_fatal(
"Failed to initialize server connection");
439 if (capture_result != 0) {
440 log_fatal(
"Failed to initialize capture subsystem");
441 return capture_result;
447 log_fatal(
"Failed to initialize audio system");
454 log_warn(
"Failed to initialize audio analysis");
483 int init_result = initialize_client_systems(
true);
484 if (init_result != 0) {
488 FATAL(init_result,
"%s", asciichat_error_string(init_result));
495 (void)atexit(shutdown_client);
514 static bool has_ever_connected =
false;
523 int reconnect_attempt = 0;
524 bool first_connection =
true;
529 (opts->
address[0] ==
'\0' || strcmp(opts->
address,
"127.0.0.1") == 0 ||
530 strcmp(opts->
address,
"localhost") == 0)) {
531 log_info(
"LAN discovery: --scan flag set, querying for available servers");
534 memset(&lan_config, 0,
sizeof(lan_config));
537 lan_config.
quiet =
true;
539 int discovered_count = 0;
545 if (selected_index < 0) {
547 if (discovered_count == 0) {
552 fprintf(stderr,
"\n");
553 fprintf(stderr,
"No ASCII-Chat servers found on the local network.\n");
554 fprintf(stderr,
"Use 'ascii-chat client <address>' to connect manually.\n");
559 log_file_msg(
"No ASCII-Chat servers found on the local network.\n");
560 log_file_msg(
"Use 'ascii-chat client <address>' to connect manually.\n");
564 int dev_null = open(
"/dev/null", O_WRONLY);
566 dup2(dev_null, STDERR_FILENO);
567 dup2(dev_null, STDOUT_FILENO);
575 log_info(
"LAN discovery: User cancelled server selection");
576 if (discovered_servers) {
590 memcpy(opts_new, opts,
sizeof(
options_t));
595 snprintf(port_str,
sizeof(port_str),
"%u", selected->
port);
627 log_error(
"Failed to initialize connection context");
649 const char *discovered_address = NULL;
650 const char *discovered_port = NULL;
653 const char *session_string =
656 if (session_string[0] !=
'\0') {
657 log_info(
"Session string detected: '%s' - performing parallel discovery (mDNS + ACDS)", session_string);
665 memset(expected_pubkey, 0, 32);
666 if (opts_discovery && opts_discovery->
server_key[0] !=
'\0') {
670 log_info(
"Server key verification enabled");
672 log_warn(
"Failed to parse server key - skipping verification");
679 log_warn(
"ACDS insecure mode enabled - no server key verification");
683 if (opts_discovery && opts_discovery->
acds_server[0] !=
'\0') {
686 if (opts_discovery && opts_discovery->
acds_port > 0) {
691 if (opts_discovery && opts_discovery->
password[0] !=
'\0') {
693 log_debug(
"Password configured for session join");
698 memset(&discovery_result, 0,
sizeof(discovery_result));
702 fprintf(stderr,
"Error: Failed to discover session '%s'\n", session_string);
703 fprintf(stderr,
" - Not found via mDNS (local network)\n");
704 fprintf(stderr,
" - Not found via ACDS (discovery server)\n");
705 fprintf(stderr,
"\nDid you mean to:\n");
706 fprintf(stderr,
" ascii-chat server # Start a new server\n");
707 fprintf(stderr,
" ascii-chat client # Connect to localhost\n");
708 fprintf(stderr,
" ascii-chat client HOST # Connect to specific host\n");
713 const char *source_name = discovery_result.
source == DISCOVERY_SOURCE_MDNS ?
"mDNS (LAN)" :
"ACDS (internet)";
719 static char address_buffer[256];
720 static char port_buffer[8];
722 snprintf(port_buffer,
sizeof(port_buffer),
"%d", discovery_result.
server_port);
723 discovered_address = address_buffer;
724 discovered_port = port_buffer;
727 if (discovery_result.
source == DISCOVERY_SOURCE_ACDS) {
735 log_debug(
"Populated session context for WebRTC fallback (session='%s')", session_string);
744 const char *address = discovered_address
746 : (opts_conn && opts_conn->
address[0] !=
'\0' ? opts_conn->
address :
"localhost");
747 const char *port_str =
748 discovered_port ? discovered_port : (opts_conn && opts_conn->
port[0] !=
'\0' ? opts_conn->
port :
"27224");
749 int port = atoi(port_str);
755 const char *acds_server =
GET_OPTION(acds_server);
756 if (!acds_server || acds_server[0] ==
'\0') {
757 acds_server =
"127.0.0.1";
760 if (acds_port <= 0 || acds_port > 65535) {
770 int connection_success = (connection_result ==
ASCIICHAT_OK) ? 0 : -1;
772 if (connection_success != 0) {
780 log_error(
"Connection failed in snapshot mode - exiting without retry");
788 int reconnect_attempts =
GET_OPTION(reconnect_attempts);
791 if (reconnect_attempts == 0) {
793 log_error(
"Connection failed (reconnection disabled via --reconnect off)");
795 }
else if (reconnect_attempts > 0 && reconnect_attempt > reconnect_attempts) {
797 log_error(
"Connection failed after %d attempts (limit set by --reconnect %d)", reconnect_attempt - 1,
803 if (has_ever_connected) {
811 if (has_ever_connected) {
812 if (reconnect_attempts == -1) {
813 log_info(
"Reconnection attempt #%d... (unlimited retries)", reconnect_attempt);
815 log_info(
"Reconnection attempt #%d/%d...", reconnect_attempt, reconnect_attempts);
818 if (reconnect_attempts == -1) {
819 log_info(
"Connection attempt #%d... (unlimited retries)", reconnect_attempt);
821 log_info(
"Connection attempt #%d/%d...", reconnect_attempt, reconnect_attempts);
830 reconnect_attempt = 0;
831 first_connection =
false;
837 log_debug(
"Active transport integrated into server connection layer");
839 log_error(
"Connection succeeded but no active transport - this should never happen");
844 if (!has_ever_connected) {
845 log_info(
"Connected successfully, starting worker threads");
846 has_ever_connected =
true;
848 log_info(
"Reconnected successfully, starting worker threads");
853 log_error(
"Failed to start connection protocols");
858 log_error(
"Protocol startup failed in snapshot mode - exiting without retry");
878 log_info(
"Connection lost detected by protocol threads");
886 log_info(
"Shutdown requested, exiting main loop");
891 log_info(
"Connection lost, cleaning up for reconnection");
895 if (has_ever_connected) {
906 log_error(
"Connection lost in snapshot mode - exiting without reconnection");
911 if (has_ever_connected) {
912 log_info(
"Waiting 1 second before attempting reconnection...");
916 log_info(
"Cleanup complete, will attempt reconnection");
922 log_info(
"ascii-chat client shutting down");
🔌 Cross-platform abstraction layer umbrella header for ascii-chat
int audio_analysis_init(void)
Initialize audio analysis.
void audio_analysis_cleanup(void)
Cleanup audio analysis.
void audio_analysis_print_report(void)
Print audio analysis report.
Audio Analysis and Debugging Interface.
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
ascii-chat Client Media Capture Management Interface
void signal_exit()
Signal client to exit.
tcp_client_t * g_client
Global TCP client instance.
int client_main(void)
Client mode entry point for unified binary.
struct webrtc_peer_manager * g_peer_manager
Global WebRTC peer manager for P2P connections.
thread_pool_t * g_client_worker_pool
Global client worker thread pool.
bool should_exit()
Check if client should exit.
atomic_bool g_should_exit
asciichat_error_t connection_context_init(connection_attempt_context_t *ctx, bool prefer_webrtc, bool no_webrtc, bool webrtc_skip_stun, bool webrtc_disable_turn)
Initialize connection attempt context.
asciichat_error_t connection_attempt_with_fallback(connection_attempt_context_t *ctx, const char *server_address, uint16_t server_port, const char *acds_server, uint16_t acds_port)
Orchestrate connection attempt with automatic fallback.
void connection_context_cleanup(connection_attempt_context_t *ctx)
Cleanup connection attempt context.
🎯 Connection state machine for Phase 3 WebRTC fallback integration
🔍 Memory debugging helpers for tracking allocations in debug builds
asciichat_error_t hex_to_pubkey(const char *hex_str, uint8_t pubkey_out[32])
Convert hex string to Ed25519 public key.
void discovery_config_init_defaults(discovery_config_t *config)
Initialize discovery config with defaults.
asciichat_error_t discover_session_parallel(const char *session_string, const discovery_config_t *config, discovery_result_t *result)
Look up session in parallel on mDNS and ACDS.
Parallel mDNS and ACDS session discovery.
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.
TUI-based service discovery for ascii-chat client.
ascii-chat Client Display Management Interface
void buffer_pool_cleanup_global(void)
void buffer_pool_init_global(void)
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.
int 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.
int display_init()
Initialize what is necessary to display ascii frames.
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.
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_MALLOC(size, cast)
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
void asciichat_errno_cleanup(void)
Cleanup error system resources.
asciichat_error_t
Error and exit codes - unified status values (0-255)
@ ERROR_WEBCAM_PERMISSION
void log_truncate_if_large(void)
Manually truncate large log files.
#define log_warn(...)
Log a WARN message.
void log_destroy(void)
Destroy the logging system and close log file.
#define log_error(...)
Log an ERROR message.
#define log_fatal(...)
Log a FATAL message.
void log_init(const char *filename, log_level_t level, bool force_stderr, bool use_mmap)
Initialize the logging system.
bool log_lock_terminal(void)
Lock terminal output for exclusive access by the calling thread.
log_level_t
Logging levels enumeration.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
#define log_file(...)
File-only logging - writes to log file only, no stderr output.
void log_file_msg(const char *fmt,...)
Log to file only, no stderr output.
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
const char * network_error_string()
Get human-readable error string for network errors.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
void update_dimensions_to_terminal_size(options_t *opts)
Update dimensions to current terminal size.
const options_t * options_get(void)
Get current options (lock-free read)
palette_type_t
Built-in palette type enumeration.
int apply_palette_config(palette_type_t type, const char *custom_chars)
Apply palette configuration (set global palette)
asciichat_error_t path_validate_user_path(const char *input, path_role_t role, char **normalized_out)
Validate and canonicalize a user-supplied filesystem path.
void webcam_print_init_error_help(asciichat_error_t error_code)
Print helpful error diagnostics for webcam initialization failures.
void webrtc_cleanup(void)
Cleanup WebRTC library resources.
asciichat_error_t webrtc_init(void)
Initialize WebRTC library (libdatachannel)
Platform initialization and static synchronization helpers.
ACIP client-side protocol library.
tcp_client_t * tcp_client_create(void)
Create and initialize TCP client.
void tcp_client_destroy(tcp_client_t **client_ptr)
Destroy TCP client and free resources.
🔒 Lock debugging and deadlock detection system for ascii-chat
📝 Logging API with multiple log levels and terminal output control
ASCII-Chat Discovery Service (ACDS) Protocol Message Formats.
🌐 Core network I/O operations with timeout support
ASCIICHAT_API bool auto_width
ASCIICHAT_API bool auto_height
⚙️ Command-line options parsing and configuration management for ascii-chat
ASCII Palette Management for Video-to-ASCII Conversion.
📂 Path Manipulation Utilities
void options_state_shutdown(void)
Shutdown RCU options system.
ascii-chat Server Mode Entry Point Header
ascii-chat Client Audio Processing Management Interface
ascii-chat Client Server Connection Management Interface
Client-side WebRTC signaling callback implementations.
Server cryptographic operations and per-client handshake management.
Server packet processing and protocol implementation.
Master context for connection attempt with fallback.
uint32_t reconnect_attempt
Reconnection attempt number (1st, 2nd, etc.)
acip_transport_t * active_transport
Currently active transport (whichever succeeded)
connection_session_context_t session_ctx
Session context from ACDS.
char session_string[128]
Session string (e.g., "mystic-stone-obelisk")
uint16_t server_port
Server port for connection.
uint8_t session_id[16]
Session UUID (binary)
char server_address[64]
Server IP/hostname for connection.
uint8_t participant_id[16]
Client's participant UUID (binary)
Session discovery configuration.
const char * password
Optional session password (NULL if none)
uint16_t acds_port
ACDS server port (default: 27225)
char acds_server[256]
ACDS server address (e.g., "localhost" or "acds.ascii-chat.com")
bool insecure_mode
Allow no verification (–acds-insecure flag)
const uint8_t * expected_pubkey
Expected server pubkey (NULL = no verification)
Result from session discovery.
uint16_t server_port
Server port (typically 27224)
uint8_t participant_id[16]
Assigned participant ID (from SESSION_JOIN)
uint8_t session_id[16]
ACDS session UUID.
char server_address[256]
Server IP or hostname.
bool success
Discovery succeeded.
enum discovery_result_t::@3 source
Which discovery method found the server.
Configuration for TUI discovery.
int max_servers
Maximum servers to collect (default: 20)
bool quiet
Suppress discovery messages (default: false)
int timeout_ms
Maximum time to wait for responses (default: 2000)
Discovered server information from mDNS.
char name[256]
Service instance name (e.g., "swift-river-canyon")
uint16_t port
Server port number.
Consolidated options structure.
bool acds_insecure
ACDS: skip server key verification (MITM-vulnerable, requires explicit opt-in)
char port[256]
Server port number.
char password[256]
Password string.
bool lan_discovery
Enable LAN service discovery via mDNS (client only)
char acds_server[256]
ACDS server address (default: 127.0.0.1)
char session_string[64]
Session string for ACDS discovery (client only)
char palette_custom[256]
Custom palette characters.
bool palette_custom_set
True if custom palette was set.
char server_key[256]
Expected server public key (client)
int acds_port
ACDS server port (default: 27225)
char log_file[256]
Log file path.
char address[256]
Server address (client) or bind address (server)
TCP client connection and state management.
Complete terminal capabilities structure.
WebRTC peer manager structure.
Symbol Resolution Cache for Backtrace Addresses.
Cross-platform system functions interface for ascii-chat.
🖥️ Cross-platform terminal interface for ascii-chat
void thread_pool_destroy(thread_pool_t *pool)
Destroy a thread pool.
thread_pool_t * thread_pool_create(const char *pool_name)
Create a new thread pool.