ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
Terminal Display

🖥️ Terminal rendering, TTY management, and frame output More...

Files

file  display.c
 💻 Client terminal display: TTY detection, frame rendering, and interactive/stdout output routing
 
file  display.h
 ascii-chat Client Display Management Interface
 

Functions

void display_set_context (session_display_ctx_t *display_ctx)
 Set the display context created by session_client_like framework.
 
int display_init ()
 Initialize what is necessary to display ascii frames.
 
bool display_has_tty ()
 Check if display has TTY capability.
 
void display_full_reset ()
 Perform full display reset.
 
void display_reset_for_new_connection ()
 Reset display state for new connection.
 
void display_disable_logging_for_first_frame ()
 Disable terminal logging for first frame.
 
void display_render_frame (const char *frame_data)
 Render ASCII frame to display.
 
void display_cleanup ()
 Cleanup display subsystem.
 

Detailed Description

🖥️ Terminal rendering, TTY management, and frame output

Terminal Display

Overview

The terminal display subsystem manages terminal initialization, ASCII frame rendering, TTY capability detection, and terminal cleanup.

Implementation: src/client/display.c, src/client/display.h

Initialization

int display_init(void)
{
// Check if stdout is a TTY
if (!isatty(STDOUT_FILENO)) {
log_warn("stdout is not a TTY - frames may not render correctly");
g_has_tty = false;
return 0;
}
g_has_tty = true;
// Initialize TTY for raw mode
if (tty_init(&g_tty_info) != 0) {
log_error("Failed to initialize TTY");
return -1;
}
return 0;
}
tty_info_t g_tty_info
int display_init()
Initialize what is necessary to display ascii frames.

Frame Rendering

void display_render_frame(const char *frame_data, bool is_snapshot_frame)
{
// Disable logging for first frame to prevent corruption
if (g_is_first_frame) {
g_is_first_frame = false;
}
if (g_has_tty) {
// Clear screen and home cursor
printf("\033[2J\033[H");
fflush(stdout);
}
// Output frame data
printf("%s", frame_data);
fflush(stdout);
// Snapshot mode: leave cursor at end
if (!is_snapshot_frame && g_has_tty) {
// Home cursor for next frame
printf("\033[H");
fflush(stdout);
}
}
void display_render_frame(const char *frame_data)
Render ASCII frame to display.
void display_disable_logging_for_first_frame()
Disable terminal logging for first frame.

First Frame Optimization

Problem: Log messages corrupt first frame display

Solution:

  • Suppress terminal logging before first frame render
  • Re-enable logging after first frame displayed
  • Prevents log messages from appearing in frame area
{
// Temporarily disable terminal logging
logging_set_terminal_enabled(false);
// Will be re-enabled after first frame
}

Display Reset

Full Reset

{
if (g_has_tty) {
printf("\033[2J\033[H"); // Clear screen and home cursor
fflush(stdout);
}
}
void display_full_reset()
Perform full display reset.

Connection Reset

{
// Reset first frame flag for reconnection
g_is_first_frame = true;
}
void display_reset_for_new_connection()
Reset display state for new connection.

Cleanup

void display_cleanup(void)
{
if (g_has_tty) {
// Restore terminal settings
tty_restore(&g_tty_info);
// Clear screen and show cursor
printf("\033[2J\033[H\033[?25h");
fflush(stdout);
}
}
void display_cleanup()
Cleanup display subsystem.
See also
src/client/display.c
src/client/display.h
lib/platform/terminal.h
Client Overview

Function Documentation

◆ display_cleanup()

void display_cleanup ( )

#include <display.c>

Cleanup display subsystem.

Destroys session display context and releases all resources including keyboard input handling.

Definition at line 362 of file src/client/display.c.

362 {
363 // Cleanup keyboard input if it was initialized
364 if (g_keyboard_enabled) {
365 keyboard_destroy();
366 g_keyboard_enabled = false;
367 }
368
369 // Cleanup optional local media capture context if it was created
370 if (g_display_capture_ctx) {
371 session_capture_destroy(g_display_capture_ctx);
372 g_display_capture_ctx = NULL;
373 }
374
375 // Display context is now managed by session_client_like framework
376 // Do not destroy it here - the framework will handle cleanup
377 g_display_ctx = NULL;
378}
void session_capture_destroy(session_capture_ctx_t *ctx)

References session_capture_destroy().

◆ display_disable_logging_for_first_frame()

