ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
protocol.c File Reference

Go to the source code of this file.

Functions

void session_protocol_init (session_protocol_t *protocol, acip_transport_t *transport, session_protocol_media_t media, acip_client_callbacks_t control)
asciichat_error_t session_protocol_dispatch (session_protocol_t *protocol, packet_type_t type, const void *payload, size_t length)
void session_protocol_build_capabilities (unsigned short width, unsigned short height, terminal_capabilities_packet_t *packet)
asciichat_error_t session_protocol_update_settings (terminal_size_t *advertised, uint64_t *advertised_revision, session_protocol_send_settings_fn send, void *context)

Function Documentation

◆ session_protocol_build_capabilities()

void session_protocol_build_capabilities ( unsigned short width,
unsigned short height,
terminal_capabilities_packet_t * packet )

Definition at line 274 of file common/session/protocol.c.

275 {
276 const options_t *opts = options_get();
277 // Log the dimensions being sent to server (helps debug dimension mismatch issues)
278 log_debug("Sending terminal size to server: %ux%u (auto_width=%d, auto_height=%d)", width, height, opts->auto_width,
279 opts->auto_height);
280
281 // Connection and transport availability is checked by threaded_send_packet()
282
283 // Build terminal capabilities packet locally
284 // Detect terminal capabilities automatically
285 terminal_capabilities_t caps = detect_terminal_capabilities(options_terminal_settings());
286
287 // Set wants_padding based on snapshot mode and TTY status
288 // Disable padding when:
289 // - In snapshot mode (one frame and exit)
290 // - When stdout is not a TTY (piped/redirected output)
291 // Enable padding for interactive terminal sessions
292 bool is_snapshot_mode = opts->snapshot_mode;
293 bool is_interactive = terminal_is_interactive();
294 caps.wants_padding = is_interactive && !is_snapshot_mode;
295
296 log_debug("Client capabilities: wants_padding=%d (snapshot=%d, interactive=%d, stdin_tty=%d, stdout_tty=%d)",
297 caps.wants_padding, is_snapshot_mode, is_interactive, terminal_is_stdin_tty(), terminal_is_stdout_tty());
298
299 // Apply user's color mode override
300 caps = apply_color_mode_override(caps, options_terminal_settings());
301
302 // TERM_COLOR_AUTO is an option/configuration sentinel, not a wire-level
303 // capability. A remote peer must receive a concrete color level; this can
304 // otherwise make the server reject the complete capabilities packet. The
305 // browser/native renderer contract supports truecolor as the safe default.
306 terminal_color_mode_t normalized_color = acip_render_settings_normalize_color_mode(caps.color_level);
307 if (normalized_color != caps.color_level) {
308 log_debug("Terminal color detection returned AUTO; advertising truecolor");
309 caps.color_level = normalized_color;
310 caps.color_count = 16777216;
311 caps.capabilities |= TERM_CAP_COLOR_16 | TERM_CAP_COLOR_256 | TERM_CAP_COLOR_TRUE;
312 }
313
314 // Check if detection was reliable, use fallback only for auto-detection
315 if (!caps.detection_reliable && opts->color_mode == COLOR_MODE_AUTO) {
316 log_warn("Terminal capability detection not reliable, using fallback");
317 SAFE_MEMSET(&caps, sizeof(caps), 0, sizeof(caps));
318 caps.color_level = TERM_COLOR_NONE;
319 caps.color_count = 2;
320 caps.capabilities = 0;
321 SAFE_STRNCPY(caps.term_type, "unknown", sizeof(caps.term_type));
322 SAFE_STRNCPY(caps.colorterm, "", sizeof(caps.colorterm));
323 caps.detection_reliable = 0;
324 // Preserve wants_padding even in fallback mode
325 caps.wants_padding = is_interactive && !is_snapshot_mode;
326 }
327
328 // Convert to network packet format with proper byte order
329 terminal_capabilities_packet_t net_packet = {0};
330 if (opts->matrix_rain)
331 caps.capabilities |= TERM_CAP_MATRIX_RAIN;
332 else
333 caps.capabilities &= ~(uint32_t)TERM_CAP_MATRIX_RAIN;
334 net_packet.capabilities = HOST_TO_NET_U32(caps.capabilities);
335 net_packet.color_level = HOST_TO_NET_U32(caps.color_level);
336 net_packet.color_count = HOST_TO_NET_U32(caps.color_count);
337 net_packet.render_mode = HOST_TO_NET_U32(opts->render_mode);
338 net_packet.color_filter = (uint8_t)opts->color_filter;
339 net_packet.width = HOST_TO_NET_U16(width);
340 net_packet.height = HOST_TO_NET_U16(height);
341 net_packet.palette_type = HOST_TO_NET_U32(opts->palette_type);
342 net_packet.utf8_support = HOST_TO_NET_U32(caps.utf8_support ? 1 : 0);
343
344 if (opts->palette_type == PALETTE_CUSTOM && opts->palette_custom_set) {
345 const char *palette_custom = opts && opts->palette_custom_set ? opts->palette_custom : "";
346 SAFE_STRNCPY(net_packet.palette_custom, palette_custom, sizeof(net_packet.palette_custom));
347 net_packet.palette_custom[sizeof(net_packet.palette_custom) - 1] = '\0';
348 } else {
349 SAFE_MEMSET(net_packet.palette_custom, sizeof(net_packet.palette_custom), 0, sizeof(net_packet.palette_custom));
350 }
351
352 // Set desired FPS
353 int fps = opts->fps;
354 if (fps > 0) {
355 net_packet.desired_fps = (uint8_t)(fps > 144 ? 144 : fps);
356 } else {
357 net_packet.desired_fps = caps.desired_fps;
358 }
359
360 if (net_packet.desired_fps == 0) {
361 net_packet.desired_fps = DEFAULT_MAX_FPS;
362 }
363
364 SAFE_STRNCPY(net_packet.term_type, caps.term_type, sizeof(net_packet.term_type));
365 net_packet.term_type[sizeof(net_packet.term_type) - 1] = '\0';
366
367 SAFE_STRNCPY(net_packet.colorterm, caps.colorterm, sizeof(net_packet.colorterm));
368 net_packet.colorterm[sizeof(net_packet.colorterm) - 1] = '\0';
369
370 net_packet.detection_reliable = caps.detection_reliable;
371 // Send UTF-8 support flag: true for AUTO (default) and TRUE settings, false for FALSE setting
372 net_packet.utf8_support = (opts->force_utf8 != UTF8_SETTING_FALSE) ? 1 : 0;
373
374 // Set wants_padding flag (1=padding enabled, 0=no padding for snapshot/piped modes)
375 net_packet.wants_padding = caps.wants_padding ? 1 : 0;
376
377 // Set codec capabilities (client supports all codecs by default)
380
381 *packet = net_packet;
382}
#define AUDIO_CODEC_CAP_ALL
Definition codecs.h:80
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define HOST_TO_NET_U32(val)
Definition endian.h:66
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
unsigned int uint32_t
Definition common.h:62
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_MEMSET(dest, dest_size, ch, count)
Definition common.h:484
unsigned char uint8_t
Definition common.h:60
#define DEFAULT_MAX_FPS
Default maximum frame rate (frames per second).
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
#define VIDEO_CODEC_CAP_ALL
Default video codec capabilities (all codecs supported).
Definition codecs.h:52
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
Definition options.h:222
struct options_state options_t
Immutable, RCU-published snapshot of the application's runtime configuration.
const options_t * options_get(void)
Get pointer to current options struct (lock-free, thread-safe).
Definition rcu.c:511
#define options_terminal_settings()
Definition options.h:1296
@ UTF8_SETTING_FALSE
Force UTF-8 OFF: disable UTF-8 support.
Definition options.h:268
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:104
terminal_color_mode_t acip_render_settings_normalize_color_mode(terminal_color_mode_t mode)
bool snapshot_mode
Snapshot mode (one frame and exit).
Definition options.h:1149
terminal_color_mode_t color_mode
Color mode (auto/none/16/256/truecolor).
Definition options.h:1092
int force_utf8
UTF-8 support setting (auto/true/false).
Definition options.h:1097
bool matrix_rain
Matrix digital rain effect (false = disabled).
Definition options.h:1152
bool auto_height
Auto-detect height from terminal.
Definition options.h:999
char palette_custom[256]
Custom palette characters.
Definition options.h:1230
bool palette_custom_set
True if custom palette was set.
Definition options.h:1231
terminal_render_mode_t render_mode
Render mode (foreground/background/half-block).
Definition options.h:1095
color_filter_t color_filter
Monochromatic color filter (none/black/white/green/etc).
Definition options.h:1093
int fps
Target framerate (0 = auto, 1-144).
Definition options.h:1099
palette_type_t palette_type
Selected palette type.
Definition options.h:1229
bool auto_width
Auto-detect width from terminal.
Definition options.h:998
Terminal capabilities packet structure (Packet Type 5).
Definition packet.h:1074
uint32_t codec_capabilities_video
Video codec capabilities bitmask (VIDEO_CODEC_CAP_*).
Definition packet.h:1106
uint32_t capabilities
Terminal capabilities bitmask (TERM_CAP_* flags).
Definition packet.h:1076
uint32_t color_level
Color level enum value (terminal_color_mode_t).
Definition packet.h:1078
uint32_t color_count
Actual color count (16, 256, or 16777216).
Definition packet.h:1080
uint16_t height
Terminal height in characters.
Definition packet.h:1086
uint8_t wants_padding
Whether client wants frame padding (1=padding enabled, 0=no padding for snapshot/piped modes).
Definition packet.h:1104
uint32_t render_mode
Render mode enum value (foreground/background/half-block).
Definition packet.h:1082
uint16_t width
Terminal width in characters.
Definition packet.h:1084
uint8_t desired_fps
Client's desired frame rate (1-144 FPS).
Definition packet.h:1100
uint8_t detection_reliable
Detection reliability flag (1=reliable detection, 0=best guess).
Definition packet.h:1092
uint32_t codec_capabilities_audio
Audio codec capabilities bitmask (AUDIO_CODEC_CAP_*).
Definition packet.h:1108
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM).
Definition packet.h:1098
char colorterm[32]
$COLORTERM environment variable value (for debugging)
Definition packet.h:1090
uint32_t palette_type
Palette type enum value (palette_type_t).
Definition packet.h:1096
uint8_t color_filter
Monochromatic color filter enum value (color_filter_t).
Definition packet.h:1102
uint32_t utf8_support
UTF-8 support flag (0=no UTF-8, 1=UTF-8 supported).
Definition packet.h:1094
char term_type[32]
$TERM environment variable value (for debugging)
Definition packet.h:1088

