source: mainline/uspace/lib/clui/tinput.c@ 6d5e378

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6d5e378 was 6d5e378, checked in by Martin Decky <martin@…>, 13 years ago

cherrypick GUI implementation (originally by Petr Koupy), with several major changes

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