Use `errno_t` in all uspace and kernel code.
[helenos.git] / uspace / lib / clui / tinput.c
blob674ffb8b80e2f97a03325c0aa25d35262f693e36
1 /*
2 * Copyright (c) 2011 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <str.h>
32 #include <io/console.h>
33 #include <io/keycode.h>
34 #include <io/style.h>
35 #include <io/color.h>
36 #include <vfs/vfs.h>
37 #include <clipboard.h>
38 #include <macros.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <stdbool.h>
42 #include <tinput.h>
44 #define LIN_TO_COL(ti, lpos) ((lpos) % ((ti)->con_cols))
45 #define LIN_TO_ROW(ti, lpos) ((lpos) / ((ti)->con_cols))
46 #define LIN_POS(ti, col, row) ((col) + (row) * (ti)->con_cols)
48 /** Seek direction */
49 typedef enum {
50 seek_backward = -1,
51 seek_forward = 1
52 } seek_dir_t;
54 static void tinput_init(tinput_t *);
55 static void tinput_insert_string(tinput_t *, const char *);
56 static void tinput_sel_get_bounds(tinput_t *, size_t *, size_t *);
57 static bool tinput_sel_active(tinput_t *);
58 static void tinput_sel_all(tinput_t *);
59 static void tinput_sel_delete(tinput_t *);
60 static void tinput_key_ctrl(tinput_t *, kbd_event_t *);
61 static void tinput_key_shift(tinput_t *, kbd_event_t *);
62 static void tinput_key_ctrl_shift(tinput_t *, kbd_event_t *);
63 static void tinput_key_unmod(tinput_t *, kbd_event_t *);
64 static void tinput_pre_seek(tinput_t *, bool);
65 static void tinput_post_seek(tinput_t *, bool);
67 static void tinput_console_set_lpos(tinput_t *ti, unsigned lpos)
69 console_set_pos(ti->console, LIN_TO_COL(ti, lpos),
70 LIN_TO_ROW(ti, lpos));
73 /** Create a new text input field. */
74 tinput_t *tinput_new(void)
76 tinput_t *ti;
78 ti = calloc(1, sizeof(tinput_t));
79 if (ti == NULL)
80 return NULL;
82 tinput_init(ti);
83 return ti;
86 /** Destroy text input field. */
87 void tinput_destroy(tinput_t *ti)
89 if (ti->prompt != NULL)
90 free(ti->prompt);
91 free(ti);
94 static void tinput_display_prompt(tinput_t *ti)
96 tinput_console_set_lpos(ti, ti->prompt_coord);
98 console_set_style(ti->console, STYLE_EMPHASIS);
99 printf("%s", ti->prompt);
100 console_flush(ti->console);
101 console_set_style(ti->console, STYLE_NORMAL);
104 static void tinput_display_tail(tinput_t *ti, size_t start, size_t pad)
106 wchar_t *dbuf = malloc((INPUT_MAX_SIZE + 1) * sizeof(wchar_t));
107 if (!dbuf)
108 return;
110 size_t sa;
111 size_t sb;
112 tinput_sel_get_bounds(ti, &sa, &sb);
114 tinput_console_set_lpos(ti, ti->text_coord + start);
115 console_set_style(ti->console, STYLE_NORMAL);
117 size_t p = start;
118 if (p < sa) {
119 memcpy(dbuf, ti->buffer + p, (sa - p) * sizeof(wchar_t));
120 dbuf[sa - p] = '\0';
121 printf("%ls", dbuf);
122 p = sa;
125 if (p < sb) {
126 console_flush(ti->console);
127 console_set_style(ti->console, STYLE_SELECTED);
129 memcpy(dbuf, ti->buffer + p,
130 (sb - p) * sizeof(wchar_t));
131 dbuf[sb - p] = '\0';
132 printf("%ls", dbuf);
133 p = sb;
136 console_flush(ti->console);
137 console_set_style(ti->console, STYLE_NORMAL);
139 if (p < ti->nc) {
140 memcpy(dbuf, ti->buffer + p,
141 (ti->nc - p) * sizeof(wchar_t));
142 dbuf[ti->nc - p] = '\0';
143 printf("%ls", dbuf);
146 for (p = 0; p < pad; p++)
147 putchar(' ');
149 console_flush(ti->console);
151 free(dbuf);
154 static char *tinput_get_str(tinput_t *ti)
156 return wstr_to_astr(ti->buffer);
159 static void tinput_position_caret(tinput_t *ti)
161 tinput_console_set_lpos(ti, ti->text_coord + ti->pos);
164 /** Update text_coord, prompt_coord in case the screen could have scrolled. */
165 static void tinput_update_origin(tinput_t *ti)
167 unsigned end_coord = ti->text_coord + ti->nc;
168 unsigned end_row = LIN_TO_ROW(ti, end_coord);
170 unsigned scroll_rows;
172 /* Update coords if the screen scrolled. */
173 if (end_row >= ti->con_rows) {
174 scroll_rows = end_row - ti->con_rows + 1;
175 ti->text_coord -= ti->con_cols * scroll_rows;
176 ti->prompt_coord -= ti->con_cols * scroll_rows;
180 static void tinput_jump_after(tinput_t *ti)
182 tinput_console_set_lpos(ti, ti->text_coord + ti->nc);
183 console_flush(ti->console);
184 putchar('\n');
187 static errno_t tinput_display(tinput_t *ti)
189 sysarg_t col0, row0;
191 if (console_get_pos(ti->console, &col0, &row0) != EOK)
192 return EIO;
194 ti->prompt_coord = row0 * ti->con_cols + col0;
195 ti->text_coord = ti->prompt_coord + str_length(ti->prompt);
197 tinput_display_prompt(ti);
198 tinput_display_tail(ti, 0, 0);
199 tinput_position_caret(ti);
201 return EOK;
204 static void tinput_insert_char(tinput_t *ti, wchar_t c)
206 if (ti->nc == INPUT_MAX_SIZE)
207 return;
209 unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + 1;
210 if (new_width % ti->con_cols == 0) {
211 /* Advancing to new line. */
212 sysarg_t new_height = (new_width / ti->con_cols) + 1;
213 if (new_height >= ti->con_rows) {
214 /* Disallow text longer than 1 page for now. */
215 return;
219 size_t i;
220 for (i = ti->nc; i > ti->pos; i--)
221 ti->buffer[i] = ti->buffer[i - 1];
223 ti->buffer[ti->pos] = c;
224 ti->pos += 1;
225 ti->nc += 1;
226 ti->buffer[ti->nc] = '\0';
227 ti->sel_start = ti->pos;
229 tinput_display_tail(ti, ti->pos - 1, 0);
230 tinput_update_origin(ti);
231 tinput_position_caret(ti);
234 static void tinput_insert_string(tinput_t *ti, const char *str)
236 size_t ilen = min(str_length(str), INPUT_MAX_SIZE - ti->nc);
237 if (ilen == 0)
238 return;
240 unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + ilen;
241 unsigned new_height = (new_width / ti->con_cols) + 1;
242 if (new_height >= ti->con_rows) {
243 /* Disallow text longer than 1 page for now. */
244 return;
247 if (ti->nc > 0) {
248 size_t i;
249 for (i = ti->nc; i > ti->pos; i--)
250 ti->buffer[i + ilen - 1] = ti->buffer[i - 1];
253 size_t off = 0;
254 size_t i = 0;
255 while (i < ilen) {
256 wchar_t c = str_decode(str, &off, STR_NO_LIMIT);
257 if (c == '\0')
258 break;
260 /* Filter out non-printable chars. */
261 if (c < 32)
262 c = 32;
264 ti->buffer[ti->pos + i] = c;
265 i++;
268 ti->pos += ilen;
269 ti->nc += ilen;
270 ti->buffer[ti->nc] = '\0';
271 ti->sel_start = ti->pos;
273 tinput_display_tail(ti, ti->pos - ilen, 0);
274 tinput_update_origin(ti);
275 tinput_position_caret(ti);
278 static void tinput_backspace(tinput_t *ti)
280 if (tinput_sel_active(ti)) {
281 tinput_sel_delete(ti);
282 return;
285 if (ti->pos == 0)
286 return;
288 size_t i;
289 for (i = ti->pos; i < ti->nc; i++)
290 ti->buffer[i - 1] = ti->buffer[i];
292 ti->pos -= 1;
293 ti->nc -= 1;
294 ti->buffer[ti->nc] = '\0';
295 ti->sel_start = ti->pos;
297 tinput_display_tail(ti, ti->pos, 1);
298 tinput_position_caret(ti);
301 static void tinput_delete(tinput_t *ti)
303 if (tinput_sel_active(ti)) {
304 tinput_sel_delete(ti);
305 return;
308 if (ti->pos == ti->nc)
309 return;
311 ti->pos += 1;
312 ti->sel_start = ti->pos;
314 tinput_backspace(ti);
317 static void tinput_seek_cell(tinput_t *ti, seek_dir_t dir, bool shift_held)
319 tinput_pre_seek(ti, shift_held);
321 if (dir == seek_forward) {
322 if (ti->pos < ti->nc)
323 ti->pos += 1;
324 } else {
325 if (ti->pos > 0)
326 ti->pos -= 1;
329 tinput_post_seek(ti, shift_held);
332 static void tinput_seek_word(tinput_t *ti, seek_dir_t dir, bool shift_held)
334 tinput_pre_seek(ti, shift_held);
336 if (dir == seek_forward) {
337 if (ti->pos == ti->nc)
338 return;
340 while (true) {
341 ti->pos += 1;
343 if (ti->pos == ti->nc)
344 break;
346 if ((ti->buffer[ti->pos - 1] == ' ') &&
347 (ti->buffer[ti->pos] != ' '))
348 break;
350 } else {
351 if (ti->pos == 0)
352 return;
354 while (true) {
355 ti->pos -= 1;
357 if (ti->pos == 0)
358 break;
360 if (ti->buffer[ti->pos - 1] == ' ' &&
361 ti->buffer[ti->pos] != ' ')
362 break;
367 tinput_post_seek(ti, shift_held);
370 static void tinput_seek_vertical(tinput_t *ti, seek_dir_t dir, bool shift_held)
372 tinput_pre_seek(ti, shift_held);
374 if (dir == seek_forward) {
375 if (ti->pos + ti->con_cols <= ti->nc)
376 ti->pos = ti->pos + ti->con_cols;
377 } else {
378 if (ti->pos >= ti->con_cols)
379 ti->pos = ti->pos - ti->con_cols;
382 tinput_post_seek(ti, shift_held);
385 static void tinput_seek_scrpos(tinput_t *ti, int col, int line, bool shift_held)
387 unsigned lpos;
388 tinput_pre_seek(ti, shift_held);
390 lpos = LIN_POS(ti, col, line);
392 if (lpos > ti->text_coord)
393 ti->pos = lpos - ti->text_coord;
394 else
395 ti->pos = 0;
396 if (ti->pos > ti->nc)
397 ti->pos = ti->nc;
399 tinput_post_seek(ti, shift_held);
402 static void tinput_seek_max(tinput_t *ti, seek_dir_t dir, bool shift_held)
404 tinput_pre_seek(ti, shift_held);
406 if (dir == seek_backward)
407 ti->pos = 0;
408 else
409 ti->pos = ti->nc;
411 tinput_post_seek(ti, shift_held);
414 static void tinput_pre_seek(tinput_t *ti, bool shift_held)
416 if ((tinput_sel_active(ti)) && (!shift_held)) {
417 /* Unselect and redraw. */
418 ti->sel_start = ti->pos;
419 tinput_display_tail(ti, 0, 0);
420 tinput_position_caret(ti);
424 static void tinput_post_seek(tinput_t *ti, bool shift_held)
426 if (shift_held) {
427 /* Selecting text. Need redraw. */
428 tinput_display_tail(ti, 0, 0);
429 } else {
430 /* Shift not held. Keep selection empty. */
431 ti->sel_start = ti->pos;
434 tinput_position_caret(ti);
437 static void tinput_history_insert(tinput_t *ti, char *str)
439 if (ti->hnum < HISTORY_LEN) {
440 ti->hnum += 1;
441 } else {
442 if (ti->history[HISTORY_LEN] != NULL)
443 free(ti->history[HISTORY_LEN]);
446 size_t i;
447 for (i = ti->hnum; i > 1; i--)
448 ti->history[i] = ti->history[i - 1];
450 ti->history[1] = str_dup(str);
452 if (ti->history[0] != NULL) {
453 free(ti->history[0]);
454 ti->history[0] = NULL;
458 static void tinput_set_str(tinput_t *ti, const char *str)
460 str_to_wstr(ti->buffer, INPUT_MAX_SIZE, str);
461 ti->nc = wstr_length(ti->buffer);
462 ti->pos = ti->nc;
463 ti->sel_start = ti->pos;
466 static void tinput_sel_get_bounds(tinput_t *ti, size_t *sa, size_t *sb)
468 if (ti->sel_start < ti->pos) {
469 *sa = ti->sel_start;
470 *sb = ti->pos;
471 } else {
472 *sa = ti->pos;
473 *sb = ti->sel_start;
477 static bool tinput_sel_active(tinput_t *ti)
479 return (ti->sel_start != ti->pos);
482 static void tinput_sel_all(tinput_t *ti)
484 ti->sel_start = 0;
485 ti->pos = ti->nc;
486 tinput_display_tail(ti, 0, 0);
487 tinput_position_caret(ti);
490 static void tinput_sel_delete(tinput_t *ti)
492 size_t sa;
493 size_t sb;
495 tinput_sel_get_bounds(ti, &sa, &sb);
496 if (sa == sb)
497 return;
499 memmove(ti->buffer + sa, ti->buffer + sb,
500 (ti->nc - sb) * sizeof(wchar_t));
502 ti->pos = ti->sel_start = sa;
503 ti->nc -= (sb - sa);
504 ti->buffer[ti->nc] = '\0';
506 tinput_display_tail(ti, sa, sb - sa);
507 tinput_position_caret(ti);
510 static void tinput_sel_copy_to_cb(tinput_t *ti)
512 size_t sa;
513 size_t sb;
515 tinput_sel_get_bounds(ti, &sa, &sb);
517 char *str;
519 if (sb < ti->nc) {
520 wchar_t tmp_c = ti->buffer[sb];
521 ti->buffer[sb] = '\0';
522 str = wstr_to_astr(ti->buffer + sa);
523 ti->buffer[sb] = tmp_c;
524 } else
525 str = wstr_to_astr(ti->buffer + sa);
527 if (str == NULL)
528 goto error;
530 if (clipboard_put_str(str) != EOK)
531 goto error;
533 free(str);
534 return;
536 error:
537 /* TODO: Give the user some kind of warning. */
538 return;
541 static void tinput_paste_from_cb(tinput_t *ti)
543 char *str;
544 errno_t rc = clipboard_get_str(&str);
546 if ((rc != EOK) || (str == NULL)) {
547 /* TODO: Give the user some kind of warning. */
548 return;
551 tinput_insert_string(ti, str);
552 free(str);
555 static void tinput_history_seek(tinput_t *ti, int offs)
557 if (offs >= 0) {
558 if (ti->hpos + offs > ti->hnum)
559 return;
560 } else {
561 if (ti->hpos < (size_t) -offs)
562 return;
565 if (ti->history[ti->hpos] != NULL) {
566 free(ti->history[ti->hpos]);
567 ti->history[ti->hpos] = NULL;
570 ti->history[ti->hpos] = tinput_get_str(ti);
571 ti->hpos += offs;
573 int pad = (int) ti->nc - str_length(ti->history[ti->hpos]);
574 if (pad < 0)
575 pad = 0;
577 tinput_set_str(ti, ti->history[ti->hpos]);
578 tinput_display_tail(ti, 0, pad);
579 tinput_update_origin(ti);
580 tinput_position_caret(ti);
583 /** Compare two entries in array of completions. */
584 static int compl_cmp(const void *va, const void *vb)
586 const char *a = *(const char **) va;
587 const char *b = *(const char **) vb;
589 return str_cmp(a, b);
592 static size_t common_pref_len(const char *a, const char *b)
594 size_t i;
595 size_t a_off, b_off;
596 wchar_t ca, cb;
598 i = 0;
599 a_off = 0;
600 b_off = 0;
602 while (true) {
603 ca = str_decode(a, &a_off, STR_NO_LIMIT);
604 cb = str_decode(b, &b_off, STR_NO_LIMIT);
606 if (ca == '\0' || cb == '\0' || ca != cb)
607 break;
608 ++i;
611 return i;
614 /* Print a list of completions */
615 static void tinput_show_completions(tinput_t *ti, char **compl, size_t cnum)
617 unsigned int i;
618 /* Determine the maximum width of the completion in chars */
619 size_t max_width = 0;
620 for (i = 0; i < cnum; i++)
621 max_width = max(max_width, str_width(compl[i]));
623 unsigned int cols = max(1, (ti->con_cols + 1) / (max_width + 1));
624 unsigned int padding = 0;
625 if ((cols * max_width) + (cols - 1) < ti->con_cols) {
626 padding = ti->con_cols - (cols * max_width) - (cols - 1);
628 unsigned int col_width = max_width + padding / cols;
629 unsigned int rows = cnum / cols + ((cnum % cols) != 0);
631 unsigned int row, col;
633 for (row = 0; row < rows; row++) {
634 unsigned int display_col = 0;
635 for (col = 0; col < cols; col++) {
636 size_t compl_idx = col * rows + row;
637 if (compl_idx >= cnum)
638 break;
639 if (col) {
640 printf(" ");
641 display_col++;
643 printf("%s", compl[compl_idx]);
644 size_t compl_width = str_width(compl[compl_idx]);
645 display_col += compl_width;
646 if (col < cols - 1) {
647 for (i = compl_width; i < col_width; i++) {
648 printf(" ");
649 display_col++;
653 if ((display_col % ti->con_cols) > 0)
654 printf("\n");
656 fflush(stdout);
660 static void tinput_text_complete(tinput_t *ti)
662 void *state;
663 size_t cstart;
664 char *ctmp;
665 char **compl; /* Array of completions */
666 size_t compl_len; /* Current length of @c compl array */
667 size_t cnum;
668 size_t i;
669 errno_t rc;
671 if (ti->compl_ops == NULL)
672 return;
675 * Obtain list of all possible completions (growing array).
678 rc = (*ti->compl_ops->init)(ti->buffer, ti->pos, &cstart, &state);
679 if (rc != EOK)
680 return;
682 cnum = 0;
684 compl_len = 1;
685 compl = malloc(compl_len * sizeof(char *));
686 if (compl == NULL) {
687 printf("Error: Out of memory.\n");
688 return;
691 while (true) {
692 rc = (*ti->compl_ops->get_next)(state, &ctmp);
693 if (rc != EOK)
694 break;
696 if (cnum >= compl_len) {
697 /* Extend array */
698 compl_len = 2 * compl_len;
699 compl = realloc(compl, compl_len * sizeof(char *));
700 if (compl == NULL) {
701 printf("Error: Out of memory.\n");
702 break;
706 compl[cnum] = str_dup(ctmp);
707 if (compl[cnum] == NULL) {
708 printf("Error: Out of memory.\n");
709 break;
711 cnum++;
714 (*ti->compl_ops->fini)(state);
716 if (cnum > 1) {
718 * More than one match. Determine maximum common prefix.
720 size_t cplen;
722 cplen = str_length(compl[0]);
723 for (i = 1; i < cnum; i++)
724 cplen = min(cplen, common_pref_len(compl[0], compl[i]));
726 /* Compute how many bytes we should skip. */
727 size_t istart = str_lsize(compl[0], ti->pos - cstart);
729 if (cplen > istart) {
730 /* Insert common prefix. */
732 /* Copy remainder of common prefix. */
733 char *cpref = str_ndup(compl[0] + istart,
734 str_lsize(compl[0], cplen - istart));
736 /* Insert it. */
737 tinput_insert_string(ti, cpref);
738 free(cpref);
739 } else {
740 /* No common prefix. Sort and display all entries. */
742 qsort(compl, cnum, sizeof(char *), compl_cmp);
744 tinput_jump_after(ti);
745 tinput_show_completions(ti, compl, cnum);
746 tinput_display(ti);
748 } else if (cnum == 1) {
750 * We have exactly one match. Insert it.
753 /* Compute how many bytes of completion string we should skip. */
754 size_t istart = str_lsize(compl[0], ti->pos - cstart);
756 /* Insert remainder of completion string at current position. */
757 tinput_insert_string(ti, compl[0] + istart);
760 for (i = 0; i < cnum; i++)
761 free(compl[i]);
762 free(compl);
765 /** Initialize text input field.
767 * Must be called before using the field. It clears the history.
769 static void tinput_init(tinput_t *ti)
771 ti->console = console_init(stdin, stdout);
772 ti->hnum = 0;
773 ti->hpos = 0;
774 ti->history[0] = NULL;
777 /** Set prompt string.
779 * @param ti Text input
780 * @param prompt Prompt string
782 * @return EOK on success, ENOMEM if out of memory.
784 errno_t tinput_set_prompt(tinput_t *ti, const char *prompt)
786 if (ti->prompt != NULL)
787 free(ti->prompt);
789 ti->prompt = str_dup(prompt);
790 if (ti->prompt == NULL)
791 return ENOMEM;
793 return EOK;
796 /** Set completion ops.
798 * Set pointer to completion ops structure that will be used for text
799 * completion.
801 void tinput_set_compl_ops(tinput_t *ti, tinput_compl_ops_t *compl_ops)
803 ti->compl_ops = compl_ops;
806 /** Handle key press event. */
807 static void tinput_key_press(tinput_t *ti, kbd_event_t *kev)
809 if (kev->key == KC_LSHIFT)
810 ti->lshift_held = true;
811 if (kev->key == KC_RSHIFT)
812 ti->rshift_held = true;
814 if (((kev->mods & KM_CTRL) != 0) &&
815 ((kev->mods & (KM_ALT | KM_SHIFT)) == 0))
816 tinput_key_ctrl(ti, kev);
818 if (((kev->mods & KM_SHIFT) != 0) &&
819 ((kev->mods & (KM_CTRL | KM_ALT)) == 0))
820 tinput_key_shift(ti, kev);
822 if (((kev->mods & KM_CTRL) != 0) &&
823 ((kev->mods & KM_SHIFT) != 0) &&
824 ((kev->mods & KM_ALT) == 0))
825 tinput_key_ctrl_shift(ti, kev);
827 if ((kev->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
828 tinput_key_unmod(ti, kev);
830 if (kev->c >= ' ') {
831 tinput_sel_delete(ti);
832 tinput_insert_char(ti, kev->c);
836 /** Handle key release event. */
837 static void tinput_key_release(tinput_t *ti, kbd_event_t *kev)
839 if (kev->key == KC_LSHIFT)
840 ti->lshift_held = false;
841 if (kev->key == KC_RSHIFT)
842 ti->rshift_held = false;
845 /** Position event */
846 static void tinput_pos(tinput_t *ti, pos_event_t *ev)
848 if (ev->type == POS_PRESS) {
849 tinput_seek_scrpos(ti, ev->hpos, ev->vpos,
850 ti->lshift_held || ti->rshift_held);
854 /** Read in one line of input with initial text provided.
856 * @param ti Text input
857 * @param istr Initial string
858 * @param dstr Place to save pointer to new string
860 * @return EOK on success
861 * @return ENOENT if user requested abort
862 * @return EIO if communication with console failed
865 errno_t tinput_read_i(tinput_t *ti, const char *istr, char **dstr)
867 console_flush(ti->console);
868 if (console_get_size(ti->console, &ti->con_cols, &ti->con_rows) != EOK)
869 return EIO;
871 tinput_set_str(ti, istr);
873 ti->sel_start = 0;
874 ti->done = false;
875 ti->exit_clui = false;
877 if (tinput_display(ti) != EOK)
878 return EIO;
880 while (!ti->done) {
881 console_flush(ti->console);
883 cons_event_t ev;
884 if (!console_get_event(ti->console, &ev))
885 return EIO;
887 switch (ev.type) {
888 case CEV_KEY:
889 if (ev.ev.key.type == KEY_PRESS)
890 tinput_key_press(ti, &ev.ev.key);
891 else
892 tinput_key_release(ti, &ev.ev.key);
893 break;
894 case CEV_POS:
895 tinput_pos(ti, &ev.ev.pos);
896 break;
900 if (ti->exit_clui)
901 return ENOENT;
903 ti->pos = ti->nc;
904 tinput_position_caret(ti);
905 putchar('\n');
907 char *str = tinput_get_str(ti);
908 if (str_cmp(str, "") != 0)
909 tinput_history_insert(ti, str);
911 ti->hpos = 0;
913 *dstr = str;
914 return EOK;
917 /** Read in one line of input.
919 * @param ti Text input
920 * @param dstr Place to save pointer to new string.
922 * @return EOK on success
923 * @return ENOENT if user requested abort
924 * @return EIO if communication with console failed
927 errno_t tinput_read(tinput_t *ti, char **dstr)
929 return tinput_read_i(ti, "", dstr);
932 static void tinput_key_ctrl(tinput_t *ti, kbd_event_t *ev)
934 switch (ev->key) {
935 case KC_LEFT:
936 tinput_seek_word(ti, seek_backward, false);
937 break;
938 case KC_RIGHT:
939 tinput_seek_word(ti, seek_forward, false);
940 break;
941 case KC_UP:
942 tinput_seek_vertical(ti, seek_backward, false);
943 break;
944 case KC_DOWN:
945 tinput_seek_vertical(ti, seek_forward, false);
946 break;
947 case KC_X:
948 tinput_sel_copy_to_cb(ti);
949 tinput_sel_delete(ti);
950 break;
951 case KC_C:
952 tinput_sel_copy_to_cb(ti);
953 break;
954 case KC_V:
955 tinput_sel_delete(ti);
956 tinput_paste_from_cb(ti);
957 break;
958 case KC_A:
959 tinput_sel_all(ti);
960 break;
961 case KC_Q:
962 /* Signal libary client to quit interactive loop. */
963 ti->done = true;
964 ti->exit_clui = true;
965 break;
966 default:
967 break;
971 static void tinput_key_ctrl_shift(tinput_t *ti, kbd_event_t *ev)
973 switch (ev->key) {
974 case KC_LEFT:
975 tinput_seek_word(ti, seek_backward, true);
976 break;
977 case KC_RIGHT:
978 tinput_seek_word(ti, seek_forward, true);
979 break;
980 case KC_UP:
981 tinput_seek_vertical(ti, seek_backward, true);
982 break;
983 case KC_DOWN:
984 tinput_seek_vertical(ti, seek_forward, true);
985 break;
986 default:
987 break;
991 static void tinput_key_shift(tinput_t *ti, kbd_event_t *ev)
993 switch (ev->key) {
994 case KC_LEFT:
995 tinput_seek_cell(ti, seek_backward, true);
996 break;
997 case KC_RIGHT:
998 tinput_seek_cell(ti, seek_forward, true);
999 break;
1000 case KC_UP:
1001 tinput_seek_vertical(ti, seek_backward, true);
1002 break;
1003 case KC_DOWN:
1004 tinput_seek_vertical(ti, seek_forward, true);
1005 break;
1006 case KC_HOME:
1007 tinput_seek_max(ti, seek_backward, true);
1008 break;
1009 case KC_END:
1010 tinput_seek_max(ti, seek_forward, true);
1011 break;
1012 default:
1013 break;
1017 static void tinput_key_unmod(tinput_t *ti, kbd_event_t *ev)
1019 switch (ev->key) {
1020 case KC_ENTER:
1021 case KC_NENTER:
1022 ti->done = true;
1023 break;
1024 case KC_BACKSPACE:
1025 tinput_backspace(ti);
1026 break;
1027 case KC_DELETE:
1028 tinput_delete(ti);
1029 break;
1030 case KC_LEFT:
1031 tinput_seek_cell(ti, seek_backward, false);
1032 break;
1033 case KC_RIGHT:
1034 tinput_seek_cell(ti, seek_forward, false);
1035 break;
1036 case KC_HOME:
1037 tinput_seek_max(ti, seek_backward, false);
1038 break;
1039 case KC_END:
1040 tinput_seek_max(ti, seek_forward, false);
1041 break;
1042 case KC_UP:
1043 tinput_history_seek(ti, 1);
1044 break;
1045 case KC_DOWN:
1046 tinput_history_seek(ti, -1);
1047 break;
1048 case KC_TAB:
1049 tinput_text_complete(ti);
1050 break;
1051 default:
1052 break;