ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches

ascii-chat Client Mode Entry Point Header More...

Go to the source code of this file.

Functions

int client_main (void)
 Client mode entry point for unified binary.
 
bool should_exit (void)
 Check if client should exit.
 
void signal_exit (void)
 Signal client to exit.
 

Variables

thread_pool_tg_client_worker_pool
 Global client worker thread pool.
 
tcp_client_tg_client
 Global TCP client instance.
 
struct webrtc_peer_managerg_peer_manager
 Global WebRTC peer manager for P2P connections.
 

Detailed Description

ascii-chat Client Mode Entry Point Header

This header exposes the client mode entry point for the unified binary architecture. The unified binary dispatches to client_main() when invoked as ascii-chat client.

Unified Binary Architecture

The ascii-chat application uses a single binary with multiple operating modes:

  • ascii-chat server - Run as server (multi-client connection manager)
  • ascii-chat client - Run as client (connects to server, streams video/audio)

This design provides several benefits:

  • Simplified distribution (single binary to install)
  • Reduced disk space (shared library code)
  • Easier testing (one binary to build and deploy)
  • Consistent versioning across modes

Mode Entry Point Contract

Each mode entry point (server_main, client_main) must:

  • Accept no arguments: int mode_main(void)
  • Options are already parsed by main dispatcher (available via global opt_* variables)
  • Return 0 on success, non-zero error code on failure
  • Perform mode-specific initialization and main loop
  • Perform complete cleanup before returning

Implementation Notes

The client_main() function is the original main() from src/client/main.c, adapted to the new dispatcher pattern. Common initialization (options parsing, logging setup, lock debugging, –show-capabilities) now happens in src/main.c before dispatch.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
2025
Version
2.0

Definition in file client/main.h.

Function Documentation

◆ client_main()

int client_main ( void  )

Client mode entry point for unified binary.

This function implements the complete client lifecycle including:

  • Client-specific initialization (display, capture, audio)
  • Server connection with reconnection logic
  • Media streaming and frame display
  • Graceful shutdown and cleanup

Options are already parsed by the main dispatcher before this function is called, so they are available via global opt_* variables.

Returns
0 on success, non-zero error code on failure
Example
# Invoked by dispatcher after options are parsed:
ascii-chat client --address localhost --audio
# Options parsed in main.c, then client_main() called

Client mode entry point for unified binary

Orchestrates the complete client lifecycle:

  • System initialization and resource allocation
  • Signal handler registration
  • Main connection/reconnection loop
  • Graceful shutdown and cleanup
Returns
0 on success, error code on failure

Definition at line 473 of file client/main.c.

