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

Reusable "fixed header + scrolling logs" terminal screen abstraction. More...

Go to the source code of this file.

Data Structures

struct  terminal_screen_config_t
 Configuration for terminal screen rendering. More...

Typedefs

typedef void(* terminal_screen_header_fn) (frame_buffer_t *buf, terminal_size_t term_size, void *user_data)
 Configuration for terminal screen rendering.

Functions

void terminal_screen_render (const terminal_screen_config_t *config)
 Render a terminal screen with fixed header and scrolling logs.
void terminal_screen_compose (frame_buffer_t *output, terminal_size_t size, const terminal_screen_config_t *config, session_log_buffer_t *logs)
void terminal_screen_cleanup (void)
 Clean up terminal screen resources.
session_log_buffer_t * terminal_screen_log_init (void)
 Initialize session log buffer for terminal screens.
void terminal_screen_log_destroy (void)
 Standard log cleanup for terminal screens.
void terminal_screen_log_clear (void)
 Clear buffered logs for terminal screens.
session_log_buffer_t * terminal_screen_get_log_buffer (void)
 Get the current session log buffer.
void terminal_screen_set_output_fd (int fd)
 Set output file descriptor for terminal screens (splash/status).
bool terminal_screen_logs_handle_key (int key)
bool terminal_screen_logs_handle_key_sized (int key, terminal_size_t size)
void terminal_screen_logs_reset (void)
size_t terminal_screen_logs_offset (void)
void terminal_screen_logs_set_offset (size_t rows)
size_t terminal_screen_logs_retain_position (uint64_t tail_sequence, size_t appended_rows, size_t maximum_offset)
uint64_t terminal_screen_logs_last_tail_sequence (void)
bool terminal_screen_logs_help_open (void)
bool terminal_screen_logs_help_too_small (void)
void terminal_screen_logs_render_help (frame_buffer_t *output, terminal_size_t size)

Detailed Description

Reusable "fixed header + scrolling logs" terminal screen abstraction.

Provides a common pattern for rendering terminal screens with:

  • Fixed header area (caller-defined via callback)
  • Scrolling log feed below header (automatically managed)
  • Physical terminal dimensions supplied by the UI controller
  • ANSI-aware line wrapping using display_width()
  • Latest log at bottom (standard terminal behavior)

Definition in file terminal_screen.h.

Typedef Documentation

◆ terminal_screen_header_fn

typedef void(* terminal_screen_header_fn) (frame_buffer_t *buf, terminal_size_t term_size, void *user_data)

Configuration for terminal screen rendering.

Callback to render the fixed header portion of the screen

Parameters
bufFrame buffer to write header content into (via frame_buffer_printf, etc.)
term_sizeCurrent physical terminal dimensions
user_dataCaller-provided context data

The callback should:

  • Append exactly the number of lines specified in terminal_screen_config_t.fixed_header_lines
  • Use display_width() to ensure lines don't exceed term_size.cols
  • Write to buf parameter (frame_buffer_printf, frame_buffer_append)
  • NOT clear the screen (terminal_screen_render does that)
  • NOT print the final newline if it would be line N+1 (causes scroll)

Definition at line 40 of file terminal_screen.h.

Function Documentation

◆ terminal_screen_cleanup()

void terminal_screen_cleanup ( void )

Clean up terminal screen resources.

Destroys the internal static frame buffer allocated by terminal_screen_render(). Call at program exit or when terminal screen rendering is no longer needed. If a render is active, destruction is deferred until that render returns; cleanup never waits for blocked terminal output.

Definition at line 307 of file terminal_screen.c.

307 {
308 for (;;) {
309 int state = atomic_load_int(&g_screen_state);
310 if (state == SCREEN_CLEANUP_PENDING || state == SCREEN_CLEANING)
311 return;
313 if (!atomic_cas_int(&g_screen_state, &state, next))
314 continue;
315 if (next == SCREEN_CLEANING) {
316 terminal_screen_free_buffer();
317 atomic_store_int(&g_screen_state, SCREEN_IDLE);
318 }
319 return;
320 }
321}
@ SCREEN_CLEANING
@ SCREEN_CLEANUP_PENDING
@ SCREEN_IDLE
@ SCREEN_RENDERING

