ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
controller.h File Reference

Go to the source code of this file.

Data Structures

struct  ui_presentation_state_t

Macros

#define UI_CONTROLLER_MAX_FRAME_BYTES   (16 * 1024 * 1024)

Typedefs

typedef void(* ui_render_fn) (terminal_size_t size, const void *snapshot)

Enumerations

enum  ui_screen_t {
  UI_SCREEN_STATUS , UI_SCREEN_MEDIA , UI_SCREEN_SPLASH , UI_SCREEN_RENDER_PROGRESS ,
  UI_SCREEN_LOGS , UI_SCREEN_ERRNO , UI_SCREEN_HELP , UI_SCREEN_STATS ,
  UI_SCREEN_SYNC , UI_SCREEN_MDNS , UI_SCREEN_UPDATE , UI_SCREEN_NOTICE ,
  UI_SCREEN_PROMPT , UI_SCREEN_ERROR , UI_SCREEN_COUNT
}
enum  ui_title_render_t { UI_TITLE_IDLE , UI_TITLE_RENDERING , UI_TITLE_FINALIZING , UI_TITLE_FAILED }
enum  ui_title_activity_t {
  UI_TITLE_ACTIVITY_IDLE , UI_TITLE_LOADING_URL , UI_TITLE_CONNECTING , UI_TITLE_RECONNECTING ,
  UI_TITLE_CREATING_SESSION , UI_TITLE_JOINING_SESSION , UI_TITLE_WAITING_PEERS
}

Functions

asciichat_error_t ui_controller_submit (ui_screen_t screen, int fd, terminal_size_t minimum, ui_render_fn render, const void *snapshot, size_t bytes)
void ui_controller_remove (ui_screen_t screen)
void ui_controller_dismiss (ui_screen_t screen, int previous)
void ui_controller_shutdown (void)
asciichat_error_t ui_controller_title_start (const char *mode, int fd)
void ui_controller_title_render (ui_title_render_t phase)
void ui_controller_title_ready (void)
void ui_controller_title_activity (ui_title_activity_t activity)
void ui_controller_title_discovery (bool has_session, bool joining)
void ui_controller_packet_begin (void)
void ui_controller_packet_end (void)
void ui_controller_restore_terminal (void)
void ui_controller_redraw (void)
void ui_controller_show_volume (void)
void ui_controller_toggle_logs (void)
void ui_controller_hide_logs (void)
bool ui_controller_is_blocked (void)
bool ui_controller_is_presenting (void)
bool ui_controller_is_owner (void)
terminal_size_t ui_controller_size (void)
ui_presentation_state_t ui_controller_state (void)
int ui_controller_current_screen (void)
int ui_controller_active_fd (void)
int ui_controller_interactive_screen (void)
void ui_controller_select_interactive (int screen)
asciichat_error_t ui_controller_write (int fd, const char *data, size_t len)
size_t ui_controller_trim_utf8_chunk (const char *data, size_t length)
void ui_controller_finish (int fd, const char *data, size_t len)
asciichat_error_t ui_controller_present (ui_screen_t screen, int fd, terminal_size_t minimum, const char *data, size_t len)
asciichat_error_t ui_controller_printf (int fd, const char *format,...)
const animation_sample_t * ui_controller_animation_sample (void)
asciichat_error_t ui_controller_present_retained (ui_screen_t screen, int fd, terminal_size_t minimum, const char *data, size_t len)
void ui_controller_frame_acquired_at (uint64_t started_ns)
void ui_controller_set_effective_fps (unsigned int fps)
asciichat_error_t ui_controller_start_terminal (int fd)
bool ui_controller_screen_live (ui_screen_t screen)
bool ui_controller_first_use_key (int key)

Macro Definition Documentation

◆ UI_CONTROLLER_MAX_FRAME_BYTES

#define UI_CONTROLLER_MAX_FRAME_BYTES   (16 * 1024 * 1024)

Maximum complete frame size accepted by the presenter, including cursor controls.

Definition at line 9 of file tui/controller.h.

Referenced by session_display_write_ascii_frame(), and ui_session_media().

Typedef Documentation

◆ ui_render_fn

typedef void(* ui_render_fn) (terminal_size_t size, const void *snapshot)

Definition at line 30 of file tui/controller.h.

Enumeration Type Documentation

◆ ui_screen_t

Rendering priority, from background to modal.

Enumerator
UI_SCREEN_STATUS 
UI_SCREEN_MEDIA 
UI_SCREEN_SPLASH 
UI_SCREEN_RENDER_PROGRESS 
UI_SCREEN_LOGS 
UI_SCREEN_ERRNO 
UI_SCREEN_HELP 
UI_SCREEN_STATS 
UI_SCREEN_SYNC 
UI_SCREEN_MDNS 
UI_SCREEN_UPDATE 
UI_SCREEN_NOTICE 
UI_SCREEN_PROMPT 
UI_SCREEN_ERROR 
UI_SCREEN_COUNT 

Definition at line 12 of file tui/controller.h.

