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

Go to the source code of this file.

Data Structures

struct  tcp_client_audio_packet_t
 Audio packet for async network transmission. More...
 
struct  tcp_client
 TCP client connection and state management. More...
 

Macros

#define TCP_CLIENT_AUDIO_QUEUE_SIZE   256
 

Typedefs

typedef struct tcp_client tcp_client_t
 TCP client connection and state management.
 

Functions

tcp_client_ttcp_client_create (void)
 Create and initialize a TCP client instance.
 
void tcp_client_destroy (tcp_client_t **client_ptr)
 Destroy TCP client and free all resources.
 
bool tcp_client_is_active (const tcp_client_t *client)
 Check if connection is currently active.
 
bool tcp_client_is_lost (const tcp_client_t *client)
 Check if connection was lost.
 
socket_t tcp_client_get_socket (const tcp_client_t *client)
 Get current socket descriptor.
 
uint32_t tcp_client_get_id (const tcp_client_t *client)
 Get client ID assigned by server.
 
void tcp_client_signal_lost (tcp_client_t *client)
 Signal that connection was lost (triggers reconnection)
 
void tcp_client_close (tcp_client_t *client)
 Close connection gracefully.
 
void tcp_client_shutdown (tcp_client_t *client)
 Shutdown connection forcefully (for signal handlers)
 
void tcp_client_cleanup (tcp_client_t *client)
 Cleanup connection resources.
 
int tcp_client_connect (tcp_client_t *client, const char *address, int port, int reconnect_attempt, bool first_connection, bool has_ever_connected)
 Establish TCP connection to server.
 
int tcp_client_send_packet (tcp_client_t *client, packet_type_t type, const void *data, size_t len)
 Send packet with thread-safe mutex protection.
 
int tcp_client_send_ping (tcp_client_t *client)
 Send ping packet.
 
int tcp_client_send_pong (tcp_client_t *client)
 Send pong packet.
 
int tcp_client_send_audio_opus (tcp_client_t *client, const uint8_t *opus_data, size_t opus_size, int sample_rate, int frame_duration)
 Send Opus-encoded audio frame.
 
int tcp_client_send_audio_opus_batch (tcp_client_t *client, const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int frame_count)
 Send Opus audio batch packet.
 
int tcp_client_send_terminal_capabilities (tcp_client_t *client, unsigned short width, unsigned short height)
 Send terminal capabilities packet.
 
int tcp_client_send_join (tcp_client_t *client, const char *display_name, uint32_t capabilities)
 Send client join packet.
 
int tcp_client_send_stream_start (tcp_client_t *client, uint32_t stream_type)
 Send stream start packet.
 
int tcp_client_send_audio_batch (tcp_client_t *client, const float *samples, int num_samples, int batch_count)
 Send audio batch packet.
 

Macro Definition Documentation

◆ TCP_CLIENT_AUDIO_QUEUE_SIZE

#define TCP_CLIENT_AUDIO_QUEUE_SIZE   256

Definition at line 64 of file lib/network/tcp/client.h.

Typedef Documentation

◆ tcp_client_t

typedef struct tcp_client tcp_client_t

TCP client connection and state management.

Encapsulates all state for a single TCP client connection, including:

  • Network connection state (socket, server info, connection flags)
  • Thread management (handles, creation flags, exit flags)
  • Audio processing state (queues, buffers, context)
  • Protocol state (packet tracking, server state)
  • Display state (terminal capabilities, TTY info)
  • Crypto state (handshake context, encryption flags)

Ownership Model

Thread Safety

  • Atomic fields: Safe for concurrent read/write without locks
  • Mutex-protected fields: Acquire mutex before access
  • Immutable after init: Safe for concurrent reads
See also
tcp_server_t For server-side equivalent structure

Function Documentation

◆ tcp_client_cleanup()

void tcp_client_cleanup ( tcp_client_t client)

Cleanup connection resources.

Parameters
clientTCP client instance

Definition at line 317 of file lib/network/tcp/client.c.

317 {
318 if (!client)
319 return;
320
321 // Close connection
322 tcp_client_close(client);
323
324 // Reset state flags
325 atomic_store(&client->connection_lost, false);
326 atomic_store(&client->should_reconnect, false);
327 memset(client->server_ip, 0, sizeof(client->server_ip));
328}
void tcp_client_close(tcp_client_t *client)
Close connection gracefully.
atomic_bool should_reconnect
atomic_bool connection_lost

References tcp_client::connection_lost, tcp_client::server_ip, tcp_client::should_reconnect, and tcp_client_close().

◆ tcp_client_close()

void tcp_client_close ( tcp_client_t client)

Close connection gracefully.

Parameters
clientTCP client instance

Definition at line 280 of file lib/network/tcp/client.c.

280 {
281 if (!client)
282 return;
283
284 log_debug("Closing client connection");
285
286 // Mark connection as inactive
287 atomic_store(&client->connection_active, false);
288
289 // Close socket
290 if (socket_is_valid(client->sockfd)) {
291 close_socket_safe(client->sockfd);
293 }
294
295 // Reset client ID
296 client->my_client_id = 0;
297}
#define log_debug(...)
Log a DEBUG message.
bool socket_is_valid(socket_t sock)
Check if a socket handle is valid.
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:52
atomic_bool connection_active

References tcp_client::connection_active, INVALID_SOCKET_VALUE, log_debug, tcp_client::my_client_id, socket_is_valid(), and tcp_client::sockfd.

Referenced by tcp_client_cleanup().

◆ tcp_client_connect()

int tcp_client_connect ( tcp_client_t client,
const char *  address,
int  port,
int  reconnect_attempt,
bool  first_connection,
bool  has_ever_connected 
)

Establish TCP connection to server.

Performs full connection lifecycle including DNS resolution, socket creation, connection with timeout, and socket configuration. Does NOT perform crypto handshake or send initial packets - those are application responsibilities.

Parameters
clientTCP client instance
addressServer hostname or IP address
portServer port number
reconnect_attemptCurrent reconnection attempt (0 for first, 1+ for retries)
first_connectionTrue if this is the very first connection since program start
has_ever_connectedTrue if client has successfully connected at least once
Returns
0 on success, negative on error

Performs full connection lifecycle:

  • DNS resolution with IPv4/IPv6 dual-stack support
  • Socket creation and connection with timeout
  • Crypto handshake (if enabled)
  • Initial capability exchange
  • Client ID assignment from local port
Parameters
clientTCP client instance
addressServer hostname or IP address
portServer port number
reconnect_attemptCurrent reconnection attempt (0 for first, 1+ for retries)
first_connectionTrue if this is the very first connection since program start
has_ever_connectedTrue if client has successfully connected at least once
Returns
0 on success, negative on error