References SCREEN_CLEANING, SCREEN_CLEANUP_PENDING, SCREEN_IDLE, and SCREEN_RENDERING.

Referenced by asciichat_shared_destroy().

◆ terminal_screen_compose()

void terminal_screen_compose ( frame_buffer_t * output,
terminal_size_t size,
const terminal_screen_config_t * config,
session_log_buffer_t * logs )

Compose the existing status/splash UI without terminal I/O.

Definition at line 262 of file session_status.c.

263 {
264 if (!output || !config || !config->render_header || size.cols < 1 || size.rows < 1)
265 return;
266 if (config->show_logs)
268 frame_buffer_t *body = frame_buffer_create(size.rows, size.cols);
269 if (!body)
270 return;
271 config->render_header(body, size, config->user_data);
272
273 // Measure the actual header, including wrapped lines, as the native status UI does.
274 int header_rows = 0;
275 const char *line = frame_buffer_get_content(body);
276 const char *end = line + frame_buffer_get_length(body);
277 for (const char *cursor = line; cursor < end; ++cursor) {
278 if (*cursor == '\n') {
280 size_t length = (size_t)(cursor - line);
281 if (length >= sizeof(text))
282 length = sizeof(text) - 1;
283 memcpy(text, line, length);
284 text[length] = '\0';
285 int height = display_height(text, size.cols);
286 header_rows += height > 0 ? height : 1;
287 line = cursor + 1;
288 }
289 }
290 if (!header_rows)
291 header_rows = config->fixed_header_lines;
292 bool entering = config->show_logs && log_search_is_entering();
293 // Search owns the final row for its prompt. Otherwise logs fill it.
294 int available = size.rows - header_rows - (entering ? 1 : 0);
295 bool compact_startup = config->screen == UI_SCREEN_SPLASH;
296 session_log_entry_t *entries = NULL;
297 size_t count = 0;
298 size_t scroll_total = 0, scroll_offset = 0;
299 if (config->show_logs && available > 0) {
301 log_search_filter_buffer(logs, &entries, &count);
302 else {
303 entries = SAFE_MALLOC(SESSION_LOG_BUFFER_SIZE * sizeof(*entries), session_log_entry_t *);
304 if (entries)
306 }
307 log_row_ref_t *rows = SAFE_MALLOC((size_t)available * sizeof(*rows), log_row_ref_t *);
308 if (rows) {
309 size_t total_rows = 0;
310 uint64_t previous_tail_sequence = 0;
311 size_t appended_rows = 0;
312 for (size_t entry = 0; entry < count; ++entry) {
313 size_t height = (size_t)log_message_rows(entries[entry].message, size.cols, compact_startup);
314 if (SIZE_MAX - total_rows < height)
315 total_rows = SIZE_MAX;
316 else
317 total_rows += height;
318 }
319 uint64_t tail_sequence = count ? entries[count - 1].sequence : 0;
320 previous_tail_sequence = terminal_screen_logs_last_tail_sequence();
321 if (previous_tail_sequence && tail_sequence > previous_tail_sequence) {
322 for (size_t entry = 0; entry < count; ++entry) {
323 if (entries[entry].sequence > previous_tail_sequence)
324 appended_rows += (size_t)log_message_rows(entries[entry].message, size.cols, compact_startup);
325 }
326 }
327 size_t max_offset = total_rows ? total_rows - 1 : 0;
328 size_t skip = terminal_screen_logs_retain_position(tail_sequence, appended_rows, max_offset);
329 scroll_total = total_rows;
330 scroll_offset = total_rows > (size_t)available + skip ? total_rows - (size_t)available - skip : 0;
331 size_t row_count = 0;
332 for (size_t entry = count; entry && row_count < (size_t)available; --entry) {
333 int height = log_message_rows(entries[entry - 1].message, size.cols, compact_startup);
334 if (skip >= (size_t)height) {
335 skip -= (size_t)height;
336 continue;
337 }
338 int row = height - 1 - (int)skip;
339 skip = 0;
340 for (; row >= 0 && row_count < (size_t)available; --row)
341 rows[row_count++] = (log_row_ref_t){.entry = entry - 1, .row = row};
342 }
343 for (size_t row = row_count; row; --row)
344 append_log_row(body, entries[rows[row - 1].entry].message, rows[row - 1].row, size.cols, compact_startup);
345 while (row_count++ < (size_t)available)
346 frame_buffer_printf(body, "\n");
347 SAFE_FREE(rows);
348 }
349 }
350 SAFE_FREE(entries);
351
352 // Preserve the existing repaint-in-place behavior, also supplying CR for browser terminals.
353 bool log_help_open = config->show_logs && terminal_screen_logs_help_open();
354 bool hide_cursor = log_help_open || (!entering && (config->hide_cursor || config->show_logs));
355 frame_buffer_printf(output, "\033[H%s", hide_cursor ? "\033[?25l" : "\033[?25h");
356 const char *content = frame_buffer_get_content(body);
357 size_t length = frame_buffer_get_length(body);
358 for (size_t i = 0; i < length; ++i) {
359 // Normalize CRLF before erasing the line; erasing after CR removes its text.
360 if (content[i] == '\r' && i + 1 < length && content[i + 1] == '\n')
361 continue;
362 if (content[i] == '\n') {
363 frame_buffer_printf(output, "\033[K\r");
364 // Do not advance past the bottom row when the idle log view fills it.
365 if (config->show_logs && !entering && i + 1 == length)
366 continue;
367 }
368 frame_buffer_append(output, content + i, 1);
369 }
370 if (config->show_logs && available > 0 && scroll_total > (size_t)available)
371 ui_scrollbar_render(output, header_rows + 1, size.cols, available, scroll_total, (size_t)available, scroll_offset);
372 if (entering) {
373 mutex_t *mutex = log_search_get_mutex();
374 mutex_lock(mutex);
375 const char *pattern = log_search_get_input_buffer();
376 int cursor = log_search_get_cursor_position();
377 // Keep the edit cursor visible even when a pattern exceeds the viewport.
378 int offset = cursor >= size.cols - 2 ? cursor - (size.cols - 3) : 0;
379 frame_buffer_printf(output, "\033[%d;1H/%.*s\033[K\033[%d;%dH", size.rows, size.cols - 2,
380 pattern ? pattern + offset : "", size.rows, cursor - offset + 2);
381 mutex_unlock(mutex);
382 }
383 if (log_help_open) {
385 } else if (config->show_logs && terminal_screen_logs_help_too_small()) {
386 frame_buffer_printf(output, "\033[%d;1H%s", size.rows, "Help needs at least 12 terminal rows");
387 }
389}
unsigned int height
Definition fft.c:23
void frame_buffer_append(frame_buffer_t *buf, const char *str, size_t len)
Append raw bytes to the buffer.
void frame_buffer_destroy(frame_buffer_t *buf)
Destroy a frame buffer.
size_t frame_buffer_get_length(const frame_buffer_t *buf)
Get the current content length of the frame buffer.
const char * frame_buffer_get_content(const frame_buffer_t *buf)
Get a pointer to the buffer content.
frame_buffer_t * frame_buffer_create(int rows, int cols)
Create a new frame buffer.
void frame_buffer_printf(frame_buffer_t *buf, const char *fmt,...)
Printf-style append to the buffer.
struct frame_buffer frame_buffer_t
Opaque frame buffer handle.
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
unsigned long long uint64_t
Definition common.h:63
int display_height(const char *text, int terminal_width)
Calculate how many display lines text will consume when wrapped.
void ui_scrollbar_render(frame_buffer_t *out, int row, int col, int height, size_t total, size_t visible, size_t offset)
Definition scrollbar.c:40
void * log_search_get_mutex(void)
Get the mutex protecting grep state.
Definition search.c:985
bool log_search_is_entering(void)
Check if currently in input mode (typing pattern).
Definition search.c:408
void log_search_process_pending(void)
Definition search.c:469
asciichat_error_t log_search_filter_buffer(session_log_buffer_t *log_buf, session_log_entry_t **out_entries, size_t *out_count)
Definition search.c:574
const char * log_search_get_input_buffer(void)
Get current input buffer content (must hold mutex).
Definition search.c:997
bool log_search_is_active(void)
Check if filtering is active.
Definition search.c:413
int log_search_get_cursor_position(void)
Get current cursor position in input buffer (must hold mutex).
Definition search.c:993
size_t session_log_buffer_get_recent(session_log_buffer_t *buf, session_log_entry_t *out_entries, size_t max_count)
Get recent log entries from buffer.
#define SESSION_LOG_BUFFER_SIZE
#define TUI_SESSION_STATUS_TEXT_BUFFER_SIZE
void terminal_screen_logs_render_help(frame_buffer_t *output, terminal_size_t size)
One formatted session log line and its monotonic history sequence.
uint64_t sequence
Sequence number (monotonic, for ordering).
terminal_screen_header_fn render_header
Callback to draw header content.
int fixed_header_lines
How many lines the header takes (e.g., 4 for status, 8 for splash).
bool show_logs
Whether to show log feed below header.
bool hide_cursor
Hide cursor while this screen owns the terminal.
void * user_data
Passed to render_header callback.
bool terminal_screen_logs_help_open(void)
size_t terminal_screen_logs_retain_position(uint64_t tail_sequence, size_t appended_rows, size_t maximum_offset)
uint64_t terminal_screen_logs_last_tail_sequence(void)
bool terminal_screen_logs_help_too_small(void)
@ UI_SCREEN_SPLASH

