ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
mirror/main.c
Go to the documentation of this file.
1
203#include "main.h"
204#include "session/client_like.h"
205#include "session/capture.h"
206#include "session/display.h"
207#include "session/render.h"
209#include <ascii-chat/log/log.h>
212#include <ascii-chat/util/time.h>
213#include <unistd.h>
214#include <string.h>
215#include <stdint.h>
216
217static inline void js_mirror_log(const char *msg) {
218 if (msg) {
219 char buffer[512];
220 snprintf(buffer, sizeof(buffer), "[MIRROR-START] %s\n", msg);
221 write(STDERR_FILENO, buffer, strlen(buffer));
222 }
223}
224
225/* ============================================================================
226 * Mode-Specific Keyboard Handler
227 * ============================================================================ */
228
236static void mirror_keyboard_handler(session_capture_ctx_t *capture, int key, void *user_data) {
237 session_display_ctx_t *display = (session_display_ctx_t *)user_data;
238 session_handle_keyboard_input(capture, display, (keyboard_key_t)key);
239}
240
241/* ============================================================================
242 * Mode-Specific Main Loop
243 * ============================================================================ */
244
258static asciichat_error_t mirror_run(session_capture_ctx_t *capture, session_display_ctx_t *display, void *user_data) {
259 (void)user_data; // Unused
260
261 js_mirror_log("mirror_run: ENTRY");
262 log_info("mirror_run: CALLED - capture=%p display=%p", (void *)capture, (void *)display);
263
264 // Get the render loop's should_exit callback from session_client_like
265 bool (*render_should_exit)(void *) = session_client_like_get_render_should_exit();
266 if (!render_should_exit) {
267 log_error("mirror_run: render_should_exit callback not initialized");
268 return SET_ERRNO(ERROR_INVALID_STATE, "Render should_exit callback not initialized");
269 }
270
271 log_info("mirror_run: Calling session_render_loop in synchronous mode");
272 // Run the unified render loop with keyboard support using synchronous mode
273 // Pass capture context directly (not as callbacks) for proper frame capture
274 asciichat_error_t result = session_render_loop(capture, // Capture context (synchronous mode)
275 display,
276 render_should_exit, // Exit check (checks global + custom)
277 NULL, // No capture callback (using capture context)
278 NULL, // No sleep callback (built-in FPS limiting)
279 mirror_keyboard_handler, // Keyboard handler
280 display); // Pass display as user_data for help screen toggle
281 log_info("mirror_run: session_render_loop returned with result=%d", result);
282 return result;
283}
284
285#ifndef NDEBUG
286/* ============================================================================
287 * Deadlock Test Code
288 * ============================================================================ */
289
290static mutex_t g_test_mutex_a, g_test_mutex_b, g_test_mutex_c;
291
292// 2-way deadlock: Thread A ↔ Thread B
293static void *test_thread_a(void *arg) {
294 (void)arg;
295 log_info("TEST DEADLOCK: Thread A acquiring mutex_a");
296 mutex_lock(&g_test_mutex_a);
297 log_info("TEST DEADLOCK: Thread A got mutex_a (0x%lx), sleeping 1s", (uintptr_t)&g_test_mutex_a);
298 sleep(1);
299 log_info("TEST DEADLOCK: Thread A trying to acquire mutex_b (0x%lx) - will deadlock", (uintptr_t)&g_test_mutex_b);
300 mutex_lock(&g_test_mutex_b); // Will block forever
301 log_info("TEST DEADLOCK: Thread A got both (should never reach)");
302 mutex_unlock(&g_test_mutex_b);
303 mutex_unlock(&g_test_mutex_a);
304 return NULL;
305}
306
307static void *test_thread_b(void *arg) {
308 (void)arg;
309 usleep(500000); // Let thread A go first (500ms)
310 log_info("TEST DEADLOCK: Thread B acquiring mutex_b");
311 mutex_lock(&g_test_mutex_b);
312 log_info("TEST DEADLOCK: Thread B got mutex_b (0x%lx), sleeping 500ms", (uintptr_t)&g_test_mutex_b);
313 usleep(500000);
314 log_info("TEST DEADLOCK: Thread B trying to acquire mutex_a (0x%lx) - will deadlock", (uintptr_t)&g_test_mutex_a);
315 mutex_lock(&g_test_mutex_a); // Will block forever
316 log_info("TEST DEADLOCK: Thread B got both (should never reach)");
317 mutex_unlock(&g_test_mutex_a);
318 mutex_unlock(&g_test_mutex_b);
319 return NULL;
320}
321
322// 3-way deadlock: Thread A → B → C → A (only used when ASCII_CHAT_TEST_DEADLOCK_3WAY is set)
323static void *test_thread_a_3way(void *arg) {
324 (void)arg;
325 log_info("TEST DEADLOCK 3-WAY: Thread A acquiring mutex_a");
326 mutex_lock(&g_test_mutex_a);
327 log_info("TEST DEADLOCK 3-WAY: Thread A got mutex_a (0x%lx)", (uintptr_t)&g_test_mutex_a);
328 sleep(1);
329 log_info("TEST DEADLOCK 3-WAY: Thread A waiting for mutex_b (0x%lx) - will form cycle", (uintptr_t)&g_test_mutex_b);
330 mutex_lock(&g_test_mutex_b);
331 mutex_unlock(&g_test_mutex_b);
332 mutex_unlock(&g_test_mutex_a);
333 return NULL;
334}
335
336static void *test_thread_b_3way(void *arg) {
337 (void)arg;
338 usleep(300000); // Let thread A acquire mutex_a first
339 log_info("TEST DEADLOCK 3-WAY: Thread B acquiring mutex_b");
340 mutex_lock(&g_test_mutex_b);
341 log_info("TEST DEADLOCK 3-WAY: Thread B got mutex_b (0x%lx)", (uintptr_t)&g_test_mutex_b);
342 usleep(300000);
343 log_info("TEST DEADLOCK 3-WAY: Thread B waiting for mutex_c (0x%lx) - will form cycle", (uintptr_t)&g_test_mutex_c);
344 mutex_lock(&g_test_mutex_c);
345 mutex_unlock(&g_test_mutex_c);
346 mutex_unlock(&g_test_mutex_b);
347 return NULL;
348}
349
350static void *test_thread_c_3way(void *arg) {
351 (void)arg;
352 usleep(600000); // Let A and B start
353 log_info("TEST DEADLOCK 3-WAY: Thread C acquiring mutex_c");
354 mutex_lock(&g_test_mutex_c);
355 log_info("TEST DEADLOCK 3-WAY: Thread C got mutex_c (0x%lx)", (uintptr_t)&g_test_mutex_c);
356 usleep(300000);
357 log_info("TEST DEADLOCK 3-WAY: Thread C waiting for mutex_a (0x%lx) - CYCLE COMPLETE", (uintptr_t)&g_test_mutex_a);
358 mutex_lock(&g_test_mutex_a); // Closes the cycle: C waits for A
359 mutex_unlock(&g_test_mutex_a);
360 mutex_unlock(&g_test_mutex_c);
361 return NULL;
362}
363#endif // !NDEBUG
364
365/* ============================================================================
366 * Mirror Mode Entry Point
367 * ============================================================================ */
368
379int mirror_main(void) {
380 log_info("[MIRROR] main ENTRY");
381
382 // Configure mode-specific session settings
384 .run_fn = mirror_run,
385 .run_user_data = NULL,
387 .keyboard_handler = mirror_keyboard_handler,
388 .print_newline_on_tty_exit = true, // Mirror prints newline to separate frame from prompt
389 };
390
391 // Run shared initialization/teardown with mode-specific loop
393 log_info("mirror_main: session_client_like_run returned with result=%d", result);
394
395 if (result != ASCIICHAT_OK) {
396 log_error("Mirror mode failed with error code: %d", result);
397 }
398
399 return (int)result;
400}
Platform abstraction layer umbrella header providing unified cross-platform API.
asciichat_error_t session_client_like_run(const session_client_like_config_t *config)
bool(*)(void *) session_client_like_get_render_should_exit(void)
Definition client_like.c:89
Shared initialization and teardown for client-like modes (mirror, client, discovery)
@ SESSION_CLIENT_LIKE_KIND_MIRROR
Definition client_like.h:64
📹 Unified media capture abstraction for session-based video sources
Centralized render loop abstraction for all display modes.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_INVALID_STATE
@ ASCIICHAT_OK
Definition error_codes.h:51
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
keyboard_key_t
Unified keyboard key code enumeration.
Definition keyboard.h:54
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
asciichat_error_t session_render_loop(session_capture_ctx_t *capture, session_display_ctx_t *display, session_should_exit_fn should_exit, session_capture_fn capture_cb, session_sleep_for_frame_fn sleep_cb, session_keyboard_handler_fn keyboard_handler, void *user_data)
Unified render loop for all display modes.
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.
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
🎮 Keyboard input handler for interactive session controls
📝 Logging API with multiple log levels and terminal output control
int mirror_main(void)
Run mirror mode main loop.
ascii-chat Server Mode Entry Point Header
🖥️ Unified terminal display abstraction for session-based rendering
#define bool
Definition stdbool.h:61
Mutex type (POSIX: pthread_mutex_t with debug tracking)
Internal session capture context structure.
session_client_like_run_fn run_fn
Internal session display context structure.
⏱️ High-precision timing utilities using sokol_time.h and uthash