Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/bdsh/input.c

    rbc77bfa r5db9084  
    11/* Copyright (c) 2008, Tim Post <tinkertim@gmail.com>
    22 * All rights reserved.
    3  * Copyright (c) 2008, Jiri Svoboda - All Rights Reserved
    43 *
    54 * Redistribution and use in source and binary forms, with or without
     
    3231#include <stdio.h>
    3332#include <stdlib.h>
    34 #include <string.h>
     33#include <str.h>
    3534#include <io/console.h>
    3635#include <io/keycode.h>
     
    4342#include <assert.h>
    4443#include <bool.h>
     44#include <tinput.h>
    4545
    4646#include "config.h"
     
    5151#include "exec.h"
    5252
    53 #define HISTORY_LEN 10
     53extern volatile unsigned int cli_quit;
    5454
    5555/** Text input field. */
    56 typedef struct {
    57         /** Buffer holding text currently being edited */
    58         wchar_t buffer[INPUT_MAX + 1];
    59         /** Screen coordinates of the top-left corner of the text field */
    60         int col0, row0;
    61         /** Screen dimensions */
    62         int con_cols, con_rows;
    63         /** Number of characters in @c buffer */
    64         int nc;
    65         /** Caret position within buffer */
    66         int pos;
    67         /** Selection mark position within buffer */
    68         int sel_start;
    69 
    70         /** History (dynamically allocated strings) */
    71         char *history[1 + HISTORY_LEN];
    72         /** Number of entries in @c history, not counting [0] */
    73         int hnum;
    74         /** Current position in history */
    75         int hpos;
    76         /** Exit flag */
    77         bool done;
    78 } tinput_t;
    79 
    80 /** Seek direction */
    81 typedef enum {
    82         seek_backward = -1,
    83         seek_forward = 1
    84 } seek_dir_t;
    85 
    86 static tinput_t tinput;
    87 
    88 static char *tinput_read(tinput_t *ti);
    89 static void tinput_insert_string(tinput_t *ti, const char *str);
    90 static void tinput_sel_get_bounds(tinput_t *ti, int *sa, int *sb);
    91 static bool tinput_sel_active(tinput_t *ti);
    92 static void tinput_sel_all(tinput_t *ti);
    93 static void tinput_sel_delete(tinput_t *ti);
    94 static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev);
    95 static void tinput_key_shift(tinput_t *ti, console_event_t *ev);
    96 static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev);
    97 static void tinput_key_unmod(tinput_t *ti, console_event_t *ev);
    98 static void tinput_pre_seek(tinput_t *ti, bool shift_held);
    99 static void tinput_post_seek(tinput_t *ti, bool shift_held);
     56static tinput_t *tinput;
    10057
    10158/* Tokenizes input from console, sees if the first word is a built-in, if so
     
    149106}
    150107
    151 static void tinput_display_tail(tinput_t *ti, int start, int pad)
    152 {
    153         static wchar_t dbuf[INPUT_MAX + 1];
    154         int sa, sb;
    155         int i, p;
    156 
    157         tinput_sel_get_bounds(ti, &sa, &sb);
    158 
    159         console_goto(fphone(stdout), (ti->col0 + start) % ti->con_cols,
    160             ti->row0 + (ti->col0 + start) / ti->con_cols);
    161         console_set_color(fphone(stdout), COLOR_BLACK, COLOR_WHITE, 0);
    162 
    163         p = start;
    164         if (p < sa) {
    165                 memcpy(dbuf, ti->buffer + p, (sa - p) * sizeof(wchar_t));
    166                 dbuf[sa - p] = '\0';
    167                 printf("%ls", dbuf);
    168                 p = sa;
    169         }
    170 
    171         if (p < sb) {
    172                 fflush(stdout);
    173                 console_set_color(fphone(stdout), COLOR_BLACK, COLOR_RED, 0);
    174                 memcpy(dbuf, ti->buffer + p,
    175                     (sb - p) * sizeof(wchar_t));
    176                 dbuf[sb - p] = '\0';
    177                 printf("%ls", dbuf);
    178                 p = sb;
    179         }
    180 
    181         fflush(stdout);
    182         console_set_color(fphone(stdout), COLOR_BLACK, COLOR_WHITE, 0);
    183 
    184         if (p < ti->nc) {
    185                 memcpy(dbuf, ti->buffer + p,
    186                     (ti->nc - p) * sizeof(wchar_t));
    187                 dbuf[ti->nc - p] = '\0';
    188                 printf("%ls", dbuf);
    189         }
    190 
    191         for (i = 0; i < pad; ++i)
    192                 putchar(' ');
    193         fflush(stdout);
    194 }
    195 
    196 static char *tinput_get_str(tinput_t *ti)
    197 {
    198         return wstr_to_astr(ti->buffer);
    199 }
    200 
    201 static void tinput_position_caret(tinput_t *ti)
    202 {
    203         console_goto(fphone(stdout), (ti->col0 + ti->pos) % ti->con_cols,
    204             ti->row0 + (ti->col0 + ti->pos) / ti->con_cols);
    205 }
    206 
    207 /** Update row0 in case the screen could have scrolled. */
    208 static void tinput_update_origin(tinput_t *ti)
    209 {
    210         int width, rows;
    211 
    212         width = ti->col0 + ti->nc;
    213         rows = (width / ti->con_cols) + 1;
    214 
    215         /* Update row0 if the screen scrolled. */
    216         if (ti->row0 + rows > ti->con_rows)
    217                 ti->row0 = ti->con_rows - rows;
    218 }
    219 
    220 static void tinput_insert_char(tinput_t *ti, wchar_t c)
    221 {
    222         int i;
    223         int new_width, new_height;
    224 
    225         if (ti->nc == INPUT_MAX)
    226                 return;
    227 
    228         new_width = ti->col0 + ti->nc + 1;
    229         if (new_width % ti->con_cols == 0) {
    230                 /* Advancing to new line. */
    231                 new_height = (new_width / ti->con_cols) + 1;
    232                 if (new_height >= ti->con_rows)
    233                         return; /* Disallow text longer than 1 page for now. */
    234         }
    235 
    236         for (i = ti->nc; i > ti->pos; --i)
    237                 ti->buffer[i] = ti->buffer[i - 1];
    238 
    239         ti->buffer[ti->pos] = c;
    240         ti->pos += 1;
    241         ti->nc += 1;
    242         ti->buffer[ti->nc] = '\0';
    243         ti->sel_start = ti->pos;
    244 
    245         tinput_display_tail(ti, ti->pos - 1, 0);
    246         tinput_update_origin(ti);
    247         tinput_position_caret(ti);
    248 }
    249 
    250 static void tinput_insert_string(tinput_t *ti, const char *str)
    251 {
    252         int i;
    253         int new_width, new_height;
    254         int ilen;
    255         wchar_t c;
    256         size_t off;
    257 
    258         ilen = min((ssize_t) str_length(str), INPUT_MAX - ti->nc);
    259         if (ilen == 0)
    260                 return;
    261 
    262         new_width = ti->col0 + ti->nc + ilen;
    263         new_height = (new_width / ti->con_cols) + 1;
    264         if (new_height >= ti->con_rows)
    265                 return; /* Disallow text longer than 1 page for now. */
    266 
    267         for (i = ti->nc - 1; i >= ti->pos; --i)
    268                 ti->buffer[i + ilen] = ti->buffer[i];
    269 
    270         off = 0; i = 0;
    271         while (i < ilen) {
    272                 c = str_decode(str, &off, STR_NO_LIMIT);
    273                 if (c == '\0')
    274                         break;
    275 
    276                 /* Filter out non-printable chars. */
    277                 if (c < 32)
    278                         c = 32;
    279 
    280                 ti->buffer[ti->pos + i] = c;
    281                 ++i;
    282         }
    283 
    284         ti->pos += ilen;
    285         ti->nc += ilen;
    286         ti->buffer[ti->nc] = '\0';
    287         ti->sel_start = ti->pos;
    288 
    289         tinput_display_tail(ti, ti->pos - ilen, 0);
    290         tinput_update_origin(ti);
    291         tinput_position_caret(ti);
    292 }
    293 
    294 static void tinput_backspace(tinput_t *ti)
    295 {
    296         int i;
    297 
    298         if (tinput_sel_active(ti)) {
    299                 tinput_sel_delete(ti);
    300                 return;
    301         }
    302 
    303         if (ti->pos == 0)
    304                 return;
    305 
    306         for (i = ti->pos; i < ti->nc; ++i)
    307                 ti->buffer[i - 1] = ti->buffer[i];
    308         ti->pos -= 1;
    309         ti->nc -= 1;
    310         ti->buffer[ti->nc] = '\0';
    311         ti->sel_start = ti->pos;
    312 
    313         tinput_display_tail(ti, ti->pos, 1);
    314         tinput_position_caret(ti);
    315 }
    316 
    317 static void tinput_delete(tinput_t *ti)
    318 {
    319         if (tinput_sel_active(ti)) {
    320                 tinput_sel_delete(ti);
    321                 return;
    322         }
    323 
    324         if (ti->pos == ti->nc)
    325                 return;
    326 
    327         ti->pos += 1;
    328         ti->sel_start = ti->pos;
    329 
    330         tinput_backspace(ti);
    331 }
    332 
    333 static void tinput_seek_cell(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                         ti->pos += 1;
    340         } else {
    341                 if (ti->pos > 0)
    342                         ti->pos -= 1;
    343         }
    344 
    345         tinput_post_seek(ti, shift_held);
    346 }
    347 
    348 static void tinput_seek_word(tinput_t *ti, seek_dir_t dir, bool shift_held)
    349 {
    350         tinput_pre_seek(ti, shift_held);
    351 
    352         if (dir == seek_forward) {
    353                 if (ti->pos == ti->nc)
    354                         return;
    355 
    356                 while (1) {
    357                         ti->pos += 1;
    358 
    359                         if (ti->pos == ti->nc)
    360                                 break;
    361 
    362                         if (ti->buffer[ti->pos - 1] == ' ' &&
    363                             ti->buffer[ti->pos] != ' ')
    364                                 break;
    365                 }
    366         } else {
    367                 if (ti->pos == 0)
    368                         return;
    369 
    370                 while (1) {
    371                         ti->pos -= 1;
    372 
    373                         if (ti->pos == 0)
    374                                 break;
    375 
    376                         if (ti->buffer[ti->pos - 1] == ' ' &&
    377                             ti->buffer[ti->pos] != ' ')
    378                                 break;
    379                 }
    380 
    381         }
    382 
    383         tinput_post_seek(ti, shift_held);
    384 }
    385 
    386 static void tinput_seek_vertical(tinput_t *ti, seek_dir_t dir, bool shift_held)
    387 {
    388         tinput_pre_seek(ti, shift_held);
    389 
    390         if (dir == seek_forward) {
    391                 if (ti->pos + ti->con_cols <= ti->nc)
    392                         ti->pos = ti->pos + ti->con_cols;
    393         } else {
    394                 if (ti->pos - ti->con_cols >= 0)
    395                         ti->pos = ti->pos - ti->con_cols;
    396         }
    397 
    398         tinput_post_seek(ti, shift_held);
    399 }
    400 
    401 static void tinput_seek_max(tinput_t *ti, seek_dir_t dir, bool shift_held)
    402 {
    403         tinput_pre_seek(ti, shift_held);
    404 
    405         if (dir == seek_backward)
    406                 ti->pos = 0;
    407         else
    408                 ti->pos = ti->nc;
    409 
    410         tinput_post_seek(ti, shift_held);
    411 }
    412 
    413 static void tinput_pre_seek(tinput_t *ti, bool shift_held)
    414 {
    415         if (tinput_sel_active(ti) && !shift_held) {
    416                 /* Unselect and redraw. */
    417                 ti->sel_start = ti->pos;
    418                 tinput_display_tail(ti, 0, 0);
    419                 tinput_position_caret(ti);
    420         }
    421 }
    422 
    423 static void tinput_post_seek(tinput_t *ti, bool shift_held)
    424 {
    425         if (shift_held) {
    426                 /* Selecting text. Need redraw. */
    427                 tinput_display_tail(ti, 0, 0);
    428         } else {
    429                 /* Shift not held. Keep selection empty. */
    430                 ti->sel_start = ti->pos;
    431         }
    432         tinput_position_caret(ti);
    433 }
    434 
    435 static void tinput_history_insert(tinput_t *ti, char *str)
    436 {
    437         int i;
    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         for (i = ti->hnum; i > 1; --i)
    447                 ti->history[i] = ti->history[i - 1];
    448 
    449         ti->history[1] = str_dup(str);
    450 
    451         if (ti->history[0] != NULL) {
    452                 free(ti->history[0]);
    453                 ti->history[0] = NULL;
    454         }
    455 }
    456 
    457 static void tinput_set_str(tinput_t *ti, char *str)
    458 {
    459         str_to_wstr(ti->buffer, INPUT_MAX, str);
    460         ti->nc = wstr_length(ti->buffer);
    461         ti->pos = ti->nc;
    462         ti->sel_start = ti->pos;
    463 }
    464 
    465 static void tinput_sel_get_bounds(tinput_t *ti, int *sa, int *sb)
    466 {
    467         if (ti->sel_start < ti->pos) {
    468                 *sa = ti->sel_start;
    469                 *sb = ti->pos;
    470         } else {
    471                 *sa = ti->pos;
    472                 *sb = ti->sel_start;
    473         }
    474 }
    475 
    476 static bool tinput_sel_active(tinput_t *ti)
    477 {
    478         return ti->sel_start != ti->pos;
    479 }
    480 
    481 static void tinput_sel_all(tinput_t *ti)
    482 {
    483         ti->sel_start = 0;
    484         ti->pos = ti->nc;
    485         tinput_display_tail(ti, 0, 0);
    486         tinput_position_caret(ti);
    487 }
    488 
    489 static void tinput_sel_delete(tinput_t *ti)
    490 {
    491         int sa, sb;
    492 
    493         tinput_sel_get_bounds(ti, &sa, &sb);
    494         if (sa == sb)
    495                 return;
    496 
    497         memmove(ti->buffer + sa, ti->buffer + sb,
    498             (ti->nc - sb) * sizeof(wchar_t));
    499         ti->pos = ti->sel_start = sa;
    500         ti->nc -= (sb - sa);
    501         ti->buffer[ti->nc] = '\0';
    502 
    503         tinput_display_tail(ti, sa, sb - sa);
    504         tinput_position_caret(ti);
    505 }
    506 
    507 static void tinput_sel_copy_to_cb(tinput_t *ti)
    508 {
    509         int sa, sb;
    510         wchar_t tmp_c;
    511         char *str;
    512 
    513         tinput_sel_get_bounds(ti, &sa, &sb);
    514 
    515         if (sb < ti->nc) {
    516                 tmp_c = ti->buffer[sb];
    517                 ti->buffer[sb] = '\0';
    518         }
    519 
    520         str = wstr_to_astr(ti->buffer + sa);
    521 
    522         if (sb < ti->nc)
    523                 ti->buffer[sb] = tmp_c;
    524 
    525         if (str == NULL)
    526                 goto error;
    527 
    528         if (clipboard_put_str(str) != EOK)
    529                 goto error;
    530 
    531         free(str);
    532         return;
    533 error:
    534         return;
    535         /* TODO: Give the user some warning. */
    536 }
    537 
    538 static void tinput_paste_from_cb(tinput_t *ti)
     108void get_input(cliuser_t *usr)
    539109{
    540110        char *str;
    541111        int rc;
    542 
    543         rc = clipboard_get_str(&str);
    544         if (rc != EOK || str == NULL)
    545                 return; /* TODO: Give the user some warning. */
    546 
    547         tinput_insert_string(ti, str);
    548         free(str);
    549 }
    550 
    551 static void tinput_history_seek(tinput_t *ti, int offs)
    552 {
    553         int pad;
    554 
    555         if (ti->hpos + offs < 0 || ti->hpos + offs > ti->hnum)
    556                 return;
    557 
    558         if (ti->history[ti->hpos] != NULL) {
    559                 free(ti->history[ti->hpos]);
    560                 ti->history[ti->hpos] = NULL;
    561         }
    562 
    563         ti->history[ti->hpos] = tinput_get_str(ti);
    564         ti->hpos += offs;
    565 
    566         pad = ti->nc - str_length(ti->history[ti->hpos]);
    567         if (pad < 0) pad = 0;
    568 
    569         tinput_set_str(ti, ti->history[ti->hpos]);
    570         tinput_display_tail(ti, 0, pad);
    571         tinput_update_origin(ti);
    572         tinput_position_caret(ti);
    573 }
    574 
    575 /** Initialize text input field.
    576  *
    577  * Must be called before using the field. It clears the history.
    578  */
    579 static void tinput_init(tinput_t *ti)
    580 {
    581         ti->hnum = 0;
    582         ti->hpos = 0;
    583         ti->history[0] = NULL;
    584 }
    585 
    586 /** Read in one line of input. */
    587 static char *tinput_read(tinput_t *ti)
    588 {
    589         console_event_t ev;
    590         char *str;
    591 
    592         fflush(stdout);
    593 
    594         if (console_get_size(fphone(stdin), &ti->con_cols, &ti->con_rows) != EOK)
    595                 return NULL;
    596         if (console_get_pos(fphone(stdin), &ti->col0, &ti->row0) != EOK)
    597                 return NULL;
    598 
    599         ti->pos = ti->sel_start = 0;
    600         ti->nc = 0;
    601         ti->buffer[0] = '\0';
    602         ti->done = false;
    603 
    604         while (!ti->done) {
    605                 fflush(stdout);
    606                 if (!console_get_event(fphone(stdin), &ev))
    607                         return NULL;
    608 
    609                 if (ev.type != KEY_PRESS)
    610                         continue;
    611 
    612                 if ((ev.mods & KM_CTRL) != 0 &&
    613                     (ev.mods & (KM_ALT | KM_SHIFT)) == 0) {
    614                         tinput_key_ctrl(ti, &ev);
    615                 }
    616 
    617                 if ((ev.mods & KM_SHIFT) != 0 &&
    618                     (ev.mods & (KM_CTRL | KM_ALT)) == 0) {
    619                         tinput_key_shift(ti, &ev);
    620                 }
    621 
    622                 if ((ev.mods & KM_CTRL) != 0 &&
    623                     (ev.mods & KM_SHIFT) != 0 &&
    624                     (ev.mods & KM_ALT) == 0) {
    625                         tinput_key_ctrl_shift(ti, &ev);
    626                 }
    627 
    628                 if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
    629                         tinput_key_unmod(ti, &ev);
    630                 }
    631 
    632                 if (ev.c >= ' ') {
    633                         tinput_sel_delete(ti);
    634                         tinput_insert_char(ti, ev.c);
    635                 }
    636         }
    637 
    638         ti->pos = ti->nc;
    639         tinput_position_caret(ti);
    640         putchar('\n');
    641 
    642         str = tinput_get_str(ti);
    643         if (str_cmp(str, "") != 0)
    644                 tinput_history_insert(ti, str);
    645 
    646         ti->hpos = 0;
    647 
    648         return str;
    649 }
    650 
    651 static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev)
    652 {
    653         switch (ev->key) {
    654         case KC_LEFT:
    655                 tinput_seek_word(ti, seek_backward, false);
    656                 break;
    657         case KC_RIGHT:
    658                 tinput_seek_word(ti, seek_forward, false);
    659                 break;
    660         case KC_UP:
    661                 tinput_seek_vertical(ti, seek_backward, false);
    662                 break;
    663         case KC_DOWN:
    664                 tinput_seek_vertical(ti, seek_forward, false);
    665                 break;
    666         case KC_X:
    667                 tinput_sel_copy_to_cb(ti);
    668                 tinput_sel_delete(ti);
    669                 break;
    670         case KC_C:
    671                 tinput_sel_copy_to_cb(ti);
    672                 break;
    673         case KC_V:
    674                 tinput_sel_delete(ti);
    675                 tinput_paste_from_cb(ti);
    676                 break;
    677         case KC_A:
    678                 tinput_sel_all(ti);
    679                 break;
    680         default:
    681                 break;
    682         }
    683 }
    684 
    685 static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev)
    686 {
    687         switch (ev->key) {
    688         case KC_LEFT:
    689                 tinput_seek_word(ti, seek_backward, true);
    690                 break;
    691         case KC_RIGHT:
    692                 tinput_seek_word(ti, seek_forward, true);
    693                 break;
    694         case KC_UP:
    695                 tinput_seek_vertical(ti, seek_backward, true);
    696                 break;
    697         case KC_DOWN:
    698                 tinput_seek_vertical(ti, seek_forward, true);
    699                 break;
    700         default:
    701                 break;
    702         }
    703 }
    704 
    705 static void tinput_key_shift(tinput_t *ti, console_event_t *ev)
    706 {
    707         switch (ev->key) {
    708         case KC_LEFT:
    709                 tinput_seek_cell(ti, seek_backward, true);
    710                 break;
    711         case KC_RIGHT:
    712                 tinput_seek_cell(ti, seek_forward, true);
    713                 break;
    714         case KC_UP:
    715                 tinput_seek_vertical(ti, seek_backward, true);
    716                 break;
    717         case KC_DOWN:
    718                 tinput_seek_vertical(ti, seek_forward, true);
    719                 break;
    720         case KC_HOME:
    721                 tinput_seek_max(ti, seek_backward, true);
    722                 break;
    723         case KC_END:
    724                 tinput_seek_max(ti, seek_forward, true);
    725                 break;
    726         default:
    727                 break;
    728         }
    729 }
    730 
    731 static void tinput_key_unmod(tinput_t *ti, console_event_t *ev)
    732 {
    733         switch (ev->key) {
    734         case KC_ENTER:
    735         case KC_NENTER:
    736                 ti->done = true;
    737                 break;
    738         case KC_BACKSPACE:
    739                 tinput_backspace(ti);
    740                 break;
    741         case KC_DELETE:
    742                 tinput_delete(ti);
    743                 break;
    744         case KC_LEFT:
    745                 tinput_seek_cell(ti, seek_backward, false);
    746                 break;
    747         case KC_RIGHT:
    748                 tinput_seek_cell(ti, seek_forward, false);
    749                 break;
    750         case KC_HOME:
    751                 tinput_seek_max(ti, seek_backward, false);
    752                 break;
    753         case KC_END:
    754                 tinput_seek_max(ti, seek_forward, false);
    755                 break;
    756         case KC_UP:
    757                 tinput_history_seek(ti, +1);
    758                 break;
    759         case KC_DOWN:
    760                 tinput_history_seek(ti, -1);
    761                 break;
    762         default:
    763                 break;
    764         }
    765 }
    766 
    767 void get_input(cliuser_t *usr)
    768 {
    769         char *str;
    770112
    771113        fflush(stdout);
     
    775117        console_set_style(fphone(stdout), STYLE_NORMAL);
    776118
    777         str = tinput_read(&tinput);
     119        rc = tinput_read(tinput, &str);
     120        if (rc == ENOENT) {
     121                /* User requested exit */
     122                cli_quit = 1;
     123                putchar('\n');
     124                return;
     125        }
     126
     127        if (rc != EOK) {
     128                /* Error in communication with console */
     129                return;
     130        }
    778131
    779132        /* Check for empty input. */
     
    787140}
    788141
    789 void input_init(void)
     142int input_init(void)
    790143{
    791         tinput_init(&tinput);
     144        tinput = tinput_new();
     145        if (tinput == NULL) {
     146                printf("Failed to initialize input.\n");
     147                return 1;
     148        }
     149
     150        return 0;
    792151}
Note: See TracChangeset for help on using the changeset viewer.