Definition at line 411 of file lib/network/tcp/client.c.

412 {
413 (void)first_connection; // Currently unused
414 (void)has_ever_connected; // Currently unused
415
416 if (!client || !address || port <= 0) {
417 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client, address, or port");
418 }
419
420 // Close any existing connection
421 if (socket_is_valid(client->sockfd)) {
422 close_socket_safe(client->sockfd);
424 }
425
426 // Apply reconnection delay if this is a retry
427 if (reconnect_attempt > 0) {
428 unsigned int delay_us = get_reconnect_delay(reconnect_attempt);
429 platform_sleep_usec(delay_us);
430 }
431
432 // Resolve server address using getaddrinfo() for IPv4/IPv6 support
433 // Special handling for localhost: ensure we try both IPv6 (::1) and IPv4 (127.0.0.1)
434 bool is_localhost =
435 (strcmp(address, "localhost") == 0 || strcmp(address, "127.0.0.1") == 0 || strcmp(address, "::1") == 0);
436
437 struct addrinfo hints, *res = NULL, *addr_iter;
438 memset(&hints, 0, sizeof(hints));
439 hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
440 hints.ai_socktype = SOCK_STREAM;
441 if (is_localhost) {
442 hints.ai_flags = AI_NUMERICSERV; // Optimize for localhost
443 }
444
445 char port_str[16];
446 SAFE_SNPRINTF(port_str, sizeof(port_str), "%d", port);
447
448 // For localhost, try IPv6 loopback (::1) first, then fall back to IPv4
449 if (is_localhost) {
450 log_debug("Localhost detected - trying IPv6 loopback [::1]:%s first...", port_str);
451 hints.ai_family = AF_INET6;
452 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
453
454 int ipv6_result = getaddrinfo("::1", port_str, &hints, &res);
455 if (ipv6_result == 0 && res != NULL) {
456 // Try IPv6 loopback connection
457 client->sockfd = socket_create(res->ai_family, res->ai_socktype, res->ai_protocol);
458 if (client->sockfd != INVALID_SOCKET_VALUE) {
459 log_info("Trying IPv6 loopback connection to [::1]:%s...", port_str);
460 if (connect_with_timeout(client->sockfd, res->ai_addr, res->ai_addrlen, CONNECT_TIMEOUT)) {
461 log_debug("Connection successful using IPv6 loopback");
462 SAFE_STRNCPY(client->server_ip, "::1", sizeof(client->server_ip));
463 freeaddrinfo(res);
464 res = NULL; // Prevent double-free at connection_success label
465 goto connection_success;
466 }
467 close_socket_safe(client->sockfd);
469 }
470 freeaddrinfo(res);
471 res = NULL;
472 }
473
474 // IPv6 failed, try IPv4 loopback (127.0.0.1)
475 log_debug("IPv6 failed, trying IPv4 loopback 127.0.0.1:%s...", port_str);
476 hints.ai_family = AF_INET;
477
478 int ipv4_result = getaddrinfo("127.0.0.1", port_str, &hints, &res);
479 if (ipv4_result == 0 && res != NULL) {
480 client->sockfd = socket_create(res->ai_family, res->ai_socktype, res->ai_protocol);
481 if (client->sockfd != INVALID_SOCKET_VALUE) {
482 log_info("Trying IPv4 loopback connection to 127.0.0.1:%s...", port_str);
483 if (connect_with_timeout(client->sockfd, res->ai_addr, res->ai_addrlen, CONNECT_TIMEOUT)) {
484 log_debug("Connection successful using IPv4 loopback");
485 SAFE_STRNCPY(client->server_ip, "127.0.0.1", sizeof(client->server_ip));
486 freeaddrinfo(res);
487 res = NULL;
488 goto connection_success;
489 }
490 close_socket_safe(client->sockfd);
492 }
493 freeaddrinfo(res);
494 res = NULL;
495 }
496
497 // Both IPv6 and IPv4 loopback failed for localhost
498 log_warn("Could not connect to localhost using either IPv6 or IPv4 loopback");
499 return -1;
500 }
501
502 // For non-localhost addresses, use standard resolution
503 log_debug("Resolving server address '%s' port %s...", address, port_str);
504 hints.ai_family = AF_UNSPEC;
505 hints.ai_flags = 0;
506 int getaddr_result = getaddrinfo(address, port_str, &hints, &res);
507 if (getaddr_result != 0) {
508 log_error("Failed to resolve server address '%s': %s", address, gai_strerror(getaddr_result));
509 return -1;
510 }
511
512 // Try each address returned by getaddrinfo() - prefer IPv6, fall back to IPv4
513 for (int address_family = AF_INET6; address_family >= AF_INET; address_family -= (AF_INET6 - AF_INET)) {
514 for (addr_iter = res; addr_iter != NULL; addr_iter = addr_iter->ai_next) {
515 if (addr_iter->ai_family != address_family) {
516 continue;
517 }
518
519 client->sockfd = socket_create(addr_iter->ai_family, addr_iter->ai_socktype, addr_iter->ai_protocol);
520 if (client->sockfd == INVALID_SOCKET_VALUE) {
521 continue;
522 }
523
524 if (addr_iter->ai_family == AF_INET) {
525 log_debug("Trying IPv4 connection...");
526 } else if (addr_iter->ai_family == AF_INET6) {
527 log_debug("Trying IPv6 connection...");
528 }
529
530 if (connect_with_timeout(client->sockfd, addr_iter->ai_addr, addr_iter->ai_addrlen, CONNECT_TIMEOUT)) {
531 log_debug("Connection successful using %s", addr_iter->ai_family == AF_INET ? "IPv4"
532 : addr_iter->ai_family == AF_INET6 ? "IPv6"
533 : "unknown protocol");
534
535 // Extract server IP address for known_hosts
536 if (format_ip_address(addr_iter->ai_family, addr_iter->ai_addr, client->server_ip, sizeof(client->server_ip)) ==
537 ASCIICHAT_OK) {
538 log_debug("Resolved server IP: %s", client->server_ip);
539 } else {
540 log_warn("Failed to format server IP address");
541 }
542
543 goto connection_success;
544 }
545
546 close_socket_safe(client->sockfd);
548 }
549 }
550
551connection_success:
552
553 if (res) {
554 freeaddrinfo(res);
555 }
556
557 // If we exhausted all addresses without success, fail
558 if (client->sockfd == INVALID_SOCKET_VALUE) {
559 log_warn("Could not connect to server %s:%d (tried all addresses)", address, port);
560 return -1;
561 }
562
563 // Extract local port for client ID
564 struct sockaddr_storage local_addr = {0};
565 socklen_t addr_len = sizeof(local_addr);
566 if (getsockname(client->sockfd, (struct sockaddr *)&local_addr, &addr_len) == -1) {
567 log_error("Failed to get local socket address: %s", network_error_string());
568 close_socket_safe(client->sockfd);
570 return -1;
571 }
572
573 // Extract port from either IPv4 or IPv6 address
574 int local_port = 0;
575 if (((struct sockaddr *)&local_addr)->sa_family == AF_INET) {
576 local_port = NET_TO_HOST_U16(((struct sockaddr_in *)&local_addr)->sin_port);
577 } else if (((struct sockaddr *)&local_addr)->sa_family == AF_INET6) {
578 local_port = NET_TO_HOST_U16(((struct sockaddr_in6 *)&local_addr)->sin6_port);
579 }
580 client->my_client_id = (uint32_t)local_port;
581
582 // Mark connection as active
583 atomic_store(&client->connection_active, true);
584 atomic_store(&client->connection_lost, false);
585 atomic_store(&client->should_reconnect, false);
586
587 // Initialize crypto (application must set crypto_initialized flag)
588 // This is done outside this function by calling client_crypto_init()
589
590 // Configure socket options
591 if (socket_set_keepalive(client->sockfd, true) < 0) {
592 log_warn("Failed to set socket keepalive: %s", network_error_string());
593 }
594
595 asciichat_error_t sock_config_result = socket_configure_buffers(client->sockfd);
596 if (sock_config_result != ASCIICHAT_OK) {
597 log_warn("Failed to configure socket: %s", network_error_string());
598 }
599
600 log_debug("Connection established successfully to %s:%d (client_id=%u)", address, port, client->my_client_id);
601 return 0;
602}
#define NET_TO_HOST_U16(val)
Definition endian.h:116
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:412
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
#define log_info(...)
Log an INFO message.
#define CONNECT_TIMEOUT
Connection timeout in seconds (3 seconds)
Definition network.h:98
bool connect_with_timeout(socket_t sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_seconds)
Connect with timeout.
Definition network.c:547
const char * network_error_string()
Get human-readable error string for network errors.
Definition network.c:535
asciichat_error_t socket_configure_buffers(socket_t sockfd)
Configure socket buffers and TCP_NODELAY for optimal performance.
Definition network.c:493
int socket_set_keepalive(socket_t sock, bool keepalive)
Set SO_KEEPALIVE socket option.
void platform_sleep_usec(unsigned int usec)
High-precision sleep function with microsecond precision.
socket_t socket_create(int domain, int type, int protocol)
Create a new socket.
asciichat_error_t format_ip_address(int family, const struct sockaddr *addr, char *output, size_t output_size)
Format IP address from socket address structure.
Definition ip.c:205

