ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
lib/network/acip/settings.c
Go to the documentation of this file.
6#include <ascii-chat/common.h>
7#include <string.h>
8
10 *values = options_t_new();
11 values->width = state->width;
12 values->height = state->height;
13 values->fps = state->caps.desired_fps;
14 values->color_mode = (terminal_color_mode_t)state->caps.color_level;
15 values->color_filter = state->caps.color_filter;
16 values->render_mode = state->caps.render_mode;
17 values->renderer = state->caps.text_renderer;
18 values->stabilize = state->caps.stabilize;
19 values->palette_explicit = state->caps.palette_explicit;
20 values->palette_type = state->caps.palette_type ? state->caps.palette_type : PALETTE_STANDARD;
21 SAFE_STRNCPY(values->palette_custom, state->caps.palette_custom, sizeof(values->palette_custom));
22 values->palette_custom_set = values->palette_custom[0] != 0;
23 values->matrix_rain = (state->caps.capabilities & TERM_CAP_MATRIX_RAIN) != 0;
24}
25
26terminal_color_mode_t acip_render_settings_normalize_color_mode(terminal_color_mode_t mode) {
27 return mode == TERM_COLOR_AUTO ? TERM_COLOR_TRUECOLOR : mode;
28}
29
31 if (!candidate || !values || values->width < 1 || values->width > 4096 || values->height < 1 ||
32 values->height > 4096 || (uint64_t)values->width * values->height > 1048576 || values->fps < 0 ||
33 values->fps > 144 || values->color_mode < TERM_COLOR_NONE || values->color_mode > TERM_COLOR_TRUECOLOR ||
34 values->color_filter < 0 || values->color_filter >= COLOR_FILTER_COUNT || values->render_mode < 0 ||
35 values->render_mode > RENDER_MODE_HALF_BLOCK || values->palette_type < PALETTE_STANDARD ||
36 values->palette_type >= PALETTE_COUNT || strlen(values->palette_custom) >= sizeof(candidate->caps.palette_custom))
37 return SET_ERRNO(
39 "Invalid remote render settings (size=%dx%d fps=%d color=%d filter=%d mode=%d palette=%d custom=%zu)",
40 values->width, values->height, values->fps, values->color_mode, values->color_filter, values->render_mode,
41 values->palette_type, strlen(values->palette_custom));
42 acip_render_settings_t next = *candidate;
43 next.width = (uint16_t)values->width;
44 next.height = (uint16_t)values->height;
45 next.caps.desired_fps =
46 candidate->fps_limit && values->fps > candidate->fps_limit ? candidate->fps_limit : values->fps;
47 next.caps.color_level = values->color_mode;
48 next.caps.color_count = values->color_mode == TERM_COLOR_TRUECOLOR ? 16777216
49 : values->color_mode == TERM_COLOR_256 ? 256
50 : values->color_mode == TERM_COLOR_16 ? 16
51 : 2;
52 next.caps.color_filter = values->color_filter;
53 next.caps.render_mode = values->render_mode;
54 next.caps.text_renderer = values->renderer;
55 next.caps.stabilize = values->stabilize;
56 next.caps.palette_explicit = values->palette_explicit;
57 next.caps.wants_background = values->render_mode == RENDER_MODE_BACKGROUND;
58 next.caps.palette_type = values->palette_type;
59 SAFE_STRNCPY(next.caps.palette_custom, values->palette_custom, sizeof(next.caps.palette_custom));
60 if (values->matrix_rain)
61 next.caps.capabilities |= TERM_CAP_MATRIX_RAIN;
62 else
63 next.caps.capabilities &= ~(uint32_t)TERM_CAP_MATRIX_RAIN;
64 if (initialize_client_palette(next.caps.palette_type, values->palette_custom, next.palette, &next.palette_length,
65 next.luminance))
66 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rendering palette");
67 if (values->renderer == ASCII_RENDERER_GLYPH && values->palette_explicit &&
69 return SET_ERRNO(ERROR_INVALID_PARAM, "Glyph palette contains characters without glyph masks");
70 if (values->renderer == ASCII_RENDERER_GLYPH && !values->palette_explicit) {
72 " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
73 sizeof(next.palette));
74 next.palette_length = strlen(next.palette);
75 }
76 next.revision = candidate->revision + 1;
77 *candidate = next;
78 return ASCIICHAT_OK;
79}
80
81static asciichat_error_t prepare_for_viewer(acip_render_settings_t *state, options_t *values) {
82 if (values->palette_type < PALETTE_STANDARD || values->palette_type >= PALETTE_COUNT)
83 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rendering palette");
84 if (!state->caps.utf8_support && values->render_mode == RENDER_MODE_HALF_BLOCK)
85 values->render_mode = RENDER_MODE_FOREGROUND;
86 values->palette_type = select_compatible_palette(values->palette_type, state->caps.utf8_support);
87 if (values->palette_type == PALETTE_CUSTOM && !state->caps.utf8_support &&
89 return SET_ERRNO(ERROR_INVALID_PARAM, "Custom palette requires UTF-8 support");
90 return acip_render_settings_prepare(state, values);
91}
92
94 const terminal_capabilities_packet_t *packet) {
95 if (!state || !packet || !memchr(packet->palette_custom, 0, sizeof(packet->palette_custom)))
96 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid render capabilities");
97 options_t *values = SAFE_MALLOC(sizeof(*values), options_t *);
98 acip_render_settings_options(state, values);
99 values->width = NET_TO_HOST_U16(packet->width);
100 values->height = NET_TO_HOST_U16(packet->height);
101 values->fps = packet->desired_fps;
102 values->color_mode = NET_TO_HOST_U32(packet->color_level);
103 values->color_filter = packet->color_filter;
104 values->render_mode = NET_TO_HOST_U32(packet->render_mode);
105 values->palette_type = NET_TO_HOST_U32(packet->palette_type);
106 // AUTO is a local option sentinel, but older/native/browser senders may
107 // still advertise it. Resolve it at the shared wire boundary so the peer
108 // cannot reject an otherwise valid capabilities packet.
110 if (values->palette_type == PALETTE_UNSET)
112 SAFE_STRNCPY(values->palette_custom, packet->palette_custom, sizeof(values->palette_custom));
113 values->matrix_rain = (NET_TO_HOST_U32(packet->capabilities) & TERM_CAP_MATRIX_RAIN) != 0;
114 acip_render_settings_t next = *state;
115 next.caps.capabilities = NET_TO_HOST_U32(packet->capabilities);
116 next.caps.utf8_support = NET_TO_HOST_U32(packet->utf8_support) != 0;
117 next.caps.wants_padding = packet->wants_padding != 0;
118 asciichat_error_t result = prepare_for_viewer(&next, values);
119 if (result == ASCIICHAT_OK)
120 *state = next;
121 SAFE_FREE(values);
122 return result;
123}
124
125asciichat_error_t acip_settings_decode(const void *payload, size_t length, acip_settings_header_t *header,
126 const char **json, size_t *json_length) {
127 if (!payload || !header || !json || !json_length || length < ACIP_SETTINGS_HEADER_SIZE ||
128 length > ACIP_SETTINGS_MAX_SIZE)
129 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid settings packet length");
130 uint32_t fields[5];
131 memcpy(fields, payload, sizeof(fields));
132 *header = (acip_settings_header_t){NET_TO_HOST_U32(fields[0]), NET_TO_HOST_U32(fields[1]), NET_TO_HOST_U32(fields[2]),
133 NET_TO_HOST_U32(fields[3]), NET_TO_HOST_U32(fields[4])};
134 if (header->schema != ACIP_SETTINGS_SCHEMA)
135 return SET_ERRNO(ERROR_NOT_SUPPORTED, "Unsupported settings schema");
136 *json = (const char *)payload + ACIP_SETTINGS_HEADER_SIZE;
137 *json_length = length - ACIP_SETTINGS_HEADER_SIZE;
138 return ASCIICHAT_OK;
139}
140
142 size_t capacity, size_t *length) {
143 if (!header || !json || !buffer || !length)
144 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing settings encoding argument");
145 size_t size = strlen(json);
146 if (size > 4096 || capacity < ACIP_SETTINGS_HEADER_SIZE + size)
147 return SET_ERRNO(ERROR_INVALID_PARAM, "Settings output too small");
148 uint32_t fields[5] = {HOST_TO_NET_U32(header->schema), HOST_TO_NET_U32(header->request),
149 HOST_TO_NET_U32(header->expected_revision), HOST_TO_NET_U32(header->revision),
150 HOST_TO_NET_U32(header->status)};
151 memcpy(buffer, fields, sizeof(fields));
152 memcpy(buffer + sizeof(fields), json, size);
153 *length = sizeof(fields) + size;
154 return ASCIICHAT_OK;
155}
156
157asciichat_error_t acip_settings_request(acip_render_settings_t *state, const void *payload, size_t length,
158 uint8_t *reply, size_t capacity, size_t *reply_length) {
159 acip_settings_header_t header = {0};
160 const char *json = NULL;
161 size_t json_length = 0;
162 asciichat_error_t result = acip_settings_decode(payload, length, &header, &json, &json_length);
163 if (result != ASCIICHAT_OK)
164 return result;
166 options_t *current = SAFE_MALLOC(sizeof(*current), options_t *),
167 *candidate = SAFE_MALLOC(sizeof(*candidate), options_t *);
168 acip_render_settings_options(state, current);
169 if (header.status)
170 result = SET_ERRNO(ERROR_INVALID_PARAM, "Unexpected settings request status");
171 else if (header.expected_revision != UINT32_MAX && header.expected_revision != state->revision)
172 result = SET_ERRNO(ERROR_INVALID_STATE, "Remote settings changed; refresh and retry");
173 else if (json_length) {
174 result = options_prepare_json(current, json, json_length, candidate, true);
175 if (result == ASCIICHAT_OK) {
176 // AUTO is a local option sentinel and is not valid in the negotiated
177 // render state. Keep legacy settings updates from invalidating a live
178 // session when they serialize the raw AUTO option.
179 candidate->color_mode = acip_render_settings_normalize_color_mode(candidate->color_mode);
180 result = prepare_for_viewer(state, candidate);
181 }
182 }
183 header.status = result;
184 header.revision = state->revision;
185 // The rejection is represented by the response status, preserving older errors.
187 acip_render_settings_options(state, current);
188 char *effective = options_values_json(current, true);
189 asciichat_error_t encoded =
190 effective ? acip_settings_encode(&header, effective, reply, capacity, reply_length) : ERROR_MEMORY;
191 SAFE_FREE(effective);
192 SAFE_FREE(candidate);
193 SAFE_FREE(current);
194 return encoded;
195}
196
198 uint32_t expected_revision) {
199 char *json = options_values_json(values, true);
200 if (!json)
201 return SET_ERRNO(ERROR_MEMORY, "Cannot encode render preferences");
203 size_t length = 0;
204 acip_settings_header_t header = {
205 .schema = ACIP_SETTINGS_SCHEMA, .request = request, .expected_revision = expected_revision};
206 asciichat_error_t result = acip_settings_encode(&header, json, buffer, sizeof(buffer), &length);
207 if (result == ASCIICHAT_OK)
208 result = packet_send_via_transport(transport, PACKET_TYPE_ACIP_SETTINGS_SYNC, buffer, length, 0);
209 SAFE_FREE(json);
210 return result;
211}
void errno_scope_end(asciichat_errno_scope_t scope, asciichat_errno_outcome_t outcome)
asciichat_errno_scope_t errno_scope_begin(void)
#define SET_ERRNO(code,...)
@ ASCIICHAT_ERRNO_HANDLED
⚙️ Common definitions, error codes, macros, and types shared throughout the application
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U32(val)
Definition endian.h:66
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
unsigned short uint16_t
Definition common.h:61
unsigned int uint32_t
Definition common.h:62
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
@ ERROR_NOT_SUPPORTED
Definition error_codes.h:69
@ PACKET_TYPE_ACIP_SETTINGS_SYNC
Settings sync (Initiator -> Host -> All Participants).
Definition packet.h:485
struct options_state options_t
Immutable, RCU-published snapshot of the application's runtime configuration.
options_t options_t_new(void)
Initialize options by parsing command-line arguments.
Definition options.c:547
@ ASCII_RENDERER_GLYPH
Definition options.h:966
bool palette_requires_utf8_encoding(const char *chars, size_t len)
Check if palette requires UTF-8 encoding.
Definition palette.c:61
palette_type_t select_compatible_palette(palette_type_t requested, bool client_utf8)
Select compatible palette based on client capabilities.
Definition palette.c:225
int initialize_client_palette(palette_type_t palette_type, const char *custom_chars, char client_palette_chars[ASCII_PALETTE_BUFFER_SIZE], size_t *client_palette_len, char client_luminance_palette[ASCII_LUMINANCE_LEVELS])
Initialize client palette with full configuration.
Definition palette.c:306
@ PALETTE_COUNT
Number of palette types (not a valid palette).
Definition palette.h:106
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:104
@ PALETTE_STANDARD
Standard ASCII palette: " ...',;:clodxkO0KXNWM".
Definition palette.h:94
@ PALETTE_UNSET
Unset palette type (will be resolved to default or custom).
Definition palette.h:92
struct acip_transport acip_transport_t
Forward declaration of ACIP transport.
Definition host.h:506
asciichat_error_t acip_settings_send(acip_transport_t *transport, const options_t *values, uint32_t request, uint32_t expected_revision)
asciichat_error_t acip_render_settings_capabilities(acip_render_settings_t *state, const terminal_capabilities_packet_t *packet)
asciichat_error_t acip_settings_encode(const acip_settings_header_t *header, const char *json, uint8_t *buffer, size_t capacity, size_t *length)
asciichat_error_t acip_render_settings_prepare(acip_render_settings_t *candidate, const options_t *values)
terminal_color_mode_t acip_render_settings_normalize_color_mode(terminal_color_mode_t mode)
asciichat_error_t acip_settings_request(acip_render_settings_t *state, const void *payload, size_t length, uint8_t *reply, size_t capacity, size_t *reply_length)
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)
bool ascii_glyph_palette_supported(const char *palette)
ASCII Palette Management for Video-to-ASCII Conversion.
char * options_values_json(const options_t *values, bool remote_only)
Definition rcu.c:1013
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 packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Definition send.c:190
ACIP shared/bidirectional packet sending functions.
🔤 String Manipulation and Shell Escaping Utilities
acip render settings keeps the values that move through the subsystem: caps, width,...
acip settings header keeps the values that move through the subsystem: schema, request,...
When code needs to work with asciichat errno snapshot, it keeps total, copied here instead of scatter...
terminal_color_mode_t color_mode
Color mode (auto/none/16/256/truecolor).
Definition options.h:1092
bool palette_explicit
Palette explicitly selected.
Definition options.h:1232
int height
Terminal height in characters (int for OPTION_TYPE_INT compat).
Definition options.h:997
bool matrix_rain
Matrix digital rain effect (false = disabled).
Definition options.h:1152
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
bool stabilize
Reduce frame-to-frame character flicker.
Definition options.h:1138
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
ascii_renderer_t renderer
Character selection algorithm.
Definition options.h:1140
int width
Terminal width in characters (int for OPTION_TYPE_INT compat).
Definition options.h:996
palette_type_t palette_type
Selected palette type.
Definition options.h:1229
Terminal capabilities packet structure (Packet Type 5).
Definition packet.h:1074
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
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
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM).
Definition packet.h:1098
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