Ignore:
File:
1 edited

Legend:

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

    r19f857a r36a75a2  
    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
     
    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
    54 
    5553/** 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);
     54static tinput_t *tinput;
    10055
    10156/* Tokenizes input from console, sees if the first word is a built-in, if so
     
    149104}
    150105
    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         char *str;
    511 
    512         tinput_sel_get_bounds(ti, &sa, &sb);
    513 
    514         if (sb < ti->nc) {
    515                 wchar_t tmp_c = ti->buffer[sb];
    516                 ti->buffer[sb] = '\0';
    517                 str = wstr_to_astr(ti->buffer + sa);
    518                 ti->buffer[sb] = tmp_c;
    519         } else
    520                 str = wstr_to_astr(ti->buffer + sa);
    521        
    522         if (str == NULL)
    523                 goto error;
    524 
    525         if (clipboard_put_str(str) != EOK)
    526                 goto error;
    527 
    528         free(str);
    529         return;
    530 error:
    531         return;
    532         /* TODO: Give the user some warning. */
    533 }
    534 
    535 static void tinput_paste_from_cb(tinput_t *ti)
    536 {
    537         char *str;
    538         int rc;
    539 
    540         rc = clipboard_get_str(&str);
    541         if (rc != EOK || str == NULL)
    542                 return; /* TODO: Give the user some warning. */
    543 
    544         tinput_insert_string(ti, str);
    545         free(str);
    546 }
    547 
    548 static void tinput_history_seek(tinput_t *ti, int offs)
    549 {
    550         int pad;
    551 
    552         if (ti->hpos + offs < 0 || ti->hpos + offs > ti->hnum)
    553                 return;
    554 
    555         if (ti->history[ti->hpos] != NULL) {
    556                 free(ti->history[ti->hpos]);
    557                 ti->history[ti->hpos] = NULL;
    558         }
    559 
    560         ti->history[ti->hpos] = tinput_get_str(ti);
    561         ti->hpos += offs;
    562 
    563         pad = ti->nc - str_length(ti->history[ti->hpos]);
    564         if (pad < 0) pad = 0;
    565 
    566         tinput_set_str(ti, ti->history[ti->hpos]);
    567         tinput_display_tail(ti, 0, pad);
    568         tinput_update_origin(ti);
    569         tinput_position_caret(ti);
    570 }
    571 
    572 /** Initialize text input field.
    573  *
    574  * Must be called before using the field. It clears the history.
    575  */
    576 static void tinput_init(tinput_t *ti)
    577 {
    578         ti->hnum = 0;
    579         ti->hpos = 0;
    580         ti->history[0] = NULL;
    581 }
    582 
    583 /** Read in one line of input. */
    584 static char *tinput_read(tinput_t *ti)
    585 {
    586         console_event_t ev;
    587         char *str;
    588 
    589         fflush(stdout);
    590 
    591         if (console_get_size(fphone(stdin), &ti->con_cols, &ti->con_rows) != EOK)
    592                 return NULL;
    593         if (console_get_pos(fphone(stdin), &ti->col0, &ti->row0) != EOK)
    594                 return NULL;
    595 
    596         ti->pos = ti->sel_start = 0;
    597         ti->nc = 0;
    598         ti->buffer[0] = '\0';
    599         ti->done = false;
    600 
    601         while (!ti->done) {
    602                 fflush(stdout);
    603                 if (!console_get_event(fphone(stdin), &ev))
    604                         return NULL;
    605 
    606                 if (ev.type != KEY_PRESS)
    607                         continue;
    608 
    609                 if ((ev.mods & KM_CTRL) != 0 &&
    610                     (ev.mods & (KM_ALT | KM_SHIFT)) == 0) {
    611                         tinput_key_ctrl(ti, &ev);
    612                 }
    613 
    614                 if ((ev.mods & KM_SHIFT) != 0 &&
    615                     (ev.mods & (KM_CTRL | KM_ALT)) == 0) {
    616                         tinput_key_shift(ti, &ev);
    617                 }
    618 
    619                 if ((ev.mods & KM_CTRL) != 0 &&
    620                     (ev.mods & KM_SHIFT) != 0 &&
    621                     (ev.mods & KM_ALT) == 0) {
    622                         tinput_key_ctrl_shift(ti, &ev);
    623                 }
    624 
    625                 if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
    626                         tinput_key_unmod(ti, &ev);
    627                 }
    628 
    629                 if (ev.c >= ' ') {
    630                         tinput_sel_delete(ti);
    631                         tinput_insert_char(ti, ev.c);
    632                 }
    633         }
    634 
    635         ti->pos = ti->nc;
    636         tinput_position_caret(ti);
    637         putchar('\n');
    638 
    639         str = tinput_get_str(ti);
    640         if (str_cmp(str, "") != 0)
    641                 tinput_history_insert(ti, str);
    642 
    643         ti->hpos = 0;
    644 
    645         return str;
    646 }
    647 
    648 static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev)
    649 {
    650         switch (ev->key) {
    651         case KC_LEFT:
    652                 tinput_seek_word(ti, seek_backward, false);
    653                 break;
    654         case KC_RIGHT:
    655                 tinput_seek_word(ti, seek_forward, false);
    656                 break;
    657         case KC_UP:
    658                 tinput_seek_vertical(ti, seek_backward, false);
    659                 break;
    660         case KC_DOWN:
    661                 tinput_seek_vertical(ti, seek_forward, false);
    662                 break;
    663         case KC_X:
    664                 tinput_sel_copy_to_cb(ti);
    665                 tinput_sel_delete(ti);
    666                 break;
    667         case KC_C:
    668                 tinput_sel_copy_to_cb(ti);
    669                 break;
    670         case KC_V:
    671                 tinput_sel_delete(ti);
    672                 tinput_paste_from_cb(ti);
    673                 break;
    674         case KC_A:
    675                 tinput_sel_all(ti);
    676                 break;
    677         default:
    678                 break;
    679         }
    680 }
    681 
    682 static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev)
    683 {
    684         switch (ev->key) {
    685         case KC_LEFT:
    686                 tinput_seek_word(ti, seek_backward, true);
    687                 break;
    688         case KC_RIGHT:
    689                 tinput_seek_word(ti, seek_forward, true);
    690                 break;
    691         case KC_UP:
    692                 tinput_seek_vertical(ti, seek_backward, true);
    693                 break;
    694         case KC_DOWN:
    695                 tinput_seek_vertical(ti, seek_forward, true);
    696                 break;
    697         default:
    698                 break;
    699         }
    700 }
    701 
    702 static void tinput_key_shift(tinput_t *ti, console_event_t *ev)
    703 {
    704         switch (ev->key) {
    705         case KC_LEFT:
    706                 tinput_seek_cell(ti, seek_backward, true);
    707                 break;
    708         case KC_RIGHT:
    709                 tinput_seek_cell(ti, seek_forward, true);
    710                 break;
    711         case KC_UP:
    712                 tinput_seek_vertical(ti, seek_backward, true);
    713                 break;
    714         case KC_DOWN:
    715                 tinput_seek_vertical(ti, seek_forward, true);
    716                 break;
    717         case KC_HOME:
    718                 tinput_seek_max(ti, seek_backward, true);
    719                 break;
    720         case KC_END:
    721                 tinput_seek_max(ti, seek_forward, true);
    722                 break;
    723         default:
    724                 break;
    725         }
    726 }
    727 
    728 static void tinput_key_unmod(tinput_t *ti, console_event_t *ev)
    729 {
    730         switch (ev->key) {
    731         case KC_ENTER:
    732         case KC_NENTER:
    733                 ti->done = true;
    734                 break;
    735         case KC_BACKSPACE:
    736                 tinput_backspace(ti);
    737                 break;
    738         case KC_DELETE:
    739                 tinput_delete(ti);
    740                 break;
    741         case KC_LEFT:
    742                 tinput_seek_cell(ti, seek_backward, false);
    743                 break;
    744         case KC_RIGHT:
    745                 tinput_seek_cell(ti, seek_forward, false);
    746                 break;
    747         case KC_HOME:
    748                 tinput_seek_max(ti, seek_backward, false);
    749                 break;
    750         case KC_END:
    751                 tinput_seek_max(ti, seek_forward, false);
    752                 break;
    753         case KC_UP:
    754                 tinput_history_seek(ti, +1);
    755                 break;
    756         case KC_DOWN:
    757                 tinput_history_seek(ti, -1);
    758                 break;
    759         default:
    760                 break;
    761         }
    762 }
    763 
    764106void get_input(cliuser_t *usr)
    765107{
     
    772114        console_set_style(fphone(stdout), STYLE_NORMAL);
    773115
    774         str = tinput_read(&tinput);
     116        str = tinput_read(tinput);
    775117
    776118        /* Check for empty input. */
     
    784126}
    785127
    786 void input_init(void)
     128int input_init(void)
    787129{
    788         tinput_init(&tinput);
     130        tinput = tinput_new();
     131        if (tinput == NULL) {
     132                printf("Failed to initialize input.\n");
     133                return 1;
     134        }
     135
     136        return 0;
    789137}
Note: See TracChangeset for help on using the changeset viewer.