References display_height(), terminal_screen_config_t::fixed_header_lines, frame_buffer_append(), frame_buffer_create(), frame_buffer_destroy(), frame_buffer_get_content(), frame_buffer_get_length(), frame_buffer_printf(), height, terminal_screen_config_t::hide_cursor, log_search_filter_buffer(), log_search_get_cursor_position(), log_search_get_input_buffer(), log_search_get_mutex(), log_search_is_active(), log_search_is_entering(), log_search_process_pending(), terminal_screen_config_t::render_header, SAFE_FREE, SAFE_MALLOC, terminal_screen_config_t::screen, session_log_entry_t::sequence, session_log_buffer_get_recent(), SESSION_LOG_BUFFER_SIZE, terminal_screen_config_t::show_logs, terminal_screen_logs_help_open(), terminal_screen_logs_help_too_small(), terminal_screen_logs_last_tail_sequence(), terminal_screen_logs_render_help(), terminal_screen_logs_retain_position(), TUI_SESSION_STATUS_TEXT_BUFFER_SIZE, UI_SCREEN_SPLASH, ui_scrollbar_render(), and terminal_screen_config_t::user_data.

Referenced by ui_session_logs_render().

◆ terminal_screen_get_log_buffer()

session_log_buffer_t * terminal_screen_get_log_buffer ( void )

