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

Go to the source code of this file.

Data Structures

struct  tui_input_events_t

Macros

#define TUI_INPUT_PASTE_BUFFER_SIZE   8192

Functions

void ui_input_poll_sync (void)
keyboard_key_t ui_input_read_key (ui_screen_t screen)
keyboard_key_t ui_input_wait_key (ui_screen_t screen, unsigned timeout_ms)
void ui_input_shutdown (void)
uint64_t ui_input_deadline (unsigned seconds)
bool ui_input_expired (uint64_t deadline)
void ui_input_timeout_report (unsigned seconds, const char *consequence)
asciichat_error_t ui_input_prompt (const char *prompt, char *buffer, size_t max_len, prompt_opts_t opts)
asciichat_error_t ui_input_take_paste (char *buffer, size_t capacity)
asciichat_error_t ui_input_prompt_prefilled (const char *prompt, char *buffer, size_t max_len, prompt_opts_t opts)
asciichat_error_t ui_input_media_begin (const char *text)
asciichat_error_t ui_input_media_take (char *output, size_t capacity)
asciichat_error_t ui_input_password (const char *target, char *buffer, size_t capacity, bool incorrect)
keyboard_key_t tui_input_events_read (tui_input_events_t *state, keyboard_key_t(*read_key)(void), bool(*classify_paste)(const char *))

Macro Definition Documentation

◆ TUI_INPUT_PASTE_BUFFER_SIZE

#define TUI_INPUT_PASTE_BUFFER_SIZE   8192

Definition at line 7 of file tui/input.h.

Function Documentation

◆ tui_input_events_read()

keyboard_key_t tui_input_events_read ( tui_input_events_t * state,
keyboard_key_t(* read_key )(void),
bool(* classify_paste )(const char *) )

Collect paste boundaries and optional application-classified text bursts. Once ready, the caller consumes/clears paste before reading another event.

Definition at line 80 of file tui/input.c.

81 {
82 if (s->ready)
83 return KEY_NONE;
84 for (unsigned i = 0; i < 1024; ++i) {
85 bool buffered = s->count != 0;
86 keyboard_key_t key = buffered ? s->keys[s->head++] : read_key();
87 if (buffered)
88 --s->count;
89 if (key == KEY_NONE)
90 return KEY_NONE;
91 if (key == KEY_PASTE_BEGIN) {
92 s->pasting = true;
93 s->invalid = false;
94 s->length = 0;
95 continue;
96 }
97 if (key == KEY_PASTE_END) {
98 if (!s->pasting)
99 continue;
100 s->pasting = false;
101 s->paste[s->length] = '\0';
102 s->ready = true;
103 return KEY_PASTE;
104 }
105 char text[UTF8_BUFFER_SIZE];
106 size_t bytes = keyboard_text_encode(key, text);
107 if (s->pasting) {
108 // Paste payloads remain raw UTF-8 bytes until whole-paste validation.
109 if (key >= 128 && key <= 255) {
110 text[0] = (char)key;
111 bytes = 1;
112 }
113 if (!bytes || (key < 32 && key != '\r' && key != '\n' && key != '\t') || key == 127 ||
114 bytes >= sizeof(s->paste) - s->length)
115 s->invalid = true;
116 else {
117 memcpy(s->paste + s->length, text, bytes);
118 s->length += bytes;
119 }
120 continue;
121 }
122 if (buffered || !bytes || key < 32 || key == 127)
123 return key;
124 s->head = 0;
125 s->keys[0] = key;
126 size_t count = 1;
127 keyboard_key_t next;
128 while (count < 8192 && (next = read_key()) != KEY_NONE) {
129 s->keys[count++] = next;
130 if (next == KEY_PASTE_BEGIN || next == KEY_PASTE_END)
131 break;
132 }
133 bool boundary = s->keys[count - 1] == KEY_PASTE_BEGIN || s->keys[count - 1] == KEY_PASTE_END;
134 size_t consumed = boundary ? count - 1 : count;
135 size_t text_count = consumed;
136 while (text_count && (s->keys[text_count - 1] == '\r' || s->keys[text_count - 1] == '\n'))
137 --text_count;
138 char burst[TUI_INPUT_BURST_BUFFER_SIZE];
139 size_t length = 0;
140 bool valid = true;
141 for (size_t n = 0; n < text_count; ++n) {
142 bytes = keyboard_text_encode(s->keys[n], text);
143 if (!bytes || s->keys[n] < 32 || s->keys[n] == 127 || bytes >= sizeof(burst) - length) {
144 valid = false;
145 break;
146 }
147 memcpy(burst + length, text, bytes);
148 length += bytes;
149 }
150 burst[length] = '\0';
151 if (text_count > 1 && classify_paste && classify_paste(burst)) {
152 // Keep the delimiter queued so the following payload remains framed.
153 s->head = consumed;
154 s->count = count - consumed;
155 s->invalid = !valid;
156 s->length = valid ? length : 0;
157 memcpy(s->paste, burst, s->length);
158 s->paste[s->length] = '\0';
159 s->ready = true;
160 return KEY_PASTE;
161 }
162 s->head = 1;
163 s->count = count - 1;
164 return key;
165 }
166 return KEY_NONE;
167}
bool valid
Definition fft.c:27
#define UTF8_BUFFER_SIZE
keyboard_key_t
Options for interactive line editing.
Definition keyboard.h:53
@ KEY_PASTE_BEGIN
Definition keyboard.h:65
@ KEY_NONE
No key pressed or no input available.
Definition keyboard.h:54
@ KEY_PASTE
Definition keyboard.h:67
@ KEY_PASTE_END
Definition keyboard.h:66
size_t keyboard_text_encode(keyboard_key_t key, char text[UTF8_BUFFER_SIZE])
#define TUI_INPUT_BURST_BUFFER_SIZE
Definition tui/input.c:4