473 {
474 // Dispatcher already printed capabilities, but honor flag defensively
475 if (GET_OPTION(show_capabilities)) {
477 caps = apply_color_mode_override(caps);
479 return 0;
480 }
481
482 // Initialize all client subsystems (shared init already completed)
483 int init_result = initialize_client_systems(true);
484 if (init_result != 0) {
485 // Check if this is a webcam-related error and print help
486 if (init_result == ERROR_WEBCAM || init_result == ERROR_WEBCAM_IN_USE || init_result == ERROR_WEBCAM_PERMISSION) {
487 webcam_print_init_error_help(init_result);
488 FATAL(init_result, "%s", asciichat_error_string(init_result));
489 }
490 // For other errors, just exit with the error code
491 return init_result;
492 }
493
494 // Register cleanup function for graceful shutdown
495 (void)atexit(shutdown_client);
496
497 // Install console control handler for graceful Ctrl+C handling
498 // Uses SetConsoleCtrlHandler on Windows, sigaction on Unix - more reliable than CRT signal()
499 platform_set_console_ctrl_handler(console_ctrl_handler);
500
501 // Install SIGWINCH handler for terminal resize (Unix only, no-op on Windows)
502 platform_signal(SIGWINCH, sigwinch_handler);
503
504#ifndef _WIN32
505 // Ignore SIGPIPE - we'll handle write errors ourselves (not available on Windows)
506 platform_signal(SIGPIPE, SIG_IGN);
507#endif
508
509 // Keep terminal logging enabled so user can see connection attempts
510 // It will be disabled after first successful connection
511 // Note: No initial terminal reset - display will only be cleared when first frame arrives
512
513 // Track if we've ever successfully connected during this session
514 static bool has_ever_connected = false;
515
516 // Startup message only logged to file (terminal output is disabled by default)
517
518 /* ========================================================================
519 * Main Connection Loop
520 * ========================================================================
521 */
522
523 int reconnect_attempt = 0;
524 bool first_connection = true;
525
526 // LAN Discovery: If --scan flag is set, discover servers on local network
527 const options_t *opts = options_get();
528 if (opts && opts->lan_discovery &&
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");
532
533 discovery_tui_config_t lan_config;
534 memset(&lan_config, 0, sizeof(lan_config));
535 lan_config.timeout_ms = 2000; // Wait up to 2 seconds for responses
536 lan_config.max_servers = 20; // Support up to 20 servers on LAN
537 lan_config.quiet = true; // Quiet during discovery, TUI will show status
538
539 int discovered_count = 0;
540 discovery_tui_server_t *discovered_servers = discovery_tui_query(&lan_config, &discovered_count);
541
542 // Use TUI for server selection
543 int selected_index = discovery_tui_select(discovered_servers, discovered_count);
544
545 if (selected_index < 0) {
546 // User cancelled or no servers found
547 if (discovered_count == 0) {
548 // No servers found - print message and prevent any further output
549 // Lock the terminal so other threads can't write
551
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");
555 fflush(stderr);
556
557 // Log to file for debugging
558 log_file_msg("\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");
561
562 // Redirect stderr and stdout to /dev/null so cleanup handlers can't write to console
563 // This is safe because we've already printed our final message
564 int dev_null = open("/dev/null", O_WRONLY);
565 if (dev_null >= 0) {
566 dup2(dev_null, STDERR_FILENO);
567 dup2(dev_null, STDOUT_FILENO);
568 close(dev_null);
569 }
570
571 // Exit - cleanup handlers will try to write to /dev/null instead of console
572 exit(1);
573 }
574 // User cancelled (had servers to choose from but pressed cancel)
575 log_info("LAN discovery: User cancelled server selection");
576 if (discovered_servers) {
577 discovery_tui_free_results(discovered_servers);
578 }
579 return 1; // User cancelled
580 }
581
582 // Update options with discovered server's address and port
583 discovery_tui_server_t *selected = &discovered_servers[selected_index];
584 const char *selected_address = discovery_tui_get_best_address(selected);
585
586 // We need to modify options, but they're immutable via RCU
587 // Create a new options copy with updated address/port
588 options_t *opts_new = SAFE_MALLOC(sizeof(options_t), options_t *);
589 if (opts_new) {
590 memcpy(opts_new, opts, sizeof(options_t));
591 SAFE_STRNCPY(opts_new->address, selected_address, sizeof(opts_new->address));
592
593 // Format port as string for compatibility
594 char port_str[16];
595 snprintf(port_str, sizeof(port_str), "%u", selected->port);
596 SAFE_STRNCPY(opts_new->port, port_str, sizeof(opts_new->port));
597
598 log_info("LAN discovery: Selected server '%s' at %s:%s", selected->name, opts_new->address, opts_new->port);
599
600 // Note: In a real scenario, we'd update the global options via RCU
601 // For now, we'll use the updated values directly for connection
602 // This is a known limitation - proper RCU update requires more infrastructure
603 }
604
605 discovery_tui_free_results(discovered_servers);
606 }
607
608 // =========================================================================
609 // Connection Fallback Context Setup
610 // =========================================================================
611 //
612 // Initialize connection fallback context for 3-stage connection attempts:
613 // 1. Direct TCP (3s timeout)
614 // 2. WebRTC + STUN (8s timeout)
615 // 3. WebRTC + TURN (15s timeout)
616 //
617 connection_attempt_context_t connection_ctx = {0};
618 asciichat_error_t ctx_init_result =
619 connection_context_init(&connection_ctx,
620 GET_OPTION(prefer_webrtc), // --prefer-webrtc flag
621 GET_OPTION(no_webrtc), // --no-webrtc flag
622 GET_OPTION(webrtc_skip_stun), // --webrtc-skip-stun flag
623 GET_OPTION(webrtc_disable_turn) // --webrtc-disable-turn flag
624 );
625
626 if (ctx_init_result != ASCIICHAT_OK) {
627 log_error("Failed to initialize connection context");
628 return 1;
629 }
630
631 // =========================================================================
632 // PHASE 1: Parallel mDNS + ACDS Session Discovery
633 // =========================================================================
634 //
635 // When a session string is detected (e.g., "swift-river-mountain"),
636 // we perform parallel discovery on both mDNS (local LAN) and ACDS (internet):
637 //
638 // 1. **mDNS Discovery**: Query _ascii-chat._tcp.local for servers with
639 // matching session_string in TXT records (2s timeout)
640 // 2. **ACDS Discovery**: Lookup session on ACDS server (5s timeout)
641 // 3. **Race to Success**: Return first successful result, cancel the other
642 // 4. **Verification**: Check host_pubkey against --server-key or --acds-insecure
643 //
644 // Usage modes:
645 // - `ascii-chat swift-river-mountain` → mDNS-only (local LAN, no verification)
646 // - `ascii-chat --server-key PUBKEY swift-river-mountain` → verified
647 // - `ascii-chat --acds-insecure swift-river-mountain` → parallel without verification
648 //
649 const char *discovered_address = NULL;
650 const char *discovered_port = NULL;
651
652 const options_t *opts_discovery = options_get();
653 const char *session_string =
654 opts_discovery && opts_discovery->session_string[0] != '\0' ? opts_discovery->session_string : "";
655
656 if (session_string[0] != '\0') {
657 log_info("Session string detected: '%s' - performing parallel discovery (mDNS + ACDS)", session_string);
658
659 // Configure discovery coordinator
660 discovery_config_t discovery_cfg;
661 discovery_config_init_defaults(&discovery_cfg);
662
663 // Parse expected server key if provided
664 uint8_t expected_pubkey[32];
665 memset(expected_pubkey, 0, 32);
666 if (opts_discovery && opts_discovery->server_key[0] != '\0') {
667 asciichat_error_t parse_err = hex_to_pubkey(opts_discovery->server_key, expected_pubkey);
668 if (parse_err == ASCIICHAT_OK) {
669 discovery_cfg.expected_pubkey = expected_pubkey;
670 log_info("Server key verification enabled");
671 } else {
672 log_warn("Failed to parse server key - skipping verification");
673 }
674 }
675
676 // Enable insecure mode if requested
677 if (opts_discovery && opts_discovery->acds_insecure) {
678 discovery_cfg.insecure_mode = true;
679 log_warn("ACDS insecure mode enabled - no server key verification");
680 }
681
682 // Configure ACDS connection details
683 if (opts_discovery && opts_discovery->acds_server[0] != '\0') {
684 SAFE_STRNCPY(discovery_cfg.acds_server, opts_discovery->acds_server, sizeof(discovery_cfg.acds_server));
685 }
686 if (opts_discovery && opts_discovery->acds_port > 0) {
687 discovery_cfg.acds_port = (uint16_t)opts_discovery->acds_port;
688 }
689
690 // Set password for session join (use pointer since it persists through discovery)
691 if (opts_discovery && opts_discovery->password[0] != '\0') {
692 discovery_cfg.password = opts_discovery->password; // Safe: opts_discovery from options_get() persists
693 log_debug("Password configured for session join");
694 }
695
696 // Perform parallel discovery
697 discovery_result_t discovery_result;
698 memset(&discovery_result, 0, sizeof(discovery_result));
699 asciichat_error_t discovery_err = discover_session_parallel(session_string, &discovery_cfg, &discovery_result);
700
701 if (discovery_err != ASCIICHAT_OK || !discovery_result.success) {
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");
709 return 1;
710 }
711
712 // Log discovery result
713 const char *source_name = discovery_result.source == DISCOVERY_SOURCE_MDNS ? "mDNS (LAN)" : "ACDS (internet)";
714 log_info("Session discovered via %s: %s:%d", source_name, discovery_result.server_address,
715 discovery_result.server_port);
716
717 // Set discovered address/port for connection
718 // Copy to static buffers since discovery_result goes out of scope after this block
719 static char address_buffer[256];
720 static char port_buffer[8];
721 SAFE_STRNCPY(address_buffer, discovery_result.server_address, sizeof(address_buffer));
722 snprintf(port_buffer, sizeof(port_buffer), "%d", discovery_result.server_port);
723 discovered_address = address_buffer;
724 discovered_port = port_buffer;
725
726 // Populate session context for WebRTC fallback (if discovery came from ACDS)
727 if (discovery_result.source == DISCOVERY_SOURCE_ACDS) {
728 SAFE_STRNCPY(connection_ctx.session_ctx.session_string, session_string,
729 sizeof(connection_ctx.session_ctx.session_string));
730 memcpy(connection_ctx.session_ctx.session_id, discovery_result.session_id, 16);
731 memcpy(connection_ctx.session_ctx.participant_id, discovery_result.participant_id, 16);
732 SAFE_STRNCPY(connection_ctx.session_ctx.server_address, discovery_result.server_address,
733 sizeof(connection_ctx.session_ctx.server_address));
734 connection_ctx.session_ctx.server_port = discovery_result.server_port;
735 log_debug("Populated session context for WebRTC fallback (session='%s')", session_string);
736 }
737
738 log_info("Connecting to server: %s:%d", discovered_address, discovery_result.server_port);
739 }
740
741 const options_t *opts_conn = options_get();
742 while (!should_exit()) {
743 // Handle connection establishment or reconnection
744 const char *address = discovered_address
745 ? 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);
750
751 // Update connection context with current attempt number
752 connection_ctx.reconnect_attempt = reconnect_attempt;
753
754 // Get ACDS server configuration from CLI options (defaults: 127.0.0.1:27225)
755 const char *acds_server = GET_OPTION(acds_server);
756 if (!acds_server || acds_server[0] == '\0') {
757 acds_server = "127.0.0.1"; // Fallback if option not set
758 }
759 int acds_port = GET_OPTION(acds_port);
760 if (acds_port <= 0 || acds_port > 65535) {
761 acds_port = 27225; // Fallback to default ACDS port
762 }
763
764 // Attempt connection with 3-stage fallback (TCP → STUN → TURN)
765 asciichat_error_t connection_result =
766 connection_attempt_with_fallback(&connection_ctx, address, (uint16_t)port, acds_server, (uint16_t)acds_port);
767
768 // Check if connection attempt succeeded
769 // Handle the error result appropriately
770 int connection_success = (connection_result == ASCIICHAT_OK) ? 0 : -1;
771
772 if (connection_success != 0) {
773 // TODO Part 5: Handle specific error codes from connection_result
774 // For now, treat all non-OK results as connection failure
775 // Authentication errors would be returned as ERROR_CRYPTO_HANDSHAKE
776
777 // In snapshot mode, exit immediately on connection failure - no retries
778 // Snapshot mode is for quick single-frame captures, not persistent connections
779 if (GET_OPTION(snapshot_mode)) {
780 log_error("Connection failed in snapshot mode - exiting without retry");
781 return 1;
782 }
783
784 // Connection failed - check if we should retry based on --reconnect setting
785 reconnect_attempt++;
786
787 // Get reconnect attempts setting (-1 = unlimited, 0 = no retry, >0 = retry N times)
788 int reconnect_attempts = GET_OPTION(reconnect_attempts);
789
790 // Check reconnection policy
791 if (reconnect_attempts == 0) {
792 // --reconnect off: Exit immediately on first failure
793 log_error("Connection failed (reconnection disabled via --reconnect off)");
794 return 1;
795 } else if (reconnect_attempts > 0 && reconnect_attempt > reconnect_attempts) {
796 // --reconnect N: Exceeded max retry attempts
797 log_error("Connection failed after %d attempts (limit set by --reconnect %d)", reconnect_attempt - 1,
798 reconnect_attempts);
799 return 1;
800 }
801 // else: reconnect_attempts == -1 (auto) means retry forever
802
803 if (has_ever_connected) {
806 } else {
807 // Add newline to separate from ASCII art display for first-time connection failures
808 printf("\n");
809 }
810
811 if (has_ever_connected) {
812 if (reconnect_attempts == -1) {
813 log_info("Reconnection attempt #%d... (unlimited retries)", reconnect_attempt);
814 } else {
815 log_info("Reconnection attempt #%d/%d...", reconnect_attempt, reconnect_attempts);
816 }
817 } else {
818 if (reconnect_attempts == -1) {
819 log_info("Connection attempt #%d... (unlimited retries)", reconnect_attempt);
820 } else {
821 log_info("Connection attempt #%d/%d...", reconnect_attempt, reconnect_attempts);
822 }
823 }
824
825 // Continue retrying based on reconnection policy
826 continue;
827 }
828
829 // Connection successful - reset counters and flags
830 reconnect_attempt = 0;
831 first_connection = false;
832
833 // Integrate the active transport from connection fallback into server connection layer
834 // (Transport is TCP for Stage 1, WebRTC DataChannel for Stages 2/3)
835 if (connection_ctx.active_transport) {
837 log_debug("Active transport integrated into server connection layer");
838 } else {
839 log_error("Connection succeeded but no active transport - this should never happen");
840 continue; // Retry connection
841 }
842
843 // Show appropriate connection message based on whether this is first connection or reconnection
844 if (!has_ever_connected) {
845 log_info("Connected successfully, starting worker threads");
846 has_ever_connected = true;
847 } else {
848 log_info("Reconnected successfully, starting worker threads");
849 }
850
851 // Start all worker threads for this connection
852 if (protocol_start_connection() != 0) {
853 log_error("Failed to start connection protocols");
855
856 // In snapshot mode, exit immediately on protocol failure - no retries
857 if (GET_OPTION(snapshot_mode)) {
858 log_error("Protocol startup failed in snapshot mode - exiting without retry");
859 return 1;
860 }
861
862 continue;
863 }
864
865 // Terminal logging is now disabled - ASCII display can begin cleanly
866 // Don't clear terminal here - let the first frame handler clear it
867 // This prevents clearing the terminal before we're ready to display content
868
869 /* ====================================================================
870 * Connection Monitoring Loop
871 * ====================================================================
872 */
873
874 // Monitor connection health until it breaks or shutdown is requested
876 // Check if any critical threads have exited (indicates connection lost)
878 log_info("Connection lost detected by protocol threads");
879 break;
880 }
881
882 platform_sleep_usec(100 * 1000); // 0.1 second monitoring interval
883 }
884
885 if (should_exit()) {
886 log_info("Shutdown requested, exiting main loop");
887 break;
888 }
889
890 // Connection broken - clean up this connection and prepare for reconnect
891 log_info("Connection lost, cleaning up for reconnection");
892
893 // Re-enable terminal logging when connection is lost for debugging reconnection
894 // (but only if we've ever successfully connected before)
895 if (has_ever_connected) {
896 printf("\n");
898 }
899
902
903 // In snapshot mode, exit immediately on connection loss - no reconnection
904 // Snapshot mode is for quick single-frame captures, not persistent connections
905 if (GET_OPTION(snapshot_mode)) {
906 log_error("Connection lost in snapshot mode - exiting without reconnection");
907 return 1;
908 }
909
910 // Add a brief delay before attempting reconnection to prevent excessive reconnection loops
911 if (has_ever_connected) {
912 log_info("Waiting 1 second before attempting reconnection...");
913 platform_sleep_usec(1000000); // 1 second delay
914 }
915
916 log_info("Cleanup complete, will attempt reconnection");
917 }
918
919 // Cleanup connection context (closes any active transports)
920 connection_context_cleanup(&connection_ctx);
921
922 log_info("ascii-chat client shutting down");
923 return 0;
924}
bool should_exit()
Check if client 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.
asciichat_error_t hex_to_pubkey(const char *hex_str, uint8_t pubkey_out[32])
Convert hex string to Ed25519 public key.
Definition discovery.c:55
void discovery_config_init_defaults(discovery_config_t *config)
Initialize discovery config with defaults.
Definition discovery.c:633
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.
Definition discovery.c:655
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.
void server_connection_close()
Close the server connection gracefully.
bool server_connection_is_active()
Check if server connection is currently active.
void server_connection_set_transport(acip_transport_t *transport)
Set ACIP transport instance from connection fallback.
void display_full_reset()
Perform full display reset.
Definition display.c:301
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.
unsigned short uint16_t
Definition common.h:57
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_MALLOC(size, cast)
Definition common.h:208
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
Definition common.h:151
unsigned char uint8_t
Definition common.h:56
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_WEBCAM_IN_USE
Definition error_codes.h:62
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_WEBCAM_PERMISSION
Definition error_codes.h:63
@ ERROR_WEBCAM
Definition error_codes.h:61
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
bool log_lock_terminal(void)
Lock terminal output for exclusive access by the calling thread.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
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.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
Definition options.h:644
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:222
signal_handler_t platform_signal(int sig, signal_handler_t handler)
Set a signal handler.
terminal_capabilities_t apply_color_mode_override(terminal_capabilities_t caps)
Apply command-line overrides to detected capabilities.
bool platform_set_console_ctrl_handler(console_ctrl_handler_t handler)
Register a console control handler (for Ctrl+C, etc.)
void print_terminal_capabilities(const terminal_capabilities_t *caps)
Print terminal capabilities to stdout.
void platform_sleep_usec(unsigned int usec)
High-precision sleep function with microsecond precision.
terminal_capabilities_t detect_terminal_capabilities(void)
Detect terminal capabilities.
void webcam_print_init_error_help(asciichat_error_t error_code)
Print helpful error diagnostics for webcam initialization failures.
Definition webcam.c:229
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.
Definition discovery.h:111
const char * password
Optional session password (NULL if none)
Definition discovery.h:127
uint16_t acds_port
ACDS server port (default: 27225)
Definition discovery.h:118
char acds_server[256]
ACDS server address (e.g., "localhost" or "acds.ascii-chat.com")
Definition discovery.h:117
bool insecure_mode
Allow no verification (–acds-insecure flag)
Definition discovery.h:114
const uint8_t * expected_pubkey
Expected server pubkey (NULL = no verification)
Definition discovery.h:113
Result from session discovery.
Definition discovery.h:85
uint16_t server_port
Server port (typically 27224)
Definition discovery.h:89
uint8_t participant_id[16]
Assigned participant ID (from SESSION_JOIN)
Definition discovery.h:101
uint8_t session_id[16]
ACDS session UUID.
Definition discovery.h:100
char server_address[256]
Server IP or hostname.
Definition discovery.h:88
bool success
Discovery succeeded.
Definition discovery.h:86
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.
Definition options.h:439
bool acds_insecure
ACDS: skip server key verification (MITM-vulnerable, requires explicit opt-in)
Definition options.h:557
char port[256]
Server port number.
Definition options.h:464
char password[256]
Password string.
Definition options.h:543
bool lan_discovery
Enable LAN service discovery via mDNS (client only)
Definition options.h:481
char acds_server[256]
ACDS server address (default: 127.0.0.1)
Definition options.h:472
char session_string[64]
Session string for ACDS discovery (client only)
Definition options.h:466
char server_key[256]
Expected server public key (client)
Definition options.h:546
int acds_port
ACDS server port (default: 27225)
Definition options.h:473
char address[256]
Server address (client) or bind address (server)
Definition options.h:462
Complete terminal capabilities structure.
Definition terminal.h:485

