source: mainline/uspace/lib/clui/tinput.c@ 18b6a88

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 18b6a88 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 22.1 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 <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>
43
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)
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 errno_t 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_scrpos(tinput_t *ti, int col, int line, bool shift_held)
386{
387 unsigned lpos;
388 tinput_pre_seek(ti, shift_held);
389
390 lpos = LIN_POS(ti, col, line);
391
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;
398
399 tinput_post_seek(ti, shift_held);
400}
401
402static void tinput_seek_max(tinput_t *ti, seek_dir_t dir, bool shift_held)
403{
404 tinput_pre_seek(ti, shift_held);
405
406 if (dir == seek_backward)
407 ti->pos = 0;
408 else
409 ti->pos = ti->nc;
410
411 tinput_post_seek(ti, shift_held);
412}
413
414static void tinput_pre_seek(tinput_t *ti, bool shift_held)
415{
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);
421 }
422}
423
424static void tinput_post_seek(tinput_t *ti, bool shift_held)
425{
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;
432 }
433
434 tinput_position_caret(ti);
435}
436
437static void tinput_history_insert(tinput_t *ti, char *str)
438{
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]);
444 }
445
446 size_t i;
447 for (i = ti->hnum; i > 1; i--)
448 ti->history[i] = ti->history[i - 1];
449
450 ti->history[1] = str_dup(str);
451
452 if (ti->history[0] != NULL) {
453 free(ti->history[0]);
454 ti->history[0] = NULL;
455 }
456}
457
458static void tinput_set_str(tinput_t *ti, const char *str)
459{
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;
464}
465
466static void tinput_sel_get_bounds(tinput_t *ti, size_t *sa, size_t *sb)
467{
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;
474 }
475}
476
477static bool tinput_sel_active(tinput_t *ti)
478{
479 return (ti->sel_start != ti->pos);
480}
481
482static void tinput_sel_all(tinput_t *ti)
483{
484 ti->sel_start = 0;
485 ti->pos = ti->nc;
486 tinput_display_tail(ti, 0, 0);
487 tinput_position_caret(ti);
488}
489
490static void tinput_sel_delete(tinput_t *ti)
491{
492 size_t sa;
493 size_t sb;
494
495 tinput_sel_get_bounds(ti, &sa, &sb);
496 if (sa == sb)
497 return;
498
499 memmove(ti->buffer + sa, ti->buffer + sb,
500 (ti->nc - sb) * sizeof(wchar_t));
501
502 ti->pos = ti->sel_start = sa;
503 ti->nc -= (sb - sa);
504 ti->buffer[ti->nc] = '\0';
505
506 tinput_display_tail(ti, sa, sb - sa);
507 tinput_position_caret(ti);
508}
509
510static void tinput_sel_copy_to_cb(tinput_t *ti)
511{
512 size_t sa;
513 size_t sb;
514
515 tinput_sel_get_bounds(ti, &sa, &sb);
516
517 char *str;
518
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);
526
527 if (str == NULL)
528 goto error;
529
530 if (clipboard_put_str(str) != EOK)
531 goto error;
532
533 free(str);
534 return;
535
536error:
537 /* TODO: Give the user some kind of warning. */
538 return;
539}
540
541static void tinput_paste_from_cb(tinput_t *ti)
542{
543 char *str;
544 errno_t rc = clipboard_get_str(&str);
545
546 if ((rc != EOK) || (str == NULL)) {
547 /* TODO: Give the user some kind of warning. */
548 return;
549 }
550
551 tinput_insert_string(ti, str);
552 free(str);
553}
554
555static void tinput_history_seek(tinput_t *ti, int offs)
556{
557 if (offs >= 0) {
558 if (ti->hpos + offs > ti->hnum)
559 return;
560 } else {
561 if (ti->hpos < (size_t) -offs)
562 return;
563 }
564
565 if (ti->history[ti->hpos] != NULL) {
566 free(ti->history[ti->hpos]);
567 ti->history[ti->hpos] = NULL;
568 }
569
570 ti->history[ti->hpos] = tinput_get_str(ti);
571 ti->hpos += offs;
572
573 int pad = (int) ti->nc - str_length(ti->history[ti->hpos]);
574 if (pad < 0)
575 pad = 0;
576
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);
581}
582
583/** Compare two entries in array of completions. */
584static int compl_cmp(const void *va, const void *vb)
585{
586 const char *a = *(const char **) va;
587 const char *b = *(const char **) vb;
588
589 return str_cmp(a, b);
590}
591
592static size_t common_pref_len(const char *a, const char *b)
593{
594 size_t i;
595 size_t a_off, b_off;
596 wchar_t ca, cb;
597
598 i = 0;
599 a_off = 0;
600 b_off = 0;
601
602 while (true) {
603 ca = str_decode(a, &a_off, STR_NO_LIMIT);
604 cb = str_decode(b, &b_off, STR_NO_LIMIT);
605
606 if (ca == '\0' || cb == '\0' || ca != cb)
607 break;
608 ++i;
609 }
610
611 return i;
612}
613
614/* Print a list of completions */
615static void tinput_show_completions(tinput_t *ti, char **compl, size_t cnum)
616{
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]));
622
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);
627 }
628 unsigned int col_width = max_width + padding / cols;
629 unsigned int rows = cnum / cols + ((cnum % cols) != 0);
630
631 unsigned int row, col;
632
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++;
642 }
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++;
650 }
651 }
652 }
653 if ((display_col % ti->con_cols) > 0)
654 printf("\n");
655 }
656 fflush(stdout);
657}
658
659
660static void tinput_text_complete(tinput_t *ti)
661{
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;
670
671 if (ti->compl_ops == NULL)
672 return;
673
674 /*
675 * Obtain list of all possible completions (growing array).
676 */
677
678 rc = (*ti->compl_ops->init)(ti->buffer, ti->pos, &cstart, &state);
679 if (rc != EOK)
680 return;
681
682 cnum = 0;
683
684 compl_len = 1;
685 compl = malloc(compl_len * sizeof(char *));
686 if (compl == NULL) {
687 printf("Error: Out of memory.\n");
688 return;
689 }
690
691 while (true) {
692 rc = (*ti->compl_ops->get_next)(state, &ctmp);
693 if (rc != EOK)
694 break;
695
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;
703 }
704 }
705
706 compl[cnum] = str_dup(ctmp);
707 if (compl[cnum] == NULL) {
708 printf("Error: Out of memory.\n");
709 break;
710 }
711 cnum++;
712 }
713
714 (*ti->compl_ops->fini)(state);
715
716 if (cnum > 1) {
717 /*
718 * More than one match. Determine maximum common prefix.
719 */
720 size_t cplen;
721
722 cplen = str_length(compl[0]);
723 for (i = 1; i < cnum; i++)
724 cplen = min(cplen, common_pref_len(compl[0], compl[i]));
725
726 /* Compute how many bytes we should skip. */
727 size_t istart = str_lsize(compl[0], ti->pos - cstart);
728
729 if (cplen > istart) {
730 /* Insert common prefix. */
731
732 /* Copy remainder of common prefix. */
733 char *cpref = str_ndup(compl[0] + istart,
734 str_lsize(compl[0], cplen - istart));
735
736 /* Insert it. */
737 tinput_insert_string(ti, cpref);
738 free(cpref);
739 } else {
740 /* No common prefix. Sort and display all entries. */
741
742 qsort(compl, cnum, sizeof(char *), compl_cmp);
743
744 tinput_jump_after(ti);
745 tinput_show_completions(ti, compl, cnum);
746 tinput_display(ti);
747 }
748 } else if (cnum == 1) {
749 /*
750 * We have exactly one match. Insert it.
751 */
752
753 /* Compute how many bytes of completion string we should skip. */
754 size_t istart = str_lsize(compl[0], ti->pos - cstart);
755
756 /* Insert remainder of completion string at current position. */
757 tinput_insert_string(ti, compl[0] + istart);
758 }
759
760 for (i = 0; i < cnum; i++)
761 free(compl[i]);
762 free(compl);
763}
764
765/** Initialize text input field.
766 *
767 * Must be called before using the field. It clears the history.
768 */
769static void tinput_init(tinput_t *ti)
770{
771 ti->console = console_init(stdin, stdout);
772 ti->hnum = 0;
773 ti->hpos = 0;
774 ti->history[0] = NULL;
775}
776
777/** Set prompt string.
778 *
779 * @param ti Text input
780 * @param prompt Prompt string
781 *
782 * @return EOK on success, ENOMEM if out of memory.
783 */
784errno_t tinput_set_prompt(tinput_t *ti, const char *prompt)
785{
786 if (ti->prompt != NULL)
787 free(ti->prompt);
788
789 ti->prompt = str_dup(prompt);
790 if (ti->prompt == NULL)
791 return ENOMEM;
792
793 return EOK;
794}
795
796/** Set completion ops.
797 *
798 * Set pointer to completion ops structure that will be used for text
799 * completion.
800 */
801void tinput_set_compl_ops(tinput_t *ti, tinput_compl_ops_t *compl_ops)
802{
803 ti->compl_ops = compl_ops;
804}
805
806/** Handle key press event. */
807static void tinput_key_press(tinput_t *ti, kbd_event_t *kev)
808{
809 if (kev->key == KC_LSHIFT)
810 ti->lshift_held = true;
811 if (kev->key == KC_RSHIFT)
812 ti->rshift_held = true;
813
814 if (((kev->mods & KM_CTRL) != 0) &&
815 ((kev->mods & (KM_ALT | KM_SHIFT)) == 0))
816 tinput_key_ctrl(ti, kev);
817
818 if (((kev->mods & KM_SHIFT) != 0) &&
819 ((kev->mods & (KM_CTRL | KM_ALT)) == 0))
820 tinput_key_shift(ti, kev);
821
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);
826
827 if ((kev->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
828 tinput_key_unmod(ti, kev);
829
830 if (kev->c >= ' ') {
831 tinput_sel_delete(ti);
832 tinput_insert_char(ti, kev->c);
833 }
834}
835
836/** Handle key release event. */
837static void tinput_key_release(tinput_t *ti, kbd_event_t *kev)
838{
839 if (kev->key == KC_LSHIFT)
840 ti->lshift_held = false;
841 if (kev->key == KC_RSHIFT)
842 ti->rshift_held = false;
843}
844
845/** Position event */
846static void tinput_pos(tinput_t *ti, pos_event_t *ev)
847{
848 if (ev->type == POS_PRESS) {
849 tinput_seek_scrpos(ti, ev->hpos, ev->vpos,
850 ti->lshift_held || ti->rshift_held);
851 }
852}
853
854/** Read in one line of input with initial text provided.
855 *
856 * @param ti Text input
857 * @param istr Initial string
858 * @param dstr Place to save pointer to new string
859 *
860 * @return EOK on success
861 * @return ENOENT if user requested abort
862 * @return EIO if communication with console failed
863 *
864 */
865errno_t tinput_read_i(tinput_t *ti, const char *istr, char **dstr)
866{
867 console_flush(ti->console);
868 if (console_get_size(ti->console, &ti->con_cols, &ti->con_rows) != EOK)
869 return EIO;
870
871 tinput_set_str(ti, istr);
872
873 ti->sel_start = 0;
874 ti->done = false;
875 ti->exit_clui = false;
876
877 if (tinput_display(ti) != EOK)
878 return EIO;
879
880 while (!ti->done) {
881 console_flush(ti->console);
882
883 cons_event_t ev;
884 if (!console_get_event(ti->console, &ev))
885 return EIO;
886
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;
897 }
898 }
899
900 if (ti->exit_clui)
901 return ENOENT;
902
903 ti->pos = ti->nc;
904 tinput_position_caret(ti);
905 putchar('\n');
906
907 char *str = tinput_get_str(ti);
908 if (str_cmp(str, "") != 0)
909 tinput_history_insert(ti, str);
910
911 ti->hpos = 0;
912
913 *dstr = str;
914 return EOK;
915}
916
917/** Read in one line of input.
918 *
919 * @param ti Text input
920 * @param dstr Place to save pointer to new string.
921 *
922 * @return EOK on success
923 * @return ENOENT if user requested abort
924 * @return EIO if communication with console failed
925 *
926 */
927errno_t tinput_read(tinput_t *ti, char **dstr)
928{
929 return tinput_read_i(ti, "", dstr);
930}
931
932static void tinput_key_ctrl(tinput_t *ti, kbd_event_t *ev)
933{
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;
968 }
969}
970
971static void tinput_key_ctrl_shift(tinput_t *ti, kbd_event_t *ev)
972{
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;
988 }
989}
990
991static void tinput_key_shift(tinput_t *ti, kbd_event_t *ev)
992{
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;
1014 }
1015}
1016
1017static void tinput_key_unmod(tinput_t *ti, kbd_event_t *ev)
1018{
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;
1053 }
1054}
Note: See TracBrowser for help on using the repository browser.