ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
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 <ascii-chat/session/display.h>
77#include <ascii-chat/session/capture.h>
78#include <ascii-chat/session/keyboard_handler.h>
79#include <ascii-chat/ui/help_screen.h>
80#include <ascii-chat/ui/splash.h>
81#include <ascii-chat/platform/abstraction.h>
82#include <ascii-chat/platform/keyboard.h>
83#include <ascii-chat/platform/util.h>
84#include <ascii-chat/options/options.h>
85#include <ascii-chat/options/rcu.h> // For RCU-based options access
86#include <ascii-chat/video/ascii.h>
87#include <ascii-chat/media/source.h>
88#include <ascii-chat/common.h>
89
90#include <fcntl.h>
91#include <string.h>
92#include <stdio.h>
93#include <stdatomic.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_bool g_is_first_frame_of_connection = true;
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 // 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}
246
255 return session_display_has_tty(g_display_ctx);
256}
257
267 if (g_display_ctx && !should_exit()) {
268 session_display_reset(g_display_ctx);
269 }
270}
271
281 atomic_store(&g_is_first_frame_of_connection, true);
282}
283
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}
302
315void display_render_frame(const char *frame_data) {
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}
353
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}
bool should_exit(void)
Definition main.c:90
ascii-chat Client Display Management Interface
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.
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)
session_capture_ctx_t * session_capture_create(const session_capture_config_t *config)
void session_capture_destroy(session_capture_ctx_t *ctx)
session_display_ctx_t * session_display_create(const session_display_config_t *config)
bool session_display_has_tty(session_display_ctx_t *ctx)
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)
void session_display_reset(session_display_ctx_t *ctx)
void log_set_terminal_output(bool enabled)
bool terminal_is_stdin_tty(void)
ascii-chat Server Mode Entry Point Header
int splash_intro_done(void)
Definition splash.c:526
Internal session display context structure.
bool snapshot_mode
Snapshot mode enabled.