Get the current session log buffer.

Returns the log buffer owned by this module, or NULL if not initialized. Used by splash/status screens to register with the logger.

Returns
Pointer to log buffer, or NULL

Definition at line 363 of file terminal_screen.c.

363 {
364 return g_session_log_buffer;
365}

◆ terminal_screen_log_clear()

void terminal_screen_log_clear ( void )

Clear buffered logs for terminal screens.

Clears all previously captured log entries from the buffer. Useful when transitioning between screens to start fresh.

Definition at line 356 of file terminal_screen.c.

356 {
357 // Process history survives splash/status transitions.
358 if (g_session_log_buffer && g_owns_log_buffer) {
359 session_log_buffer_clear(g_session_log_buffer);
360 }
361}
void session_log_buffer_clear(session_log_buffer_t *buf)
Clear all log entries from buffer.

References session_log_buffer_clear().

Referenced by splash_log_clear(), ui_mdns_log_clear(), and ui_status_log_clear().

◆ terminal_screen_log_destroy()

void terminal_screen_log_destroy ( void )

Standard log cleanup for terminal screens.

Destroys the session log buffer after screens are done rendering.

Definition at line 339 of file terminal_screen.c.

339 {
342 NAMED_UNREGISTER(&g_screen_state);
343 if (g_session_log_buffer) {
344 if (g_owns_log_buffer)
345 session_log_buffer_destroy(g_session_log_buffer);
346 g_session_log_buffer = NULL;
347 }
349 int expected = LOG_VIEWER_MUTEX_READY;
350 if (atomic_cas_int(&g_log_viewer_mutex_state, &expected, LOG_VIEWER_MUTEX_DESTROYING)) {
351 mutex_destroy(&g_log_viewer_mutex);
352 atomic_store_int(&g_log_viewer_mutex_state, LOG_VIEWER_MUTEX_UNINITIALIZED);
353 }
354}
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
void session_log_buffer_destroy(session_log_buffer_t *buf)
Cleanup session log buffer instance.
@ LOG_VIEWER_MUTEX_READY
@ LOG_VIEWER_MUTEX_DESTROYING
@ LOG_VIEWER_MUTEX_UNINITIALIZED
void terminal_screen_logs_reset(void)
void ui_controller_remove(ui_screen_t screen)
@ UI_SCREEN_STATUS