References ASCIICHAT_OK, CONNECT_TIMEOUT, connect_with_timeout(), tcp_client::connection_active, tcp_client::connection_lost, ERROR_INVALID_PARAM, format_ip_address(), INVALID_SOCKET_VALUE, log_debug, log_error, log_info, log_warn, tcp_client::my_client_id, NET_TO_HOST_U16, network_error_string(), platform_sleep_usec(), SAFE_SNPRINTF, SAFE_STRNCPY, tcp_client::server_ip, SET_ERRNO, tcp_client::should_reconnect, socket_configure_buffers(), socket_create(), socket_is_valid(), socket_set_keepalive(), and tcp_client::sockfd.

◆ tcp_client_create()

tcp_client_t * tcp_client_create ( void  )

Create and initialize a TCP client instance.

Allocates a new tcp_client_t structure and initializes all fields to safe defaults. This function must be called before starting any worker threads.

Initialization Steps

  1. Allocate tcp_client_t structure
  2. Zero-initialize all fields
  3. Set atomic flags to initial states
  4. Initialize mutexes and condition variables
  5. Set socket to INVALID_SOCKET_VALUE

Error Handling

Returns NULL if allocation fails or mutex initialization fails. Check errno or use HAS_ERRNO() for detailed error information.

Returns
Pointer to initialized client, or NULL on failure
Note
Caller must call tcp_client_destroy() when done
See also
tcp_client_destroy() For proper cleanup

Create and initialize a TCP client instance.

Allocates tcp_client_t and initializes all fields to safe defaults.

Definition at line 95 of file lib/network/tcp/client.c.

95 {
97 if (!client) {
98 log_error("Failed to allocate tcp_client_t");
99 return NULL;
100 }
101
102 // Zero-initialize all fields
103 memset(client, 0, sizeof(*client));
104
105 /* Connection State */
107 atomic_store(&client->connection_active, false);
108 atomic_store(&client->connection_lost, false);
109 atomic_store(&client->should_reconnect, false);
110 client->my_client_id = 0;
111 memset(client->server_ip, 0, sizeof(client->server_ip));
112 client->encryption_enabled = false;
113
114 // Initialize send mutex
115 if (mutex_init(&client->send_mutex) != 0) {
116 log_error("Failed to initialize send mutex");
117 SAFE_FREE(client);
118 return NULL;
119 }
120
121 /* Audio State */
122 memset(&client->audio_ctx, 0, sizeof(client->audio_ctx));
123 memset(client->audio_send_queue, 0, sizeof(client->audio_send_queue));
124 client->audio_send_queue_head = 0;
125 client->audio_send_queue_tail = 0;
126 client->audio_send_queue_initialized = false;
127 atomic_store(&client->audio_sender_should_exit, false);
128
129 // Initialize audio queue mutex and condition variable
130 if (mutex_init(&client->audio_send_queue_mutex) != 0) {
131 log_error("Failed to initialize audio queue mutex");
132 mutex_destroy(&client->send_mutex);
133 SAFE_FREE(client);
134 return NULL;
135 }
136
137 if (cond_init(&client->audio_send_queue_cond) != 0) {
138 log_error("Failed to initialize audio queue cond");
140 mutex_destroy(&client->send_mutex);
141 SAFE_FREE(client);
142 return NULL;
143 }
144
145 client->audio_capture_thread_created = false;
146 client->audio_sender_thread_created = false;
147 atomic_store(&client->audio_capture_thread_exited, false);
148
149 /* Protocol State */
150 client->data_thread_created = false;
151 atomic_store(&client->data_thread_exited, false);
152 client->last_active_count = 0;
153 client->server_state_initialized = false;
154 client->should_clear_before_next_frame = false;
155
156 /* Capture State */
157 client->capture_thread_created = false;
158 atomic_store(&client->capture_thread_exited, false);
159
160 /* Keepalive State */
161 client->ping_thread_created = false;
162 atomic_store(&client->ping_thread_exited, false);
163
164 /* Display State */
165 client->has_tty = false;
166 atomic_store(&client->is_first_frame_of_connection, true);
167 memset(&client->tty_info, 0, sizeof(client->tty_info));
168
169 /* Crypto State */
170 memset(&client->crypto_ctx, 0, sizeof(client->crypto_ctx));
171 client->crypto_initialized = false;
172
173 log_debug("TCP client created successfully");
174 return client;
175}
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
int mutex_init(mutex_t *mutex)
Initialize a mutex.
int cond_init(cond_t *cond)
Initialize a condition variable.
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
TCP client connection and state management.
crypto_handshake_context_t crypto_ctx
atomic_bool ping_thread_exited
atomic_bool audio_capture_thread_exited
atomic_bool audio_sender_should_exit
audio_context_t audio_ctx
atomic_bool capture_thread_exited
tcp_client_audio_packet_t audio_send_queue[256]
atomic_bool is_first_frame_of_connection
atomic_bool data_thread_exited