References options_state::acds_insecure, discovery_config_t::acds_port, options_state::acds_port, discovery_config_t::acds_server, options_state::acds_server, connection_attempt_context_t::active_transport, options_state::address, apply_color_mode_override(), ASCIICHAT_OK, connection_attempt_with_fallback(), connection_context_cleanup(), connection_context_init(), detect_terminal_capabilities(), discover_session_parallel(), discovery_config_init_defaults(), discovery_tui_free_results(), discovery_tui_get_best_address(), discovery_tui_query(), discovery_tui_select(), display_full_reset(), ERROR_WEBCAM, ERROR_WEBCAM_IN_USE, ERROR_WEBCAM_PERMISSION, discovery_config_t::expected_pubkey, FATAL, GET_OPTION, hex_to_pubkey(), discovery_config_t::insecure_mode, options_state::lan_discovery, log_debug, log_error, log_file_msg(), log_info, log_lock_terminal(), log_set_terminal_output(), log_warn, discovery_tui_config_t::max_servers, discovery_tui_server_t::name, options_get(), discovery_result_t::participant_id, connection_session_context_t::participant_id, discovery_config_t::password, options_state::password, platform_set_console_ctrl_handler(), platform_signal(), platform_sleep_usec(), discovery_tui_server_t::port, options_state::port, print_terminal_capabilities(), protocol_connection_lost(), protocol_start_connection(), protocol_stop_connection(), discovery_tui_config_t::quiet, connection_attempt_context_t::reconnect_attempt, SAFE_MALLOC, SAFE_STRNCPY, discovery_result_t::server_address, connection_session_context_t::server_address, server_connection_close(), server_connection_is_active(), server_connection_set_transport(), options_state::server_key, discovery_result_t::server_port, connection_session_context_t::server_port, connection_attempt_context_t::session_ctx, discovery_result_t::session_id, connection_session_context_t::session_id, connection_session_context_t::session_string, options_state::session_string, should_exit(), discovery_result_t::source, discovery_result_t::success, discovery_tui_config_t::timeout_ms, and webcam_print_init_error_help().