References LOG_VIEWER_MUTEX_DESTROYING, LOG_VIEWER_MUTEX_READY, LOG_VIEWER_MUTEX_UNINITIALIZED, NAMED_UNREGISTER, session_log_buffer_destroy(), terminal_screen_logs_reset(), ui_controller_remove(), UI_SCREEN_SPLASH, and UI_SCREEN_STATUS.

Referenced by splash_log_destroy(), ui_mdns_log_destroy(), and ui_status_log_destroy().

◆ terminal_screen_log_init()

session_log_buffer_t * terminal_screen_log_init ( void )

Initialize session log buffer for terminal screens.

Creates a new session log buffer owned by this module. Splash and status screens use this buffer to display logs.

Returns
Pointer to created buffer, or NULL on failure

Definition at line 323 of file terminal_screen.c.

323 {
325 if (!terminal_screen_logs_mutex_init())
326 return NULL;
327 if (g_session_log_buffer) {
328 return g_session_log_buffer; // Already initialized
329 }
330
331 NAMED_REGISTER_ATOMIC(&g_screen_state, "terminal_screen_state", NULL);
332 g_session_log_buffer = log_get_history_buffer();
333 g_owns_log_buffer = !g_session_log_buffer;
334 if (g_owns_log_buffer)
335 g_session_log_buffer = session_log_buffer_create();
336 return g_session_log_buffer;
337}
#define NAMED_REGISTER_ATOMIC(a, name, parent_ptr)
Register an atomic_t with automatic format specifier.
session_log_buffer_t * log_get_history_buffer(void)
Definition log.c:565
asciichat_error_t log_search_init(void)
Initialize interactive grep subsystem.
Definition search.c:144
session_log_buffer_t * session_log_buffer_create(void)
Create a new session log buffer instance.

References log_get_history_buffer(), log_search_init(), NAMED_REGISTER_ATOMIC, and session_log_buffer_create().

Referenced by splash_log_init(), ui_mdns_log_init(), and ui_status_log_init().

◆ terminal_screen_logs_handle_key()

bool terminal_screen_logs_handle_key ( int key)

Handle a key while the live log viewer owns the terminal.

Definition at line 255 of file terminal_screen.c.

255 {
257}
bool terminal_screen_logs_handle_key_sized(int key, terminal_size_t size)
terminal_size_t ui_controller_size(void)

References terminal_screen_logs_handle_key_sized(), and ui_controller_size().

Referenced by ui_input_read_key().

◆ terminal_screen_logs_handle_key_sized()

bool terminal_screen_logs_handle_key_sized ( int key,
terminal_size_t size )

Browser/embedded sessions supply their own viewport instead of a native controller.

Definition at line 178 of file terminal_screen.c.