References tcp_client::audio_capture_thread_created, tcp_client::audio_capture_thread_exited, tcp_client::audio_ctx, tcp_client::audio_send_queue, tcp_client::audio_send_queue_cond, tcp_client::audio_send_queue_head, tcp_client::audio_send_queue_initialized, tcp_client::audio_send_queue_mutex, tcp_client::audio_send_queue_tail, tcp_client::audio_sender_should_exit, tcp_client::audio_sender_thread_created, tcp_client::capture_thread_created, tcp_client::capture_thread_exited, cond_init(), tcp_client::connection_active, tcp_client::connection_lost, tcp_client::crypto_ctx, tcp_client::crypto_initialized, tcp_client::data_thread_created, tcp_client::data_thread_exited, tcp_client::encryption_enabled, tcp_client::has_tty, INVALID_SOCKET_VALUE, tcp_client::is_first_frame_of_connection, tcp_client::last_active_count, log_debug, log_error, mutex_destroy(), mutex_init(), tcp_client::my_client_id, tcp_client::ping_thread_created, tcp_client::ping_thread_exited, SAFE_FREE, SAFE_MALLOC, tcp_client::send_mutex, tcp_client::server_ip, tcp_client::server_state_initialized, tcp_client::should_clear_before_next_frame, tcp_client::should_reconnect, tcp_client::sockfd, and tcp_client::tty_info.

◆ tcp_client_destroy()

void tcp_client_destroy ( tcp_client_t **  client_ptr)

Destroy TCP client and free all resources.

Destroys all synchronization primitives and frees the client structure. This function must be called AFTER all worker threads have been joined.

Cleanup Steps

  1. Verify all threads have exited (debug builds only)
  2. Destroy all mutexes and condition variables
  3. Free tcp_client_t structure
  4. Set pointer to NULL (via parameter)

Thread Safety

This function is NOT thread-safe. All worker threads must be joined before calling this function.

Parameters
client_ptrPointer to client pointer (set to NULL after free)
Warning
All threads must be stopped and joined before calling
Note
No-op if client_ptr is NULL or *client_ptr is NULL

Destroy TCP client and free all resources.

Must be called AFTER all threads have been joined.

Definition at line 182 of file lib/network/tcp/client.c.

182 {
183 if (!client_ptr || !*client_ptr) {
184 return;
185 }
186
187 tcp_client_t *client = *client_ptr;
188
189#ifndef NDEBUG
190 // Debug: verify all threads have exited
191 if (client->audio_capture_thread_created && !atomic_load(&client->audio_capture_thread_exited)) {
192 log_warn("Destroying client while audio capture thread may still be running");
193 }
194 if (client->data_thread_created && !atomic_load(&client->data_thread_exited)) {
195 log_warn("Destroying client while data thread may still be running");
196 }
197 if (client->capture_thread_created && !atomic_load(&client->capture_thread_exited)) {
198 log_warn("Destroying client while capture thread may still be running");
199 }
200 if (client->ping_thread_created && !atomic_load(&client->ping_thread_exited)) {
201 log_warn("Destroying client while ping thread may still be running");
202 }
203#endif
204
205 // Close socket if still open
206 if (socket_is_valid(client->sockfd)) {
207 close_socket_safe(client->sockfd);
209 }
210
211 // Destroy synchronization primitives
212 mutex_destroy(&client->send_mutex);
215
216 // Free client structure
217 SAFE_FREE(client);
218 *client_ptr = NULL;
219
220 log_debug("TCP client destroyed");
221}
int cond_destroy(cond_t *cond)
Destroy a condition variable.

References tcp_client::audio_capture_thread_created, tcp_client::audio_capture_thread_exited, tcp_client::audio_send_queue_cond, tcp_client::audio_send_queue_mutex, tcp_client::capture_thread_created, tcp_client::capture_thread_exited, cond_destroy(), tcp_client::data_thread_created, tcp_client::data_thread_exited, INVALID_SOCKET_VALUE, log_debug, log_warn, mutex_destroy(), tcp_client::ping_thread_created, tcp_client::ping_thread_exited, SAFE_FREE, tcp_client::send_mutex, socket_is_valid(), and tcp_client::sockfd.

Referenced by connection_context_cleanup().

◆ tcp_client_get_id()

uint32_t tcp_client_get_id ( const tcp_client_t client)

Get client ID assigned by server.

Parameters
clientTCP client instance
Returns
Client ID (from local port) or 0 if not connected

Definition at line 255 of file lib/network/tcp/client.c.

255 {
256 return client ? client->my_client_id : 0;
257}

References tcp_client::my_client_id.

◆ tcp_client_get_socket()

socket_t tcp_client_get_socket ( const tcp_client_t client)

Get current socket descriptor.

Parameters
clientTCP client instance
Returns
Socket descriptor or INVALID_SOCKET_VALUE if not connected

Definition at line 248 of file lib/network/tcp/client.c.

248 {
249 return client ? client->sockfd : INVALID_SOCKET_VALUE;
250}

References INVALID_SOCKET_VALUE, and tcp_client::sockfd.

◆ tcp_client_is_active()

bool tcp_client_is_active ( const tcp_client_t client)

Check if connection is currently active.

Parameters
clientTCP client instance
Returns
true if connection is active, false otherwise

Definition at line 230 of file lib/network/tcp/client.c.

230 {
231 if (!client)
232 return false;
233 return atomic_load(&client->connection_active);
234}

References tcp_client::connection_active.

◆ tcp_client_is_lost()

bool tcp_client_is_lost ( const tcp_client_t client)

Check if connection was lost.

Parameters
clientTCP client instance
Returns
true if connection loss was detected, false otherwise

Definition at line 239 of file lib/network/tcp/client.c.

239 {
240 if (!client)
241 return false;
242 return atomic_load(&client->connection_lost);
243}

References tcp_client::connection_lost.

◆ tcp_client_send_audio_batch()

