Changeset 61c0402 in mainline for uspace/app/bdsh/input.c


Ignore:
Timestamp:
2010-01-15T19:36:53Z (14 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
92bee46
Parents:
50f9c3a (diff), 963462af (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge mainline changes.

File:
1 edited

Legend:

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

    r50f9c3a r61c0402  
    3636#include <io/keycode.h>
    3737#include <io/style.h>
     38#include <io/color.h>
    3839#include <vfs/vfs.h>
     40#include <clipboard.h>
     41#include <macros.h>
    3942#include <errno.h>
     43#include <assert.h>
    4044#include <bool.h>
    4145
     
    4751#include "exec.h"
    4852
    49 static void read_line(char *, int);
     53#define HISTORY_LEN 10
     54
     55/** Text input field. */
     56typedef 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 */
     81typedef enum {
     82        seek_backward = -1,
     83        seek_forward = 1
     84} seek_dir_t;
     85
     86static tinput_t tinput;
     87
     88static char *tinput_read(tinput_t *ti);
     89static void tinput_insert_string(tinput_t *ti, const char *str);
     90static void tinput_sel_get_bounds(tinput_t *ti, int *sa, int *sb);
     91static bool tinput_sel_active(tinput_t *ti);
     92static void tinput_sel_all(tinput_t *ti);
     93static void tinput_sel_delete(tinput_t *ti);
     94static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev);
     95static void tinput_key_shift(tinput_t *ti, console_event_t *ev);
     96static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev);
     97static void tinput_key_unmod(tinput_t *ti, console_event_t *ev);
     98static void tinput_pre_seek(tinput_t *ti, bool shift_held);
     99static void tinput_post_seek(tinput_t *ti, bool shift_held);
    50100
    51101/* Tokenizes input from console, sees if the first word is a built-in, if so
     
    99149}
    100150
    101 static void read_line(char *buffer, int n)
     151static 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
     196static char *tinput_get_str(tinput_t *ti)
     197{
     198        return wstr_to_astr(ti->buffer);
     199}
     200
     201static 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. */
     208static 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
     220static 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
     250static 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
     294static 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
     317static 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
     333static 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
     348static 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
     386static 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
     401static 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
     413static 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
     423static 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
     435static 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
     457static 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
     465static 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
     476static bool tinput_sel_active(tinput_t *ti)
     477{
     478        return ti->sel_start != ti->pos;
     479}
     480
     481static 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
     489static 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
     507static 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;
     533error:
     534        return;
     535        /* TODO: Give the user some warning. */
     536}
     537
     538static void tinput_paste_from_cb(tinput_t *ti)
     539{
     540        char *str;
     541        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
     551static 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 */
     579static 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. */
     587static char *tinput_read(tinput_t *ti)
    102588{
    103589        console_event_t ev;
    104         size_t offs, otmp;
    105         wchar_t dec;
    106 
    107         offs = 0;
    108         while (true) {
     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) {
    109605                fflush(stdout);
    110606                if (!console_get_event(fphone(stdin), &ev))
    111                         return;
    112                
     607                        return NULL;
     608
    113609                if (ev.type != KEY_PRESS)
    114610                        continue;
    115                
    116                 if (ev.key == KC_ENTER || ev.key == KC_NENTER)
    117                         break;
    118                 if (ev.key == KC_BACKSPACE) {
    119                         if (offs > 0) {
    120                                 /*
    121                                  * Back up until we reach valid start of
    122                                  * character.
    123                                  */
    124                                 while (offs > 0) {
    125                                         --offs; otmp = offs;
    126                                         dec = str_decode(buffer, &otmp, n);
    127                                         if (dec != U_SPECIAL)
    128                                                 break;
    129                                 }
    130                                 putchar('\b');
    131                         }
    132                         continue;
     611
     612                if ((ev.mods & KM_CTRL) != 0 &&
     613                    (ev.mods & (KM_ALT | KM_SHIFT)) == 0) {
     614                        tinput_key_ctrl(ti, &ev);
    133615                }
     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
    134632                if (ev.c >= ' ') {
    135                         if (chr_encode(ev.c, buffer, &offs, n - 1) == EOK)
    136                                 putchar(ev.c);
     633                        tinput_sel_delete(ti);
     634                        tinput_insert_char(ti, ev.c);
    137635                }
    138636        }
     637
     638        ti->pos = ti->nc;
     639        tinput_position_caret(ti);
    139640        putchar('\n');
    140         buffer[offs] = '\0';
    141 }
    142 
    143 /* TODO:
    144  * Implement something like editline() / readline(), if even
    145  * just for command history and making arrows work. */
     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
     651static 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
     685static 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
     705static 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
     731static 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
    146767void get_input(cliuser_t *usr)
    147768{
    148         char line[INPUT_MAX];
     769        char *str;
    149770
    150771        fflush(stdout);
     
    154775        console_set_style(fphone(stdout), STYLE_NORMAL);
    155776
    156         read_line(line, INPUT_MAX);
    157         /* Make sure we don't have rubbish or a C/R happy user */
    158         if (str_cmp(line, "") == 0 || str_cmp(line, "\n") == 0)
    159                 return;
    160         usr->line = str_dup(line);
    161 
     777        str = tinput_read(&tinput);
     778
     779        /* Check for empty input. */
     780        if (str_cmp(str, "") == 0) {
     781                free(str);
     782                return;
     783        }
     784
     785        usr->line = str;
    162786        return;
    163787}
    164788
     789void input_init(void)
     790{
     791        tinput_init(&tinput);
     792}
Note: See TracChangeset for help on using the changeset viewer.