178 {
179 if (!terminal_screen_logs_mutex_init())
180 return false;
181 bool redraw = false;
182 mutex_lock(&g_log_viewer_mutex);
183 if (g_log_help) {
184 if (key == KEY_ESCAPE || key == '?') {
185 g_log_help = false;
186 redraw = true;
187 }
188 mutex_unlock(&g_log_viewer_mutex);
189 if (redraw)
191 return true;
192 }
193 if (key == '?') {
194 g_log_help_too_small = size.rows < 12 || size.cols < 8;
195 g_log_help = !g_log_help_too_small;
196 redraw = true;
197 mutex_unlock(&g_log_viewer_mutex);
199 return true;
200 }
201 int direction = 0;
202 if (key == KEY_UP || key == 'k')
203 direction = 1;
204 else if (key == KEY_DOWN || key == 'j')
205 direction = -1;
206 else if (key == KEY_PAGEUP)
207 direction = 1000000;
208 else if (key == KEY_PAGEDOWN)
209 direction = -1000000;
210 else if (key == KEY_HOME) {
211 g_log_scroll_rows = SIZE_MAX;
212 g_log_help_too_small = false;
213 mutex_unlock(&g_log_viewer_mutex);
215 return true;
216 } else if (key == KEY_END) {
217 g_log_scroll_rows = 0;
218 g_log_help_too_small = false;
219 mutex_unlock(&g_log_viewer_mutex);
221 return true;
222 } else if (key == KEY_LEFT || key == 21)
223 direction = 1000000;
224 else if (key == KEY_RIGHT || key == 4)
225 direction = -1000000;
226 else {
227 g_log_help_too_small = false;
228 mutex_unlock(&g_log_viewer_mutex);
229 return false;
230 }
231
232 ascii_otel_ui_usage("logs", direction > 0 ? "scroll_older" : "scroll_newer", 0);
233 size_t step = 1;
234 if (direction == 1 || direction == -1) {
235 /* Arrow keys move by one complete log entry, including wrapped rows. */
236 step = terminal_screen_log_step_locked(size.cols);
237 } else {
238 int rows = size.rows;
239 step = (size_t)(rows > 6 ? (rows - 5) / 2 : 1);
240 }
241 if (direction > 0) {
242 if (SIZE_MAX - g_log_scroll_rows < step)
243 g_log_scroll_rows = SIZE_MAX;
244 else
245 g_log_scroll_rows += step;
246 } else {
247 g_log_scroll_rows = g_log_scroll_rows > step ? g_log_scroll_rows - step : 0;
248 }
249 g_log_help_too_small = false;
250 mutex_unlock(&g_log_viewer_mutex);
252 return true;
253}
@ KEY_UP
Up arrow key.
Definition keyboard.h:57
@ KEY_ESCAPE
Escape key (ESC).
Definition keyboard.h:55
@ KEY_PAGEDOWN
Definition keyboard.h:71
@ KEY_PAGEUP
Definition keyboard.h:70
@ KEY_LEFT
Left arrow key.
Definition keyboard.h:59
@ KEY_RIGHT
Right arrow key.
Definition keyboard.h:60
@ KEY_END
End key (move to end of line).
Definition keyboard.h:63
@ KEY_DOWN
Down arrow key.
Definition keyboard.h:58
@ KEY_HOME
Home key (move to start of line).
Definition keyboard.h:62
void ascii_otel_ui_usage(const char *screen, const char *event, uint64_t duration_ns)
Definition otel.c:434
void ui_controller_redraw(void)

References ascii_otel_ui_usage(), KEY_DOWN, KEY_END, KEY_ESCAPE, KEY_HOME, KEY_LEFT, KEY_PAGEDOWN, KEY_PAGEUP, KEY_RIGHT, KEY_UP, and ui_controller_redraw().

Referenced by terminal_screen_logs_handle_key().

◆ terminal_screen_logs_help_open()

bool terminal_screen_logs_help_open ( void )

True while the log-viewer's own shortcut dialog is displayed.

Definition at line 259 of file terminal_screen.c.

259 {
260 if (!terminal_screen_logs_mutex_init())
261 return false;
262 mutex_lock(&g_log_viewer_mutex);
263 bool help_open = g_log_help;
264 mutex_unlock(&g_log_viewer_mutex);
265 return help_open;
266}

Referenced by terminal_screen_compose(), and ui_input_read_key().

◆ terminal_screen_logs_help_too_small()

bool terminal_screen_logs_help_too_small ( void )

True when help was requested but the current terminal cannot display it.

Definition at line 268 of file terminal_screen.c.