int tcp_client_send_audio_batch ( tcp_client_t client,
const float *  samples,
int  num_samples,
int  batch_count 
)

Send audio batch packet.

Parameters
clientTCP client instance
samplesAudio sample buffer
num_samplesNumber of samples in buffer
batch_countNumber of chunks in batch
Returns
0 on success, negative on error

Definition at line 878 of file lib/network/tcp/client.c.

878 {
879 if (!client || !samples) {
880 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client or samples");
881 }
882
883 if (!atomic_load(&client->connection_active)) {
884 return SET_ERRNO(ERROR_NETWORK, "Connection not active");
885 }
886
887 mutex_lock(&client->send_mutex);
888
889 if (!atomic_load(&client->connection_active) || client->sockfd == INVALID_SOCKET_VALUE) {
890 mutex_unlock(&client->send_mutex);
891 return -1;
892 }
893
894 crypto_context_t *crypto_ctx = NULL;
896 crypto_ctx = crypto_handshake_get_context(&client->crypto_ctx);
897 }
898
899 int result = send_audio_batch_packet(client->sockfd, samples, num_samples, batch_count, crypto_ctx);
900
901 mutex_unlock(&client->send_mutex);
902
903 if (result < 0) {
905 }
906
907 return result;
908}
const crypto_context_t * crypto_handshake_get_context(const crypto_handshake_context_t *ctx)
Get the crypto context for encryption/decryption.
bool crypto_handshake_is_ready(const crypto_handshake_context_t *ctx)
Check if handshake is complete and encryption is ready.
@ ERROR_NETWORK
Definition error_codes.h:69
asciichat_error_t send_audio_batch_packet(socket_t sockfd, const float *samples, int num_samples, int batch_count, crypto_context_t *crypto_ctx)
Send a batched audio packet with encryption support.
Definition packet.c:1072
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
Definition mutex.h:140
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
Definition mutex.h:175
void tcp_client_signal_lost(tcp_client_t *client)
Signal that connection was lost (triggers reconnection)
Cryptographic context structure.

References tcp_client::connection_active, tcp_client::crypto_ctx, crypto_handshake_get_context(), crypto_handshake_is_ready(), tcp_client::crypto_initialized, ERROR_INVALID_PARAM, ERROR_NETWORK, INVALID_SOCKET_VALUE, mutex_lock, mutex_unlock, send_audio_batch_packet(), tcp_client::send_mutex, SET_ERRNO, tcp_client::sockfd, and tcp_client_signal_lost().

◆ tcp_client_send_audio_opus()

int tcp_client_send_audio_opus ( tcp_client_t client,
const uint8_t opus_data,
size_t  opus_size,
int  sample_rate,
int  frame_duration 
)

Send Opus-encoded audio frame.

Parameters
clientTCP client instance
opus_dataOpus-encoded audio data
opus_sizeSize of encoded frame
sample_rateSample rate in Hz
frame_durationFrame duration in milliseconds
Returns
0 on success, negative on error

Definition at line 618 of file lib/network/tcp/client.c.

619 {
620 if (!client || !opus_data) {
621 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client or opus_data");
622 }
623
624 if (!atomic_load(&client->connection_active)) {
625 return SET_ERRNO(ERROR_NETWORK, "Connection not active");
626 }
627
628 mutex_lock(&client->send_mutex);
629
630 // Recheck connection status inside mutex to prevent TOCTOU race
631 if (!atomic_load(&client->connection_active) || client->sockfd == INVALID_SOCKET_VALUE) {
632 mutex_unlock(&client->send_mutex);
633 return SET_ERRNO(ERROR_NETWORK, "Connection not active");
634 }
635
636 // Get crypto context if encryption is enabled
637 crypto_context_t *crypto_ctx = NULL;
639 crypto_ctx = crypto_handshake_get_context(&client->crypto_ctx);
640 }
641
642 // Build Opus packet with header
643 size_t header_size = 16; // sample_rate (4), frame_duration (4), reserved (8)
644 size_t total_size = header_size + opus_size;
645 void *packet_data = buffer_pool_alloc(NULL, total_size);
646 if (!packet_data) {
647 mutex_unlock(&client->send_mutex);
648 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for Opus packet: %zu bytes", total_size);
649 }
650
651 // Write header in network byte order
652 uint8_t *buf = (uint8_t *)packet_data;
653 uint32_t sr = HOST_TO_NET_U32((uint32_t)sample_rate);
654 uint32_t fd = HOST_TO_NET_U32((uint32_t)frame_duration);
655 memcpy(buf, &sr, 4);
656 memcpy(buf + 4, &fd, 4);
657 memset(buf + 8, 0, 8); // Reserved
658
659 // Copy Opus data
660 memcpy(buf + header_size, opus_data, opus_size);
661
662 // Send packet with encryption if available
663 asciichat_error_t result;
664 if (crypto_ctx) {
665 result = send_packet_secure(client->sockfd, PACKET_TYPE_AUDIO_OPUS, packet_data, total_size, crypto_ctx);
666 } else {
667 result = packet_send(client->sockfd, PACKET_TYPE_AUDIO_OPUS, packet_data, total_size);
668 }
669
670 buffer_pool_free(NULL, packet_data, total_size);
671 mutex_unlock(&client->send_mutex);
672
673 if (result != ASCIICHAT_OK) {
675 }
676
677 return result;
678}
#define HOST_TO_NET_U32(val)
Definition endian.h:71
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
unsigned char uint8_t
Definition common.h:56
@ ERROR_MEMORY
Definition error_codes.h:53
asciichat_error_t send_packet_secure(socket_t sockfd, packet_type_t type, const void *data, size_t len, crypto_context_t *crypto_ctx)
Send a packet with encryption and compression support.
Definition packet.c:433
asciichat_error_t packet_send(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a packet with proper header and CRC32.
Definition packet.c:291
@ PACKET_TYPE_AUDIO_OPUS
Opus-encoded single audio frame.
Definition packet.h:357

References ASCIICHAT_OK, buffer_pool_alloc(), buffer_pool_free(), tcp_client::connection_active, tcp_client::crypto_ctx, crypto_handshake_get_context(), crypto_handshake_is_ready(), tcp_client::crypto_initialized, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, HOST_TO_NET_U32, INVALID_SOCKET_VALUE, mutex_lock, mutex_unlock, packet_send(), PACKET_TYPE_AUDIO_OPUS, tcp_client::send_mutex, send_packet_secure(), SET_ERRNO, tcp_client::sockfd, and tcp_client_signal_lost().

◆ tcp_client_send_audio_opus_batch()

int tcp_client_send_audio_opus_batch ( tcp_client_t client,
const uint8_t opus_data,
size_t  opus_size,
const uint16_t frame_sizes,
int  frame_count 
)

Send Opus audio batch packet.

Parameters
clientTCP client instance
opus_dataOpus-encoded audio data (multiple frames)
opus_sizeTotal size of Opus data
frame_sizesArray of individual frame sizes
frame_countNumber of frames in batch
Returns
0 on success, negative on error

Definition at line 690 of file lib/network/tcp/client.c.

691 {
692 if (!client || !opus_data || !frame_sizes) {
693 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client, opus_data, or frame_sizes");
694 }
695
696 if (!atomic_load(&client->connection_active)) {
697 return SET_ERRNO(ERROR_NETWORK, "Connection not active");
698 }
699
700 mutex_lock(&client->send_mutex);
701
702 if (!atomic_load(&client->connection_active) || client->sockfd == INVALID_SOCKET_VALUE) {
703 mutex_unlock(&client->send_mutex);
704 return -1;
705 }
706
707 crypto_context_t *crypto_ctx = NULL;
709 crypto_ctx = crypto_handshake_get_context(&client->crypto_ctx);
710 }
711
712 // Opus uses 20ms frames at 48kHz
713 int result =
714 av_send_audio_opus_batch(client->sockfd, opus_data, opus_size, frame_sizes, 48000, 20, frame_count, crypto_ctx);
715
716 mutex_unlock(&client->send_mutex);
717
718 if (result < 0) {
720 }
721
722 return result;
723}
asciichat_error_t av_send_audio_opus_batch(socket_t sockfd, const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int sample_rate, int frame_duration, int frame_count, crypto_context_t *crypto_ctx)
Send Opus-encoded audio batch packet with encryption support.
Definition packet.c:1136