◆ should_exit()

bool should_exit ( void  )

Check if client should exit.

Thread-safe check of the global exit flag. Used by all client threads to coordinate graceful shutdown.

Returns
true if exit signal received, false otherwise

Check if shutdown has been requested

Returns
true if shutdown requested, false otherwise

Definition at line 160 of file client/main.c.

160 {
161 return atomic_load(&g_should_exit);
162}
atomic_bool g_should_exit

References g_should_exit.

Referenced by __attribute__(), client_main(), client_receive_thread(), display_full_reset(), and server_connection_establish().

◆ signal_exit()

void signal_exit ( void  )

Signal client to exit.

Sets the global exit flag to trigger graceful shutdown of all client threads. Thread-safe and can be called from signal handlers.

Signal that shutdown should be requested

Definition at line 167 of file client/main.c.

167 {
168 atomic_store(&g_should_exit, true);
169}

References g_should_exit.

Variable Documentation

◆ g_client

tcp_client_t* g_client
extern

Global TCP client instance.

Central connection and state management structure for the client. Replaces scattered global variables from server.c, protocol.c, audio.c, etc. Follows the same pattern as tcp_server_t used by the server.

This instance provides:

  • Connection state management (socket, server address, client ID)
  • Thread-safe packet transmission with mutex protection
  • Audio queue management for async audio streaming
  • Crypto handshake context for encrypted connections
  • Terminal capability tracking for rendering