268 {
269 if (!terminal_screen_logs_mutex_init())
270 return false;
271 mutex_lock(&g_log_viewer_mutex);
272 bool too_small = g_log_help_too_small;
273 mutex_unlock(&g_log_viewer_mutex);
274 return too_small;
275}

Referenced by terminal_screen_compose().

◆ terminal_screen_logs_last_tail_sequence()

uint64_t terminal_screen_logs_last_tail_sequence ( void )

Sequence number at the live tail from the previous composition.

Definition at line 147 of file terminal_screen.c.

147 {
148 if (!terminal_screen_logs_mutex_init())
149 return 0;
150 mutex_lock(&g_log_viewer_mutex);
151 uint64_t sequence = g_log_last_tail_sequence;
152 mutex_unlock(&g_log_viewer_mutex);
153 return sequence;
154}

Referenced by terminal_screen_compose().

◆ terminal_screen_logs_offset()

size_t terminal_screen_logs_offset ( void )

Number of rendered rows currently held above the live tail.

Definition at line 110 of file terminal_screen.c.

110 {
111 if (!terminal_screen_logs_mutex_init())
112 return g_log_scroll_rows;
113 mutex_lock(&g_log_viewer_mutex);
114 size_t offset = g_log_scroll_rows;
115 mutex_unlock(&g_log_viewer_mutex);
116 return offset;
117}

Referenced by ui_session_logs_render().

◆ terminal_screen_logs_render_help()

void terminal_screen_logs_render_help ( frame_buffer_t * output,
terminal_size_t size )

Recompose log-viewer help above modal error notifications.

Definition at line 391 of file session_status.c.

391 {
392 const ui_help_entry_t entries[] = {{"Down", "newer log"}, {"Up", "older log"}, {"PageDown", "newer page"},
393 {"PageUp", "older page"}, {"End", "newest log"}, {"Home", "oldest log"},
394 {"/", "search logs"}, {"q / Esc", "close viewer"}};
396 ui_help_keys(keys, sizeof(keys), "? / Esc");
397 snprintf(footer, sizeof(footer), "%s closes this help", keys);
398 ui_help_menu_t menu = {.title = "Log Viewer Keyboard Controls",
399 .entries = entries,
400 .count = sizeof(entries) / sizeof(*entries),
401 .footer = footer};
402 (void)ui_help_menu_render(output, size, &menu);
403}
void ui_help_keys(char *out, size_t cap, const char *keys)
Definition help_menu.c:25
bool ui_help_menu_render(frame_buffer_t *out, terminal_size_t size, const ui_help_menu_t *m)
Definition help_menu.c:101
#define TUI_SESSION_STATUS_FOOTER_BUFFER_SIZE
#define TUI_SESSION_STATUS_KEYS_BUFFER_SIZE
Definition help_menu.h:3
ui help entry keeps the values that move through the subsystem: keys, description....
Definition help_menu.h:12

References TUI_SESSION_STATUS_FOOTER_BUFFER_SIZE, TUI_SESSION_STATUS_KEYS_BUFFER_SIZE, ui_help_keys(), and ui_help_menu_render().

Referenced by terminal_screen_compose().

◆ terminal_screen_logs_reset()

void terminal_screen_logs_reset ( void )

Reset the retained log-viewer position when the viewer is closed.

Definition at line 100 of file terminal_screen.c.

100 {
101 if (!terminal_screen_logs_mutex_init()) {
102 terminal_screen_logs_reset_locked();
103 return;
104 }
105 mutex_lock(&g_log_viewer_mutex);
106 terminal_screen_logs_reset_locked();
107 mutex_unlock(&g_log_viewer_mutex);
108}

Referenced by terminal_screen_log_destroy(), and ui_controller_hide_logs().

◆ terminal_screen_logs_retain_position()

size_t terminal_screen_logs_retain_position ( uint64_t tail_sequence,
size_t appended_rows,
size_t maximum_offset )

Retain the selected history row while new entries arrive, then clamp it.

Definition at line 129 of file terminal_screen.c.