References av_send_audio_opus_batch(), tcp_client::connection_active, tcp_client::crypto_ctx, crypto_handshake_get_context(), crypto_handshake_is_ready(), tcp_client::crypto_initialized, ERROR_INVALID_PARAM, ERROR_NETWORK, INVALID_SOCKET_VALUE, mutex_lock, mutex_unlock, tcp_client::send_mutex, SET_ERRNO, tcp_client::sockfd, and tcp_client_signal_lost().

◆ tcp_client_send_join()

int tcp_client_send_join ( tcp_client_t client,
const char *  display_name,
uint32_t  capabilities 
)

Send client join packet.

Parameters
clientTCP client instance
display_nameClient display name
capabilitiesClient capability flags
Returns
0 on success, negative on error

Definition at line 814 of file lib/network/tcp/client.c.

814 {
815 if (!client) {
816 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client");
817 }
818
819 if (!atomic_load(&client->connection_active) || client->sockfd == INVALID_SOCKET_VALUE) {
820 return -1;
821 }
822
823 // Build CLIENT_JOIN packet
824 client_info_packet_t join_packet;
825 SAFE_MEMSET(&join_packet, sizeof(join_packet), 0, sizeof(join_packet));
826 join_packet.client_id = HOST_TO_NET_U32(0); // Will be assigned by server
827 SAFE_SNPRINTF(join_packet.display_name, MAX_DISPLAY_NAME_LEN, "%s", display_name ? display_name : "Unknown");
828 join_packet.capabilities = HOST_TO_NET_U32(capabilities);
829
830 int send_result = tcp_client_send_packet(client, PACKET_TYPE_CLIENT_JOIN, &join_packet, sizeof(join_packet));
831 if (send_result == 0) {
832 mutex_lock(&client->send_mutex);
833 bool active = atomic_load(&client->connection_active);
834 socket_t socket_snapshot = client->sockfd;
835 crypto_context_t *crypto_ctx = NULL;
837 crypto_ctx = crypto_handshake_get_context(&client->crypto_ctx);
838 }
839 if (active && socket_snapshot != INVALID_SOCKET_VALUE) {
841 socket_snapshot, (const struct crypto_context_t *)crypto_ctx, LOG_INFO, REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER,
842 "CLIENT_JOIN sent (display=\"%s\", capabilities=0x%x)", join_packet.display_name, capabilities);
843 }
844 mutex_unlock(&client->send_mutex);
845 }
846 return send_result;
847}
#define SAFE_MEMSET(dest, dest_size, ch, count)
Definition common.h:389
#define MAX_DISPLAY_NAME_LEN
Maximum display name length in characters.
Definition limits.h:20
asciichat_error_t log_network_message(socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *fmt,...)
Send a formatted log message over the network.
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER
@ LOG_INFO
Definition log/logging.h:62
char display_name[MAX_DISPLAY_NAME_LEN]
User display name (null-terminated, max MAX_DISPLAY_NAME_LEN bytes)
Definition packet.h:549
uint32_t client_id
Unique client identifier (1-9, 0 = server)
Definition packet.h:547
uint32_t capabilities
Client capabilities bitmask (CLIENT_CAP_VIDEO | CLIENT_CAP_AUDIO | CLIENT_CAP_COLOR | CLIENT_CAP_STRE...
Definition packet.h:552
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
Definition packet.h:300
int socket_t
Socket handle type (POSIX: int)
Definition socket.h:50
int tcp_client_send_packet(tcp_client_t *client, packet_type_t type, const void *data, size_t len)
Send packet with thread-safe mutex protection.
Client information packet structure.
Definition packet.h:545

References client_info_packet_t::capabilities, client_info_packet_t::client_id, tcp_client::connection_active, tcp_client::crypto_ctx, crypto_handshake_get_context(), crypto_handshake_is_ready(), tcp_client::crypto_initialized, client_info_packet_t::display_name, ERROR_INVALID_PARAM, HOST_TO_NET_U32, INVALID_SOCKET_VALUE, LOG_INFO, log_network_message(), MAX_DISPLAY_NAME_LEN, mutex_lock, mutex_unlock, PACKET_TYPE_CLIENT_JOIN, REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER, SAFE_MEMSET, SAFE_SNPRINTF, tcp_client::send_mutex, SET_ERRNO, tcp_client::sockfd, and tcp_client_send_packet().

◆ tcp_client_send_packet()

int tcp_client_send_packet ( tcp_client_t client,
packet_type_t  type,
const void *  data,
size_t  len 
)

Send packet with thread-safe mutex protection.

All packet transmission goes through this function to ensure packets aren't interleaved on the wire. Automatically handles encryption if crypto context is ready.

Parameters
clientTCP client instance
typePacket type identifier
dataPacket payload
lenPayload length
Returns
0 on success, negative on error

All packet transmission goes through this function to ensure packets aren't interleaved on the wire.

Definition at line 340 of file lib/network/tcp/client.c.

340 {
341 if (!client) {
342 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client");
343 }
344
345 if (!atomic_load(&client->connection_active)) {
346 return SET_ERRNO(ERROR_NETWORK, "Connection not active");
347 }
348
349 // Acquire send mutex for thread-safe transmission
350 mutex_lock(&client->send_mutex);
351
352 // Determine if encryption should be used
353 crypto_context_t *crypto_ctx = NULL;
355 crypto_ctx = crypto_handshake_get_context(&client->crypto_ctx);
356 }
357
358 // Send packet (encrypted if crypto context available)
359 asciichat_error_t result = send_packet_secure(client->sockfd, type, data, len, crypto_ctx);
360
361 mutex_unlock(&client->send_mutex);
362
363 if (result != ASCIICHAT_OK) {
364 log_debug("Failed to send packet type %d: %s", type, asciichat_error_string(result));
365 return -1;
366 }
367
368 return 0;
369}