void display_disable_logging_for_first_frame ( )

#include <display.c>

Disable terminal logging for first frame.

Disables terminal logging before clearing the display for the first frame to prevent log output from interfering with ASCII display.

Call this before clearing the display for the first frame to prevent log output from interfering with ASCII display.

Definition at line 292 of file src/client/display.c.

292 {
293 // Disable terminal logging before clearing display and rendering first frame
294 if (atomic_load(&g_is_first_frame_of_connection)) {
296 atomic_store(&g_is_first_frame_of_connection, false);
297
298 // Signal the intro splash screen to stop - first frame is ready to render
300 }
301}
void log_set_terminal_output(bool enabled)
int splash_intro_done(void)
Definition splash.c:526

References log_set_terminal_output(), and splash_intro_done().

Referenced by display_render_frame().

◆ display_full_reset()

void display_full_reset ( )

#include <display.c>

Perform full display reset.

Executes complete terminal reset sequence for clean display state. Safe to call multiple times and handles mode-specific behavior.

Definition at line 266 of file src/client/display.c.

266 {
267 if (g_display_ctx && !should_exit()) {
268 session_display_reset(g_display_ctx);
269 }
270}
bool should_exit(void)
Definition main.c:90
void session_display_reset(session_display_ctx_t *ctx)

References session_display_reset(), and should_exit().

Referenced by server_connection_lost().

◆ display_has_tty()

bool display_has_tty ( )

#include <display.c>

Check if display has TTY capability.

Returns
true if TTY is available for interactive output, false otherwise
true if TTY available, false otherwise

Definition at line 254 of file src/client/display.c.

254 {
255 return session_display_has_tty(g_display_ctx);
256}
bool session_display_has_tty(session_display_ctx_t *ctx)

References session_display_has_tty().

◆ display_init()

int display_init ( )

#include <display.c>

Initialize what is necessary to display ascii frames.

Initialize display subsystem.

Creates session display context for TTY management and frame rendering. Must be called once during client startup.

Returns
0 on success, negative on error
0 on success, negative on error

Definition at line 180 of file src/client/display.c.

180 {
181 // Build display configuration from options
182 session_display_config_t config = {
183 .snapshot_mode = GET_OPTION(snapshot_mode),
184 .palette_type = GET_OPTION(palette_type),
185 .custom_palette = GET_OPTION(palette_custom_set) ? GET_OPTION(palette_custom) : NULL,
186 .color_mode = TERM_COLOR_AUTO // Will be overridden by command-line options
187 };
188
189 // Create display context using session library
190 g_display_ctx = session_display_create(&config);
191 if (!g_display_ctx) {
192 SET_ERRNO(ERROR_DISPLAY, "Failed to initialize display");
193 return -1;
194 }
195
196 // Check if client mode should load local media (--file or --url)
197 const char *media_url = GET_OPTION(media_url);
198 const char *media_file = GET_OPTION(media_file);
199
200 if ((media_url && strlen(media_url) > 0) || (media_file && strlen(media_file) > 0)) {
201 // Client mode with local media file/URL - create capture context for keyboard controls
202 session_capture_config_t capture_config = {0};
203 capture_config.target_fps = 60;
204 capture_config.resize_for_network = false;
205 capture_config.should_exit_callback = NULL;
206 capture_config.callback_data = NULL;
207
208 if (media_url && strlen(media_url) > 0) {
209 capture_config.type = MEDIA_SOURCE_FILE;
210 capture_config.path = media_url;
211 } else {
212 if (strcmp(media_file, "-") == 0) {
213 capture_config.type = MEDIA_SOURCE_STDIN;
214 capture_config.path = NULL;
215 } else {
216 capture_config.type = MEDIA_SOURCE_FILE;
217 capture_config.path = media_file;
218 }
219 }
220
221 capture_config.loop = GET_OPTION(media_loop);
222 capture_config.initial_seek_timestamp = GET_OPTION(media_seek_timestamp);
223
224 g_display_capture_ctx = session_capture_create(&capture_config);
225 if (!g_display_capture_ctx) {
226 log_warn("Failed to create capture context for local media - keyboard seek/pause disabled");
227 g_display_capture_ctx = NULL;
228 }
229 }
230
231 // Initialize keyboard input for interactive controls (volume, color mode, flip, seek, pause)
232 // Only initialize in TTY mode to avoid interfering with piped/redirected I/O
233 if (terminal_is_stdin_tty()) {
234 asciichat_error_t kb_result = keyboard_init();
235 if (kb_result == ASCIICHAT_OK) {
236 g_keyboard_enabled = true;
237 } else {
238 // Non-fatal: client can work without keyboard support
239 log_warn("Failed to initialize keyboard input: %s", asciichat_error_string(kb_result));
240 g_keyboard_enabled = false;
241 }
242 }
243
244 return 0;
245}
session_capture_ctx_t * session_capture_create(const session_capture_config_t *config)
session_display_ctx_t * session_display_create(const session_display_config_t *config)
bool terminal_is_stdin_tty(void)