129 {
130 if (!terminal_screen_logs_mutex_init())
131 return 0;
132 mutex_lock(&g_log_viewer_mutex);
133 if (g_log_scroll_rows && g_log_last_tail_sequence && tail_sequence > g_log_last_tail_sequence) {
134 if (SIZE_MAX - g_log_scroll_rows < appended_rows)
135 g_log_scroll_rows = SIZE_MAX;
136 else
137 g_log_scroll_rows += appended_rows;
138 }
139 g_log_last_tail_sequence = tail_sequence;
140 if (g_log_scroll_rows > maximum_offset)
141 g_log_scroll_rows = maximum_offset;
142 size_t offset = g_log_scroll_rows;
143 mutex_unlock(&g_log_viewer_mutex);
144 return offset;
145}

Referenced by terminal_screen_compose().

◆ terminal_screen_logs_set_offset()

void terminal_screen_logs_set_offset ( size_t rows)

Clamp the retained position after the available history changes.

Definition at line 119 of file terminal_screen.c.

119 {
120 if (!terminal_screen_logs_mutex_init()) {
121 g_log_scroll_rows = rows;
122 return;
123 }
124 mutex_lock(&g_log_viewer_mutex);
125 g_log_scroll_rows = rows;
126 mutex_unlock(&g_log_viewer_mutex);
127}

◆ terminal_screen_render()

void terminal_screen_render ( const terminal_screen_config_t * config)

Render a terminal screen with fixed header and scrolling logs.

Renders a screen following the pattern:

  1. Clear screen and move cursor to home (both stdout and stderr)
  2. Call render_header() callback to draw fixed header
  3. Calculate log area: rows - fixed_header_lines - 1 (prevent scroll)
  4. Fetch recent logs from session_log_buffer
  5. Calculate which logs fit (working backwards, accounting for wrapping)
  6. Display logs chronologically (oldest to newest, latest at bottom)
  7. Fill remaining lines to prevent terminal scroll
  8. Flush stdout
Parameters
configScreen configuration (header callback, line count, etc.)

Copies the header data and submits it to the UI controller. The controller owns size checks, terminal writes, and resize refreshes.

Definition at line 382 of file terminal_screen.c.

382 {
383 if (!config || !config->render_header || !config->user_data || !config->user_data_size)
384 return;
385 size_t bytes = sizeof(screen_snapshot_t) + config->user_data_size;
386 screen_snapshot_t *snapshot = SAFE_MALLOC(bytes, screen_snapshot_t *);
387 if (!snapshot)
388 return;
389 snapshot->config = *config;
390 memcpy(snapshot->header, config->user_data, config->user_data_size);
391 terminal_size_t minimum = {.cols = config->minimum_cols,
392 .rows = config->fixed_header_lines + (log_search_is_entering() ? 4 : 2)};
393 (void)ui_controller_submit(config->screen, config->output_fd, minimum, render_screen_snapshot, snapshot, bytes);
394 SAFE_FREE(snapshot);
395}
When code needs to work with media snapshot slot, it keeps busy, snapshot, capacity here instead of s...
terminal_screen_config_t config
unsigned char header[]
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)

References screen_snapshot_t::config, terminal_screen_config_t::fixed_header_lines, screen_snapshot_t::header, log_search_is_entering(), terminal_screen_config_t::minimum_cols, terminal_screen_config_t::output_fd, terminal_screen_config_t::render_header, SAFE_FREE, SAFE_MALLOC, terminal_screen_config_t::screen, ui_controller_submit(), terminal_screen_config_t::user_data, and terminal_screen_config_t::user_data_size.

Referenced by ui_status_display(), and ui_status_display_interactive().

◆ terminal_screen_set_output_fd()

void terminal_screen_set_output_fd ( int fd)

Set output file descriptor for terminal screens (splash/status).

Parameters
fdFile descriptor (STDOUT_FILENO for TTY, STDERR_FILENO for piped)

Configures where splash and status screens write their output. Should be called before terminal_screen_render() to control output destination.

Definition at line 367 of file terminal_screen.c.

367 {
369}
void frame_buffer_set_screen_output_fd(int fd)
Set output file descriptor for terminal screen (splash/status) rendering.

References frame_buffer_set_screen_output_fd().

Referenced by splash_intro_start(), and ui_status_display().