Initialized by tcp_client_create() in client_main(). Destroyed by tcp_client_destroy() at cleanup.

All client modules (server.c, protocol.c, audio.c, capture.c, display.c) should use this instance instead of accessing global connection state.

Global TCP client instance

Central connection and state management structure for the client. Replaces scattered global variables from server.c, protocol.c, audio.c, etc. Follows the same pattern as tcp_server_t used by the server.

Initialized by tcp_client_create() in client_main(). Destroyed by tcp_client_destroy() at cleanup.

Definition at line 146 of file client/main.c.

◆ g_client_worker_pool

thread_pool_t* g_client_worker_pool
extern

Global client worker thread pool.

Manages all client worker threads including:

  • Data reception thread (protocol.c)
  • Webcam capture thread (capture.c)
  • Ping/keepalive thread (keepalive.c)
  • Audio capture thread (audio.c)
  • Audio sender thread (audio.c)

This pool is initialized in client_main() and accessible from all client modules for spawning and managing worker threads.

Global client worker thread pool

Manages all client worker threads including:

  • Data reception thread (protocol.c)
  • Webcam capture thread (capture.c)
  • Ping/keepalive thread (keepalive.c)
  • Audio capture thread (audio.c)
  • Audio sender thread (audio.c)

Definition at line 134 of file client/main.c.

Referenced by audio_start_thread(), capture_start_thread(), keepalive_start_thread(), protocol_start_connection(), and protocol_stop_connection().

◆ g_peer_manager

struct webrtc_peer_manager* g_peer_manager
extern

Global WebRTC peer manager for P2P connections.

Manages WebRTC peer connections for ACDS session participants. Handles SDP/ICE exchange and DataChannel lifecycle for P2P ACIP transport.

NULL if WebRTC not initialized (TCP-only mode). Initialized when joining/creating ACDS sessions with WebRTC transport.

NULL if WebRTC not initialized. Initialized when joining/creating ACDS sessions.

Definition at line 153 of file client/main.c.