References acip_render_settings_normalize_color_mode(), AUDIO_CODEC_CAP_ALL, options_state::auto_height, options_state::auto_width, terminal_capabilities_packet_t::capabilities, terminal_capabilities_packet_t::codec_capabilities_audio, terminal_capabilities_packet_t::codec_capabilities_video, terminal_capabilities_packet_t::color_count, options_state::color_filter, terminal_capabilities_packet_t::color_filter, terminal_capabilities_packet_t::color_level, options_state::color_mode, COLOR_MODE_AUTO, terminal_capabilities_packet_t::colorterm, DEFAULT_MAX_FPS, terminal_capabilities_packet_t::desired_fps, terminal_capabilities_packet_t::detection_reliable, options_state::force_utf8, options_state::fps, height, terminal_capabilities_packet_t::height, HOST_TO_NET_U16, HOST_TO_NET_U32, log_debug, log_warn, options_state::matrix_rain, options_get(), options_terminal_settings, PALETTE_CUSTOM, options_state::palette_custom, terminal_capabilities_packet_t::palette_custom, options_state::palette_custom_set, options_state::palette_type, terminal_capabilities_packet_t::palette_type, options_state::render_mode, terminal_capabilities_packet_t::render_mode, SAFE_MEMSET, SAFE_STRNCPY, options_state::snapshot_mode, terminal_capabilities_packet_t::term_type, UTF8_SETTING_FALSE, terminal_capabilities_packet_t::utf8_support, VIDEO_CODEC_CAP_ALL, terminal_capabilities_packet_t::wants_padding, terminal_capabilities_packet_t::width, and width.

