ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
help_screen.c File Reference

Help screen TUI rendering implementation. More...

Go to the source code of this file.

Macros

#define ENABLED_COLOR   LOG_COLOR_INFO /* Green */
 
#define DISABLED_COLOR   LOG_COLOR_ERROR /* Red */
 
#define APPEND(fmt, ...)
 

Functions

void session_display_render_help (session_display_ctx_t *ctx)
 Render help screen centered on terminal.
 

Detailed Description

Help screen TUI rendering implementation.

Definition in file help_screen.c.

Macro Definition Documentation

◆ APPEND

#define APPEND (   fmt,
  ... 
)
Value:
do { \
int written = snprintf(buffer + buf_pos, BUFFER_SIZE - buf_pos, fmt, ##__VA_ARGS__); \
if (written > 0) { \
buf_pos += written; \
} \
} while (0)

◆ DISABLED_COLOR

#define DISABLED_COLOR   LOG_COLOR_ERROR /* Red */

Definition at line 22 of file help_screen.c.

◆ ENABLED_COLOR

#define ENABLED_COLOR   LOG_COLOR_INFO /* Green */

Definition at line 21 of file help_screen.c.

Function Documentation

◆ session_display_render_help()

void session_display_render_help ( session_display_ctx_t ctx)

Render help screen centered on terminal.

Definition at line 234 of file help_screen.c.

234 {
235 if (!ctx) {
236 return;
237 }
238
239 // Get terminal dimensions
240 int term_width = (int)GET_OPTION(width);
241 int term_height = (int)GET_OPTION(height);
242
243 // Minimum viable terminal size
244 if (term_width < 50 || term_height < 20) {
245 // Terminal too small - just show a minimal message
246 const char *msg = "\n[Terminal too small for help screen - try resizing]\n";
247 session_display_write_raw(ctx, msg, strlen(msg));
248 return;
249 }
250
251 // Help screen box dimensions (24 rows total: border + title + nav (7 lines) + separator + settings + blank + footer +
252 // border)
253 const int box_width = 48; // Display columns
254 const int box_height = 24; // Total rows including borders
255
256 // Calculate centering position
257 // Horizontal centering
258 int start_col = (term_width - box_width) / 2;
259 if (start_col < 0) {
260 start_col = 0;
261 }
262
263 // Vertical centering
264 int start_row = (term_height - box_height) / 2;
265 if (start_row < 1) {
266 start_row = 1; // Never put at top of screen (leave room for prompts)
267 }
268
269 // Build help screen content
270 const size_t BUFFER_SIZE = 8192; // Increased from 4096 to ensure all content fits
271 char *buffer = SAFE_MALLOC(BUFFER_SIZE, char *);
272 size_t buf_pos = 0;
273
274#define APPEND(fmt, ...) \
275 do { \
276 int written = snprintf(buffer + buf_pos, BUFFER_SIZE - buf_pos, fmt, ##__VA_ARGS__); \
277 if (written > 0) { \
278 buf_pos += written; \
279 } \
280 } while (0)
281
282 // Clear screen and position cursor
283 APPEND("\033[2J"); // Clear screen
284 APPEND("\033[H"); // Cursor to home
285
286 // Build help screen with proper spacing using UTF-8 width-aware padding
287 char line_buf[256];
288
289 // Top border
290 APPEND("\033[%d;%dH", start_row + 1, start_col + 1);
291 APPEND("╔══════════════════════════════════════════════╗");
292
293 // Title
294 APPEND("\033[%d;%dH", start_row + 2, start_col + 1);
295 build_help_line(line_buf, sizeof(line_buf), "ascii-chat Keyboard Shortcuts");
296 APPEND("%s", line_buf);
297
298 // Separator after title
299 APPEND("\033[%d;%dH", start_row + 3, start_col + 1);
300 APPEND("╠══════════════════════════════════════════════╣");
301
302 // Navigation section
303 APPEND("\033[%d;%dH", start_row + 4, start_col + 1);
304 build_help_line(line_buf, sizeof(line_buf), "Navigation & Control:");
305 APPEND("%s", line_buf);
306
307 APPEND("\033[%d;%dH", start_row + 5, start_col + 1);
308 build_help_line(line_buf, sizeof(line_buf), "─────────────────────");
309 APPEND("%s", line_buf);
310
311 APPEND("\033[%d;%dH", start_row + 6, start_col + 1);
312 build_help_line(line_buf, sizeof(line_buf), "? Toggle this help screen");
313 APPEND("%s", line_buf);
314
315 APPEND("\033[%d;%dH", start_row + 7, start_col + 1);
316 build_help_line(line_buf, sizeof(line_buf), "Space Play/Pause (files only)");
317 APPEND("%s", line_buf);
318
319 APPEND("\033[%d;%dH", start_row + 8, start_col + 1);
320 build_help_line(line_buf, sizeof(line_buf), "← / → Seek backward/forward 30s");
321 APPEND("%s", line_buf);
322
323 APPEND("\033[%d;%dH", start_row + 9, start_col + 1);
324 build_help_line(line_buf, sizeof(line_buf), "m Mute/Unmute audio");
325 APPEND("%s", line_buf);
326
327 APPEND("\033[%d;%dH", start_row + 10, start_col + 1);
328 build_help_line(line_buf, sizeof(line_buf), "↑ / ↓ Volume up/down (10%)");
329 APPEND("%s", line_buf);
330
331 APPEND("\033[%d;%dH", start_row + 11, start_col + 1);
332 build_help_line(line_buf, sizeof(line_buf), "c Cycle color mode");
333 APPEND("%s", line_buf);
334
335 APPEND("\033[%d;%dH", start_row + 12, start_col + 1);
336 build_help_line(line_buf, sizeof(line_buf), "f Flip webcam horizontally");
337 APPEND("%s", line_buf);
338
339 APPEND("\033[%d;%dH", start_row + 13, start_col + 1);
340 build_help_line(line_buf, sizeof(line_buf), "r Cycle render mode");
341 APPEND("%s", line_buf);
342
343#ifndef NDEBUG
344 APPEND("\033[%d;%dH", start_row + 14, start_col + 1);
345 build_help_line(line_buf, sizeof(line_buf), "Ctrl+L Print held lock state");
346 APPEND("%s", line_buf);
347
348 // Blank line before settings section
349 APPEND("\033[%d;%dH", start_row + 15, start_col + 1);
350 build_help_line(line_buf, sizeof(line_buf), "");
351 APPEND("%s", line_buf);
352
353 // Current settings section (adjusted row numbers for Ctrl+L line)
354 APPEND("\033[%d;%dH", start_row + 16, start_col + 1);
355 build_help_line(line_buf, sizeof(line_buf), "Current Settings:");
356 APPEND("%s", line_buf);
357
358 APPEND("\033[%d;%dH", start_row + 17, start_col + 1);
359 build_help_line(line_buf, sizeof(line_buf), "───────────────");
360 APPEND("%s", line_buf);
361#else
362 // Blank line before settings section
363 APPEND("\033[%d;%dH", start_row + 14, start_col + 1);
364 build_help_line(line_buf, sizeof(line_buf), "");
365 APPEND("%s", line_buf);
366
367 // Current settings section
368 APPEND("\033[%d;%dH", start_row + 15, start_col + 1);
369 build_help_line(line_buf, sizeof(line_buf), "Current Settings:");
370 APPEND("%s", line_buf);
371
372 APPEND("\033[%d;%dH", start_row + 16, start_col + 1);
373 build_help_line(line_buf, sizeof(line_buf), "───────────────");
374 APPEND("%s", line_buf);
375#endif
376
377 // Current settings section
378 APPEND("\033[%d;%dH", start_row + 15, start_col + 1);
379 build_help_line(line_buf, sizeof(line_buf), "Current Settings:");
380 APPEND("%s", line_buf);
381
382 APPEND("\033[%d;%dH", start_row + 16, start_col + 1);
383 build_help_line(line_buf, sizeof(line_buf), "───────────────");
384 APPEND("%s", line_buf);
385
386 // Get current option values
387 double current_volume = GET_OPTION(speakers_volume);
388 int current_color_mode = (int)GET_OPTION(color_mode);
389 int current_render_mode = (int)GET_OPTION(render_mode);
390 bool flip_x = (bool)GET_OPTION(flip_x);
391 bool flip_y = (bool)GET_OPTION(flip_y);
392 bool current_audio = (bool)GET_OPTION(audio_enabled);
393
394 // Format volume bar as "[======== ] 80%"
395 char volume_bar[32];
396 format_volume_bar(current_volume, volume_bar, sizeof(volume_bar));
397
398 // Get string values
399 const char *color_str = color_mode_to_string(current_color_mode);
400 const char *render_str = render_mode_to_string(current_render_mode);
401
402 // Create colored strings for flip state
403 const char *flip_text = "None";
404 if (flip_x && flip_y) {
405 flip_text = colored_string(ENABLED_COLOR, "X & Y");
406 } else if (flip_x) {
407 flip_text = colored_string(ENABLED_COLOR, "X");
408 } else if (flip_y) {
409 flip_text = colored_string(ENABLED_COLOR, "Y");
410 } else {
411 flip_text = colored_string(DISABLED_COLOR, "None");
412 }
413
414 // Audio state (Enabled = green, Disabled = red)
415 const char *audio_text =
416 current_audio ? colored_string(ENABLED_COLOR, "Enabled") : colored_string(DISABLED_COLOR, "Disabled");
417
418 // Build settings lines with UTF-8 width-aware padding (ordered to match keybinds: m, ↑/↓, c, r, f)
419#ifndef NDEBUG
420 APPEND("\033[%d;%dH", start_row + 18, start_col + 1);
421 build_settings_line(line_buf, sizeof(line_buf), "Audio", audio_text);
422 APPEND("%s", line_buf);
423
424 APPEND("\033[%d;%dH", start_row + 19, start_col + 1);
425 build_settings_line(line_buf, sizeof(line_buf), "Volume", volume_bar);
426 APPEND("%s", line_buf);
427
428 APPEND("\033[%d;%dH", start_row + 20, start_col + 1);
429 build_settings_line(line_buf, sizeof(line_buf), "Color", color_str);
430 APPEND("%s", line_buf);
431
432 APPEND("\033[%d;%dH", start_row + 21, start_col + 1);
433 build_settings_line(line_buf, sizeof(line_buf), "Render", render_str);
434 APPEND("%s", line_buf);
435
436 APPEND("\033[%d;%dH", start_row + 22, start_col + 1);
437 build_settings_line(line_buf, sizeof(line_buf), "Flip", flip_text);
438 APPEND("%s", line_buf);
439
440 // Blank line before footer
441 APPEND("\033[%d;%dH", start_row + 23, start_col + 1);
442 build_help_line(line_buf, sizeof(line_buf), "");
443 APPEND("%s", line_buf);
444
445 // Footer
446 APPEND("\033[%d;%dH", start_row + 24, start_col + 1);
447 build_help_line(line_buf, sizeof(line_buf), "Press ? to close");
448 APPEND("%s", line_buf);
449
450 // Bottom border
451 APPEND("\033[%d;%dH", start_row + 25, start_col + 1);
452 APPEND("╚══════════════════════════════════════════════╝");
453#else
454 APPEND("\033[%d;%dH", start_row + 17, start_col + 1);
455 build_settings_line(line_buf, sizeof(line_buf), "Audio", audio_text);
456 APPEND("%s", line_buf);
457
458 APPEND("\033[%d;%dH", start_row + 18, start_col + 1);
459 build_settings_line(line_buf, sizeof(line_buf), "Volume", volume_bar);
460 APPEND("%s", line_buf);
461
462 APPEND("\033[%d;%dH", start_row + 19, start_col + 1);
463 build_settings_line(line_buf, sizeof(line_buf), "Color", color_str);
464 APPEND("%s", line_buf);
465
466 APPEND("\033[%d;%dH", start_row + 20, start_col + 1);
467 build_settings_line(line_buf, sizeof(line_buf), "Render", render_str);
468 APPEND("%s", line_buf);
469
470 APPEND("\033[%d;%dH", start_row + 21, start_col + 1);
471 build_settings_line(line_buf, sizeof(line_buf), "Flip", flip_text);
472 APPEND("%s", line_buf);
473
474 // Blank line before footer
475 APPEND("\033[%d;%dH", start_row + 22, start_col + 1);
476 build_help_line(line_buf, sizeof(line_buf), "");
477 APPEND("%s", line_buf);
478
479 // Footer
480 APPEND("\033[%d;%dH", start_row + 23, start_col + 1);
481 build_help_line(line_buf, sizeof(line_buf), "Press ? to close");
482 APPEND("%s", line_buf);
483
484 // Bottom border
485 APPEND("\033[%d;%dH", start_row + 24, start_col + 1);
486 APPEND("╚══════════════════════════════════════════════╝");
487#endif
488
489 // Cursor positioning after rendering
490#ifndef NDEBUG
491 APPEND("\033[%d;%dH", start_row + 26, start_col + 1);
492#else
493 APPEND("\033[%d;%dH", start_row + 25, start_col + 1);
494#endif
495
496#undef APPEND
497
498 // Write buffer to terminal
499 session_display_write_raw(ctx, buffer, buf_pos);
500
501 // Flush output
502 if (ctx && session_display_has_tty(ctx)) {
503 int tty_fd = session_display_get_tty_fd(ctx);
504 if (tty_fd >= 0) {
505 (void)terminal_flush(tty_fd);
506 }
507 }
508
509 SAFE_FREE(buffer);
510}
#define ENABLED_COLOR
Definition help_screen.c:21
#define DISABLED_COLOR
Definition help_screen.c:22
#define APPEND(fmt,...)
void session_display_write_raw(session_display_ctx_t *ctx, const char *data, size_t len)
bool session_display_has_tty(session_display_ctx_t *ctx)
int session_display_get_tty_fd(session_display_ctx_t *ctx)
const char * color_mode_to_string(terminal_color_mode_t mode)
const char * render_mode_to_string(render_mode_t mode)
asciichat_error_t terminal_flush(int fd)
#define bool
Definition stdbool.h:22
const char * colored_string(log_color_t color, const char *text)

References APPEND, bool, color_mode_to_string(), colored_string(), DISABLED_COLOR, ENABLED_COLOR, render_mode_to_string(), session_display_get_tty_fd(), session_display_has_tty(), session_display_write_raw(), and terminal_flush().

Referenced by display_render_frame(), session_handle_keyboard_input(), and session_render_loop().