ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
src/client/display.c
Go to the documentation of this file.
1
72#include "display.h"
73#include "main.h"
74#include "../main.h" // Global exit API
75
76#include "display.h"
77#include "capture.h"
84#include <ascii-chat/options/rcu.h> // For RCU-based options access
87#include <ascii-chat/common.h>
88
89#include <fcntl.h>
90#include <string.h>
91#include <stdio.h>
92#include <ascii-chat/atomic.h>
94#ifndef _WIN32
95#include <unistd.h>
96#endif
97
98/* ============================================================================
99 * Session Display Context
100 * ============================================================================ */
101
110static session_display_ctx_t *g_display_ctx = NULL;
111
121static atomic_t g_is_first_frame_of_connection = {0};
122
130static bool g_keyboard_enabled = false;
131
141static session_capture_ctx_t *g_display_capture_ctx = NULL;
142
143/* TTY detection and terminal reset now handled by session display library */
144
145/* Frame rendering now handled by session display library */
146
147/* ============================================================================
148 * Internal Setter Functions (for framework integration)
149 * ============================================================================ */
150
163 g_display_ctx = display_ctx;
164}
165
166/* ============================================================================
167 * Public Interface Functions
168 * ============================================================================ */
169
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 // Keyboard is pre-initialized from asciichat_shared_init()
232 // Only enable keyboard input if stdin is a TTY (avoid interfering with piped/redirected I/O)
233 g_keyboard_enabled = terminal_is_stdin_tty();
234
235 // Register display atomics with named debug registry
236 static bool display_atomics_registered = false;
237 if (!display_atomics_registered) {
238 NAMED_REGISTER_ATOMIC(&g_is_first_frame_of_connection, "first_frame_of_display_connection", NULL);
239 display_atomics_registered = true;
240 }
241
242 return 0;
243}
244
253 return session_display_has_tty(g_display_ctx);
254}
255
265 if (g_display_ctx && !should_exit()) {
266 session_display_reset(g_display_ctx);
267 }
268}
269
279 atomic_store_bool(&g_is_first_frame_of_connection, true);
280}
281
291 // Atomically check if this is the first frame and mark as not-first
292 // Avoids TOCTOU race where multiple threads could both think they're first
293 bool was_first_frame = atomic_exchange_bool(&g_is_first_frame_of_connection, false);
294
295 if (was_first_frame) {
296 // Disable terminal logging before clearing display and rendering first frame
298
299 // Signal the intro splash screen to stop - first frame is ready to render
301 }
302}
303
316void display_render_frame(const char *frame_data) {
317 if (!frame_data) {
318 SET_ERRNO(ERROR_INVALID_PARAM, "Attempted to render NULL frame data");
319 return;
320 }
321
322 // Stop splash screen animation on first frame
323 // This must be called BEFORE any frame rendering to prevent splash/frame flickering
325
326 // Render help screen if active (suppresses frame rendering)
327 if (keyboard_help_is_active(g_display_ctx)) {
328 keyboard_help_render(g_display_ctx);
329
330 // Still poll keyboard for interactive controls while help is visible
331 if (g_keyboard_enabled) {
333 if (key != KEY_NONE) {
334 session_handle_keyboard_input(g_display_capture_ctx, g_display_ctx, key);
335 }
336 }
337 return; // Skip normal frame rendering
338 }
339
340 // Use session display library for frame rendering
341 session_display_render_frame(g_display_ctx, frame_data);
342
343 // Poll for keyboard input and handle interactive controls
344 // - If client mode has local media (--file/--url), pass capture context for full controls
345 // (seek, pause, play, volume, color mode, flip)
346 // - If client mode is network-only, pass NULL (volume, color mode, flip work; seek/pause ignored)
347 if (g_keyboard_enabled) {
349 if (key != KEY_NONE) {
350 session_handle_keyboard_input(g_display_capture_ctx, g_display_ctx, key);
351 }
352 }
353}
354
364 // Cleanup keyboard input if it was initialized
365 if (g_keyboard_enabled) {
367 g_keyboard_enabled = false;
368 }
369
370 // Cleanup optional local media capture context if it was created
371 if (g_display_capture_ctx) {
372 session_capture_destroy(g_display_capture_ctx);
373 g_display_capture_ctx = NULL;
374 }
375
376 // Display context is now managed by session_client_like framework
377 // Do not destroy it here - the framework will handle cleanup
378 g_display_ctx = NULL;
379}
Platform abstraction layer umbrella header providing unified cross-platform API.
void atomic_store_bool(atomic_t *a, bool value)
Atomically store a boolean value.
Definition atomic.c:177
bool atomic_exchange_bool(atomic_t *a, bool new_value)
Atomically exchange a boolean and return the old value.
Definition atomic.c:303
⚛️ Atomic operations abstraction layer with debug tracking
📹 Unified media capture abstraction for session-based video sources
⚙️ Common definitions, error codes, macros, and types shared throughout the application
Named object registry for debugging — log identifiable resource names.
void display_set_context(session_display_ctx_t *display_ctx)
Set the display context created by session_client_like framework.
void display_render_frame(const char *frame_data)
Render ASCII frame to display.
bool display_has_tty()
Check if display has TTY capability.
int display_init()
Initialize what is necessary to display ascii frames.
void display_full_reset()
Perform full display reset.
void display_cleanup()
Cleanup display subsystem.
void display_disable_logging_for_first_frame()
Disable terminal logging for first frame.
void display_reset_for_new_connection()
Reset display state for new connection.
#define NAMED_REGISTER_ATOMIC(a, name, parent_ptr)
Register an atomic_t with automatic format specifier.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_DISPLAY
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
Definition log/log.c:700
@ MEDIA_SOURCE_STDIN
Piped or redirected input.
Definition source.h:84
@ MEDIA_SOURCE_FILE
Media file (video/audio)
Definition source.h:83
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
keyboard_key_t keyboard_read_nonblocking(void)
Read next keyboard input without blocking.
void keyboard_destroy(void)
Cleanup keyboard input system and restore terminal.
keyboard_key_t
Unified keyboard key code enumeration.
Definition keyboard.h:54
@ KEY_NONE
No key pressed or no input available.
Definition keyboard.h:55
session_display_ctx_t * session_display_create(const session_display_config_t *config)
Create a new session display context.
bool keyboard_help_is_active(session_display_ctx_t *ctx)
Check if keyboard help is currently active.
int splash_intro_done(void)
Signal that intro splash should end (first frame ready to render)
Definition splash.c:693
bool session_display_has_tty(session_display_ctx_t *ctx)
Check if display has a TTY (terminal) available.
void keyboard_help_render(session_display_ctx_t *ctx)
Render keyboard help centered on terminal.
void session_handle_keyboard_input(session_capture_ctx_t *capture, session_display_ctx_t *display, keyboard_key_t key)
Handle keyboard input in a session.
void session_display_render_frame(session_display_ctx_t *ctx, const char *frame_data)
Render an ASCII frame to the terminal.
session_capture_ctx_t * session_capture_create(const session_capture_config_t *config)
Legacy function - creates either mirror or network capture based on options.
void session_capture_destroy(session_capture_ctx_t *ctx)
Destroy session capture context and free resources.
void session_display_reset(session_display_ctx_t *ctx)
Reset terminal to default state.
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
🎮 Cross-platform keyboard input interface for ascii-chat
🎮 Keyboard input handler for interactive session controls
🆘 Interactive keyboard help overlay for session keyboard shortcuts
bool should_exit(void)
Definition misc.c:131
bool terminal_is_stdin_tty(void)
Check if stdin is connected to a TTY.
ascii-chat Server Mode Entry Point Header
🎬 Unified media source abstraction for webcam, files, and stdin
Intro and status screen display for ascii-chat with animated rainbow effects.
🖥️ Unified terminal display abstraction for session-based rendering
Atomic value wrapper for integral/boolean types.
Definition atomic.h:76
Configuration for session capture context.
media_source_type_t type
Media source type (WEBCAM, FILE, STDIN, TEST)
const char * path
Device index (for webcam) or file path (for file sources)
uint32_t target_fps
Target frame rate in FPS (e.g., 60 for display, 144 for network)
double initial_seek_timestamp
Seek to this timestamp after opening media (0.0 = no seek)
session_capture_should_exit_fn should_exit_callback
Optional: callback to check if initialization should be cancelled (e.g., shutdown signal)
void * callback_data
Opaque data passed to should_exit_callback.
bool resize_for_network
Resize frames to network-optimal dimensions (MAX_FRAME_WIDTH x MAX_FRAME_HEIGHT)
bool loop
Enable loop playback for file sources.
Internal session capture context structure.
Configuration for session display context.
bool snapshot_mode
Enable snapshot mode (single frame capture)
Internal session display context structure.
@ TERM_COLOR_AUTO
Auto-detect color support from terminal capabilities.
Definition terminal.h:580