Referenced by threaded_send_terminal_size_with_auto_detect().

◆ session_protocol_dispatch()

asciichat_error_t session_protocol_dispatch ( session_protocol_t * protocol,
packet_type_t type,
const void * payload,
size_t length )

Definition at line 210 of file common/session/protocol.c.

211 {
212 if (!protocol || (!payload && length))
213 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid participant protocol packet");
214 protocol->callback_result = ASCIICHAT_OK;
215 if (type == PACKET_TYPE_ACIP_SETTINGS_ACK) {
217 const char *json;
218 size_t json_length;
219 asciichat_error_t result = acip_settings_decode(payload, length, &header, &json, &json_length);
220 if (result != ASCIICHAT_OK)
221 return result;
222 if ((header.request && header.request < protocol->settings_request) ||
223 header.revision < protocol->settings_revision)
224 return ASCIICHAT_OK;
225 options_t *current = SAFE_MALLOC(sizeof(*current), options_t *),
226 *candidate = SAFE_MALLOC(sizeof(*candidate), options_t *);
228 result = options_prepare_json(current, json, json_length, candidate, true);
229 if (result == ASCIICHAT_OK)
230 result = acip_render_settings_prepare(&protocol->effective_render_settings, candidate);
231 SAFE_FREE(current);
232 SAFE_FREE(candidate);
233 if (result != ASCIICHAT_OK)
234 return result;
235 protocol->effective_render_settings.revision = header.revision;
236 if (header.request)
237 protocol->settings_request = header.request;
238 protocol->settings_revision = header.revision;
239 protocol->settings_status = header.status;
240 if (header.status)
241 log_warn("Remote rendering update rejected (request=%u, status=%u)", header.request, header.status);
242 else
243 log_info("Remote rendering update accepted (request=%u, revision=%u)", header.request, header.revision);
244 return ASCIICHAT_OK;
245 }
246 acip_client_callbacks_t media = {.on_ascii_frame = receive_frame,
247 .on_audio_batch = receive_samples,
248 .on_audio_opus_batch = receive_opus_batch,
249 .on_server_state = receive_state,
250 .on_error = receive_error,
251 .app_ctx = protocol};
252 bool audio = type == PACKET_TYPE_AUDIO_BATCH || type == PACKET_TYPE_AUDIO_OPUS_BATCH;
253 bool common =
255 asciichat_error_t result;
256 if (type == PACKET_TYPE_PING && !protocol->control.on_ping)
257 result = acip_send_pong(protocol->transport);
258 else
259 result =
260 acip_handle_client_packet(protocol->transport, type, payload, length, common ? &media : &protocol->control);
261 if (result == ASCIICHAT_OK)
262 result = protocol->callback_result;
263 if (result == ASCIICHAT_OK) {
264 protocol->packets_received++;
265 if (audio) {
266 protocol->audio_packets_received++;
267 if (protocol->media.on_audio_packet)
268 protocol->media.on_audio_packet(length, protocol->media.context);
269 }
270 }
271 return result;
272}
#define SET_ERRNO(code,...)
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:398
@ PACKET_TYPE_SERVER_STATE
Server sends current state to clients.
Definition packet.h:421
@ PACKET_TYPE_ACIP_SETTINGS_ACK
Settings acknowledgment (Participant -> Initiator).
Definition packet.h:487
@ PACKET_TYPE_ASCII_FRAME
Complete ASCII frame with all metadata.
Definition packet.h:383
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:374
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:407
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:396
asciichat_error_t acip_handle_client_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, const acip_client_callbacks_t *callbacks)
Client-side packet handler callbacks.
Definition handlers.c:203
asciichat_error_t acip_render_settings_prepare(acip_render_settings_t *candidate, const options_t *values)
asciichat_error_t acip_settings_decode(const void *payload, size_t length, acip_settings_header_t *header, const char **json, size_t *json_length)
void acip_render_settings_options(const acip_render_settings_t *state, options_t *values)
asciichat_error_t options_prepare_json(const options_t *current, const char *json, size_t length, options_t *candidate, bool remote_only)
Definition rcu.c:987
asciichat_error_t acip_send_pong(acip_transport_t *transport)
Send pong packet.
Definition send.c:395
Client-side packet handler callbacks.
Definition handlers.h:51
void(* on_ping)(void *ctx)
Called when ping received (should send pong).
Definition handlers.h:77
acip settings header keeps the values that move through the subsystem: schema, request,...
void(* on_audio_packet)(size_t length, void *context)
acip_render_settings_t effective_render_settings
session_protocol_media_t media
acip_client_callbacks_t control

