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.

Functions

void ui_input_edit_key (char *buffer, size_t capacity, size_t *length, size_t *cursor, keyboard_key_t key)
asciichat_error_t ui_input_render (frame_buffer_t *output, int row, int column, int width, int height, const char *text, size_t cursor, bool preserve_quotes)
asciichat_error_t ui_input_insert (char *buffer, size_t capacity, size_t *length, size_t *cursor, const char *text)

Function Documentation

◆ ui_input_edit_key()

void ui_input_edit_key ( char * buffer,
size_t capacity,
size_t * length,
size_t * cursor,
keyboard_key_t key )

UTF-8 cursor movement/deletion in caller-owned single-line storage.

Definition at line 83 of file ui/input.c.

83 {
84 if (!buffer || !capacity || !length || !position || *length >= capacity || *position > *length ||
85 strnlen(buffer, capacity) != *length || !utf8_is_valid(buffer) ||
86 (*position && ((unsigned char)buffer[*position] & 0xc0) == 0x80)) {
87 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid line editor storage, UTF-8 or cursor");
88 return;
89 }
90 size_t len = *length, cursor = *position, start = cursor, end = cursor;
91 if (key == KEY_HOME)
92 cursor = 0;
93 else if (key == KEY_END)
94 cursor = len;
95 else if (key == KEY_LEFT)
96 cursor = previous(buffer, cursor);
97 else if (key == KEY_RIGHT && cursor < len)
98 cursor += (size_t)utf8_next_char_bytes(buffer + cursor, len - cursor);
99 else if (key == 8 || key == 127)
100 start = previous(buffer, cursor);
101 else if (key == KEY_DELETE && cursor < len)
102 end += (size_t)utf8_next_char_bytes(buffer + cursor, len - cursor);
103 else if (key == 23) {
104 while (start && word_space((unsigned char)buffer[start - 1]))
105 start = previous(buffer, start);
106 while (start && !word_space((unsigned char)buffer[start - 1]))
107 start = previous(buffer, start);
108 } else if (key == KEY_CTRL_DELETE) {
109 while (end < len && !word_space((unsigned char)buffer[end]))
110 end += (size_t)utf8_next_char_bytes(buffer + end, len - end);
111 while (end < len && word_space((unsigned char)buffer[end]))
112 ++end;
113 } else {
114 char text[UTF8_BUFFER_SIZE];
115 if (key == '\t' || (key >= 32 && key != 127)) {
116 if (keyboard_text_encode(key, text))
117 (void)ui_input_insert(buffer, capacity, length, position, text);
118 }
119 return;
120 }
121 if (start != end) {
122 memmove(buffer + start, buffer + end, len - end + 1);
123 len -= end - start;
124 cursor = start;
125 }
126 *length = len;
127 *position = cursor;
128}
#define SET_ERRNO(code,...)
#define UTF8_BUFFER_SIZE
@ ERROR_INVALID_PARAM
@ 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_HOME
Home key (move to start of line).
Definition keyboard.h:62
@ KEY_DELETE
Delete key (forward delete).
Definition keyboard.h:61
@ KEY_CTRL_DELETE
Ctrl+Delete (delete word forward).
Definition keyboard.h:64
bool utf8_is_valid(const char *str)
Check if a string is valid UTF-8.
Definition utf8.c:128
int utf8_next_char_bytes(const char *str, size_t max_bytes)
Get byte length of next UTF-8 character.
Definition utf8.c:177
size_t keyboard_text_encode(keyboard_key_t key, char text[UTF8_BUFFER_SIZE])
asciichat_error_t ui_input_insert(char *buffer, size_t capacity, size_t *length, size_t *cursor, const char *text)
Definition ui/input.c:130

References ERROR_INVALID_PARAM, KEY_CTRL_DELETE, KEY_DELETE, KEY_END, KEY_HOME, KEY_LEFT, KEY_RIGHT, keyboard_text_encode(), SET_ERRNO, ui_input_insert(), UTF8_BUFFER_SIZE, utf8_is_valid(), and utf8_next_char_bytes().

Referenced by ui_input_read_key().

◆ ui_input_insert()

asciichat_error_t ui_input_insert ( char * buffer,
size_t capacity,
size_t * length,
size_t * cursor,
const char * text )

Insert UTF-8 text atomically, retaining storage unchanged on failure.

Definition at line 130 of file ui/input.c.