References session_capture_create(), session_display_create(), session_display_ctx::snapshot_mode, and terminal_is_stdin_tty().

◆ display_render_frame()

void display_render_frame ( const char *  frame_data)

#include <display.c>

Render ASCII frame to display.

Render ASCII frame to display

Uses session display library for frame output routing based on display mode and snapshot requirements. Also polls for keyboard input for interactive controls (volume, color mode, flip).

Parameters
frame_dataASCII frame data to render
is_snapshot_frameWhether this is the final snapshot frame
frame_dataASCII frame data to render

Definition at line 315 of file src/client/display.c.

315 {
316 if (!frame_data) {
317 SET_ERRNO(ERROR_INVALID_PARAM, "Attempted to render NULL frame data");
318 return;
319 }
320
321 // Stop splash screen animation on first frame
322 // This must be called BEFORE any frame rendering to prevent splash/frame flickering
324
325 // Render help screen if active (suppresses frame rendering)
326 if (session_display_is_help_active(g_display_ctx)) {
327 session_display_render_help(g_display_ctx);
328
329 // Still poll keyboard for interactive controls while help is visible
330 if (g_keyboard_enabled) {
331 keyboard_key_t key = keyboard_read_nonblocking();
332 if (key != KEY_NONE) {
333 session_handle_keyboard_input(g_display_capture_ctx, g_display_ctx, key);
334 }
335 }
336 return; // Skip normal frame rendering
337 }
338
339 // Use session display library for frame rendering
340 session_display_render_frame(g_display_ctx, frame_data);
341
342 // Poll for keyboard input and handle interactive controls
343 // - If client mode has local media (--file/--url), pass capture context for full controls
344 // (seek, pause, play, volume, color mode, flip)
345 // - If client mode is network-only, pass NULL (volume, color mode, flip work; seek/pause ignored)
346 if (g_keyboard_enabled) {
347 keyboard_key_t key = keyboard_read_nonblocking();
348 if (key != KEY_NONE) {
349 session_handle_keyboard_input(g_display_capture_ctx, g_display_ctx, key);
350 }
351 }
352}
void session_display_render_help(session_display_ctx_t *ctx)
Render help screen centered on terminal.
void session_handle_keyboard_input(session_capture_ctx_t *capture, session_display_ctx_t *display, keyboard_key_t key)
void session_display_render_frame(session_display_ctx_t *ctx, const char *frame_data)
bool session_display_is_help_active(session_display_ctx_t *ctx)
Check if help screen is currently active (implemented in display.c for struct access)

References display_disable_logging_for_first_frame(), session_display_is_help_active(), session_display_render_frame(), session_display_render_help(), and session_handle_keyboard_input().

◆ display_reset_for_new_connection()

void display_reset_for_new_connection ( )

#include <display.c>

Reset display state for new connection.

Resets the first frame tracking flag to prepare for a new connection. Call this when starting a new connection to reset first frame tracking.

Call this when starting a new connection to reset first frame tracking.

Definition at line 280 of file src/client/display.c.

280 {
281 atomic_store(&g_is_first_frame_of_connection, true);
282}

Referenced by protocol_start_connection().

◆ display_set_context()

void display_set_context ( session_display_ctx_t display_ctx)

#include <display.c>

Set the display context created by session_client_like framework.

Set the display context (framework integration)

Client mode doesn't create its own display context anymore. Instead, session_client_like_run() creates it and passes it to client_run(). This setter allows client_run() to make the framework-created context available to protocol threads that need to render frames.

Parameters
display_ctxDisplay context created by framework (may be NULL)

Called by client_run() to pass the display context created by session_client_like_run() to the display module so protocol threads can render frames.

Parameters
display_ctxDisplay context from framework (may be NULL)

Definition at line 162 of file src/client/display.c.

162 {
163 g_display_ctx = display_ctx;
164}