source: mainline/uspace/lib/clui/tinput.c@ e8d3165e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e8d3165e was 68f1254c, checked in by Jiri Svoboda <jiri@…>, 6 years ago

Terminal/console need to handle invalid coordinates

chargrid_set_cursor asserted that arguments are valid yet they are passed
directly from client by terminal/console. Chargrid is probably best
positioned to validate the agruments so instead of assert, check
the arguments and don't do anything if they are invalid.

tinput was clearly forgetting about some cases where the screen could have
scrolled so simply call tinput_update_origin every time from
tinput_position_caret. This was happenning when the input line was
longer than one screen row and tab completion was invoked
(which caused assertion failure in chargrid).

  • Property mode set to 100644
File size: 22.2 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
16 *
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.
27 */
28
29/** @addtogroup libclui
30 * @{
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <str.h>
36#include <io/console.h>
37#include <io/keycode.h>
38#include <io/style.h>
39#include <io/color.h>
40#include <vfs/vfs.h>
41#include <clipboard.h>
42#include <macros.h>
43#include <errno.h>
44#include <assert.h>
45#include <stdbool.h>
46#include <tinput.h>
47
48#define LIN_TO_COL(ti, lpos) ((lpos) % ((ti)->con_cols))
49#define LIN_TO_ROW(ti, lpos) ((lpos) / ((ti)->con_cols))
50#define LIN_POS(ti, col, row) ((col) + (row) * (ti)->con_cols)
51
52/** Seek direction */
53typedef enum {
54 seek_backward = -1,
55 seek_forward = 1
56} seek_dir_t;
57
58static void tinput_update_origin(tinput_t *);
59static void tinput_init(tinput_t *);
60static void tinput_insert_string(tinput_t *, const char *);
61static void tinput_sel_get_bounds(tinput_t *, size_t *, size_t *);
62static bool tinput_sel_active(tinput_t *);
63static void tinput_sel_all(tinput_t *);
64static void tinput_sel_delete(tinput_t *);
65static void tinput_key_ctrl(tinput_t *, kbd_event_t *);
66static void tinput_key_shift(tinput_t *, kbd_event_t *);
67static void tinput_key_ctrl_shift(tinput_t *, kbd_event_t *);
68static void tinput_key_unmod(tinput_t *, kbd_event_t *);
69static void tinput_pre_seek(tinput_t *, bool);
70static void tinput_post_seek(tinput_t *, bool);
71
72static void tinput_console_set_lpos(tinput_t *ti, unsigned lpos)
73{
74 unsigned col = LIN_TO_COL(ti, lpos);
75 unsigned row = LIN_TO_ROW(ti, lpos);
76
77 assert(col < ti->con_cols);
78 assert(row < ti->con_rows);
79 console_set_pos(ti->console, col, row);
80}
81
82/** Create a new text input field. */
83tinput_t *tinput_new(void)
84{
85 tinput_t *ti;
86
87 ti = calloc(1, sizeof(tinput_t));
88 if (ti == NULL)
89 return NULL;
90
91 tinput_init(ti);
92 return ti;
93}
94
95/** Destroy text input field. */
96void tinput_destroy(tinput_t *ti)
97{
98 if (ti->prompt != NULL)
99 free(ti->prompt);
100 free(ti);
101}
102
103static void tinput_display_prompt(tinput_t *ti)
104{
105 tinput_console_set_lpos(ti, ti->prompt_coord);
106
107 console_set_style(ti->console, STYLE_EMPHASIS);
108 printf("%s", ti->prompt);
109 console_flush(ti->console);
110 console_set_style(ti->console, STYLE_NORMAL);
111}
112
113static void tinput_display_tail(tinput_t *ti, size_t start, size_t pad)
114{
115 wchar_t *dbuf = malloc((INPUT_MAX_SIZE + 1) * sizeof(wchar_t));
116 if (!dbuf)
117 return;
118
119 size_t sa;
120 size_t sb;
121 tinput_sel_get_bounds(ti, &sa, &sb);
122
123 tinput_console_set_lpos(ti, ti->text_coord + start);
124 console_set_style(ti->console, STYLE_NORMAL);
125
126 size_t p = start;
127 if (p < sa) {
128 memcpy(dbuf, ti->buffer + p, (sa - p) * sizeof(wchar_t));
129 dbuf[sa - p] = '\0';
130 printf("%ls", dbuf);
131 p = sa;
132 }
133
134 if (p < sb) {
135 console_flush(ti->console);
136 console_set_style(ti->console, STYLE_SELECTED);
137
138 memcpy(dbuf, ti->buffer + p,
139 (sb - p) * sizeof(wchar_t));
140 dbuf[sb - p] = '\0';
141 printf("%ls", dbuf);
142 p = sb;
143 }
144
145 console_flush(ti->console);
146 console_set_style(ti->console, STYLE_NORMAL);
147
148 if (p < ti->nc) {
149 memcpy(dbuf, ti->buffer + p,
150 (ti->nc - p) * sizeof(wchar_t));
151 dbuf[ti->nc - p] = '\0';
152 printf("%ls", dbuf);
153 }
154
155 for (p = 0; p < pad; p++)
156 putwchar(' ');
157
158 console_flush(ti->console);
159
160 free(dbuf);
161}
162
163static char *tinput_get_str(tinput_t *ti)
164{
165 return wstr_to_astr(ti->buffer);
166}
167
168static void tinput_position_caret(tinput_t *ti)
169{
170 tinput_update_origin(ti);
171 tinput_console_set_lpos(ti, ti->text_coord + ti->pos);
172}
173
174/** Update text_coord, prompt_coord in case the screen could have scrolled. */
175static void tinput_update_origin(tinput_t *ti)
176{
177 unsigned end_coord = ti->text_coord + ti->nc;
178 unsigned end_row = LIN_TO_ROW(ti, end_coord);
179
180 unsigned scroll_rows;
181
182 /* Update coords if the screen scrolled. */
183 if (end_row >= ti->con_rows) {
184 scroll_rows = end_row - ti->con_rows + 1;
185 ti->text_coord -= ti->con_cols * scroll_rows;
186 ti->prompt_coord -= ti->con_cols * scroll_rows;
187 }
188}
189
190static void tinput_jump_after(tinput_t *ti)
191{
192 tinput_console_set_lpos(ti, ti->text_coord + ti->nc);
193 console_flush(ti->console);
194 putwchar('\n');
195}
196
197static errno_t tinput_display(tinput_t *ti)
198{
199 sysarg_t col0, row0;
200
201 if (console_get_pos(ti->console, &col0, &row0) != EOK)
202 return EIO;
203
204 ti->prompt_coord = row0 * ti->con_cols + col0;
205 ti->text_coord = ti->prompt_coord + str_length(ti->prompt);
206
207 tinput_display_prompt(ti);
208 tinput_display_tail(ti, 0, 0);
209 tinput_position_caret(ti);
210
211 return EOK;
212}
213
214static void tinput_insert_char(tinput_t *ti, wchar_t c)
215{
216 if (ti->nc == INPUT_MAX_SIZE)
217 return;
218
219 unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + 1;
220 if (new_width % ti->con_cols == 0) {
221 /* Advancing to new line. */
222 sysarg_t new_height = (new_width / ti->con_cols) + 1;
223 if (new_height >= ti->con_rows) {
224 /* Disallow text longer than 1 page for now. */
225 return;
226 }
227 }
228
229 size_t i;
230 for (i = ti->nc; i > ti->pos; i--)
231 ti->buffer[i] = ti->buffer[i - 1];
232
233 ti->buffer[ti->pos] = c;
234 ti->pos += 1;
235 ti->nc += 1;
236 ti->buffer[ti->nc] = '\0';
237 ti->sel_start = ti->pos;
238
239 tinput_display_tail(ti, ti->pos - 1, 0);
240 tinput_position_caret(ti);
241}
242
243static void tinput_insert_string(tinput_t *ti, const char *str)
244{
245 size_t ilen = min(str_length(str), INPUT_MAX_SIZE - ti->nc);
246 if (ilen == 0)
247 return;
248
249 unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + ilen;
250 unsigned new_height = (new_width / ti->con_cols) + 1;
251 if (new_height >= ti->con_rows) {
252 /* Disallow text longer than 1 page for now. */
253 return;
254 }
255
256 if (ti->nc > 0) {
257 size_t i;
258 for (i = ti->nc; i > ti->pos; i--)
259 ti->buffer[i + ilen - 1] = ti->buffer[i - 1];
260 }
261
262 size_t off = 0;
263 size_t i = 0;
264 while (i < ilen) {
265 wchar_t c = str_decode(str, &off, STR_NO_LIMIT);
266 if (c == '\0')
267 break;
268
269 /* Filter out non-printable chars. */
270 if (c < 32)
271 c = 32;
272
273 ti->buffer[ti->pos + i] = c;
274 i++;
275 }
276
277 ti->pos += ilen;
278 ti->nc += ilen;
279 ti->buffer[ti->nc] = '\0';
280 ti->sel_start = ti->pos;
281
282 tinput_display_tail(ti, ti->pos - ilen, 0);
283 tinput_position_caret(ti);
284}
285
286static void tinput_backspace(tinput_t *ti)
287{
288 if (tinput_sel_active(ti)) {
289 tinput_sel_delete(ti);
290 return;
291 }
292
293 if (ti->pos == 0)
294 return;
295
296 size_t i;
297 for (i = ti->pos; i < ti->nc; i++)
298 ti->buffer[i - 1] = ti->buffer[i];
299
300 ti->pos -= 1;
301 ti->nc -= 1;
302 ti->buffer[ti->nc] = '\0';
303 ti->sel_start = ti->pos;
304
305 tinput_display_tail(ti, ti->pos, 1);
306 tinput_position_caret(ti);
307}
308
309static void tinput_delete(tinput_t *ti)
310{
311 if (tinput_sel_active(ti)) {
312 tinput_sel_delete(ti);
313 return;
314 }
315
316 if (ti->pos == ti->nc)
317 return;
318
319 ti->pos += 1;
320 ti->sel_start = ti->pos;
321
322 tinput_backspace(ti);
323}
324
325static void tinput_seek_cell(tinput_t *ti, seek_dir_t dir, bool shift_held)
326{
327 tinput_pre_seek(ti, shift_held);
328
329 if (dir == seek_forward) {
330 if (ti->pos < ti->nc)
331 ti->pos += 1;
332 } else {
333 if (ti->pos > 0)
334 ti->pos -= 1;
335 }
336
337 tinput_post_seek(ti, shift_held);
338}
339
340static void tinput_seek_word(tinput_t *ti, seek_dir_t dir, bool shift_held)
341{
342 tinput_pre_seek(ti, shift_held);
343
344 if (dir == seek_forward) {
345 if (ti->pos == ti->nc)
346 return;
347
348 while (true) {
349 ti->pos += 1;
350
351 if (ti->pos == ti->nc)
352 break;
353
354 if ((ti->buffer[ti->pos - 1] == ' ') &&
355 (ti->buffer[ti->pos] != ' '))
356 break;
357 }
358 } else {
359 if (ti->pos == 0)
360 return;
361
362 while (true) {
363 ti->pos -= 1;
364
365 if (ti->pos == 0)
366 break;
367
368 if (ti->buffer[ti->pos - 1] == ' ' &&
369 ti->buffer[ti->pos] != ' ')
370 break;
371 }
372
373 }
374
375 tinput_post_seek(ti, shift_held);
376}
377
378static void tinput_seek_vertical(tinput_t *ti, seek_dir_t dir, bool shift_held)
379{
380 tinput_pre_seek(ti, shift_held);
381
382 if (dir == seek_forward) {
383 if (ti->pos + ti->con_cols <= ti->nc)
384 ti->pos = ti->pos + ti->con_cols;
385 } else {
386 if (ti->pos >= ti->con_cols)
387 ti->pos = ti->pos - ti->con_cols;
388 }
389
390 tinput_post_seek(ti, shift_held);
391}
392
393static void tinput_seek_scrpos(tinput_t *ti, int col, int line, bool shift_held)
394{
395 unsigned lpos;
396 tinput_pre_seek(ti, shift_held);
397
398 lpos = LIN_POS(ti, col, line);
399
400 if (lpos > ti->text_coord)
401 ti->pos = lpos - ti->text_coord;
402 else
403 ti->pos = 0;
404 if (ti->pos > ti->nc)
405 ti->pos = ti->nc;
406
407 tinput_post_seek(ti, shift_held);
408}
409
410static void tinput_seek_max(tinput_t *ti, seek_dir_t dir, bool shift_held)
411{
412 tinput_pre_seek(ti, shift_held);
413
414 if (dir == seek_backward)
415 ti->pos = 0;
416 else
417 ti->pos = ti->nc;
418
419 tinput_post_seek(ti, shift_held);
420}
421
422static void tinput_pre_seek(tinput_t *ti, bool shift_held)
423{
424 if ((tinput_sel_active(ti)) && (!shift_held)) {
425 /* Unselect and redraw. */
426 ti->sel_start = ti->pos;
427 tinput_display_tail(ti, 0, 0);
428 tinput_position_caret(ti);
429 }
430}
431
432static void tinput_post_seek(tinput_t *ti, bool shift_held)
433{
434 if (shift_held) {
435 /* Selecting text. Need redraw. */
436 tinput_display_tail(ti, 0, 0);
437 } else {
438 /* Shift not held. Keep selection empty. */
439 ti->sel_start = ti->pos;
440 }
441
442 tinput_position_caret(ti);
443}
444
445static void tinput_history_insert(tinput_t *ti, char *str)
446{
447 if (ti->hnum < HISTORY_LEN) {
448 ti->hnum += 1;
449 } else {
450 if (ti->history[HISTORY_LEN] != NULL)
451 free(ti->history[HISTORY_LEN]);
452 }
453
454 size_t i;
455 for (i = ti->hnum; i > 1; i--)
456 ti->history[i] = ti->history[i - 1];
457
458 ti->history[1] = str_dup(str);
459
460 if (ti->history[0] != NULL) {
461 free(ti->history[0]);
462 ti->history[0] = NULL;
463 }
464}
465
466static void tinput_set_str(tinput_t *ti, const char *str)
467{
468 str_to_wstr(ti->buffer, INPUT_MAX_SIZE, str);
469 ti->nc = wstr_length(ti->buffer);
470 ti->pos = ti->nc;
471 ti->sel_start = ti->pos;
472}
473
474static void tinput_sel_get_bounds(tinput_t *ti, size_t *sa, size_t *sb)
475{
476 if (ti->sel_start < ti->pos) {
477 *sa = ti->sel_start;
478 *sb = ti->pos;
479 } else {
480 *sa = ti->pos;
481 *sb = ti->sel_start;
482 }
483}
484
485static bool tinput_sel_active(tinput_t *ti)
486{
487 return (ti->sel_start != ti->pos);
488}
489
490static void tinput_sel_all(tinput_t *ti)
491{
492 ti->sel_start = 0;
493 ti->pos = ti->nc;
494 tinput_display_tail(ti, 0, 0);
495 tinput_position_caret(ti);
496}
497
498static void tinput_sel_delete(tinput_t *ti)
499{
500 size_t sa;
501 size_t sb;
502
503 tinput_sel_get_bounds(ti, &sa, &sb);
504 if (sa == sb)
505 return;
506
507 memmove(ti->buffer + sa, ti->buffer + sb,
508 (ti->nc - sb) * sizeof(wchar_t));
509
510 ti->pos = ti->sel_start = sa;
511 ti->nc -= (sb - sa);
512 ti->buffer[ti->nc] = '\0';
513
514 tinput_display_tail(ti, sa, sb - sa);
515 tinput_position_caret(ti);
516}
517
518static void tinput_sel_copy_to_cb(tinput_t *ti)
519{
520 size_t sa;
521 size_t sb;
522
523 tinput_sel_get_bounds(ti, &sa, &sb);
524
525 char *str;
526
527 if (sb < ti->nc) {
528 wchar_t tmp_c = ti->buffer[sb];
529 ti->buffer[sb] = '\0';
530 str = wstr_to_astr(ti->buffer + sa);
531 ti->buffer[sb] = tmp_c;
532 } else
533 str = wstr_to_astr(ti->buffer + sa);
534
535 if (str == NULL)
536 goto error;
537
538 if (clipboard_put_str(str) != EOK)
539 goto error;
540
541 free(str);
542 return;
543
544error:
545 /* TODO: Give the user some kind of warning. */
546 return;
547}
548
549static void tinput_paste_from_cb(tinput_t *ti)
550{
551 char *str;
552 errno_t rc = clipboard_get_str(&str);
553
554 if ((rc != EOK) || (str == NULL)) {
555 /* TODO: Give the user some kind of warning. */
556 return;
557 }
558
559 tinput_insert_string(ti, str);
560 free(str);
561}
562
563static void tinput_history_seek(tinput_t *ti, int offs)
564{
565 if (offs >= 0) {
566 if (ti->hpos + offs > ti->hnum)
567 return;
568 } else {
569 if (ti->hpos < (size_t) -offs)
570 return;
571 }
572
573 if (ti->history[ti->hpos] != NULL) {
574 free(ti->history[ti->hpos]);
575 ti->history[ti->hpos] = NULL;
576 }
577
578 ti->history[ti->hpos] = tinput_get_str(ti);
579 ti->hpos += offs;
580
581 int pad = (int) ti->nc - str_length(ti->history[ti->hpos]);
582 if (pad < 0)
583 pad = 0;
584
585 tinput_set_str(ti, ti->history[ti->hpos]);
586 tinput_display_tail(ti, 0, pad);
587 tinput_update_origin(ti);
588 tinput_position_caret(ti);
589}
590
591/** Compare two entries in array of completions. */
592static int compl_cmp(const void *va, const void *vb)
593{
594 const char *a = *(const char **) va;
595 const char *b = *(const char **) vb;
596
597 return str_cmp(a, b);
598}
599
600static size_t common_pref_len(const char *a, const char *b)
601{
602 size_t i;
603 size_t a_off, b_off;
604 wchar_t ca, cb;
605
606 i = 0;
607 a_off = 0;
608 b_off = 0;
609
610 while (true) {
611 ca = str_decode(a, &a_off, STR_NO_LIMIT);
612 cb = str_decode(b, &b_off, STR_NO_LIMIT);
613
614 if (ca == '\0' || cb == '\0' || ca != cb)
615 break;
616 ++i;
617 }
618
619 return i;
620}
621
622/* Print a list of completions */
623static void tinput_show_completions(tinput_t *ti, char **compl, size_t cnum)
624{
625 unsigned int i;
626 /* Determine the maximum width of the completion in chars */
627 size_t max_width = 0;
628 for (i = 0; i < cnum; i++)
629 max_width = max(max_width, str_width(compl[i]));
630
631 unsigned int cols = max(1, (ti->con_cols + 1) / (max_width + 1));
632 unsigned int padding = 0;
633 if (cols * max_width + (cols - 1) < ti->con_cols) {
634 padding = ti->con_cols - cols * max_width - (cols - 1);
635 }
636 unsigned int col_width = max_width + padding / cols;
637 unsigned int rows = cnum / cols + ((cnum % cols) != 0);
638
639 unsigned int row, col;
640
641 for (row = 0; row < rows; row++) {
642 unsigned int display_col = 0;
643 for (col = 0; col < cols; col++) {
644 size_t compl_idx = col * rows + row;
645 if (compl_idx >= cnum)
646 break;
647 if (col) {
648 printf(" ");
649 display_col++;
650 }
651 printf("%s", compl[compl_idx]);
652 size_t compl_width = str_width(compl[compl_idx]);
653 display_col += compl_width;
654 if (col < cols - 1) {
655 for (i = compl_width; i < col_width; i++) {
656 printf(" ");
657 display_col++;
658 }
659 }
660 }
661 if ((display_col % ti->con_cols) > 0)
662 printf("\n");
663 }
664 fflush(stdout);
665}
666
667static void tinput_text_complete(tinput_t *ti)
668{
669 void *state;
670 size_t cstart;
671 char *ctmp;
672 char **compl; /* Array of completions */
673 size_t compl_len; /* Current length of @c compl array */
674 size_t cnum;
675 size_t i;
676 errno_t rc;
677
678 if (ti->compl_ops == NULL)
679 return;
680
681 /*
682 * Obtain list of all possible completions (growing array).
683 */
684
685 rc = (*ti->compl_ops->init)(ti->buffer, ti->pos, &cstart, &state);
686 if (rc != EOK)
687 return;
688
689 cnum = 0;
690
691 compl_len = 1;
692 compl = malloc(compl_len * sizeof(char *));
693 if (compl == NULL) {
694 printf("Error: Out of memory.\n");
695 return;
696 }
697
698 while (true) {
699 rc = (*ti->compl_ops->get_next)(state, &ctmp);
700 if (rc != EOK)
701 break;
702
703 if (cnum >= compl_len) {
704 /* Extend array */
705 compl_len = 2 * compl_len;
706 compl = realloc(compl, compl_len * sizeof(char *));
707 if (compl == NULL) {
708 printf("Error: Out of memory.\n");
709 break;
710 }
711 }
712
713 compl[cnum] = str_dup(ctmp);
714 if (compl[cnum] == NULL) {
715 printf("Error: Out of memory.\n");
716 break;
717 }
718 cnum++;
719 }
720
721 (*ti->compl_ops->fini)(state);
722
723 if (cnum > 1) {
724 /*
725 * More than one match. Determine maximum common prefix.
726 */
727 size_t cplen;
728
729 cplen = str_length(compl[0]);
730 for (i = 1; i < cnum; i++)
731 cplen = min(cplen, common_pref_len(compl[0], compl[i]));
732
733 /* Compute how many bytes we should skip. */
734 size_t istart = str_lsize(compl[0], ti->pos - cstart);
735
736 if (cplen > istart) {
737 /* Insert common prefix. */
738
739 /* Copy remainder of common prefix. */
740 char *cpref = str_ndup(compl[0] + istart,
741 str_lsize(compl[0], cplen - istart));
742
743 /* Insert it. */
744 tinput_insert_string(ti, cpref);
745 free(cpref);
746 } else {
747 /* No common prefix. Sort and display all entries. */
748
749 qsort(compl, cnum, sizeof(char *), compl_cmp);
750
751 tinput_jump_after(ti);
752 tinput_show_completions(ti, compl, cnum);
753 tinput_display(ti);
754 }
755 } else if (cnum == 1) {
756 /*
757 * We have exactly one match. Insert it.
758 */
759
760 /* Compute how many bytes of completion string we should skip. */
761 size_t istart = str_lsize(compl[0], ti->pos - cstart);
762
763 /* Insert remainder of completion string at current position. */
764 tinput_insert_string(ti, compl[0] + istart);
765 }
766
767 for (i = 0; i < cnum; i++)
768 free(compl[i]);
769 free(compl);
770}
771
772/** Initialize text input field.
773 *
774 * Must be called before using the field. It clears the history.
775 */
776static void tinput_init(tinput_t *ti)
777{
778 ti->console = console_init(stdin, stdout);
779 ti->hnum = 0;
780 ti->hpos = 0;
781 ti->history[0] = NULL;
782}
783
784/** Set prompt string.
785 *
786 * @param ti Text input
787 * @param prompt Prompt string
788 *
789 * @return EOK on success, ENOMEM if out of memory.
790 */
791errno_t tinput_set_prompt(tinput_t *ti, const char *prompt)
792{
793 if (ti->prompt != NULL)
794 free(ti->prompt);
795
796 ti->prompt = str_dup(prompt);
797 if (ti->prompt == NULL)
798 return ENOMEM;
799
800 return EOK;
801}
802
803/** Set completion ops.
804 *
805 * Set pointer to completion ops structure that will be used for text
806 * completion.
807 */
808void tinput_set_compl_ops(tinput_t *ti, tinput_compl_ops_t *compl_ops)
809{
810 ti->compl_ops = compl_ops;
811}
812
813/** Handle key press event. */
814static void tinput_key_press(tinput_t *ti, kbd_event_t *kev)
815{
816 if (kev->key == KC_LSHIFT)
817 ti->lshift_held = true;
818 if (kev->key == KC_RSHIFT)
819 ti->rshift_held = true;
820
821 if (((kev->mods & KM_CTRL) != 0) &&
822 ((kev->mods & (KM_ALT | KM_SHIFT)) == 0))
823 tinput_key_ctrl(ti, kev);
824
825 if (((kev->mods & KM_SHIFT) != 0) &&
826 ((kev->mods & (KM_CTRL | KM_ALT)) == 0))
827 tinput_key_shift(ti, kev);
828
829 if (((kev->mods & KM_CTRL) != 0) &&
830 ((kev->mods & KM_SHIFT) != 0) &&
831 ((kev->mods & KM_ALT) == 0))
832 tinput_key_ctrl_shift(ti, kev);
833
834 if ((kev->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
835 tinput_key_unmod(ti, kev);
836
837 if (kev->c >= ' ') {
838 tinput_sel_delete(ti);
839 tinput_insert_char(ti, kev->c);
840 }
841}
842
843/** Handle key release event. */
844static void tinput_key_release(tinput_t *ti, kbd_event_t *kev)
845{
846 if (kev->key == KC_LSHIFT)
847 ti->lshift_held = false;
848 if (kev->key == KC_RSHIFT)
849 ti->rshift_held = false;
850}
851
852/** Position event */
853static void tinput_pos(tinput_t *ti, pos_event_t *ev)
854{
855 if (ev->type == POS_PRESS) {
856 tinput_seek_scrpos(ti, ev->hpos, ev->vpos,
857 ti->lshift_held || ti->rshift_held);
858 }
859}
860
861/** Read in one line of input with initial text provided.
862 *
863 * @param ti Text input
864 * @param istr Initial string
865 * @param dstr Place to save pointer to new string
866 *
867 * @return EOK on success
868 * @return ENOENT if user requested abort
869 * @return EIO if communication with console failed
870 *
871 */
872errno_t tinput_read_i(tinput_t *ti, const char *istr, char **dstr)
873{
874 console_flush(ti->console);
875 if (console_get_size(ti->console, &ti->con_cols, &ti->con_rows) != EOK)
876 return EIO;
877
878 tinput_set_str(ti, istr);
879
880 ti->sel_start = 0;
881 ti->done = false;
882 ti->exit_clui = false;
883
884 if (tinput_display(ti) != EOK)
885 return EIO;
886
887 while (!ti->done) {
888 console_flush(ti->console);
889
890 cons_event_t ev;
891 if (!console_get_event(ti->console, &ev))
892 return EIO;
893
894 switch (ev.type) {
895 case CEV_KEY:
896 if (ev.ev.key.type == KEY_PRESS)
897 tinput_key_press(ti, &ev.ev.key);
898 else
899 tinput_key_release(ti, &ev.ev.key);
900 break;
901 case CEV_POS:
902 tinput_pos(ti, &ev.ev.pos);
903 break;
904 }
905 }
906
907 if (ti->exit_clui)
908 return ENOENT;
909
910 ti->pos = ti->nc;
911 tinput_position_caret(ti);
912 putchar('\n');
913
914 char *str = tinput_get_str(ti);
915 if (str_cmp(str, "") != 0)
916 tinput_history_insert(ti, str);
917
918 ti->hpos = 0;
919
920 *dstr = str;
921 return EOK;
922}
923
924/** Read in one line of input.
925 *
926 * @param ti Text input
927 * @param dstr Place to save pointer to new string.
928 *
929 * @return EOK on success
930 * @return ENOENT if user requested abort
931 * @return EIO if communication with console failed
932 *
933 */
934errno_t tinput_read(tinput_t *ti, char **dstr)
935{
936 return tinput_read_i(ti, "", dstr);
937}
938
939static void tinput_key_ctrl(tinput_t *ti, kbd_event_t *ev)
940{
941 switch (ev->key) {
942 case KC_LEFT:
943 tinput_seek_word(ti, seek_backward, false);
944 break;
945 case KC_RIGHT:
946 tinput_seek_word(ti, seek_forward, false);
947 break;
948 case KC_UP:
949 tinput_seek_vertical(ti, seek_backward, false);
950 break;
951 case KC_DOWN:
952 tinput_seek_vertical(ti, seek_forward, false);
953 break;
954 case KC_X:
955 tinput_sel_copy_to_cb(ti);
956 tinput_sel_delete(ti);
957 break;
958 case KC_C:
959 tinput_sel_copy_to_cb(ti);
960 break;
961 case KC_V:
962 tinput_sel_delete(ti);
963 tinput_paste_from_cb(ti);
964 break;
965 case KC_A:
966 tinput_sel_all(ti);
967 break;
968 case KC_Q:
969 /* Signal libary client to quit interactive loop. */
970 ti->done = true;
971 ti->exit_clui = true;
972 break;
973 default:
974 break;
975 }
976}
977
978static void tinput_key_ctrl_shift(tinput_t *ti, kbd_event_t *ev)
979{
980 switch (ev->key) {
981 case KC_LEFT:
982 tinput_seek_word(ti, seek_backward, true);
983 break;
984 case KC_RIGHT:
985 tinput_seek_word(ti, seek_forward, true);
986 break;
987 case KC_UP:
988 tinput_seek_vertical(ti, seek_backward, true);
989 break;
990 case KC_DOWN:
991 tinput_seek_vertical(ti, seek_forward, true);
992 break;
993 default:
994 break;
995 }
996}
997
998static void tinput_key_shift(tinput_t *ti, kbd_event_t *ev)
999{
1000 switch (ev->key) {
1001 case KC_LEFT:
1002 tinput_seek_cell(ti, seek_backward, true);
1003 break;
1004 case KC_RIGHT:
1005 tinput_seek_cell(ti, seek_forward, true);
1006 break;
1007 case KC_UP:
1008 tinput_seek_vertical(ti, seek_backward, true);
1009 break;
1010 case KC_DOWN:
1011 tinput_seek_vertical(ti, seek_forward, true);
1012 break;
1013 case KC_HOME:
1014 tinput_seek_max(ti, seek_backward, true);
1015 break;
1016 case KC_END:
1017 tinput_seek_max(ti, seek_forward, true);
1018 break;
1019 default:
1020 break;
1021 }
1022}
1023
1024static void tinput_key_unmod(tinput_t *ti, kbd_event_t *ev)
1025{
1026 switch (ev->key) {
1027 case KC_ENTER:
1028 case KC_NENTER:
1029 ti->done = true;
1030 break;
1031 case KC_BACKSPACE:
1032 tinput_backspace(ti);
1033 break;
1034 case KC_DELETE:
1035 tinput_delete(ti);
1036 break;
1037 case KC_LEFT:
1038 tinput_seek_cell(ti, seek_backward, false);
1039 break;
1040 case KC_RIGHT:
1041 tinput_seek_cell(ti, seek_forward, false);
1042 break;
1043 case KC_HOME:
1044 tinput_seek_max(ti, seek_backward, false);
1045 break;
1046 case KC_END:
1047 tinput_seek_max(ti, seek_forward, false);
1048 break;
1049 case KC_UP:
1050 tinput_history_seek(ti, 1);
1051 break;
1052 case KC_DOWN:
1053 tinput_history_seek(ti, -1);
1054 break;
1055 case KC_TAB:
1056 tinput_text_complete(ti);
1057 break;
1058 default:
1059 break;
1060 }
1061}
1062
1063/**
1064 * @}
1065 */
Note: See TracBrowser for help on using the repository browser.