References acip_handle_client_packet(), acip_render_settings_options(), acip_render_settings_prepare(), acip_send_pong(), acip_settings_decode(), ASCIICHAT_OK, session_protocol_t::audio_packets_received, session_protocol_t::callback_result, session_protocol_media_t::context, session_protocol_t::control, session_protocol_t::effective_render_settings, ERROR_INVALID_PARAM, log_info, log_warn, session_protocol_t::media, session_protocol_media_t::on_audio_packet, acip_client_callbacks_t::on_ping, options_prepare_json(), PACKET_TYPE_ACIP_SETTINGS_ACK, PACKET_TYPE_ASCII_FRAME, PACKET_TYPE_AUDIO_BATCH, PACKET_TYPE_AUDIO_OPUS_BATCH, PACKET_TYPE_ERROR_MESSAGE, PACKET_TYPE_PING, PACKET_TYPE_SERVER_STATE, session_protocol_t::packets_received, acip_render_settings_t::revision, SAFE_FREE, SAFE_MALLOC, SET_ERRNO, session_protocol_t::settings_request, session_protocol_t::settings_revision, session_protocol_t::settings_status, and session_protocol_t::transport.

◆ session_protocol_init()

void session_protocol_init ( session_protocol_t * protocol,
acip_transport_t * transport,
session_protocol_media_t media,
acip_client_callbacks_t control )