References tui_input_events_t::count, tui_input_events_t::head, tui_input_events_t::invalid, KEY_NONE, KEY_PASTE, KEY_PASTE_BEGIN, KEY_PASTE_END, keyboard_text_encode(), tui_input_events_t::keys, tui_input_events_t::length, tui_input_events_t::paste, tui_input_events_t::pasting, tui_input_events_t::ready, TUI_INPUT_BURST_BUFFER_SIZE, UTF8_BUFFER_SIZE, and valid.

◆ ui_input_deadline()

uint64_t ui_input_deadline ( unsigned seconds)

Absolute monotonic deadline; user activity never extends it.

Definition at line 48 of file tui/input.c.

48 {
49 return time_get_ns() + (uint64_t)seconds * NS_PER_SEC_INT;
50}
unsigned long long uint64_t
Definition common.h:63
#define NS_PER_SEC_INT
Definition time.h:169

References NS_PER_SEC_INT.

Referenced by ui_input_media_begin(), and ui_mdns_select().

◆ ui_input_expired()

bool ui_input_expired ( uint64_t deadline)

Definition at line 52 of file tui/input.c.

52 {
53 return time_get_ns() >= deadline;
54}

Referenced by ui_input_read_key().

◆ ui_input_media_begin()

asciichat_error_t ui_input_media_begin ( const char * text)

Begin media text entry without blocking capture or network processing.

Definition at line 773 of file tui/input.c.

773 {
774 if (!text || !utf8_is_valid(text) || strlen(text) >= sizeof(g_media_editor.buffer) || !input_initialize())
775 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid media prompt text");
776 mutex_lock(&g_input_mutex);
777 memset(&g_media_editor, 0, sizeof(g_media_editor));
778 memcpy(g_media_editor.buffer, text, strlen(text) + 1);
779 g_media_editor.length = g_media_editor.cursor = strlen(text);
780 g_media_editor.deadline = ui_input_deadline(120);
781 asciichat_error_t result =
782 ui_prompt_present_kind("Open media: files or URLs\nDrop more files to queue; Enter plays; Esc cancels",
783 g_media_editor.buffer, g_media_editor.cursor, false, UI_PROMPT_MEDIA_INPUT);
784 g_media_editor.active = result == ASCIICHAT_OK;
785 input_unlock();
786 return result;
787}
#define SET_ERRNO(code,...)
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
bool utf8_is_valid(const char *str)
Check if a string is valid UTF-8.
Definition utf8.c:128
asciichat_error_t ui_prompt_present_kind(const char *prompt, const char *visible, size_t cursor, bool secret, ui_prompt_kind_t kind)
Definition prompt.c:270
uint64_t ui_input_deadline(unsigned seconds)
Definition tui/input.c:48

