source: mainline/uspace/lib/clui/tinput.c@ 8c95dff

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

Honor shift (select) during mouse seek.

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