References ASCIICHAT_OK, tcp_client::connection_active, tcp_client::crypto_ctx, crypto_handshake_get_context(), crypto_handshake_is_ready(), tcp_client::crypto_initialized, ERROR_INVALID_PARAM, ERROR_NETWORK, log_debug, mutex_lock, mutex_unlock, tcp_client::send_mutex, send_packet_secure(), SET_ERRNO, and tcp_client::sockfd.

Referenced by tcp_client_send_join(), tcp_client_send_ping(), tcp_client_send_pong(), tcp_client_send_stream_start(), and tcp_client_send_terminal_capabilities().

◆ tcp_client_send_ping()

int tcp_client_send_ping ( tcp_client_t client)

Send ping packet.

Parameters
clientTCP client instance
Returns
0 on success, negative on error

Definition at line 374 of file lib/network/tcp/client.c.

374 {
375 if (!client)
376 return -1;
377 return tcp_client_send_packet(client, PACKET_TYPE_PING, NULL, 0);
378}
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:295

References PACKET_TYPE_PING, and tcp_client_send_packet().

◆ tcp_client_send_pong()

int tcp_client_send_pong ( tcp_client_t client)

Send pong packet.

Parameters
clientTCP client instance
Returns
0 on success, negative on error

Definition at line 383 of file lib/network/tcp/client.c.

383 {
384 if (!client)
385 return -1;
386 return tcp_client_send_packet(client, PACKET_TYPE_PONG, NULL, 0);
387}
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:297

References PACKET_TYPE_PONG, and tcp_client_send_packet().

◆ tcp_client_send_stream_start()

int tcp_client_send_stream_start ( tcp_client_t client,
uint32_t  stream_type 
)

Send stream start packet.

Parameters
clientTCP client instance
stream_typeType of stream (audio/video)
Returns
0 on success, negative on error

Definition at line 856 of file lib/network/tcp/client.c.

856 {
857 if (!client) {
858 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client");
859 }
860
861 if (!atomic_load(&client->connection_active) || client->sockfd == INVALID_SOCKET_VALUE) {
862 return -1;
863 }
864
865 uint32_t type_data = HOST_TO_NET_U32(stream_type);
866 return tcp_client_send_packet(client, PACKET_TYPE_STREAM_START, &type_data, sizeof(type_data));
867}
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
Definition packet.h:304

References tcp_client::connection_active, ERROR_INVALID_PARAM, HOST_TO_NET_U32, INVALID_SOCKET_VALUE, PACKET_TYPE_STREAM_START, SET_ERRNO, tcp_client::sockfd, and tcp_client_send_packet().

◆ tcp_client_send_terminal_capabilities()

int tcp_client_send_terminal_capabilities ( tcp_client_t client,
unsigned short  width,
unsigned short  height 
)

Send terminal capabilities packet.

Parameters
clientTCP client instance
widthTerminal width in characters
heightTerminal height in characters
Returns
0 on success, negative on error

Definition at line 733 of file lib/network/tcp/client.c.