References ASCIICHAT_OK, ERROR_INVALID_PARAM, SET_ERRNO, ui_input_deadline(), ui_prompt_present_kind(), and utf8_is_valid().

Referenced by session_handle_keyboard_input().

◆ ui_input_media_take()

asciichat_error_t ui_input_media_take ( char * output,
size_t capacity )

Consume the value signaled by KEY_MEDIA_SUBMIT.

Definition at line 789 of file tui/input.c.

789 {
790 if (!output || !input_initialize())
791 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid media prompt destination");
792 mutex_lock(&g_input_mutex);
794 if (!g_media_editor.submitted || g_media_editor.length >= capacity)
795 result = SET_ERRNO(ERROR_INVALID_STATE, "No submitted media prompt");
796 else {
797 memcpy(output, g_media_editor.buffer, g_media_editor.length + 1);
799 media_playlist_t playlist = {0};
800 if (media_playlist_parse(output, &playlist) == ASCIICHAT_OK) {
801 bool file = false, url = false;
802 for (size_t i = 0; i < playlist.count; ++i) {
803 file |= !playlist.urls[i];
804 url |= playlist.urls[i];
805 }
806 if (file)
807 ascii_otel_ui_usage("media_input_prompt", "source_file", 0);
808 if (url)
809 ascii_otel_ui_usage("media_input_prompt", "source_url", 0);
810 ascii_otel_ui_usage("media_input_prompt", "queue_submitted", 0);
811 } else {
812 ascii_otel_ui_usage("media_input_prompt", "rejected", 0);
813 }
815 }
816 g_media_editor.submitted = false;
817 input_unlock();
818 return result;
819}
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
@ ERROR_INVALID_STATE
void ascii_otel_ui_usage(const char *screen, const char *event, uint64_t duration_ns)
Definition otel.c:434
asciichat_error_t media_playlist_parse(const char *text, media_playlist_t *list)
Definition playlist.c:32
When code needs to work with asciichat errno snapshot, it keeps total, copied here instead of scatter...
bool urls[64]
Definition playlist.h:11

References ascii_otel_ui_usage(), ASCIICHAT_ERRNO_DISMISSED, ASCIICHAT_OK, media_playlist_t::count, errno_scope_begin(), errno_scope_end(), ERROR_INVALID_PARAM, ERROR_INVALID_STATE, media_playlist_parse(), SET_ERRNO, and media_playlist_t::urls.

Referenced by session_handle_keyboard_input().

◆ ui_input_password()

asciichat_error_t ui_input_password ( const char * target,
char * buffer,
size_t capacity,
bool incorrect )

Server password dialog; cancellation requests normal application shutdown.

Definition at line 750 of file tui/input.c.

750 {
751 if (!target || !buffer || capacity < 2)
752 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid password dialog");
753 sodium_memzero(buffer, capacity);
754 if (env_pop_prompt_response(buffer, capacity))
755 return ASCIICHAT_OK;
756 if (!platform_is_interactive())
757 return SET_ERRNO(ERROR_CRYPTO_AUTH, "Server password requires an interactive terminal or --password");
758 asciichat_error_t result = input_prompt(target, buffer, capacity, PROMPT_OPTS_PASSWORD, true, incorrect);
759 if (result != ASCIICHAT_OK) {
762 return SET_ERRNO(ERROR_CRYPTO_AUTH, "Password entry cancelled");
763 }
764 return result;
765}
#define APP_CALLBACK_VOID(callback_name)
bool env_pop_prompt_response(char *response_out, size_t response_size)
Pop the first response from ASCII_CHAT_QUESTION_PROMPT_RESPONSE stack.
Definition env.c:96
@ ERROR_CRYPTO_AUTH
Definition error_codes.h:95
void shutdown_request(void)
Definition common.c:82
void signal_exit(void)
Definition main.c:253