130 {
131 if (!buffer || !length || !cursor || !text || *length >= capacity || *cursor > *length ||
132 strnlen(buffer, capacity) != *length || !utf8_is_valid(text) || !utf8_is_valid(buffer) ||
133 (*cursor && ((unsigned char)buffer[*cursor] & 0xc0) == 0x80))
134 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid input insertion storage, text or cursor");
135 size_t bytes = strlen(text);
136 if (bytes >= capacity - *length)
137 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Input insertion exceeds field capacity");
138 memmove(buffer + *cursor + bytes, buffer + *cursor, *length - *cursor + 1);
139 memcpy(buffer + *cursor, text, bytes);
140 *cursor += bytes;
141 *length += bytes;
142 return ASCIICHAT_OK;
143}
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_BUFFER_OVERFLOW

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

Referenced by log_search_insert_text(), and ui_input_edit_key().

◆ ui_input_render()

asciichat_error_t ui_input_render ( frame_buffer_t * output,
int row,
int column,
int width,
int height,
const char * text,
size_t cursor,
bool preserve_quotes )

Render a scrollable UTF-8 field with an active-row marker and visible cursor. Width excludes the two-cell marker. Quoted paths may retain their opening quote.

Definition at line 6 of file ui/input.c.

7 {
8 if (!buffer || !text || row < 1 || col < 1 || width < 5 || height < 1 || cursor > strlen(text) ||
9 !utf8_is_valid(text) || (cursor && ((unsigned char)text[cursor] & 0xc0) == 0x80))
10 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid input field text, cursor or geometry");
11 int lines = 1;
12 size_t cursor_line = 0;
13 for (size_t i = 0; text[i]; ++i)
14 if (text[i] == '\n') {
15 ++lines;
16 if (i < cursor)
17 ++cursor_line;
18 }
19 int shown = lines < height ? lines : height;
20 int first = cursor_line >= (size_t)shown ? (int)cursor_line - shown + 1 : 0;
21 const char *line = text;
22 int number = 0, cursor_col = col + 2;
23 while (line && number < first + shown) {
24 const char *end = strchr(line, '\n');
25 if (!end)
26 end = line + strlen(line);
27 if (number >= first) {
28 const char *start = line;
29 bool active_line = (size_t)number == cursor_line;
30 bool quoted = preserve_quotes && end - line >= 2 && (*line == '"' || *line == '\'') && end[-1] == *line;
31 const char *anchor = active_line ? text + cursor : end;
32 int prefix = 0;
33 if (quoted && utf8_display_width_n(line, (size_t)(anchor - line)) > width - 1) {
34 // Keep the opening quote visible while scrolling the path's interior.
35 // The tail includes the closing quote when the cursor is at the end.
36 start = line + 1;
37 prefix = 4;
38 while (start < anchor && utf8_display_width_n(start, (size_t)(anchor - start)) > width - prefix - 1)
39 start += utf8_next_char_bytes(start, (size_t)(end - start));
40 } else if (active_line) {
41 while (start < anchor && utf8_display_width_n(start, (size_t)(anchor - start)) > width - 1)
42 start += utf8_next_char_bytes(start, (size_t)(end - start));
43 }
44 if (active_line)
45 cursor_col = col + 2 + prefix + utf8_display_width_n(start, (size_t)(anchor - start));
46 frame_buffer_printf(buffer, "\033[%d;%dH%s", row + number - first, col, active_line ? "> " : " ");
47 if (prefix)
48 frame_buffer_printf(buffer, "%c...", *line);
49 int used = prefix;
50 while (start < end) {
51 int bytes = utf8_next_char_bytes(start, (size_t)(end - start));
52 if (bytes <= 0)
53 break;
54 int cells = utf8_display_width_n(start, (size_t)bytes);
55 if (used + cells > width)
56 break;
57 frame_buffer_append(buffer, start, (size_t)bytes);
58 used += cells;
59 start += bytes;
60 }
61 frame_buffer_printf(buffer, "%*s", width - used, "");
62 }
63 ++number;
64 line = *end ? end + 1 : NULL;
65 }
66 frame_buffer_printf(buffer, "\033[%d;%dH\033[?25h", row + (int)cursor_line - first, cursor_col);
67 return ASCIICHAT_OK;
68}
char cells[(512 *256 *2)]
Definition fft.c:21
unsigned int height
Definition fft.c:23
unsigned int width
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_printf(frame_buffer_t *buf, const char *fmt,...)
Printf-style append to the buffer.
int utf8_display_width_n(const char *str, size_t max_bytes)
Calculate terminal display width of a UTF-8 string up to byte limit.
Definition utf8.c:102

References ASCIICHAT_OK, cells, ERROR_INVALID_PARAM, frame_buffer_append(), frame_buffer_printf(), height, SET_ERRNO, utf8_display_width_n(), utf8_is_valid(), utf8_next_char_bytes(), and width.

Referenced by ui_media_panel_render().