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>
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>
94#include <ascii-chat/util/time.h>
95#include <ascii-chat/util/url.h>
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>
106#include <ascii-chat/util/path.h>
109#include <ascii-chat/debug/lock.h>
111#include <ascii-chat/debug/memory.h>
119#include <stdatomic.h>
197static void client_handle_sigwinch(
int sigwinch) {
204 unsigned short int term_width, term_height;
223static void client_handle_sigwinch(
int sigwinch) {
225 log_console(LOG_DEBUG,
"SIGWINCH received (Windows no-op implementation)");
238static void shutdown_client() {
240 static bool shutdown_done =
false;
244 shutdown_done =
true;
264 log_debug(
"Application client context destroyed successfully");
275 if (GET_OPTION(audio_analysis_enabled)) {
314 log_debug(
"Client shutdown complete");
335static int initialize_client_systems(
void) {
343 log_fatal(
"Failed to create client worker thread pool");
349 if (!GET_OPTION(quiet)) {
361 log_fatal(
"Failed to create application client context");
362 return ERROR_NETWORK;
364 log_debug(
"Application client context created successfully");
369 log_fatal(
"Failed to initialize server connection");
370 return ERROR_NETWORK;
375 if (capture_result != 0) {
376 log_fatal(
"Failed to initialize capture subsystem");
377 return capture_result;
381 if (GET_OPTION(audio_enabled)) {
383 log_warn(
"Failed to initialize audio system");
388 if (GET_OPTION(audio_analysis_enabled)) {
390 log_warn(
"Failed to initialize audio analysis");
432static bool client_should_reconnect(asciichat_error_t last_error,
int attempt_number,
void *user_data) {
434 (void)attempt_number;
438 if (GET_OPTION(snapshot_mode)) {
439 log_error(
"Connection lost in snapshot mode - not retrying");
463static asciichat_error_t client_run(session_capture_ctx_t *capture,
session_display_ctx_t *display,
void *user_data) {
472 if (!render_should_exit) {
473 return SET_ERRNO(ERROR_INVALID_STATE,
"Render should_exit callback not initialized");
482 (uint16_t)(g_client_session.discovered_port > 0 ? g_client_session.discovered_port : 27224),
483 framework_tcp_client);
487 log_info(
"Shutdown requested during connection attempt");
488 return ERROR_NETWORK;
491 if (connection_result != ASCIICHAT_OK) {
493 log_error(
"Connection attempt failed");
494 return connection_result;
502 log_error(
"Connection succeeded but no active transport");
503 return ERROR_NETWORK;
508 log_info(
"Connected");
511 log_info(
"Reconnected");
516 log_error(
"Failed to start connection protocols");
519 return ERROR_NETWORK;
525 log_debug(
"Connection lost detected");
532 log_debug(
"Shutdown requested, cleaning up connection");
534 log_debug(
"Connection lost, preparing for reconnection attempt");
548 log_error(
"Failed to recreate worker thread pool");
556 log_error(
"Failed to re-initialize connection context");
557 return ERROR_NETWORK;
566 return ERROR_NETWORK;
570 log_debug(
"client_main() starting");
574 int init_result = initialize_client_systems();
575 if (init_result != 0) {
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)");
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));
589 init_result = initialize_client_systems();
590 if (init_result != 0) {
591 log_error(
"Failed to initialize even with test pattern fallback");
593 FATAL(init_result,
"%s", asciichat_error_string(init_result));
595 log_debug(
"Successfully initialized with test pattern fallback");
603 if (init_result == ERROR_WEBCAM || init_result == ERROR_WEBCAM_IN_USE || init_result == ERROR_WEBCAM_PERMISSION) {
605 FATAL(init_result,
"%s", asciichat_error_string(init_result));
613 (void)atexit(shutdown_client);
621 platform_signal(SIGWINCH, client_handle_sigwinch);
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");
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;
642 lan_config.max_servers = 20;
643 lan_config.quiet =
true;
645 int discovered_count = 0;
646 discovery_tui_server_t *discovered_servers =
discovery_tui_query(&lan_config, &discovered_count);
651 if (selected_index < 0) {
653 if (discovered_count == 0) {
660 log_error(
"No ascii-chat servers found on the local network.\nUse 'ascii-chat client <address>' to connect "
664 platform_force_exit(1);
667 log_debug(
"LAN discovery: User cancelled server selection");
668 if (discovered_servers) {
675 discovery_tui_server_t *selected = &discovered_servers[selected_index];
680 options_t *opts_new = SAFE_MALLOC(
sizeof(options_t), options_t *);
682 memcpy(opts_new, opts,
sizeof(options_t));
683 SAFE_STRNCPY(opts_new->address, selected_address,
sizeof(opts_new->address));
685 opts_new->port = (int)selected->port;
687 log_debug(
"LAN discovery: Selected server '%s' at %s:%d", selected->name, opts_new->address, opts_new->port);
708 const char *discovered_address = NULL;
709 int discovered_port = 0;
713 const char *provided_address = opts_websocket && opts_websocket->address[0] !=
'\0' ? opts_websocket->address : NULL;
717 log_debug(
"Client: Direct WebSocket URL: %s", provided_address);
718 discovered_address = provided_address;
722 log_debug(
"Client: discovered_address=%s, discovered_port=%d", discovered_address ? discovered_address :
"NULL",
726 static char address_storage[BUFFER_SIZE_SMALL];
727 if (discovered_address) {
728 SAFE_STRNCPY(address_storage, discovered_address,
sizeof(address_storage));
733 if (opts_fallback && opts_fallback->address[0] !=
'\0') {
734 SAFE_STRNCPY(address_storage, opts_fallback->address,
sizeof(address_storage));
745 if (ctx_init_result != ASCIICHAT_OK) {
746 log_error(
"Failed to initialize connection context");
770 int reconnect_attempts = GET_OPTION(reconnect_attempts);
773 session_client_like_config_t config = {
774 .run_fn = client_run,
775 .run_user_data = NULL,
777 .websocket_client = NULL,
779 .custom_should_exit = NULL,
780 .exit_user_data = NULL,
781 .keyboard_handler = NULL,
782 .max_reconnect_attempts = reconnect_attempts,
783 .should_reconnect_callback = client_should_reconnect,
784 .reconnect_user_data = NULL,
785 .reconnect_delay_ms = 1000,
786 .print_newline_on_tty_exit =
false,
789 log_debug(
"Client: calling session_client_like_run() with %d max reconnection attempts", reconnect_attempts);
791 log_debug(
"Client: session_client_like_run() returned %d", session_result);
799 log_debug(
"ascii-chat client shutting down");
810 return (session_result == ASCIICHAT_OK) ? 0 : 1;
int audio_analysis_init(void)
void audio_analysis_print_report(void)
void audio_analysis_destroy(void)
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)
tcp_client_t * session_client_like_get_tcp_client(void)
bool(*)(void *) session_client_like_get_render_should_exit(void)
void asciichat_shared_destroy(void)
Clean up all shared library subsystems.
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)
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)
void lock_debug_destroy(void)
void log_truncate_if_large(void)
bool log_lock_terminal(void)
void log_set_terminal_output(bool enabled)
void set_interrupt_callback(void(*cb)(void))
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 options_set_int(const char *field_name, int value)
void options_state_destroy(void)
const options_t * options_get(void)
asciichat_error_t options_set_bool(const char *field_name, bool value)
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)
ascii-chat Client Audio Processing Management Interface
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)
void platform_cleanup_binary_path_cache(void)
Cleanup the binary PATH cache.
void thread_pool_destroy(thread_pool_t *pool)
thread_pool_t * thread_pool_create(const char *pool_name)
bool url_is_websocket(const char *url)
void webcam_print_init_error_help(asciichat_error_t error_code)