References APP_CALLBACK_VOID, ASCIICHAT_OK, env_pop_prompt_response(), ERROR_CRYPTO_AUTH, ERROR_INVALID_PARAM, SET_ERRNO, shutdown_request(), and signal_exit().

Referenced by session_connection_establish().

◆ ui_input_poll_sync()

void ui_input_poll_sync ( void )

Poll debug controls without waiting for another input reader.

Definition at line 282 of file tui/input.c.

282 {
283#ifndef NDEBUG
284 if (atomic_load(&g_input_lifecycle.state) == LIFECYCLE_INITIALIZING || !input_initialize() || !input_ready() ||
285 mutex_trylock(&g_input_mutex) != 0)
286 return;
287 if (g_switch_pending) {
288 input_unlock();
289 return;
290 }
293 keyboard_key_t key = input_read_event();
294 if (key != KEY_PASTE && !log_search_is_entering())
295 key = ui_sync_handle_key(key);
296 if (key && g_sync_key_count < TUI_SYNC_KEY_CAPACITY) {
297 g_sync_keys[(g_sync_key_head + g_sync_key_count++) % 256] = key;
298 g_switch_pending = input_switch_key(key);
299 }
300 }
301 input_unlock();
302#endif
303}
bool log_search_is_entering(void)
Check if currently in input mode (typing pattern).
Definition search.c:408
keyboard_key_t ui_sync_handle_key(keyboard_key_t key)
Definition sync.c:364
int ui_controller_current_screen(void)
bool ui_controller_is_blocked(void)
@ UI_SCREEN_SYNC
#define TUI_SYNC_KEY_CAPACITY
Definition tui/input.c:10

References KEY_PASTE, log_search_is_entering(), TUI_SYNC_KEY_CAPACITY, ui_controller_current_screen(), ui_controller_is_blocked(), UI_SCREEN_SYNC, and ui_sync_handle_key().

◆ ui_input_prompt()

asciichat_error_t ui_input_prompt ( const char * prompt,
char * buffer,
size_t max_len,
prompt_opts_t opts )

Definition at line 767 of file tui/input.c.

767 {
768 if (buffer && max_len)
769 buffer[0] = '\0';
770 return ui_input_prompt_prefilled(prompt, buffer, max_len, opts);
771}
asciichat_error_t ui_input_prompt_prefilled(const char *prompt, char *buffer, size_t max_len, prompt_opts_t opts)
Definition tui/input.c:746

References ui_input_prompt_prefilled().

◆ ui_input_prompt_prefilled()

asciichat_error_t ui_input_prompt_prefilled ( const char * prompt,
char * buffer,
size_t max_len,
prompt_opts_t opts )

Editable prompt initialized with buffer contents.

Definition at line 746 of file tui/input.c.

746 {
747 return input_prompt(prompt, buffer, max_len, opts, false, false);
748}

Referenced by ui_input_prompt().

◆ ui_input_read_key()

keyboard_key_t ui_input_read_key ( ui_screen_t screen)

Input policy is separate from presentation. Covered controls are consumed and discarded.

Definition at line 353 of file tui/input.c.