12 {
26 UI_SCREEN_ERROR, // Virtual notification layer; rendered above screens and below help.
ui_screen_t
@ UI_SCREEN_PROMPT
@ UI_SCREEN_COUNT
@ UI_SCREEN_NOTICE
@ UI_SCREEN_LOGS
@ UI_SCREEN_ERRNO
@ UI_SCREEN_MEDIA
@ UI_SCREEN_STATS
@ UI_SCREEN_UPDATE
@ UI_SCREEN_SYNC
@ UI_SCREEN_SPLASH
@ UI_SCREEN_STATUS
@ UI_SCREEN_MDNS
@ UI_SCREEN_HELP
@ UI_SCREEN_ERROR
@ UI_SCREEN_RENDER_PROGRESS

◆ ui_title_activity_t

Enumerator
UI_TITLE_ACTIVITY_IDLE 
UI_TITLE_LOADING_URL 
UI_TITLE_CONNECTING 
UI_TITLE_RECONNECTING 
UI_TITLE_CREATING_SESSION 
UI_TITLE_JOINING_SESSION 
UI_TITLE_WAITING_PEERS 

Definition at line 50 of file tui/controller.h.

◆ ui_title_render_t

Enumerator
UI_TITLE_IDLE 
UI_TITLE_RENDERING 
UI_TITLE_FINALIZING 
UI_TITLE_FAILED 

Definition at line 47 of file tui/controller.h.

Function Documentation

◆ ui_controller_active_fd()

int ui_controller_active_fd ( void )

Output descriptor owned by the active screen, or stdout when none is active.

Definition at line 280 of file tui/controller.c.

280 {
281 int fd = atomic_load(&g_default_fd);
282 if (!lifecycle_is_initialized(&g_lifecycle))
283 return fd;
284 mutex_lock(&g_mutex);
285 if (g_active >= 0 && g_active < UI_SCREEN_COUNT && g_screens[g_active].render)
286 fd = g_screens[g_active].fd;
287 mutex_unlock(&g_mutex);
288 return fd;
289}

References screen::fd, screen::render, and UI_SCREEN_COUNT.

Referenced by tui_stats_render(), ui_controller_first_use_key(), ui_errno_stacks_toggle(), and ui_input_read_key().

◆ ui_controller_animation_sample()

const animation_sample_t * ui_controller_animation_sample ( void )

Animation sample owned by the common runtime during a presentation callback.

Definition at line 671 of file tui/controller.c.

671 {
672 return g_animation;
673}

Referenced by ui_media_panel_render().

◆ ui_controller_current_screen()

int ui_controller_current_screen ( void )

Last published screen priority; does not acquire application locks.

Definition at line 264 of file tui/controller.c.

264 {
265 int background = atomic_load(&g_background_screen);
266 int interactive = atomic_load(&g_interactive_screen);
267 if (interactive == UI_SCREEN_HELP && (atomic_load(&g_screen_mask) & (1u << UI_SCREEN_HELP)) &&
270 return UI_SCREEN_HELP;
271 if ((background >= 0 || interactive >= 0) && ui_error_popup_visible())
272 return UI_SCREEN_ERROR;
273 if (interactive < 0 && background < UI_SCREEN_MDNS && (atomic_load(&g_screen_mask) & (1u << UI_SCREEN_MDNS)))
274 return UI_SCREEN_MDNS;
275 return background > interactive ? background : interactive;
276}
bool ui_error_popup_visible(void)
Definition error_popup.c:71
int keyboard_help_parent_screen(void)

References keyboard_help_parent_screen(), ui_error_popup_visible(), UI_SCREEN_ERROR, UI_SCREEN_HELP, UI_SCREEN_MDNS, and UI_SCREEN_UPDATE.

Referenced by keyboard_help_toggle_terminal(), session_pipeline_run_main(), ui_controller_is_presenting(), ui_controller_state(), ui_controller_toggle_logs(), ui_controller_write(), ui_errno_stacks_handle_key(), and ui_input_poll_sync().

◆ ui_controller_dismiss()

void ui_controller_dismiss ( ui_screen_t screen,
int previous )

Retire a modal and atomically restore its previous interactive owner if still live. Does not steal focus from a screen selected after the modal opened.

Definition at line 1683 of file tui/controller.c.

1683 {
1684 remove_screen(screen, previous, NULL);
1685}

Referenced by keyboard_help_toggle_terminal(), and ui_prompt_dismiss().

◆ ui_controller_finish()

void ui_controller_finish ( int fd,
const char * data,
size_t len )

Retire live screens and synchronously render final text before the caller exits.

Definition at line 610 of file tui/controller.c.

610 {
611 atomic_store(&g_sync_finishing, true);
612 if (!g_owner)
613 while (atomic_load(&g_sync_rendering))
616 screen_t retired[UI_SCREEN_COUNT] = {0};
617 bool locked = lifecycle_is_initialized(&g_lifecycle);
618 if (locked) {
619 mutex_lock(&g_mutex);
620 g_finished = true;
621 memcpy(retired, g_screens, sizeof(retired));
622 memset(g_screens, 0, sizeof(g_screens));
623 atomic_store(&g_background_screen, -1);
624 atomic_store(&g_screen_mask, 0);
626 cond_signal(&g_render_requested);
627 uint64_t pending = g_render_started;
628 while (!g_render_snapshot && g_render_completed < pending)
629 cond_wait(&g_render_done, &g_mutex);
630 mutex_unlock(&g_mutex);
631 }
632 for (int i = 0; i < UI_SCREEN_COUNT; ++i)
633 release_screen(&retired[i], i == UI_SCREEN_NOTICE);
634 terminal_screen_leave();
635 platform_write_all(fd, data, len);
636 atomic_store_bool(&g_live, false);
637}
void errno_notifications_enable(bool enabled)
unsigned long long uint64_t
Definition common.h:63
size_t platform_write_all(int fd, const void *buf, size_t count)
Write all bytes to a file descriptor, handling partial writes.
void platform_sleep_ms(unsigned int ms)
Sleep for a specified number of milliseconds.
struct screen screen_t
void ui_controller_select_interactive(int screen)

References errno_notifications_enable(), screen::fd, platform_sleep_ms(), platform_write_all(), ui_controller_select_interactive(), UI_SCREEN_COUNT, and UI_SCREEN_NOTICE.

Referenced by ui_notice_present(), and update_banner_print_instructions().

◆ ui_controller_first_use_key()

bool ui_controller_first_use_key ( int key)

Handle input for a first-use notice, retaining its snapshot while it is used.

Definition at line 1687 of file tui/controller.c.

1687 {
1688 if (!lifecycle_is_initialized(&g_lifecycle))
1689 return false;
1690 mutex_lock(&g_mutex);
1691 screen_snapshot_t *owned = g_screens[UI_SCREEN_NOTICE].owned;
1692 if (owned)
1693 atomic_fetch_add(&owned->references, 1);
1694 mutex_unlock(&g_mutex);
1695 if (!owned)
1696 return false;
1697 ui_notice_key_result_t result = ui_notice_first_use_key(owned->data, key);
1698 if (result == UI_NOTICE_KEY_HELP)
1700 if (result == UI_NOTICE_KEY_COMPLETE)
1701 ui_notice_first_use_complete(owned->data);
1702 if (result == UI_NOTICE_KEY_COMPLETE || result == UI_NOTICE_KEY_DISMISS)
1703 remove_screen(UI_SCREEN_NOTICE, -1, owned);
1704 else if (result == UI_NOTICE_KEY_HANDLED && (key == '\r' || key == '\n' || key == KEY_LEFT || key == KEY_RIGHT))
1706 release_snapshot(owned);
1707 return result != UI_NOTICE_KEY_ORDINARY;
1708}
@ KEY_LEFT
Left arrow key.
Definition keyboard.h:59
@ KEY_RIGHT
Right arrow key.
Definition keyboard.h:60
void keyboard_help_toggle_terminal(int fd)
ui_notice_key_result_t ui_notice_first_use_key(const void *data, int key)
Definition notice.c:453
void ui_notice_first_use_complete(const void *data)
Definition notice.c:478
ui_notice_key_result_t
Definition notice.h:30
@ UI_NOTICE_KEY_HELP
Definition notice.h:33
@ UI_NOTICE_KEY_ORDINARY
Definition notice.h:31
@ UI_NOTICE_KEY_DISMISS
Definition notice.h:34
@ UI_NOTICE_KEY_COMPLETE
Definition notice.h:35
@ UI_NOTICE_KEY_HANDLED
Definition notice.h:32
When code needs to work with media snapshot slot, it keeps busy, snapshot, capacity here instead of s...
_Atomic unsigned references
void ui_controller_redraw(void)
int ui_controller_active_fd(void)

References KEY_LEFT, KEY_RIGHT, keyboard_help_toggle_terminal(), screen_snapshot_t::references, ui_controller_active_fd(), ui_controller_redraw(), ui_notice_first_use_complete(), ui_notice_first_use_key(), UI_NOTICE_KEY_COMPLETE, UI_NOTICE_KEY_DISMISS, UI_NOTICE_KEY_HANDLED, UI_NOTICE_KEY_HELP, UI_NOTICE_KEY_ORDINARY, and UI_SCREEN_NOTICE.

Referenced by ui_input_read_key().

◆ ui_controller_frame_acquired_at()

void ui_controller_frame_acquired_at ( uint64_t started_ns)

Carry the acquisition start time with the next media snapshot on this producer thread.

Definition at line 357 of file tui/controller.c.

357 {
358 g_acquisition_started_ns = started_ns;
359}

Referenced by session_pipeline_run_main().

◆ ui_controller_hide_logs()

void ui_controller_hide_logs ( void )

Definition at line 1952 of file tui/controller.c.

1952 {
1955}
void terminal_screen_logs_reset(void)
void ui_controller_remove(ui_screen_t screen)

References terminal_screen_logs_reset(), ui_controller_remove(), and UI_SCREEN_LOGS.

Referenced by ui_controller_toggle_logs().

◆ ui_controller_interactive_screen()

int ui_controller_interactive_screen ( void )

One interactive screen replaces the previous one; sync can switch without application locks.

Definition at line 297 of file tui/controller.c.

297 {
298 return atomic_load(&g_interactive_screen);
299}

Referenced by keyboard_help_is_active(), keyboard_help_is_active_global(), keyboard_help_toggle_terminal(), session_handle_keyboard_input(), stats_runtime_active(), and ui_sync_is_visible().

◆ ui_controller_is_blocked()

bool ui_controller_is_blocked ( void )

Atomic query, safe for interrupt handling.

Definition at line 639 of file tui/controller.c.

639 {
640 return atomic_load_bool(&g_blocked);
641}

Referenced by keyboard_help_is_active_global(), and ui_input_poll_sync().

◆ ui_controller_is_owner()

bool ui_controller_is_owner ( void )

Definition at line 647 of file tui/controller.c.

647 {
648 return g_owner;
649}

Referenced by log_console_impl(), and ui_notice_present().

◆ ui_controller_is_presenting()

bool ui_controller_is_presenting ( void )

Lock-free query for console logging, including logs from rendering callbacks.

Definition at line 643 of file tui/controller.c.

643 {
644 return ui_controller_current_screen() >= 0;
645}
int ui_controller_current_screen(void)

References ui_controller_current_screen().

Referenced by log_console_impl(), log_msg(), ui_controller_write(), ui_notice_present(), and ui_prompt_dismiss().

◆ ui_controller_packet_begin()

void ui_controller_packet_begin ( void )

Keep a complete terminal protocol packet and its flush separate from title updates. Call outside presentation callbacks; application shutdown must follow packet completion.

Definition at line 1410 of file tui/controller.c.

1410 {
1411 if (lifecycle_is_initialized(&g_lifecycle))
1412 mutex_lock(&g_title_packet_mutex);
1413}

Referenced by kitty_image_write().

◆ ui_controller_packet_end()

void ui_controller_packet_end ( void )

Definition at line 1415 of file tui/controller.c.

1415 {
1416 if (lifecycle_is_initialized(&g_lifecycle))
1417 mutex_unlock(&g_title_packet_mutex);
1418}

Referenced by kitty_image_write().

◆ ui_controller_present()

asciichat_error_t ui_controller_present ( ui_screen_t screen,
int fd,
terminal_size_t minimum,
const char * data,
size_t len )

Publish an owned copy of an already rendered frame.

Definition at line 1973 of file tui/controller.c.

1974 {
1975 return present_text(screen, fd, minimum, data, len, true);
1976}

Referenced by ascii_write(), and session_display_write_ascii_frame().

◆ ui_controller_present_retained()

asciichat_error_t ui_controller_present_retained ( ui_screen_t screen,
int fd,
terminal_size_t minimum,
const char * data,
size_t len )

Redraw retained media without treating it as a newly acquired frame for FPS.

Definition at line 1977 of file tui/controller.c.

1978 {
1979 return present_text(screen, fd, minimum, data, len, false);
1980}

Referenced by session_display_create(), and session_display_write_ascii_frame().

◆ ui_controller_printf()

asciichat_error_t ui_controller_printf ( int fd,
const char * format,
... )

Definition at line 1929 of file tui/controller.c.

1929 {
1931 va_list args;
1932 va_start(args, format);
1933 int len = vsnprintf(buffer, sizeof(buffer), format, args);
1934 va_end(args);
1935 if (len < 0)
1936 return SET_ERRNO(ERROR_INVALID_PARAM, "Cannot format terminal output");
1937 size_t bytes = (size_t)len < sizeof(buffer) ? (size_t)len : sizeof(buffer) - 1;
1938 return ui_controller_write(fd, buffer, bytes);
1939}
action_args_t args
Definition actions.c:78
#define SET_ERRNO(code,...)
@ ERROR_INVALID_PARAM
#define TUI_CONTROLLER_BUFFER_BUFFER_SIZE
asciichat_error_t ui_controller_write(int fd, const char *data, size_t len)

References args, ERROR_INVALID_PARAM, SET_ERRNO, TUI_CONTROLLER_BUFFER_BUFFER_SIZE, and ui_controller_write().

Referenced by log_search_render_input_line(), and session_render_loop().

◆ ui_controller_redraw()

void ui_controller_redraw ( void )

Definition at line 1857 of file tui/controller.c.

1857 {
1858 if (!lifecycle_is_initialized(&g_lifecycle))
1859 return;
1860 mutex_lock(&g_mutex);
1861 g_redraw = true;
1862 cond_signal(&g_render_requested);
1863 mutex_unlock(&g_mutex);
1864}

References ui_session_redraw().

Referenced by ascii_write_init(), keyboard_help_render(), session_display_clear(), session_display_cursor_home(), session_display_reset(), terminal_screen_logs_handle_key_sized(), ui_controller_first_use_key(), ui_error_popup_key(), and update_banner_install_update().

◆ ui_controller_remove()

◆ ui_controller_restore_terminal()

void ui_controller_restore_terminal ( void )

Stop presentation and restore cursor visibility on an interactive terminal.

Definition at line 1941 of file tui/controller.c.

1941 {
1943 if (platform_isatty(STDOUT_FILENO))
1944 ui_controller_write(STDOUT_FILENO, "\033[0m\033[?25h", 10);
1945}
void ui_controller_shutdown(void)

References ui_controller_shutdown(), and ui_controller_write().

◆ ui_controller_screen_live()

bool ui_controller_screen_live ( ui_screen_t screen)

Definition at line 1454 of file tui/controller.c.

1454 {
1455 return screen >= 0 && screen < UI_SCREEN_COUNT && (atomic_load(&g_screen_mask) & (1u << screen));
1456}

References UI_SCREEN_COUNT.

Referenced by stats_runtime_start(), and ui_input_read_key().

◆ ui_controller_select_interactive()

void ui_controller_select_interactive ( int screen)

Definition at line 301 of file tui/controller.c.

301 {
302 atomic_store(&g_interactive_screen, screen);
303}

Referenced by stats_runtime_toggle(), ui_controller_finish(), ui_controller_shutdown(), ui_sync_handle_key(), and ui_sync_stop().

◆ ui_controller_set_effective_fps()

void ui_controller_set_effective_fps ( unsigned int fps)

Set the target FPS displayed by the media overlay.

Definition at line 361 of file tui/controller.c.

361 {
362 atomic_store_int(&g_effective_fps, (int)(fps > 0 ? fps : 60));
363 if (g_fps)
365}
void fps_counter_set_effective_fps(fps_counter_t *counter, unsigned int fps)

References fps_counter_set_effective_fps().

◆ ui_controller_show_volume()

void ui_controller_show_volume ( void )

Show volume feedback for 1.5 seconds after a volume or mute control is used.

Definition at line 221 of file tui/controller.c.

221 {
222 if (!GET_OPTION(snapshot_mode))
223 atomic_store_u64(&g_volume_until, time_get_ns() + 1500 * NS_PER_MS_INT);
224}
#define NS_PER_MS_INT
Definition time.h:168
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336

References GET_OPTION, and NS_PER_MS_INT.

◆ ui_controller_shutdown()

void ui_controller_shutdown ( void )

Definition at line 1888 of file tui/controller.c.

1888 {
1889 if (!lifecycle_destroy_once(&g_lifecycle))
1890 return;
1891 mutex_lock(&g_mutex);
1892 atomic_store_bool(&g_presentation_stop_requested, true);
1893 cond_signal(&g_render_requested);
1894 mutex_unlock(&g_mutex);
1895 thread_join(&g_thread, NULL);
1896 // Shutdown may precede the next presentation tick. Keep pending errors readable
1897 // after exit without delaying transport teardown or waiting for terminal input.
1898 ui_error_popup_preserve(atomic_load(&g_default_fd), unpack_size(atomic_load(&g_size)));
1899 ui_session_destroy(g_session);
1900 g_session = NULL;
1903 atomic_store_u64(&g_volume_until, 0);
1904 ui_sync_stop();
1905 for (int i = 0; i < UI_SCREEN_COUNT; ++i) {
1906 release_screen(&g_screens[i], i == UI_SCREEN_NOTICE);
1907 }
1908 destroy_media_snapshots();
1909 cond_destroy(&g_render_requested);
1910 cond_destroy(&g_render_done);
1911 mutex_destroy(&g_title_packet_mutex);
1912 mutex_destroy(&g_mutex);
1913 atomic_store_bool(&g_live, false);
1914 atomic_store(&g_background_screen, -1);
1915 atomic_store(&g_screen_mask, 0);
1917 terminal_screen_leave();
1918 NAMED_UNREGISTER(&g_title_active);
1919 NAMED_UNREGISTER(&g_title_render);
1920 NAMED_UNREGISTER(&g_title_ready);
1921 NAMED_UNREGISTER(&g_title_activity);
1922 NAMED_UNREGISTER(&g_live);
1923 NAMED_UNREGISTER(&g_presentation_stop_requested);
1924 NAMED_UNREGISTER(&g_blocked);
1925 NAMED_UNREGISTER(&g_lifecycle.state);
1926 lifecycle_destroy_commit(&g_lifecycle);
1927}
void ui_error_popup_preserve(int fd, terminal_size_t size)
void ui_error_popup_reset(void)
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
void ui_session_destroy(ui_session_t *s)
void ui_sync_stop(void)
Definition sync.c:339

References errno_notifications_enable(), NAMED_UNREGISTER, ui_controller_select_interactive(), ui_error_popup_preserve(), ui_error_popup_reset(), UI_SCREEN_COUNT, UI_SCREEN_NOTICE, ui_session_destroy(), and ui_sync_stop().

Referenced by asciichat_shared_destroy(), stats_runtime_stop(), and ui_controller_restore_terminal().

◆ ui_controller_size()

terminal_size_t ui_controller_size ( void )

Definition at line 651 of file tui/controller.c.

651 {
652 if (g_owner)
653 return g_render_size;
654 terminal_size_t size = {.cols = 80, .rows = 24};
655 if (lifecycle_is_initialized(&g_lifecycle)) {
656 size = unpack_size(atomic_load(&g_size));
657 }
658 // A title-only controller has no screen to poll geometry yet.
659 if (!lifecycle_is_initialized(&g_lifecycle) || atomic_load(&g_screen_mask) == 0) {
660 terminal_size_t detected = {0};
662 int fd = atomic_load(&g_default_fd);
663 if (platform_isatty(fd) && terminal_get_size_fd(fd, &detected) == ASCIICHAT_OK && detected.cols > 0 &&
664 detected.rows > 0)
665 size = detected;
667 }
668 return size;
669}
void errno_scope_end(asciichat_errno_scope_t scope, asciichat_errno_outcome_t outcome)
asciichat_errno_scope_t errno_scope_begin(void)
@ ASCIICHAT_ERRNO_DISMISSED
@ ASCIICHAT_OK
Definition error_codes.h:46
When code needs to work with asciichat errno snapshot, it keeps total, copied here instead of scatter...

References ASCIICHAT_ERRNO_DISMISSED, ASCIICHAT_OK, errno_scope_begin(), errno_scope_end(), screen::fd, and ui_session_size().

Referenced by key_identity_format_terminal(), keyboard_help_toggle_terminal(), prompt_unknown_host(), terminal_screen_logs_handle_key(), ui_controller_state(), ui_notice_present(), and ui_session_logs_key().

◆ ui_controller_start_terminal()

asciichat_error_t ui_controller_start_terminal ( int fd)

Start terminal interaction without publishing a background screen.

Definition at line 1420 of file tui/controller.c.

1420 {
1421 if (fd < 0)
1422 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid UI terminal descriptor");
1423 atomic_store(&g_default_fd, fd);
1424 asciichat_error_t result = controller_start();
1425 return result == ASCIICHAT_OK ? ui_sync_start(fd) : result;
1426}
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
asciichat_error_t ui_sync_start(int fd)
Definition sync.c:311

References ASCIICHAT_OK, ERROR_INVALID_PARAM, SET_ERRNO, and ui_sync_start().

Referenced by stats_runtime_start().

◆ ui_controller_state()

ui_presentation_state_t ui_controller_state ( void )

Definition at line 1710 of file tui/controller.c.

1710 {
1711 ui_presentation_state_t state = {.screen = -1};
1712 if (!lifecycle_is_initialized(&g_lifecycle))
1713 return state;
1715 if (state.screen == UI_SCREEN_ERROR) {
1717 return state;
1718 }
1719 if (state.screen >= 0 && state.screen < UI_SCREEN_COUNT) {
1720 uint64_t published = atomic_load(&g_input_state);
1721 // Sync has an independent mailbox instead of a controller slot. Treat an
1722 // unpublished slot as absent so the session can dispatch queued switches.
1723 if (!(published & (UINT64_C(1) << state.screen)))
1724 state.screen = -1;
1725 else
1726 state.covered = (published & (UINT64_C(1) << (state.screen + 32))) != 0;
1727 }
1728 return state;
1729}
terminal_size_t ui_error_popup_minimum(void)
Definition error_popup.c:83
bool ui_too_small(terminal_size_t actual, terminal_size_t minimum)
Definition too_small.c:10
terminal_size_t ui_controller_size(void)

References ui_presentation_state_t::screen, ui_controller_current_screen(), ui_controller_size(), ui_error_popup_minimum(), UI_SCREEN_COUNT, UI_SCREEN_ERROR, and ui_too_small().

Referenced by stats_runtime_start(), and ui_input_read_key().

◆ ui_controller_submit()

asciichat_error_t ui_controller_submit ( ui_screen_t screen,
int fd,
terminal_size_t minimum,
ui_render_fn render,
const void * snapshot,
size_t bytes )

Copies snapshot before returning. Interactive callbacks run on the presentation thread; batch output renders synchronously. Pointer members must remain valid until ui_controller_remove() returns. Callbacks may remove themselves. Callbacks run without the controller mutex. External removal waits for an in-flight callback to finish, so producers must not hold locks needed by that callback while removing its screen.

Definition at line 1623 of file tui/controller.c.

1624 {
1625 return submit_screen(screen, fd, minimum, render, snapshot, bytes, NULL, true);
1626}

Referenced by keyboard_help_toggle_terminal(), terminal_screen_render(), ui_controller_toggle_logs(), ui_media_loading_present(), ui_notice_present(), ui_notice_present_first_use(), ui_password_prompt_present(), ui_prompt_present_kind(), and update_banner_show_prompt().

◆ ui_controller_title_activity()

void ui_controller_title_activity ( ui_title_activity_t activity)

Definition at line 766 of file tui/controller.c.

766 {
767 atomic_store_int(&g_title_activity, activity);
768}

Referenced by session_display_write_ascii_frame(), splash_clear_discovery_session(), and ui_controller_title_discovery().

◆ ui_controller_title_discovery()

void ui_controller_title_discovery ( bool has_session,
bool joining )

Definition at line 769 of file tui/controller.c.

769 {
771 : has_session ? UI_TITLE_WAITING_PEERS
773}
void ui_controller_title_activity(ui_title_activity_t activity)

References ui_controller_title_activity(), UI_TITLE_CREATING_SESSION, UI_TITLE_JOINING_SESSION, and UI_TITLE_WAITING_PEERS.

Referenced by splash_set_discovery_session().

◆ ui_controller_title_ready()

void ui_controller_title_ready ( void )

Definition at line 757 of file tui/controller.c.

757 {
758 atomic_store_bool(&g_title_ready, true);
759}

Referenced by session_display_write_ascii_frame(), session_server_like_run(), and ui_controller_title_render().

◆ ui_controller_title_render()

void ui_controller_title_render ( ui_title_render_t phase)

Definition at line 760 of file tui/controller.c.

760 {
761 atomic_store_int(&g_title_render, phase);
762 if (phase != UI_TITLE_IDLE)
764}
void ui_controller_title_ready(void)

References ui_controller_title_ready(), and UI_TITLE_IDLE.

Referenced by render_file_create(), render_file_destroy(), and render_file_write_frame().

◆ ui_controller_title_start()

asciichat_error_t ui_controller_title_start ( const char * mode,
int fd )

Start interactive title presentation. Producer events never write to the terminal.

Definition at line 1428 of file tui/controller.c.

1428 {
1429 if (!mode || !platform_isatty(fd) ||
1430 (GET_OPTION(snapshot_mode) && !GET_OPTION(render_file)[0] && !GET_OPTION(render_kitty)))
1431 return ASCIICHAT_OK;
1432 if (atomic_load_bool(&g_title_active))
1433 return SET_ERRNO(ERROR_INVALID_STATE, "Terminal title is already initialized");
1434 atomic_store(&g_default_fd, fd);
1435 asciichat_error_t result = controller_start();
1436 if (result != ASCIICHAT_OK)
1437 return result;
1438 g_title_fd = platform_dup(fd);
1439 if (g_title_fd < 0)
1440 return SET_ERRNO_SYS(ERROR_TERMINAL, "Cannot retain title terminal");
1441 g_title_mode = mode;
1442 g_title_started = time_get_ns();
1443 g_last_title[0] = '\0';
1444 g_title_written = false;
1445 g_title_startup_finished = false;
1446 atomic_store_int(&g_title_render, UI_TITLE_IDLE);
1447 atomic_store_int(&g_title_activity, UI_TITLE_ACTIVITY_IDLE);
1448 atomic_store_bool(&g_title_ready, false);
1449 // Publish only after the retained descriptor and immutable configuration are ready.
1450 atomic_store_bool(&g_title_active, true);
1451 return ASCIICHAT_OK;
1452}
#define SET_ERRNO_SYS(code,...)
@ ERROR_INVALID_STATE
@ ERROR_TERMINAL
Definition error_codes.h:64
int platform_dup(int fd)

References ASCIICHAT_OK, ERROR_INVALID_STATE, ERROR_TERMINAL, GET_OPTION, platform_dup(), SET_ERRNO, SET_ERRNO_SYS, UI_TITLE_ACTIVITY_IDLE, and UI_TITLE_IDLE.

Referenced by main().

◆ ui_controller_toggle_logs()

void ui_controller_toggle_logs ( void )

Definition at line 1957 of file tui/controller.c.

1957 {
1961 return;
1962 }
1963 if (!platform_isatty(STDOUT_FILENO) || GET_OPTION(snapshot_mode))
1964 return;
1965 const bool snapshot = true;
1966 asciichat_error_t result =
1967 ui_controller_submit(UI_SCREEN_LOGS, STDOUT_FILENO, (terminal_size_t){.cols = 40, .rows = 6}, render_logs,
1968 &snapshot, sizeof(snapshot));
1969 if (result != ASCIICHAT_OK)
1970 log_warn("Cannot show application logs: %d", result);
1971}
void ui_errno_stacks_hide(void)
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
asciichat_error_t ui_controller_submit(ui_screen_t screen, int fd, terminal_size_t minimum, ui_render_fn render, const void *snapshot, size_t bytes)
void ui_controller_hide_logs(void)

References ASCIICHAT_OK, GET_OPTION, log_warn, ui_controller_current_screen(), ui_controller_hide_logs(), ui_controller_submit(), ui_errno_stacks_hide(), and UI_SCREEN_LOGS.

◆ ui_controller_trim_utf8_chunk()

size_t ui_controller_trim_utf8_chunk ( const char * data,
size_t length )

Return the largest prefix of a chunk that does not end in a UTF-8 continuation byte.

Definition at line 440 of file tui/controller.c.

440 {
441 while (data && length && ((unsigned char)data[length - 1] & 0xc0) == 0x80)
442 --length;
443 return length;
444}

Referenced by ui_controller_write().

◆ ui_controller_write()

asciichat_error_t ui_controller_write ( int fd,
const char * data,
size_t len )

Final output sink. Live screen writes must originate in a presentation callback. Other terminal text is suppressed while a screen owns the terminal; non-TTY output remains synchronous.

Definition at line 446 of file tui/controller.c.

446 {
447 if (!data || !len)
448 return ASCIICHAT_OK;
449 if (g_capture) {
450 frame_buffer_append(g_capture, data, len);
451 return ASCIICHAT_OK;
452 }
453 // Logging may call this while holding subsystem locks; never wait for a
454 // renderer callback here. Live screens already capture logs in their model.
455 if (!g_owner && ui_controller_is_presenting())
456 return ASCIICHAT_OK;
457 if (g_owner && g_render_fd >= 0)
458 fd = g_render_fd;
460 uint64_t stats_start = g_stats_media ? time_get_ns() : 0;
461 // Terminals supporting synchronized output present the art and its overlays
462 // together, even when a large UTF-8 frame requires several writes.
463 bool help_media_overlay =
465 bool synchronized = g_render_snapshot != NULL || help_media_overlay;
466 g_help_sync_open = help_media_overlay;
467 bool started = !synchronized || write_output_chunk(fd, "\033[?2026h", 8) == 8;
468 uint64_t sync_started_ns = g_stats_media && synchronized ? time_get_ns() : 0;
469 size_t written = 0;
470 bool superseded = false;
471 while (started && written < len) {
472 // A pending editor or overlay supersedes the remainder of a media frame.
473 // Bound each console write so a large frame cannot delay that transition.
474 if (g_stats_media && ui_controller_current_screen() != UI_SCREEN_MEDIA) {
475 superseded = true;
476 break;
477 }
478 size_t count = len - written;
479 bool row_boundary = false;
480 if (g_stats_media && count > 32768) {
481 count = 32768;
482 size_t boundary = media_chunk_boundary(data + written, count);
483 if (boundary) {
484 count = boundary;
485 row_boundary = true;
486 }
487 // Never inspect beyond the candidate chunk: the next byte may belong
488 // to unrelated memory or to the next output chunk.
489 count = ui_controller_trim_utf8_chunk(data + written, count);
490 }
491 size_t sent = write_output_chunk(fd, data + written, count);
492 written += sent;
493 if (sent != count)
494 break;
495 if (synchronized && g_stats_media && row_boundary && written < len &&
496 time_get_ns() - sync_started_ns >= 40 * NS_PER_MS_INT) {
497 // Give the terminal a complete set of rows before its synchronized
498 // output watchdog can expose a partly written row.
499 if (write_output_chunk(fd, "\033[?2026l\033[?2026h", 16) != 16) {
500 started = false;
501 break;
502 }
503 sync_started_ns = time_get_ns();
504 }
505 }
506 // End the update even when input superseded the frame halfway through.
507 static const char end_with_query[] = "\033[?2026l\033]11;?\033\\";
508 bool probe = synchronized && g_probe_frame && written == len && started;
509 if (probe)
510 g_probe_requested_ns = time_get_ns();
511 size_t end_length = probe ? sizeof(end_with_query) - 1 : 8;
512 bool ended = g_help_sync_open || !synchronized || write_output_chunk(fd, end_with_query, end_length) == end_length;
513 if (!ended)
514 g_probe_requested_ns = 0;
515 // Switching screens intentionally cancels the remainder of an old media frame.
516 bool complete = (written == len || superseded) && started && ended;
517 if (g_stats_media) {
518 g_stats_write_ns += time_get_ns() - stats_start;
519 g_stats_writes++;
520 g_stats_failed |= !complete;
521 }
522 fps_counter_write_end(g_fps, complete);
523 return complete ? ASCIICHAT_OK
524 : SET_ERRNO(ERROR_FILE_OPERATION, "Incomplete terminal frame write (%zu/%zu bytes)", written, len);
525}
void frame_buffer_append(frame_buffer_t *buf, const char *str, size_t len)
Append raw bytes to the buffer.
@ ERROR_FILE_OPERATION
void fps_counter_write_begin(fps_counter_t *counter)
void fps_counter_write_end(fps_counter_t *counter, bool complete)
bool ui_controller_is_presenting(void)
size_t ui_controller_trim_utf8_chunk(const char *data, size_t length)

References ASCIICHAT_OK, ERROR_FILE_OPERATION, screen::fd, fps_counter_write_begin(), fps_counter_write_end(), frame_buffer_append(), keyboard_help_parent_screen(), NS_PER_MS_INT, SET_ERRNO, ui_controller_current_screen(), ui_controller_is_presenting(), ui_controller_trim_utf8_chunk(), UI_SCREEN_HELP, and UI_SCREEN_MEDIA.

Referenced by ascii_write(), ascii_write_destroy(), ascii_write_init(), log_console_impl(), log_search_render_input_line(), main(), session_display_write_ascii_frame(), session_display_write_raw(), tui_stats_render(), ui_controller_printf(), ui_controller_restore_terminal(), ui_notice_present(), ui_prompt_dismiss(), and ui_sync_render().