ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
session_help.c
Go to the documentation of this file.
1
2/* Storage capacities in elements; text buffers include the terminating NUL. */
3#define TUI_SESSION_HELP_SETTINGS_COUNT 20
4#define TUI_SESSION_HELP_VOLUME_BAR_BUFFER_SIZE 32
5#define TUI_SESSION_HELP_KEY_STORAGE_COUNT 40
6#define TUI_SESSION_HELP_FOOTER_BUFFER_SIZE 128
7
8/* Existing buffer budgets in bytes, including NUL for text storage. */
9#define SESSION_HELP_SETTING_BUFFER_SIZE 256
10
11/* Existing buffer budgets in bytes, including NUL for text storage. */
12#define SESSION_HELP_KEY_BUFFER_SIZE 48
13
14/* Existing buffer budgets in bytes, including NUL for text storage. */
15#define SESSION_HELP_ENTRY_CAPACITY 40
16
20#include <ascii-chat/common.h>
22#include <ascii-chat/log/log.h>
27#include <stdio.h>
28#include <string.h>
29/* Color codes for enabled/disabled settings */
30#define ENABLED_COLOR LOG_COLOR_INFO /* Green */
31#define DISABLED_COLOR LOG_COLOR_ERROR /* Red */
32
33void ui_session_help_free(char *text) {
34 SAFE_FREE(text);
35}
36
37/* ============================================================================
38 * Keyboard Help Rendering
39 * ============================================================================ */
40
50static void format_volume_bar(double volume, char *bar_output, size_t bar_output_size) {
51 if (!bar_output || bar_output_size < 25) {
52 return;
53 }
54
55 // Clamp volume to [0.0, 1.0]
56 if (volume < 0.0) {
57 volume = 0.0;
58 }
59 if (volume > 1.0) {
60 volume = 1.0;
61 }
62
63 // Calculate filled blocks (10 blocks total for 10% granularity)
64 int filled = (int)(volume * 10.0);
65 int empty = 10 - filled;
66
67 // Build bar: "[====== ] 80%"
68 // Use simple ASCII characters to avoid UTF-8 encoding issues
69 snprintf(bar_output, bar_output_size, "[%.*s%.*s] %d%%", filled, "==========", empty, " ",
70 (int)(volume * 100.0));
71}
72
76static const char *color_mode_to_string(int mode) {
77 switch (mode) {
78 case -1:
79 return "Auto";
80 case 0:
81 return "Mono";
82 case 1:
83 return "16-color";
84 case 2:
85 return "256-color";
86 case 3:
87 return "Truecolor";
88 default:
89 return "Unknown";
90 }
91}
92
96static const char *render_mode_to_string(int mode) {
97 switch (mode) {
98 case 0:
99 return "Foreground";
100 case 1:
101 return "Background";
102 case 2:
103 return "Half-block";
104 default:
105 return "Unknown";
106 }
107}
108
112static const char *color_filter_to_string(color_filter_t filter) {
113 if (filter == COLOR_FILTER_NONE) {
114 return "None";
115 }
117 return def ? def->name : "Unknown";
118}
119
120static const char *status_indicator(bool enabled) {
122}
123
124char *ui_session_help_render(terminal_size_t size, bool has_media, bool browser, terminal_size_t *minimum) {
125 if (!minimum) {
126 SET_ERRNO(ERROR_INVALID_PARAM, "Help minimum geometry output is required");
127 return NULL;
128 }
129 const char *navigation[] = {
130 "Navigation & Control:",
131 "─────────────────────",
132 "? Toggle this help screen TUI",
133 "q / Esc Quit ascii-chat (Shift+Key to panic exit)",
134 browser ? NULL : "v Open from clipboard",
135 browser ? NULL : "Paste Queue dropped/pasted media",
136 browser ? NULL : "< / > Previous/next media (restart)",
137 has_media ? "Space Play/Pause (files only)" : NULL,
138 has_media ? "[ Toggle Media Playback" : NULL,
139 has_media ? (terminal_should_use_utf8(GET_OPTION(force_utf8)) ? "← / → Seek backward/forward 30s"
140 : "Left / Right Seek backward/forward 30s")
141 : NULL,
142 "m / M Mute/Unmute audio",
143 terminal_should_use_utf8(GET_OPTION(force_utf8)) ? "↑ / ↓ Volume up/down (10%)"
144 : "Up / Down Volume up/down (10%)",
145 "c / C Cycle color mode",
146 "f / F Cycle color filter",
147 "x / X Flip webcam horizontally",
148 "y / Y Flip webcam vertically",
149 "w / W Toggle Face Follow",
150 "r / R Cycle render mode",
151 "- FPS counter",
152#if defined(ASCIICHAT_DIAGNOSTIC_UI)
153 browser ? NULL : "` Error stacks diagnostic TUI",
154#endif
155 browser ? NULL : "o TUI to open media from path or url",
156 "] Live log viewer/searcher TUI",
157 "F12 Version update checker TUI",
158 browser ? NULL : "= Running app statistics TUI",
159#ifndef NDEBUG
160 browser ? NULL : "0 C code sync primitives TUI",
161#endif
162 };
163 char settings[TUI_SESSION_HELP_SETTINGS_COUNT][SESSION_HELP_SETTING_BUFFER_SIZE] = {"Current Settings:", ""};
165 format_volume_bar(GET_OPTION(speakers_volume), volume_bar, sizeof(volume_bar));
166 snprintf(settings[2], sizeof(settings[2]), "Audio : %s", status_indicator(GET_OPTION(audio_enabled)));
167 snprintf(settings[3], sizeof(settings[3]), "Volume : %s", volume_bar);
168 snprintf(settings[4], sizeof(settings[4]), "Color : %s", color_mode_to_string(GET_OPTION(color_mode)));
169 snprintf(settings[5], sizeof(settings[5]), "Filter : %s", color_filter_to_string(GET_OPTION(color_filter)));
170 snprintf(settings[6], sizeof(settings[6]), "Render : %s", render_mode_to_string(GET_OPTION(render_mode)));
171 snprintf(settings[7], sizeof(settings[7]), "Flip : rows=%s cols=%s", status_indicator(GET_OPTION(flip_y)),
172 status_indicator(GET_OPTION(flip_x)));
173 snprintf(settings[8], sizeof(settings[8]), "Face Follow : %s", status_indicator(GET_OPTION(face_follow)));
174 int settings_count = 9;
175 snprintf(settings[settings_count++], sizeof(settings[0]), "Animations (number key toggle):");
176 snprintf(settings[settings_count++], sizeof(settings[0]), "-------------------------------");
177 snprintf(settings[settings_count++], sizeof(settings[0]), "(1) Matrix \"Digital Rain\" : %s",
178 status_indicator(GET_OPTION(matrix_rain)));
179 snprintf(settings[settings_count++], sizeof(settings[0]), "(2) Audio Waveform : %s",
180 status_indicator(GET_OPTION(waveform)));
181 snprintf(settings[settings_count++], sizeof(settings[0]), "(3) Audio Frequencies (FFT) : %s",
182 status_indicator(GET_OPTION(fft)));
183 snprintf(settings[settings_count++], sizeof(settings[0]), "(z) Zalgo intensity : %d", GET_OPTION(zalgo));
184 snprintf(settings[settings_count++], sizeof(settings[0]), "(-) FPS Counter : %s",
185 status_indicator(GET_OPTION(fps_counter)));
186 bool server = !browser && (GET_OPTION(detected_mode) == MODE_SERVER || GET_OPTION(detected_mode) == MODE_MCP_SERVER);
187 bool discovery = !browser && GET_OPTION(detected_mode) == MODE_DISCOVERY_SERVICE;
190 size_t count = 0;
191 for (size_t i = 0; i < sizeof(navigation) / sizeof(navigation[0]); ++i) {
192 const char *line = navigation[i];
193 if (!line || i == 1)
194 continue;
195 if (line[0] == '?' || line[0] == ']' || line[0] == '=' || line[0] == '0' || line[0] == '`' || line[0] == 'o' ||
196 !strncmp(line, "F12", 3))
197 continue;
198 if ((server || discovery) && i > 1 && line[0] != '?' && strncmp(line, "Esc", 3) && line[0] != ']' &&
199 line[0] != '=' && line[0] != '0' && line[0] != '`' && strncmp(line, "F12", 3))
200 continue;
201 const char *end = strstr(line, " ");
202 if (end) {
203 size_t length = (size_t)(end - line);
204 if (length >= sizeof(key_storage[0]))
205 length = sizeof(key_storage[0]) - 1;
206 memcpy(key_storage[count], line, length);
207 key_storage[count][length] = '\0';
208 while (*end == ' ')
209 ++end;
210 entries[count] = (ui_help_entry_t){.keys = key_storage[count], .description = end};
211 } else
212 entries[count] = (ui_help_entry_t){.description = line};
213 ++count;
214 }
215 count += tui_shortcut_help(entries + count, 40 - count, server || discovery, browser);
216 if (server || discovery) {
217 settings_count = 4;
218 snprintf(settings[0], sizeof(settings[0]), "%s", discovery ? "Discovery service:" : "Server:");
219 snprintf(settings[1], sizeof(settings[1]), "Port : %d", GET_OPTION(port));
220 snprintf(settings[2], sizeof(settings[2]), "Status screen : %s", status_indicator(GET_OPTION(status_screen)));
221 snprintf(settings[3], sizeof(settings[3]), "Log level : %d", GET_OPTION(log_level));
222 }
223 const char *settings_text[TUI_SESSION_HELP_SETTINGS_COUNT];
224 for (int i = 0; i < settings_count; ++i)
225 settings_text[i] = settings[i];
227 snprintf(footer, sizeof(footer), "Press %s to close", colored_string(LOG_COLOR_WARN, "?"));
228 ui_help_menu_t menu = {.title = discovery ? "ascii-chat Discovery Service Keyboard Shortcuts"
229 : server ? "ascii-chat Server Keyboard Shortcuts"
230 : "ascii-chat Keyboard Shortcuts",
231 .entries = entries,
232 .count = count,
233 .settings = settings_text,
234 .settings_count = (size_t)settings_count,
235 .footer = footer,
236 // Browser sessions draw a four-row animated logo above the popup.
237 .top_margin = browser ? 6 : 0};
238 *minimum = ui_help_menu_measure(&menu);
239 frame_buffer_t *output = frame_buffer_create(size.rows, size.cols);
240 if (!output)
241 return NULL;
242 ui_help_menu_render(output, size, &menu);
243 size_t length = frame_buffer_get_length(output);
244 char *result = SAFE_MALLOC(length + 1, char *);
245 memcpy(result, frame_buffer_get_content(output), length);
246 result[length] = '\0';
247 frame_buffer_destroy(output);
248 return result;
249}
250
251char *ui_session_context_help_render(terminal_size_t size, ui_screen_t screen, terminal_size_t *minimum) {
252 if (!minimum) {
253 SET_ERRNO(ERROR_INVALID_PARAM, "Help minimum geometry output is required");
254 return NULL;
255 }
256 static const ui_help_entry_t sync[] = {{"0 / Esc", "Close sync viewer"},
257 {"Up / Down", "Select primitive"},
258 {"Left / Right", "Previous/next page"},
259 {"Home / End", "First/last primitive"},
260 {"=", "Running app statistics"},
261 {"]", "Log viewer"},
262 {"?", "Close this help"}};
263 static const ui_help_entry_t stats[] = {{"= / Esc", "Close statistics"},
264 {"Tab / Left / Right", "Switch data page"},
265 {"Up / Down", "Select and scroll rows"},
266 {"Home / End", "First/last row"},
267 {"0", "Sync primitives"},
268 {"]", "Log viewer"},
269 {"?", "Close this help"}};
270 static const ui_help_entry_t errors[] = {
271 {"` / Esc", "Close error stacks"}, {"Tab", "Current/history page"}, {"Up / Down", "Select and scroll errors"},
272 {"s", "Change sort order"}, {"x", "Clear selected error"}, {"=", "Running app statistics"},
273 {"0", "Sync primitives"}, {"?", "Close this help"}};
274 static const ui_help_entry_t update[] = {{"F12 / Esc", "Close update checker"},
275 {"Left / Right / Tab", "Select button"},
276 {"Enter", "Activate selected button"},
277 {"O", "Open release page"},
278 {"R", "Check again"},
279 {"?", "Close this help"}};
280 static const ui_help_entry_t lan[] = {{"Up / Down / j / k", "Select LAN server"},
281 {"0-9", "Enter server number"},
282 {"Enter", "Connect to selected server"},
283 {"Esc", "Cancel LAN selection"},
284 {"=", "Running app statistics"},
285 {"0", "Sync primitives"},
286 {"?", "Close this help"}};
287 static const ui_help_entry_t media[] = {
288 {"Enter", "Play the entered paths or URLs"}, {"Esc / Ctrl-O", "Cancel media input"},
289 {"Left / Right", "Move the text cursor"}, {"Home / End", "Beginning/end of input"},
290 {"Backspace / Delete", "Delete a character"}, {"?", "Help when the input is empty"}};
291 const ui_help_entry_t *entries = NULL;
292 size_t count = 0;
293 const char *title = NULL;
294 switch (screen) {
295 case UI_SCREEN_ERROR: {
296 static const ui_help_entry_t popup[] = {{"Left / Right", "Previous / next error"},
297 {"Home / End", "First / last error"},
298 {"Up / Down", "Scroll error details"},
299 {"PgUp / PgDn", "Scroll a page"},
300 {"Enter / Esc", "Dismiss all errors"},
301 {"q", "Dismiss all errors"},
302 {"?", "Close this help"}};
303 entries = popup;
304 count = sizeof(popup) / sizeof(*popup);
305 title = "Error Popup Keyboard Shortcuts";
306 break;
307 }
308 case UI_SCREEN_SYNC:
309 entries = sync;
310 count = sizeof(sync) / sizeof(*sync);
311 title = "Sync Viewer Keyboard Shortcuts";
312 break;
313 case UI_SCREEN_STATS:
314 entries = stats;
315 count = sizeof(stats) / sizeof(*stats);
316 title = "Statistics Keyboard Shortcuts";
317 break;
318 case UI_SCREEN_ERRNO:
319 entries = errors;
320 count = sizeof(errors) / sizeof(*errors);
321 title = "Error Stacks Keyboard Shortcuts";
322 break;
323 case UI_SCREEN_PROMPT:
324 entries = media;
325 count = sizeof(media) / sizeof(*media);
326 title = "Media Input Keyboard Shortcuts";
327 break;
328 case UI_SCREEN_MDNS:
329 entries = lan;
330 count = sizeof(lan) / sizeof(*lan);
331 title = "LAN Selection Keyboard Shortcuts";
332 break;
333 case UI_SCREEN_UPDATE:
334 entries = update;
335 count = sizeof(update) / sizeof(*update);
336 title = "Update Checker Keyboard Shortcuts";
337 break;
338 default:
339 return ui_session_help_render(size, GET_OPTION(media_file)[0] || GET_OPTION(media_url)[0], false, minimum);
340 }
342 snprintf(footer, sizeof(footer), "Press %s to close", colored_string(LOG_COLOR_WARN, "?"));
343 ui_help_menu_t menu = {.title = title, .entries = entries, .count = count, .footer = footer, .top_margin = 0};
344 *minimum = ui_help_menu_measure(&menu);
345 frame_buffer_t *buffer = frame_buffer_create(size.rows, size.cols);
346 if (!buffer)
347 return NULL;
348 ui_help_menu_render(buffer, size, &menu);
349 size_t length = frame_buffer_get_length(buffer);
350 char *result = SAFE_MALLOC(length + 1, char *);
351 memcpy(result, frame_buffer_get_content(buffer), length);
352 result[length] = '\0';
353 frame_buffer_destroy(buffer);
354 return result;
355}
#define SET_ERRNO(code,...)
⚙️ Common definitions, error codes, macros, and types shared throughout the application
int color_mode
Definition fft.c:25
void frame_buffer_destroy(frame_buffer_t *buf)
Destroy a frame buffer.
size_t frame_buffer_get_length(const frame_buffer_t *buf)
Get the current content length of the frame buffer.
const char * frame_buffer_get_content(const frame_buffer_t *buf)
Get a pointer to the buffer content.
frame_buffer_t * frame_buffer_create(int rows, int cols)
Create a new frame buffer.
struct frame_buffer frame_buffer_t
Opaque frame buffer handle.
bool enabled
Is filtering active?
Definition grep.c:62
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
@ ERROR_INVALID_PARAM
@ LOG_COLOR_ERROR
Definition log/log.h:152
@ LOG_COLOR_INFO
Definition log/log.h:150
@ LOG_COLOR_WARN
Definition log/log.h:151
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
@ MODE_MCP_SERVER
Model Context Protocol rendering server.
Definition options.h:925
@ MODE_DISCOVERY_SERVICE
Discovery server mode - session management and WebRTC signaling.
Definition options.h:923
@ MODE_SERVER
Server mode - network server options.
Definition options.h:920
const char * colored_string(log_color_t color, const char *text)
Build a colored string for terminal output.
Definition string.c:284
const color_filter_def_t * color_filter_get_metadata(color_filter_t filter)
Get color filter metadata by enum value.
terminal_size_t ui_help_menu_measure(const ui_help_menu_t *m)
Definition help_menu.c:85
bool ui_help_menu_render(frame_buffer_t *out, terminal_size_t size, const ui_help_menu_t *m)
Definition help_menu.c:101
🖥️ Display Width and Terminal Centering Utilities
📝 Logging API with multiple log levels and terminal output control
const char * color_mode_to_string(terminal_color_mode_t mode)
Convert color mode enum to string.
const char * render_mode_to_string(terminal_render_mode_t mode)
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
void ui_session_help_free(char *text)
#define TUI_SESSION_HELP_KEY_STORAGE_COUNT
Definition session_help.c:5
#define SESSION_HELP_ENTRY_CAPACITY
char * ui_session_context_help_render(terminal_size_t size, ui_screen_t screen, terminal_size_t *minimum)
char * ui_session_help_render(terminal_size_t size, bool has_media, bool browser, terminal_size_t *minimum)
#define TUI_SESSION_HELP_VOLUME_BAR_BUFFER_SIZE
Definition session_help.c:4
#define SESSION_HELP_KEY_BUFFER_SIZE
#define SESSION_HELP_SETTING_BUFFER_SIZE
Definition session_help.c:9
#define TUI_SESSION_HELP_FOOTER_BUFFER_SIZE
Definition session_help.c:6
#define TUI_SESSION_HELP_SETTINGS_COUNT
Definition session_help.c:3
size_t tui_shortcut_help(ui_help_entry_t *entries, size_t capacity, bool server, bool browser)
Definition shortcuts.c:39
🔤 String Manipulation and Shell Escaping Utilities
For color filter def, this is the live record that ties name, cli_name, r, g together....
const char * name
Human-readable filter name (e.g., "matrix-green").
Definition help_menu.h:3
ui help entry keeps the values that move through the subsystem: keys, description....
Definition help_menu.h:12
ui_screen_t
@ UI_SCREEN_PROMPT
@ UI_SCREEN_ERRNO
@ UI_SCREEN_STATS
@ UI_SCREEN_UPDATE
@ UI_SCREEN_SYNC
@ UI_SCREEN_MDNS
@ UI_SCREEN_ERROR
🔤 UTF-8 Encoding and Decoding Utilities