353 {
354 if (!input_initialize())
355 return KEY_NONE;
356 if (!input_ready()) {
357 // Once recovery has an empty input queue, the next key is a fresh action.
358 if (atomic_load(&g_discarding_covered_input) && !ui_controller_state().covered)
359 atomic_store(&g_discarding_covered_input, false);
360 return KEY_NONE;
361 }
362 mutex_lock(&g_input_mutex);
364 // The previous event has now been dispatched by the session. Background
365 // polling must not overtake a pending switch with the next shortcut.
366 g_switch_pending = false;
367 if (input_discard_covered(state.covered)) {
368 input_unlock();
369 return KEY_NONE;
370 }
371 if (state.screen == UI_SCREEN_HELP &&
374 if (key == KEY_ESCAPE || key == KEY_QUESTION)
376 if (key == KEY_PASTE)
377 g_events.ready = false;
378 input_unlock();
379 return KEY_NONE;
380 }
381 if (state.screen == UI_SCREEN_ERROR && terminal_screen_logs_help_open()) {
382 keyboard_key_t key = input_next_key();
383 if (key != KEY_NONE)
385 if (key == KEY_PASTE)
386 g_events.ready = false;
387 input_unlock();
388 return KEY_NONE;
389 }
390 if (state.screen == UI_SCREEN_ERROR) {
391 keyboard_key_t key = input_next_key();
392 if (key == KEY_QUESTION)
394 else if (key != KEY_NONE && key != KEY_PASTE && key != 3) {
396 /* The first q dismisses the modal error. Only a later q, after the
397 * final error is gone, is allowed to reach the session exit handler. */
398 if (key == 'q' && !ui_error_popup_visible()) {
399 input_unlock();
400 return key;
401 }
402 }
403 if (key == KEY_PASTE)
404 g_events.ready = false;
405 input_unlock();
406 return key == 3 ? key : KEY_NONE;
407 }
408 if (state.screen == UI_SCREEN_NOTICE) {
409 g_events.ready = false;
410 g_sync_key_count = 0;
411 keyboard_key_t notice_key = input_read_event();
412 if (notice_key == KEY_PASTE)
413 g_events.ready = false;
414 bool first_use = ui_controller_first_use_key(notice_key);
415 if (!first_use &&
416 (notice_key == KEY_ESCAPE || notice_key == '\r' || notice_key == '\n' || notice_key == 'q' || notice_key == 3))
418 input_unlock();
419 return notice_key == 'q' || notice_key == 3 ? notice_key : KEY_NONE;
420 }
421 if (state.screen == UI_SCREEN_UPDATE && update_banner_is_overlay_open()) {
422 keyboard_key_t update_key = input_read_event();
423 if (update_key == '=' || update_key == KEY_0 || update_key == KEY_QUESTION || update_key == ']') {
424 if (update_key != KEY_QUESTION)
426 if (update_key == KEY_QUESTION) {
428 } else {
429 update_key = input_common_key(update_key, UI_SCREEN_UPDATE);
430 (void)ui_sync_handle_key(update_key);
431 }
432 } else if (update_key != KEY_NONE)
433 (void)update_banner_handle_key(update_key);
434 input_unlock();
435 return KEY_NONE;
436 }
437 if (screen == UI_SCREEN_MEDIA && g_media_editor.active && state.screen == UI_SCREEN_PROMPT) {
438 keyboard_key_t key = input_next_key();
439 bool switch_screen = input_switch_key(key) && key != '=' && (g_media_editor.length == 0 || key == 15);
440#ifndef NDEBUG
441 switch_screen |= key == KEY_0 && g_media_editor.length == 0;
442#endif
443 if (switch_screen) {
444 // Keep the old prompt on screen until its replacement is published.
445 if (key != KEY_QUESTION)
446 g_media_editor.active = false;
447 if (key == KEY_0)
448 key = ui_sync_handle_key(key);
449 key = input_common_key(key, state.screen);
450 g_switch_pending = input_switch_key(key);
451 input_unlock();
452 return key;
453 }
454 if (key == KEY_ESCAPE || key == 3 || ui_input_expired(g_media_editor.deadline)) {
455 if (key == KEY_PASTE)
456 g_events.ready = false;
457 ascii_otel_ui_usage("media_input_prompt", ui_input_expired(g_media_editor.deadline) ? "timed_out" : "cancelled",
458 0);
459 g_media_editor.active = false;
461 } else if (key == '\r' || key == '\n') {
462 g_media_editor.active = false;
463 g_media_editor.submitted = true;
464 ascii_otel_ui_usage("media_input_prompt", "submitted", 0);
465 input_unlock();
466 return KEY_MEDIA_SUBMIT;
467 } else if (key != KEY_NONE) {
468 if (key == KEY_PASTE)
469 ascii_otel_ui_usage("media_input_prompt", "paste", 0);
470 if (key == KEY_PASTE) {
471 if (!g_events.invalid && utf8_is_valid(g_events.paste)) {
472 media_playlist_t items = {0}, added = {0};
473 bool valid = !g_media_editor.length || media_playlist_parse(g_media_editor.buffer, &items) == ASCIICHAT_OK;
474 valid = valid && media_playlist_parse(g_events.paste, &added) == ASCIICHAT_OK;
475 if (valid) {
476 for (size_t i = 0; i < added.count && valid; ++i)
477 valid = media_playlist_append(&items, added.paths[i], added.urls[i]) == ASCIICHAT_OK;
478 char formatted[sizeof(g_media_editor.buffer)];
479 valid = valid && media_playlist_format(&items, formatted, sizeof(formatted)) == ASCIICHAT_OK;
480 if (valid) {
481 memcpy(g_media_editor.buffer, formatted, strlen(formatted) + 1);
482 g_media_editor.length = g_media_editor.cursor = strlen(formatted);
483 }
484 }
485 if (valid)
486 ascii_otel_ui_usage("media_input_prompt", "queue_changed", 0);
487 CLEAR_ERRNO();
488 }
489 g_events.ready = false;
490 g_media_editor.pasted = true;
491 } else if (!(key == ' ' && g_media_editor.pasted && g_media_editor.cursor == g_media_editor.length)) {
492 g_media_editor.pasted = false;
493 ui_input_edit_key(g_media_editor.buffer, sizeof(g_media_editor.buffer), &g_media_editor.length,
494 &g_media_editor.cursor, key);
495 }
496 if (ui_prompt_present_kind("Open media: files or URLs\nDrop more files to queue; Enter plays; Esc cancels",
497 g_media_editor.buffer, g_media_editor.cursor, false,
498 UI_PROMPT_MEDIA_INPUT) != ASCIICHAT_OK) {
499 g_media_editor.active = false;
501 CLEAR_ERRNO();
502 }
503 }
504 input_unlock();
505 return KEY_NONE;
506 }
507 bool primary =
509 bool server = GET_OPTION(detected_mode) == MODE_SERVER || GET_OPTION(detected_mode) == MODE_DISCOVERY_SERVICE ||
510 GET_OPTION(detected_mode) == MODE_MCP_SERVER;
511 bool server_owner = screen == UI_SCREEN_STATS && server && !ui_controller_screen_live(UI_SCREEN_STATUS);
512 bool active = state.screen < 0 || state.screen == (int)screen ||
513 ((primary || server_owner) && state.screen <= UI_SCREEN_SYNC && state.screen != UI_SCREEN_STATS);
515 if (active) {
516 if (g_sync_key_count) {
517 key = g_sync_keys[g_sync_key_head];
518 g_sync_key_head = (g_sync_key_head + 1) % 256;
519 --g_sync_key_count;
520 }
521 if (key == KEY_NONE)
522 key = state.screen == UI_SCREEN_HELP && keyboard_help_check_signal_cancel() ? KEY_QUESTION : input_read_event();
523 if (key == KEY_F12) {
524 // Synchronous prompts own their input, including function keys.
525 // Opening the update checker here would suspend credential entry.
528 key = KEY_NONE;
529 }
530 if (screen != UI_SCREEN_PROMPT) {
531 key = input_common_key(key, state.screen);
532 if (key != KEY_PASTE && !log_search_is_entering() &&
533 !(screen == UI_SCREEN_MDNS && state.screen == UI_SCREEN_MDNS))
534 key = ui_sync_handle_key(key);
535 }
536 }
537 if (key == KEY_PASTE && log_search_is_entering()) {
538 if (!g_events.invalid)
539 (void)log_search_insert_text(g_events.paste);
540 g_events.ready = false;
541 key = KEY_NONE;
542 }
543 if (key == KEY_PASTE && screen != UI_SCREEN_MEDIA && screen != UI_SCREEN_PROMPT) {
544 g_events.ready = false;
545 key = KEY_NONE;
546 }
548 key = KEY_NONE;
549 g_switch_pending |= input_switch_key(key);
550 input_unlock();
551 return key;
552}
#define CLEAR_ERRNO()
bool ui_error_popup_visible(void)
Definition error_popup.c:71
void ui_error_popup_key(keyboard_key_t key)
Definition error_popup.c:87
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
@ MODE_MCP_SERVER
Model Context Protocol rendering server.
Definition options.h:925
@ MODE_DISCOVERY_SERVICE
Discovery server mode - session management and WebRTC signaling.
Definition options.h:923
@ MODE_SERVER
Server mode - network server options.
Definition options.h:920
@ KEY_ESCAPE
Escape key (ESC).
Definition keyboard.h:55
@ KEY_MEDIA_SUBMIT
Nonblocking media prompt submitted.
Definition keyboard.h:68
@ KEY_0
'0' key - toggle sync debug screen
Definition keyboard.h:75
@ KEY_F12
Open the version update checker TUI.
Definition keyboard.h:69
@ KEY_QUESTION
'?' key - show help screen
Definition keyboard.h:83
void keyboard_help_toggle_terminal(int fd)
int keyboard_help_parent_screen(void)
bool update_banner_handle_key(int key)
bool keyboard_help_check_signal_cancel(void)
Check if help screen closure was requested by signal handler.
void update_banner_toggle_overlay(void)
bool update_banner_is_overlay_open(void)
asciichat_error_t media_playlist_format(const media_playlist_t *list, char *text, size_t capacity)
Definition playlist.c:89
asciichat_error_t media_playlist_append(media_playlist_t *list, const char *path, bool url)
Definition playlist.c:7
void ui_prompt_remove(void)
Definition prompt.c:307
asciichat_error_t log_search_insert_text(const char *text)
Definition search.c:480
bool terminal_screen_logs_help_open(void)
bool terminal_screen_logs_handle_key(int key)
void ui_controller_remove(ui_screen_t screen)
bool ui_controller_first_use_key(int key)
ui_presentation_state_t ui_controller_state(void)
bool ui_controller_screen_live(ui_screen_t screen)
int ui_controller_active_fd(void)
@ UI_SCREEN_PROMPT
@ UI_SCREEN_NOTICE
@ UI_SCREEN_MEDIA
@ UI_SCREEN_STATS
@ UI_SCREEN_UPDATE
@ UI_SCREEN_SPLASH
@ UI_SCREEN_STATUS
@ UI_SCREEN_MDNS
@ UI_SCREEN_HELP
@ UI_SCREEN_ERROR
bool ui_input_expired(uint64_t deadline)
Definition tui/input.c:52
void ui_input_edit_key(char *buffer, size_t capacity, size_t *length, size_t *position, keyboard_key_t key)
Definition ui/input.c:83

