Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/clui/tinput.c

    r96b02eb9 rbe61b8f  
    11/*
    2  * Copyright (c) 2010 Jiri Svoboda
     2 * Copyright (c) 2011 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    2727 */
    2828
     29#include <sort.h>
    2930#include <stdio.h>
    3031#include <stdlib.h>
     
    4243#include <tinput.h>
    4344
     45#define LIN_TO_COL(ti, lpos) ((lpos) % ((ti)->con_cols))
     46#define LIN_TO_ROW(ti, lpos) ((lpos) / ((ti)->con_cols))
     47
    4448/** Seek direction */
    4549typedef enum {
     
    5458static void tinput_sel_all(tinput_t *);
    5559static void tinput_sel_delete(tinput_t *);
    56 static void tinput_key_ctrl(tinput_t *, console_event_t *);
    57 static void tinput_key_shift(tinput_t *, console_event_t *);
    58 static void tinput_key_ctrl_shift(tinput_t *, console_event_t *);
    59 static void tinput_key_unmod(tinput_t *, console_event_t *);
     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 *);
    6064static void tinput_pre_seek(tinput_t *, bool);
    6165static void tinput_post_seek(tinput_t *, bool);
    6266
     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
    6373/** Create a new text input field. */
    6474tinput_t *tinput_new(void)
     
    6676        tinput_t *ti;
    6777       
    68         ti = malloc(sizeof(tinput_t));
     78        ti = calloc(1, sizeof(tinput_t));
    6979        if (ti == NULL)
    7080                return NULL;
     
    7787void tinput_destroy(tinput_t *ti)
    7888{
     89        if (ti->prompt != NULL)
     90                free(ti->prompt);
    7991        free(ti);
     92}
     93
     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);
    80102}
    81103
     
    88110        tinput_sel_get_bounds(ti, &sa, &sb);
    89111       
    90         console_set_pos(fphone(stdout), (ti->col0 + start) % ti->con_cols,
    91             ti->row0 + (ti->col0 + start) / ti->con_cols);
    92         console_set_style(fphone(stdout), STYLE_NORMAL);
     112        tinput_console_set_lpos(ti, ti->text_coord + start);
     113        console_set_style(ti->console, STYLE_NORMAL);
    93114       
    94115        size_t p = start;
     
    101122       
    102123        if (p < sb) {
    103                 fflush(stdout);
    104                 console_set_style(fphone(stdout), STYLE_SELECTED);
     124                console_flush(ti->console);
     125                console_set_style(ti->console, STYLE_SELECTED);
     126               
    105127                memcpy(dbuf, ti->buffer + p,
    106128                    (sb - p) * sizeof(wchar_t));
     
    110132        }
    111133       
    112         fflush(stdout);
    113         console_set_style(fphone(stdout), STYLE_NORMAL);
     134        console_flush(ti->console);
     135        console_set_style(ti->console, STYLE_NORMAL);
    114136       
    115137        if (p < ti->nc) {
     
    123145                putchar(' ');
    124146       
    125         fflush(stdout);
     147        console_flush(ti->console);
    126148}
    127149
     
    133155static void tinput_position_caret(tinput_t *ti)
    134156{
    135         console_set_pos(fphone(stdout), (ti->col0 + ti->pos) % ti->con_cols,
    136             ti->row0 + (ti->col0 + ti->pos) / ti->con_cols);
    137 }
    138 
    139 /** Update row0 in case the screen could have scrolled. */
     157        tinput_console_set_lpos(ti, ti->text_coord + ti->pos);
     158}
     159
     160/** Update text_coord, prompt_coord in case the screen could have scrolled. */
    140161static void tinput_update_origin(tinput_t *ti)
    141162{
    142         sysarg_t width = ti->col0 + ti->nc;
    143         sysarg_t rows = (width / ti->con_cols) + 1;
    144        
    145         /* Update row0 if the screen scrolled. */
    146         if (ti->row0 + rows > ti->con_rows)
    147                 ti->row0 = ti->con_rows - rows;
     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;
     189       
     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;
    148198}
    149199
     
    153203                return;
    154204       
    155         sysarg_t new_width = ti->col0 + ti->nc + 1;
     205        unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + 1;
    156206        if (new_width % ti->con_cols == 0) {
    157207                /* Advancing to new line. */
     
    184234                return;
    185235       
    186         sysarg_t new_width = ti->col0 + ti->nc + ilen;
    187         sysarg_t new_height = (new_width / ti->con_cols) + 1;
     236        unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + ilen;
     237        unsigned new_height = (new_width / ti->con_cols) + 1;
    188238        if (new_height >= ti->con_rows) {
    189239                /* Disallow text longer than 1 page for now. */
     
    510560}
    511561
     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
     593/* Print a list of completions */
     594static void tinput_show_completions(tinput_t *ti, char **compl, size_t cnum)
     595{
     596        unsigned int i;
     597        /* Determine the maximum length of the completion in chars */
     598        size_t max_length = 0;
     599        for (i = 0; i < cnum; i++)
     600                max_length = max(max_length, str_length(compl[i]));
     601       
     602        unsigned int cols = max(1, (ti->con_cols + 1) / (max_length + 1));
     603        unsigned int col_width = ti->con_cols / cols;
     604        unsigned int rows = cnum / cols + ((cnum % cols) != 0);
     605       
     606        unsigned int row, col;
     607       
     608        for (row = 0; row < rows; row++) {
     609                bool wlc = false;
     610                for (col = 0; col < cols; col++) {
     611                        size_t compl_idx = col * rows + row;
     612                        if (compl_idx >= cnum)
     613                                break;
     614                        if (col)
     615                                printf(" ");
     616                        printf("%s", compl[compl_idx]);
     617                        size_t compl_len = str_length(compl[compl_idx]);
     618                        if (col == cols -1) {
     619                                wlc = (compl_len == max_length);
     620                        }
     621                        else {
     622                                for (i = compl_len; i < col_width; i++) {
     623                                        printf(" ");
     624                                }
     625                        }
     626                }
     627                if (!wlc) printf("\n");
     628        }
     629}
     630
     631
     632static void tinput_text_complete(tinput_t *ti)
     633{
     634        void *state;
     635        size_t cstart;
     636        char *ctmp;
     637        char **compl;           /* Array of completions */
     638        size_t compl_len;       /* Current length of @c compl array */
     639        size_t cnum;
     640        size_t i;
     641        int rc;
     642
     643        if (ti->compl_ops == NULL)
     644                return;
     645
     646        /*
     647         * Obtain list of all possible completions (growing array).
     648         */
     649
     650        rc = (*ti->compl_ops->init)(ti->buffer, ti->pos, &cstart, &state);
     651        if (rc != EOK)
     652                return;
     653
     654        cnum = 0;
     655
     656        compl_len = 1;
     657        compl = malloc(compl_len * sizeof(char *));
     658        if (compl == NULL) {
     659                printf("Error: Out of memory.\n");
     660                return;
     661        }
     662
     663        while (true) {
     664                rc = (*ti->compl_ops->get_next)(state, &ctmp);
     665                if (rc != EOK)
     666                        break;
     667
     668                if (cnum >= compl_len) {
     669                        /* Extend array */
     670                        compl_len = 2 * compl_len;
     671                        compl = realloc(compl, compl_len * sizeof(char *));
     672                        if (compl == NULL) {
     673                                printf("Error: Out of memory.\n");
     674                                break;
     675                        }
     676                }
     677
     678                compl[cnum] = str_dup(ctmp);
     679                if (compl[cnum] == NULL) {
     680                        printf("Error: Out of memory.\n");
     681                        break;
     682                }
     683                cnum++;
     684        }
     685
     686        (*ti->compl_ops->fini)(state);
     687
     688        if (cnum > 1) {
     689                /*
     690                 * More than one match. Determine maximum common prefix.
     691                 */
     692                size_t cplen;
     693
     694                cplen = str_length(compl[0]);
     695                for (i = 1; i < cnum; i++)
     696                        cplen = min(cplen, common_pref_len(compl[0], compl[i]));
     697
     698                /* Compute how many bytes we should skip. */
     699                size_t istart = str_lsize(compl[0], ti->pos - cstart);
     700
     701                if (cplen > istart) {
     702                        /* Insert common prefix. */
     703
     704                        /* Copy remainder of common prefix. */
     705                        char *cpref = str_ndup(compl[0] + istart,
     706                            str_lsize(compl[0], cplen - istart));
     707
     708                        /* Insert it. */
     709                        tinput_insert_string(ti, cpref);
     710                        free(cpref);
     711                } else {
     712                        /* No common prefix. Sort and display all entries. */
     713
     714                        qsort(compl, cnum, sizeof(char *), compl_cmp, NULL);
     715
     716                        tinput_jump_after(ti);
     717                        tinput_show_completions(ti, compl, cnum);
     718                        tinput_display(ti);
     719                }
     720        } else if (cnum == 1) {
     721                /*
     722                 * We have exactly one match. Insert it.
     723                 */
     724
     725                /* Compute how many bytes of completion string we should skip. */
     726                size_t istart = str_lsize(compl[0], ti->pos - cstart);
     727
     728                /* Insert remainder of completion string at current position. */
     729                tinput_insert_string(ti, compl[0] + istart);
     730        }
     731
     732        for (i = 0; i < cnum; i++)
     733                free(compl[i]);
     734        free(compl);
     735}
     736
    512737/** Initialize text input field.
    513738 *
     
    516741static void tinput_init(tinput_t *ti)
    517742{
     743        ti->console = console_init(stdin, stdout);
    518744        ti->hnum = 0;
    519745        ti->hpos = 0;
    520746        ti->history[0] = NULL;
     747}
     748
     749/** Set prompt string.
     750 *
     751 * @param ti            Text input
     752 * @param prompt        Prompt string
     753 *
     754 * @return              EOK on success, ENOMEM if out of memory.
     755 */
     756int tinput_set_prompt(tinput_t *ti, const char *prompt)
     757{
     758        if (ti->prompt != NULL)
     759                free(ti->prompt);
     760       
     761        ti->prompt = str_dup(prompt);
     762        if (ti->prompt == NULL)
     763                return ENOMEM;
     764       
     765        return EOK;
     766}
     767
     768/** Set completion ops.
     769 *
     770 * Set pointer to completion ops structure that will be used for text
     771 * completion.
     772 */
     773void tinput_set_compl_ops(tinput_t *ti, tinput_compl_ops_t *compl_ops)
     774{
     775        ti->compl_ops = compl_ops;
    521776}
    522777
     
    533788int tinput_read(tinput_t *ti, char **dstr)
    534789{
    535         fflush(stdout);
    536         if (console_get_size(fphone(stdin), &ti->con_cols, &ti->con_rows) != EOK)
    537                 return EIO;
    538        
    539         if (console_get_pos(fphone(stdin), &ti->col0, &ti->row0) != EOK)
     790        console_flush(ti->console);
     791        if (console_get_size(ti->console, &ti->con_cols, &ti->con_rows) != EOK)
    540792                return EIO;
    541793       
     
    547799        ti->exit_clui = false;
    548800       
     801        if (tinput_display(ti) != EOK)
     802                return EIO;
     803       
    549804        while (!ti->done) {
    550                 fflush(stdout);
    551                
    552                 console_event_t ev;
    553                 if (!console_get_event(fphone(stdin), &ev))
     805                console_flush(ti->console);
     806               
     807                kbd_event_t ev;
     808                if (!console_get_kbd_event(ti->console, &ev))
    554809                        return EIO;
    555810               
     
    596851}
    597852
    598 static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev)
     853static void tinput_key_ctrl(tinput_t *ti, kbd_event_t *ev)
    599854{
    600855        switch (ev->key) {
     
    635890}
    636891
    637 static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev)
     892static void tinput_key_ctrl_shift(tinput_t *ti, kbd_event_t *ev)
    638893{
    639894        switch (ev->key) {
     
    655910}
    656911
    657 static void tinput_key_shift(tinput_t *ti, console_event_t *ev)
     912static void tinput_key_shift(tinput_t *ti, kbd_event_t *ev)
    658913{
    659914        switch (ev->key) {
     
    681936}
    682937
    683 static void tinput_key_unmod(tinput_t *ti, console_event_t *ev)
     938static void tinput_key_unmod(tinput_t *ti, kbd_event_t *ev)
    684939{
    685940        switch (ev->key) {
     
    712967                tinput_history_seek(ti, -1);
    713968                break;
     969        case KC_TAB:
     970                tinput_text_complete(ti);
     971                break;
    714972        default:
    715973                break;
Note: See TracChangeset for help on using the changeset viewer.