2 * Copyright (c) 2011 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <io/console.h>
33 #include <io/keycode.h>
37 #include <clipboard.h>
44 #define LIN_TO_COL(ti, lpos) ((lpos) % ((ti)->con_cols))
45 #define LIN_TO_ROW(ti, lpos) ((lpos) / ((ti)->con_cols))
46 #define LIN_POS(ti, col, row) ((col) + (row) * (ti)->con_cols)
54 static void tinput_init(tinput_t
*);
55 static void tinput_insert_string(tinput_t
*, const char *);
56 static void tinput_sel_get_bounds(tinput_t
*, size_t *, size_t *);
57 static bool tinput_sel_active(tinput_t
*);
58 static void tinput_sel_all(tinput_t
*);
59 static void tinput_sel_delete(tinput_t
*);
60 static void tinput_key_ctrl(tinput_t
*, kbd_event_t
*);
61 static void tinput_key_shift(tinput_t
*, kbd_event_t
*);
62 static void tinput_key_ctrl_shift(tinput_t
*, kbd_event_t
*);
63 static void tinput_key_unmod(tinput_t
*, kbd_event_t
*);
64 static void tinput_pre_seek(tinput_t
*, bool);
65 static void tinput_post_seek(tinput_t
*, bool);
67 static void tinput_console_set_lpos(tinput_t
*ti
, unsigned lpos
)
69 console_set_pos(ti
->console
, LIN_TO_COL(ti
, lpos
),
70 LIN_TO_ROW(ti
, lpos
));
73 /** Create a new text input field. */
74 tinput_t
*tinput_new(void)
78 ti
= calloc(1, sizeof(tinput_t
));
86 /** Destroy text input field. */
87 void tinput_destroy(tinput_t
*ti
)
89 if (ti
->prompt
!= NULL
)
94 static void tinput_display_prompt(tinput_t
*ti
)
96 tinput_console_set_lpos(ti
, ti
->prompt_coord
);
98 console_set_style(ti
->console
, STYLE_EMPHASIS
);
99 printf("%s", ti
->prompt
);
100 console_flush(ti
->console
);
101 console_set_style(ti
->console
, STYLE_NORMAL
);
104 static void tinput_display_tail(tinput_t
*ti
, size_t start
, size_t pad
)
106 wchar_t *dbuf
= malloc((INPUT_MAX_SIZE
+ 1) * sizeof(wchar_t));
112 tinput_sel_get_bounds(ti
, &sa
, &sb
);
114 tinput_console_set_lpos(ti
, ti
->text_coord
+ start
);
115 console_set_style(ti
->console
, STYLE_NORMAL
);
119 memcpy(dbuf
, ti
->buffer
+ p
, (sa
- p
) * sizeof(wchar_t));
126 console_flush(ti
->console
);
127 console_set_style(ti
->console
, STYLE_SELECTED
);
129 memcpy(dbuf
, ti
->buffer
+ p
,
130 (sb
- p
) * sizeof(wchar_t));
136 console_flush(ti
->console
);
137 console_set_style(ti
->console
, STYLE_NORMAL
);
140 memcpy(dbuf
, ti
->buffer
+ p
,
141 (ti
->nc
- p
) * sizeof(wchar_t));
142 dbuf
[ti
->nc
- p
] = '\0';
146 for (p
= 0; p
< pad
; p
++)
149 console_flush(ti
->console
);
154 static char *tinput_get_str(tinput_t
*ti
)
156 return wstr_to_astr(ti
->buffer
);
159 static void tinput_position_caret(tinput_t
*ti
)
161 tinput_console_set_lpos(ti
, ti
->text_coord
+ ti
->pos
);
164 /** Update text_coord, prompt_coord in case the screen could have scrolled. */
165 static void tinput_update_origin(tinput_t
*ti
)
167 unsigned end_coord
= ti
->text_coord
+ ti
->nc
;
168 unsigned end_row
= LIN_TO_ROW(ti
, end_coord
);
170 unsigned scroll_rows
;
172 /* Update coords if the screen scrolled. */
173 if (end_row
>= ti
->con_rows
) {
174 scroll_rows
= end_row
- ti
->con_rows
+ 1;
175 ti
->text_coord
-= ti
->con_cols
* scroll_rows
;
176 ti
->prompt_coord
-= ti
->con_cols
* scroll_rows
;
180 static void tinput_jump_after(tinput_t
*ti
)
182 tinput_console_set_lpos(ti
, ti
->text_coord
+ ti
->nc
);
183 console_flush(ti
->console
);
187 static errno_t
tinput_display(tinput_t
*ti
)
191 if (console_get_pos(ti
->console
, &col0
, &row0
) != EOK
)
194 ti
->prompt_coord
= row0
* ti
->con_cols
+ col0
;
195 ti
->text_coord
= ti
->prompt_coord
+ str_length(ti
->prompt
);
197 tinput_display_prompt(ti
);
198 tinput_display_tail(ti
, 0, 0);
199 tinput_position_caret(ti
);
204 static void tinput_insert_char(tinput_t
*ti
, wchar_t c
)
206 if (ti
->nc
== INPUT_MAX_SIZE
)
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. */
220 for (i
= ti
->nc
; i
> ti
->pos
; i
--)
221 ti
->buffer
[i
] = ti
->buffer
[i
- 1];
223 ti
->buffer
[ti
->pos
] = c
;
226 ti
->buffer
[ti
->nc
] = '\0';
227 ti
->sel_start
= ti
->pos
;
229 tinput_display_tail(ti
, ti
->pos
- 1, 0);
230 tinput_update_origin(ti
);
231 tinput_position_caret(ti
);
234 static void tinput_insert_string(tinput_t
*ti
, const char *str
)
236 size_t ilen
= min(str_length(str
), INPUT_MAX_SIZE
- ti
->nc
);
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. */
249 for (i
= ti
->nc
; i
> ti
->pos
; i
--)
250 ti
->buffer
[i
+ ilen
- 1] = ti
->buffer
[i
- 1];
256 wchar_t c
= str_decode(str
, &off
, STR_NO_LIMIT
);
260 /* Filter out non-printable chars. */
264 ti
->buffer
[ti
->pos
+ i
] = c
;
270 ti
->buffer
[ti
->nc
] = '\0';
271 ti
->sel_start
= ti
->pos
;
273 tinput_display_tail(ti
, ti
->pos
- ilen
, 0);
274 tinput_update_origin(ti
);
275 tinput_position_caret(ti
);
278 static void tinput_backspace(tinput_t
*ti
)
280 if (tinput_sel_active(ti
)) {
281 tinput_sel_delete(ti
);
289 for (i
= ti
->pos
; i
< ti
->nc
; i
++)
290 ti
->buffer
[i
- 1] = ti
->buffer
[i
];
294 ti
->buffer
[ti
->nc
] = '\0';
295 ti
->sel_start
= ti
->pos
;
297 tinput_display_tail(ti
, ti
->pos
, 1);
298 tinput_position_caret(ti
);
301 static void tinput_delete(tinput_t
*ti
)
303 if (tinput_sel_active(ti
)) {
304 tinput_sel_delete(ti
);
308 if (ti
->pos
== ti
->nc
)
312 ti
->sel_start
= ti
->pos
;
314 tinput_backspace(ti
);
317 static void tinput_seek_cell(tinput_t
*ti
, seek_dir_t dir
, bool shift_held
)
319 tinput_pre_seek(ti
, shift_held
);
321 if (dir
== seek_forward
) {
322 if (ti
->pos
< ti
->nc
)
329 tinput_post_seek(ti
, shift_held
);
332 static void tinput_seek_word(tinput_t
*ti
, seek_dir_t dir
, bool shift_held
)
334 tinput_pre_seek(ti
, shift_held
);
336 if (dir
== seek_forward
) {
337 if (ti
->pos
== ti
->nc
)
343 if (ti
->pos
== ti
->nc
)
346 if ((ti
->buffer
[ti
->pos
- 1] == ' ') &&
347 (ti
->buffer
[ti
->pos
] != ' '))
360 if (ti
->buffer
[ti
->pos
- 1] == ' ' &&
361 ti
->buffer
[ti
->pos
] != ' ')
367 tinput_post_seek(ti
, shift_held
);
370 static void tinput_seek_vertical(tinput_t
*ti
, seek_dir_t dir
, bool shift_held
)
372 tinput_pre_seek(ti
, shift_held
);
374 if (dir
== seek_forward
) {
375 if (ti
->pos
+ ti
->con_cols
<= ti
->nc
)
376 ti
->pos
= ti
->pos
+ ti
->con_cols
;
378 if (ti
->pos
>= ti
->con_cols
)
379 ti
->pos
= ti
->pos
- ti
->con_cols
;
382 tinput_post_seek(ti
, shift_held
);
385 static void tinput_seek_scrpos(tinput_t
*ti
, int col
, int line
, bool shift_held
)
388 tinput_pre_seek(ti
, shift_held
);
390 lpos
= LIN_POS(ti
, col
, line
);
392 if (lpos
> ti
->text_coord
)
393 ti
->pos
= lpos
- ti
->text_coord
;
396 if (ti
->pos
> ti
->nc
)
399 tinput_post_seek(ti
, shift_held
);
402 static void tinput_seek_max(tinput_t
*ti
, seek_dir_t dir
, bool shift_held
)
404 tinput_pre_seek(ti
, shift_held
);
406 if (dir
== seek_backward
)
411 tinput_post_seek(ti
, shift_held
);
414 static void tinput_pre_seek(tinput_t
*ti
, bool shift_held
)
416 if ((tinput_sel_active(ti
)) && (!shift_held
)) {
417 /* Unselect and redraw. */
418 ti
->sel_start
= ti
->pos
;
419 tinput_display_tail(ti
, 0, 0);
420 tinput_position_caret(ti
);
424 static void tinput_post_seek(tinput_t
*ti
, bool shift_held
)
427 /* Selecting text. Need redraw. */
428 tinput_display_tail(ti
, 0, 0);
430 /* Shift not held. Keep selection empty. */
431 ti
->sel_start
= ti
->pos
;
434 tinput_position_caret(ti
);
437 static void tinput_history_insert(tinput_t
*ti
, char *str
)
439 if (ti
->hnum
< HISTORY_LEN
) {
442 if (ti
->history
[HISTORY_LEN
] != NULL
)
443 free(ti
->history
[HISTORY_LEN
]);
447 for (i
= ti
->hnum
; i
> 1; i
--)
448 ti
->history
[i
] = ti
->history
[i
- 1];
450 ti
->history
[1] = str_dup(str
);
452 if (ti
->history
[0] != NULL
) {
453 free(ti
->history
[0]);
454 ti
->history
[0] = NULL
;
458 static void tinput_set_str(tinput_t
*ti
, const char *str
)
460 str_to_wstr(ti
->buffer
, INPUT_MAX_SIZE
, str
);
461 ti
->nc
= wstr_length(ti
->buffer
);
463 ti
->sel_start
= ti
->pos
;
466 static void tinput_sel_get_bounds(tinput_t
*ti
, size_t *sa
, size_t *sb
)
468 if (ti
->sel_start
< ti
->pos
) {
477 static bool tinput_sel_active(tinput_t
*ti
)
479 return (ti
->sel_start
!= ti
->pos
);
482 static void tinput_sel_all(tinput_t
*ti
)
486 tinput_display_tail(ti
, 0, 0);
487 tinput_position_caret(ti
);
490 static void tinput_sel_delete(tinput_t
*ti
)
495 tinput_sel_get_bounds(ti
, &sa
, &sb
);
499 memmove(ti
->buffer
+ sa
, ti
->buffer
+ sb
,
500 (ti
->nc
- sb
) * sizeof(wchar_t));
502 ti
->pos
= ti
->sel_start
= sa
;
504 ti
->buffer
[ti
->nc
] = '\0';
506 tinput_display_tail(ti
, sa
, sb
- sa
);
507 tinput_position_caret(ti
);
510 static void tinput_sel_copy_to_cb(tinput_t
*ti
)
515 tinput_sel_get_bounds(ti
, &sa
, &sb
);
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
;
525 str
= wstr_to_astr(ti
->buffer
+ sa
);
530 if (clipboard_put_str(str
) != EOK
)
537 /* TODO: Give the user some kind of warning. */
541 static void tinput_paste_from_cb(tinput_t
*ti
)
544 errno_t rc
= clipboard_get_str(&str
);
546 if ((rc
!= EOK
) || (str
== NULL
)) {
547 /* TODO: Give the user some kind of warning. */
551 tinput_insert_string(ti
, str
);
555 static void tinput_history_seek(tinput_t
*ti
, int offs
)
558 if (ti
->hpos
+ offs
> ti
->hnum
)
561 if (ti
->hpos
< (size_t) -offs
)
565 if (ti
->history
[ti
->hpos
] != NULL
) {
566 free(ti
->history
[ti
->hpos
]);
567 ti
->history
[ti
->hpos
] = NULL
;
570 ti
->history
[ti
->hpos
] = tinput_get_str(ti
);
573 int pad
= (int) ti
->nc
- str_length(ti
->history
[ti
->hpos
]);
577 tinput_set_str(ti
, ti
->history
[ti
->hpos
]);
578 tinput_display_tail(ti
, 0, pad
);
579 tinput_update_origin(ti
);
580 tinput_position_caret(ti
);
583 /** Compare two entries in array of completions. */
584 static int compl_cmp(const void *va
, const void *vb
)
586 const char *a
= *(const char **) va
;
587 const char *b
= *(const char **) vb
;
589 return str_cmp(a
, b
);
592 static size_t common_pref_len(const char *a
, const char *b
)
603 ca
= str_decode(a
, &a_off
, STR_NO_LIMIT
);
604 cb
= str_decode(b
, &b_off
, STR_NO_LIMIT
);
606 if (ca
== '\0' || cb
== '\0' || ca
!= cb
)
614 /* Print a list of completions */
615 static void tinput_show_completions(tinput_t
*ti
, char **compl, size_t cnum
)
618 /* Determine the maximum width of the completion in chars */
619 size_t max_width
= 0;
620 for (i
= 0; i
< cnum
; i
++)
621 max_width
= max(max_width
, str_width(compl[i
]));
623 unsigned int cols
= max(1, (ti
->con_cols
+ 1) / (max_width
+ 1));
624 unsigned int padding
= 0;
625 if ((cols
* max_width
) + (cols
- 1) < ti
->con_cols
) {
626 padding
= ti
->con_cols
- (cols
* max_width
) - (cols
- 1);
628 unsigned int col_width
= max_width
+ padding
/ cols
;
629 unsigned int rows
= cnum
/ cols
+ ((cnum
% cols
) != 0);
631 unsigned int row
, col
;
633 for (row
= 0; row
< rows
; row
++) {
634 unsigned int display_col
= 0;
635 for (col
= 0; col
< cols
; col
++) {
636 size_t compl_idx
= col
* rows
+ row
;
637 if (compl_idx
>= cnum
)
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
++) {
653 if ((display_col
% ti
->con_cols
) > 0)
660 static void tinput_text_complete(tinput_t
*ti
)
665 char **compl; /* Array of completions */
666 size_t compl_len
; /* Current length of @c compl array */
671 if (ti
->compl_ops
== NULL
)
675 * Obtain list of all possible completions (growing array).
678 rc
= (*ti
->compl_ops
->init
)(ti
->buffer
, ti
->pos
, &cstart
, &state
);
685 compl = malloc(compl_len
* sizeof(char *));
687 printf("Error: Out of memory.\n");
692 rc
= (*ti
->compl_ops
->get_next
)(state
, &ctmp
);
696 if (cnum
>= compl_len
) {
698 compl_len
= 2 * compl_len
;
699 compl = realloc(compl, compl_len
* sizeof(char *));
701 printf("Error: Out of memory.\n");
706 compl[cnum
] = str_dup(ctmp
);
707 if (compl[cnum
] == NULL
) {
708 printf("Error: Out of memory.\n");
714 (*ti
->compl_ops
->fini
)(state
);
718 * More than one match. Determine maximum common prefix.
722 cplen
= str_length(compl[0]);
723 for (i
= 1; i
< cnum
; i
++)
724 cplen
= min(cplen
, common_pref_len(compl[0], compl[i
]));
726 /* Compute how many bytes we should skip. */
727 size_t istart
= str_lsize(compl[0], ti
->pos
- cstart
);
729 if (cplen
> istart
) {
730 /* Insert common prefix. */
732 /* Copy remainder of common prefix. */
733 char *cpref
= str_ndup(compl[0] + istart
,
734 str_lsize(compl[0], cplen
- istart
));
737 tinput_insert_string(ti
, cpref
);
740 /* No common prefix. Sort and display all entries. */
742 qsort(compl, cnum
, sizeof(char *), compl_cmp
);
744 tinput_jump_after(ti
);
745 tinput_show_completions(ti
, compl, cnum
);
748 } else if (cnum
== 1) {
750 * We have exactly one match. Insert it.
753 /* Compute how many bytes of completion string we should skip. */
754 size_t istart
= str_lsize(compl[0], ti
->pos
- cstart
);
756 /* Insert remainder of completion string at current position. */
757 tinput_insert_string(ti
, compl[0] + istart
);
760 for (i
= 0; i
< cnum
; i
++)
765 /** Initialize text input field.
767 * Must be called before using the field. It clears the history.
769 static void tinput_init(tinput_t
*ti
)
771 ti
->console
= console_init(stdin
, stdout
);
774 ti
->history
[0] = NULL
;
777 /** Set prompt string.
779 * @param ti Text input
780 * @param prompt Prompt string
782 * @return EOK on success, ENOMEM if out of memory.
784 errno_t
tinput_set_prompt(tinput_t
*ti
, const char *prompt
)
786 if (ti
->prompt
!= NULL
)
789 ti
->prompt
= str_dup(prompt
);
790 if (ti
->prompt
== NULL
)
796 /** Set completion ops.
798 * Set pointer to completion ops structure that will be used for text
801 void tinput_set_compl_ops(tinput_t
*ti
, tinput_compl_ops_t
*compl_ops
)
803 ti
->compl_ops
= compl_ops
;
806 /** Handle key press event. */
807 static void tinput_key_press(tinput_t
*ti
, kbd_event_t
*kev
)
809 if (kev
->key
== KC_LSHIFT
)
810 ti
->lshift_held
= true;
811 if (kev
->key
== KC_RSHIFT
)
812 ti
->rshift_held
= true;
814 if (((kev
->mods
& KM_CTRL
) != 0) &&
815 ((kev
->mods
& (KM_ALT
| KM_SHIFT
)) == 0))
816 tinput_key_ctrl(ti
, kev
);
818 if (((kev
->mods
& KM_SHIFT
) != 0) &&
819 ((kev
->mods
& (KM_CTRL
| KM_ALT
)) == 0))
820 tinput_key_shift(ti
, kev
);
822 if (((kev
->mods
& KM_CTRL
) != 0) &&
823 ((kev
->mods
& KM_SHIFT
) != 0) &&
824 ((kev
->mods
& KM_ALT
) == 0))
825 tinput_key_ctrl_shift(ti
, kev
);
827 if ((kev
->mods
& (KM_CTRL
| KM_ALT
| KM_SHIFT
)) == 0)
828 tinput_key_unmod(ti
, kev
);
831 tinput_sel_delete(ti
);
832 tinput_insert_char(ti
, kev
->c
);
836 /** Handle key release event. */
837 static void tinput_key_release(tinput_t
*ti
, kbd_event_t
*kev
)
839 if (kev
->key
== KC_LSHIFT
)
840 ti
->lshift_held
= false;
841 if (kev
->key
== KC_RSHIFT
)
842 ti
->rshift_held
= false;
845 /** Position event */
846 static void tinput_pos(tinput_t
*ti
, pos_event_t
*ev
)
848 if (ev
->type
== POS_PRESS
) {
849 tinput_seek_scrpos(ti
, ev
->hpos
, ev
->vpos
,
850 ti
->lshift_held
|| ti
->rshift_held
);
854 /** Read in one line of input with initial text provided.
856 * @param ti Text input
857 * @param istr Initial string
858 * @param dstr Place to save pointer to new string
860 * @return EOK on success
861 * @return ENOENT if user requested abort
862 * @return EIO if communication with console failed
865 errno_t
tinput_read_i(tinput_t
*ti
, const char *istr
, char **dstr
)
867 console_flush(ti
->console
);
868 if (console_get_size(ti
->console
, &ti
->con_cols
, &ti
->con_rows
) != EOK
)
871 tinput_set_str(ti
, istr
);
875 ti
->exit_clui
= false;
877 if (tinput_display(ti
) != EOK
)
881 console_flush(ti
->console
);
884 if (!console_get_event(ti
->console
, &ev
))
889 if (ev
.ev
.key
.type
== KEY_PRESS
)
890 tinput_key_press(ti
, &ev
.ev
.key
);
892 tinput_key_release(ti
, &ev
.ev
.key
);
895 tinput_pos(ti
, &ev
.ev
.pos
);
904 tinput_position_caret(ti
);
907 char *str
= tinput_get_str(ti
);
908 if (str_cmp(str
, "") != 0)
909 tinput_history_insert(ti
, str
);
917 /** Read in one line of input.
919 * @param ti Text input
920 * @param dstr Place to save pointer to new string.
922 * @return EOK on success
923 * @return ENOENT if user requested abort
924 * @return EIO if communication with console failed
927 errno_t
tinput_read(tinput_t
*ti
, char **dstr
)
929 return tinput_read_i(ti
, "", dstr
);
932 static void tinput_key_ctrl(tinput_t
*ti
, kbd_event_t
*ev
)
936 tinput_seek_word(ti
, seek_backward
, false);
939 tinput_seek_word(ti
, seek_forward
, false);
942 tinput_seek_vertical(ti
, seek_backward
, false);
945 tinput_seek_vertical(ti
, seek_forward
, false);
948 tinput_sel_copy_to_cb(ti
);
949 tinput_sel_delete(ti
);
952 tinput_sel_copy_to_cb(ti
);
955 tinput_sel_delete(ti
);
956 tinput_paste_from_cb(ti
);
962 /* Signal libary client to quit interactive loop. */
964 ti
->exit_clui
= true;
971 static void tinput_key_ctrl_shift(tinput_t
*ti
, kbd_event_t
*ev
)
975 tinput_seek_word(ti
, seek_backward
, true);
978 tinput_seek_word(ti
, seek_forward
, true);
981 tinput_seek_vertical(ti
, seek_backward
, true);
984 tinput_seek_vertical(ti
, seek_forward
, true);
991 static void tinput_key_shift(tinput_t
*ti
, kbd_event_t
*ev
)
995 tinput_seek_cell(ti
, seek_backward
, true);
998 tinput_seek_cell(ti
, seek_forward
, true);
1001 tinput_seek_vertical(ti
, seek_backward
, true);
1004 tinput_seek_vertical(ti
, seek_forward
, true);
1007 tinput_seek_max(ti
, seek_backward
, true);
1010 tinput_seek_max(ti
, seek_forward
, true);
1017 static void tinput_key_unmod(tinput_t
*ti
, kbd_event_t
*ev
)
1025 tinput_backspace(ti
);
1031 tinput_seek_cell(ti
, seek_backward
, false);
1034 tinput_seek_cell(ti
, seek_forward
, false);
1037 tinput_seek_max(ti
, seek_backward
, false);
1040 tinput_seek_max(ti
, seek_forward
, false);
1043 tinput_history_seek(ti
, 1);
1046 tinput_history_seek(ti
, -1);
1049 tinput_text_complete(ti
);