References ascii_otel_ui_usage(), ASCIICHAT_OK, CLEAR_ERRNO, GET_OPTION, KEY_0, KEY_ESCAPE, KEY_F12, KEY_MEDIA_SUBMIT, KEY_NONE, KEY_PASTE, KEY_QUESTION, keyboard_help_check_signal_cancel(), keyboard_help_parent_screen(), keyboard_help_toggle_terminal(), log_search_insert_text(), log_search_is_entering(), media_playlist_append(), media_playlist_format(), media_playlist_parse(), MODE_DISCOVERY_SERVICE, MODE_MCP_SERVER, MODE_SERVER, media_playlist_t::paths, terminal_screen_logs_handle_key(), terminal_screen_logs_help_open(), ui_controller_active_fd(), ui_controller_first_use_key(), ui_controller_remove(), ui_controller_screen_live(), ui_controller_state(), ui_error_popup_key(), ui_error_popup_visible(), ui_input_edit_key(), ui_input_expired(), ui_prompt_present_kind(), ui_prompt_remove(), UI_SCREEN_ERROR, UI_SCREEN_HELP, UI_SCREEN_MDNS, UI_SCREEN_MEDIA, UI_SCREEN_NOTICE, UI_SCREEN_PROMPT, UI_SCREEN_SPLASH, UI_SCREEN_STATS, UI_SCREEN_STATUS, UI_SCREEN_SYNC, UI_SCREEN_UPDATE, ui_sync_handle_key(), update_banner_handle_key(), update_banner_is_overlay_open(), update_banner_toggle_overlay(), utf8_is_valid(), and valid.

