source: mainline/uspace/lib/clui/tinput.c@ 07b7c48

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

Extend console library API to support different event types.

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