Definition at line 204 of file common/session/protocol.c.

205 {
206 *protocol = (session_protocol_t){.transport = transport, .media = media, .control = control};
207 protocol->effective_render_settings.caps.utf8_support = GET_OPTION(force_utf8) != UTF8_SETTING_FALSE;
208}
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
session protocol media keeps the values that move through the subsystem: context, terminal_output,...

References acip_render_settings_t::caps, session_protocol_t::effective_render_settings, GET_OPTION, and UTF8_SETTING_FALSE.

Referenced by client_protocol_start().

◆ session_protocol_update_settings()

asciichat_error_t session_protocol_update_settings ( terminal_size_t * advertised,
uint64_t * advertised_revision,
session_protocol_send_settings_fn send,
void * context )

Definition at line 384 of file common/session/protocol.c.

385 {
386 if (!advertised || !advertised_revision || !send)
387 return SET_ERRNO(ERROR_INVALID_PARAM, "Settings update requires state and a send callback");
388 if (advertised->cols > 0 && advertised->rows > 0 && *advertised_revision == options_revision())
389 return ASCIICHAT_OK;
391 asciichat_error_t result = options_read_snapshot(snapshot);
392 if (result != ASCIICHAT_OK) {
393 return result;
394 }
395 terminal_size_t size = {.cols = snapshot->values.width, .rows = snapshot->values.height};
396 if (size.cols <= 0 || size.rows <= 0) {
397 return ASCIICHAT_OK;
398 }
399 if (snapshot->values.color_mode == TERM_COLOR_AUTO) {
400 terminal_capabilities_t caps = detect_terminal_capabilities(options_terminal_settings());
401 snapshot->values.color_mode = caps.detection_reliable ? caps.color_level : TERM_COLOR_NONE;
402 }
403 result = send(&snapshot->values, (uint32_t)snapshot->revision, context);
404 if (result == ASCIICHAT_OK) {
405 *advertised = size;
406 *advertised_revision = snapshot->revision;
407 }
408
409 return result;
410}
#define SAFE_MALLOC_DEFER_FREE(name, size, cast)
Definition common.h:386
uint64_t options_revision(void)
Definition rcu.c:876
asciichat_error_t options_read_snapshot(options_snapshot_t *snapshot)
Definition rcu.c:865

References ASCIICHAT_OK, ERROR_INVALID_PARAM, options_read_snapshot(), options_revision(), options_terminal_settings, SAFE_MALLOC_DEFER_FREE, and SET_ERRNO.