Referenced by display_poll_keyboard(), session_render_loop(), ui_input_wait_key(), and ui_status_display_interactive().

◆ ui_input_shutdown()

void ui_input_shutdown ( void )

Definition at line 565 of file tui/input.c.

565 {
566 if (!lifecycle_destroy_once(&g_input_lifecycle))
567 return;
568 mutex_destroy(&g_input_prompt_mutex);
569 mutex_destroy(&g_input_mutex);
570 NAMED_UNREGISTER(&g_input_lifecycle.state);
571 lifecycle_destroy_commit(&g_input_lifecycle);
572}
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.

References NAMED_UNREGISTER.

Referenced by asciichat_shared_destroy().

◆ ui_input_take_paste()

asciichat_error_t ui_input_take_paste ( char * buffer,
size_t capacity )

Copy a complete paste event. Invalid/oversized payloads fail without truncation.

Definition at line 266 of file tui/input.c.

266 {
267 if (!buffer || !capacity || !input_initialize())
268 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid paste buffer");
269 mutex_lock(&g_input_mutex);
271 if (g_events.invalid || g_events.length >= capacity || !utf8_is_valid(g_events.paste))
272 result = SET_ERRNO(ERROR_INVALID_PARAM, "Paste media paths or URLs without terminal controls (maximum 8191 bytes)");
273 else
274 memcpy(buffer, g_events.paste, g_events.length + 1);
275 g_events.length = 0;
276 sodium_memzero(g_events.paste, sizeof(g_events.paste));
277 g_events.ready = false;
278 input_unlock();
279 return result;
280}

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