733 {
734 if (!client) {
735 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client");
736 }
737
738 if (!atomic_load(&client->connection_active) || client->sockfd == INVALID_SOCKET_VALUE) {
739 return -1;
740 }
741
742 // Detect terminal capabilities automatically
744
745 // Apply user's color mode override
746 caps = apply_color_mode_override(caps);
747
748 // Check if detection was reliable, use fallback only for auto-detection
749 if (!caps.detection_reliable && (int)GET_OPTION(color_mode) == COLOR_MODE_AUTO) {
750 log_warn("Terminal capability detection not reliable, using fallback");
751 SAFE_MEMSET(&caps, sizeof(caps), 0, sizeof(caps));
753 caps.color_count = 2;
754 caps.capabilities = 0;
755 SAFE_STRNCPY(caps.term_type, "unknown", sizeof(caps.term_type));
756 SAFE_STRNCPY(caps.colorterm, "", sizeof(caps.colorterm));
757 caps.detection_reliable = 0;
758 }
759
760 // Convert to network packet format
762 net_packet.capabilities = HOST_TO_NET_U32(caps.capabilities);
763 net_packet.color_level = HOST_TO_NET_U32(caps.color_level);
764 net_packet.color_count = HOST_TO_NET_U32(caps.color_count);
765 net_packet.render_mode = HOST_TO_NET_U32(caps.render_mode);
766 net_packet.width = HOST_TO_NET_U16(width);
767 net_packet.height = HOST_TO_NET_U16(height);
768 net_packet.palette_type = HOST_TO_NET_U32(GET_OPTION(palette_type));
769 net_packet.utf8_support = HOST_TO_NET_U32(caps.utf8_support ? 1 : 0);
770
771 const options_t *opts = options_get();
772 if (GET_OPTION(palette_type) == PALETTE_CUSTOM && GET_OPTION(palette_custom_set)) {
773 const char *palette_custom = opts && opts->palette_custom_set ? opts->palette_custom : "";
774 SAFE_STRNCPY(net_packet.palette_custom, palette_custom, sizeof(net_packet.palette_custom));
775 net_packet.palette_custom[sizeof(net_packet.palette_custom) - 1] = '\0';
776 } else {
777 SAFE_MEMSET(net_packet.palette_custom, sizeof(net_packet.palette_custom), 0, sizeof(net_packet.palette_custom));
778 }
779
780 // Set desired FPS (from global g_max_fps if available, otherwise from caps)
781 extern int g_max_fps; // Will be passed via options in future refactoring
782 if (g_max_fps > 0) {
783 net_packet.desired_fps = (uint8_t)(g_max_fps > 144 ? 144 : g_max_fps);
784 } else {
785 net_packet.desired_fps = caps.desired_fps;
786 }
787
788 if (net_packet.desired_fps == 0) {
789 net_packet.desired_fps = DEFAULT_MAX_FPS;
790 }
791
792 SAFE_STRNCPY(net_packet.term_type, caps.term_type, sizeof(net_packet.term_type));
793 net_packet.term_type[sizeof(net_packet.term_type) - 1] = '\0';
794
795 SAFE_STRNCPY(net_packet.colorterm, caps.colorterm, sizeof(net_packet.colorterm));
796 net_packet.colorterm[sizeof(net_packet.colorterm) - 1] = '\0';
797
798 net_packet.detection_reliable = caps.detection_reliable;
799 net_packet.utf8_support = GET_OPTION(force_utf8) ? 1 : 0;
800
801 SAFE_MEMSET(net_packet.reserved, sizeof(net_packet.reserved), 0, sizeof(net_packet.reserved));
802
803 return tcp_client_send_packet(client, PACKET_TYPE_CLIENT_CAPABILITIES, &net_packet, sizeof(net_packet));
804}
#define HOST_TO_NET_U16(val)
Definition endian.h:101
#define DEFAULT_MAX_FPS
Default maximum frame rate (frames per second)
Definition limits.h:26
int g_max_fps
Runtime configurable maximum frame rate (can be overridden via environment or command line)
Definition common.c:30
uint32_t capabilities
Terminal capabilities bitmask (TERM_CAP_* flags)
Definition packet.h:911
uint32_t color_level
Color level enum value (terminal_color_mode_t)
Definition packet.h:913
uint32_t color_count
Actual color count (16, 256, or 16777216)
Definition packet.h:915
uint16_t height
Terminal height in characters.
Definition packet.h:921
uint8_t reserved[2]
Reserved bytes for alignment (must be zero)
Definition packet.h:937
uint32_t render_mode
Render mode enum value (foreground/background/half-block)
Definition packet.h:917
uint16_t width
Terminal width in characters.
Definition packet.h:919
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
Definition packet.h:935
uint8_t detection_reliable
Detection reliability flag (1=reliable detection, 0=best guess)
Definition packet.h:927
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM)
Definition packet.h:933
char colorterm[32]
$COLORTERM environment variable value (for debugging)
Definition packet.h:925
uint32_t palette_type
Palette type enum value (palette_type_t)
Definition packet.h:931
uint32_t utf8_support
UTF-8 support flag (0=no UTF-8, 1=UTF-8 supported)
Definition packet.h:929
char term_type[32]
$TERM environment variable value (for debugging)
Definition packet.h:923
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
Definition options.h:644
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
Definition options.h:156
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:222
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
Definition packet.h:293
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:96
terminal_capabilities_t apply_color_mode_override(terminal_capabilities_t caps)
Apply command-line overrides to detected capabilities.
terminal_capabilities_t detect_terminal_capabilities(void)
Detect terminal capabilities.
@ TERM_COLOR_NONE
No color support (monochrome terminal)
Definition terminal.h:428
Consolidated options structure.
Definition options.h:439
char palette_custom[256]
Custom palette characters.
Definition options.h:582
bool palette_custom_set
True if custom palette was set.
Definition options.h:583
Terminal capabilities packet structure (Packet Type 5)
Definition packet.h:909
Complete terminal capabilities structure.
Definition terminal.h:485
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
Definition terminal.h:487
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
Definition terminal.h:509
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
Definition terminal.h:497
bool utf8_support
True if terminal supports UTF-8 encoding.
Definition terminal.h:493
uint32_t capabilities
Capability flags bitmask (terminal_capability_flags_t)
Definition terminal.h:489
uint32_t color_count
Maximum number of colors (16, 256, or 16777216)
Definition terminal.h:491
char term_type[64]
$TERM environment variable value (for debugging)
Definition terminal.h:499
char colorterm[64]
$COLORTERM environment variable value (for debugging)
Definition terminal.h:501
bool detection_reliable
True if detection is confident (reliable detection)
Definition terminal.h:495

References apply_color_mode_override(), terminal_capabilities_packet_t::capabilities, terminal_capabilities_t::capabilities, terminal_capabilities_packet_t::color_count, terminal_capabilities_t::color_count, terminal_capabilities_packet_t::color_level, terminal_capabilities_t::color_level, COLOR_MODE_AUTO, terminal_capabilities_packet_t::colorterm, terminal_capabilities_t::colorterm, tcp_client::connection_active, DEFAULT_MAX_FPS, terminal_capabilities_packet_t::desired_fps, terminal_capabilities_t::desired_fps, detect_terminal_capabilities(), terminal_capabilities_packet_t::detection_reliable, terminal_capabilities_t::detection_reliable, ERROR_INVALID_PARAM, g_max_fps, GET_OPTION, terminal_capabilities_packet_t::height, HOST_TO_NET_U16, HOST_TO_NET_U32, INVALID_SOCKET_VALUE, log_warn, options_get(), PACKET_TYPE_CLIENT_CAPABILITIES, terminal_capabilities_packet_t::palette_custom, PALETTE_CUSTOM, terminal_capabilities_packet_t::palette_type, terminal_capabilities_packet_t::render_mode, terminal_capabilities_t::render_mode, terminal_capabilities_packet_t::reserved, SAFE_MEMSET, SAFE_STRNCPY, SET_ERRNO, tcp_client::sockfd, tcp_client_send_packet(), TERM_COLOR_NONE, terminal_capabilities_packet_t::term_type, terminal_capabilities_t::term_type, terminal_capabilities_packet_t::utf8_support, terminal_capabilities_t::utf8_support, and terminal_capabilities_packet_t::width.

◆ tcp_client_shutdown()

void tcp_client_shutdown ( tcp_client_t client)

Shutdown connection forcefully (for signal handlers)

Parameters
clientTCP client instance

Definition at line 302 of file lib/network/tcp/client.c.

302 {
303 if (!client)
304 return;
305
306 atomic_store(&client->connection_active, false);
307
308 // Shutdown socket for reading/writing to interrupt blocking calls
309 if (socket_is_valid(client->sockfd)) {
310 socket_shutdown(client->sockfd, SHUT_RDWR);
311 }
312}
int socket_shutdown(socket_t sock, int how)
Shutdown socket I/O.

References tcp_client::connection_active, socket_is_valid(), socket_shutdown(), and tcp_client::sockfd.

◆ tcp_client_signal_lost()

void tcp_client_signal_lost ( tcp_client_t client)

Signal that connection was lost (triggers reconnection)

Parameters
clientTCP client instance

Definition at line 266 of file lib/network/tcp/client.c.

266 {
267 if (!client)
268 return;
269
270 if (!atomic_load(&client->connection_lost)) {
271 atomic_store(&client->connection_lost, true);
272 atomic_store(&client->connection_active, false);
273 log_info("Connection lost signaled");
274 }
275}

References tcp_client::connection_active, tcp_client::connection_lost, and log_info.

Referenced by tcp_client_send_audio_batch(), tcp_client_send_audio_opus(), and tcp_client_send_audio_opus_batch().