Referenced by session_handle_keyboard_input().

◆ ui_input_timeout_report()

void ui_input_timeout_report ( unsigned seconds,
const char * consequence )

Definition at line 56 of file tui/input.c.

56 {
58 int length = snprintf(message, sizeof(message), "\nPrompt timed out after %u seconds; %s.\n", seconds, consequence);
59 // This diagnostic must remain visible even while a background screen owns the terminal.
60 if (length > 0)
61 platform_write_all(STDERR_FILENO, message, (size_t)length < sizeof(message) ? (size_t)length : sizeof(message) - 1);
62 SET_ERRNO(ERROR_PROMPT_TIMEOUT, "Prompt timed out after %u seconds: %s", seconds, consequence);
63}
@ ERROR_PROMPT_TIMEOUT
size_t platform_write_all(int fd, const void *buf, size_t count)
Write all bytes to a file descriptor, handling partial writes.
#define TUI_INPUT_MESSAGE_BUFFER_SIZE
Definition tui/input.c:3

References ERROR_PROMPT_TIMEOUT, platform_write_all(), SET_ERRNO, and TUI_INPUT_MESSAGE_BUFFER_SIZE.

◆ ui_input_wait_key()

keyboard_key_t ui_input_wait_key ( ui_screen_t screen,
unsigned timeout_ms )

Definition at line 554 of file tui/input.c.

554 {
555 uint64_t deadline = time_get_ns() + (uint64_t)timeout_ms * NS_PER_MS_INT;
556 do {
558 if (key != KEY_NONE || shutdown_is_requested())
559 return key;
560 platform_sleep_ns(NS_PER_MS_INT);
561 } while (time_get_ns() < deadline);
562 return KEY_NONE;
563}
#define NS_PER_MS_INT
Definition time.h:168
bool shutdown_is_requested(void)
Check if shutdown has been requested.
Definition common.c:86
keyboard_key_t ui_input_read_key(ui_screen_t screen)
Definition tui/input.c:353

References KEY_NONE, NS_PER_MS_INT, shutdown_is_requested(), and ui_input_read_key().

Referenced by update_banner_show_prompt().