Ignore:
File:
1 edited

Legend:

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

    rbe61b8f r96b02eb9  
    11/*
    2  * Copyright (c) 2011 Jiri Svoboda
     2 * Copyright (c) 2010 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    2727 */
    2828
    29 #include <sort.h>
    3029#include <stdio.h>
    3130#include <stdlib.h>
     
    4342#include <tinput.h>
    4443
    45 #define LIN_TO_COL(ti, lpos) ((lpos) % ((ti)->con_cols))
    46 #define LIN_TO_ROW(ti, lpos) ((lpos) / ((ti)->con_cols))
    47 
    4844/** Seek direction */
    4945typedef enum {
     
    5854static void tinput_sel_all(tinput_t *);
    5955static void tinput_sel_delete(tinput_t *);
    60 static void tinput_key_ctrl(tinput_t *, kbd_event_t *);
    61 static void tinput_key_shift(tinput_t *, kbd_event_t *);
    62 static void tinput_key_ctrl_shift(tinput_t *, kbd_event_t *);
    63 static void tinput_key_unmod(tinput_t *, kbd_event_t *);
     56static void tinput_key_ctrl(tinput_t *, console_event_t *);
     57static void tinput_key_shift(tinput_t *, console_event_t *);
     58static void tinput_key_ctrl_shift(tinput_t *, console_event_t *);
     59static void tinput_key_unmod(tinput_t *, console_event_t *);
    6460static void tinput_pre_seek(tinput_t *, bool);
    6561static void tinput_post_seek(tinput_t *, bool);
    6662
    67 static 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 
    7363/** Create a new text input field. */
    7464tinput_t *tinput_new(void)
     
    7666        tinput_t *ti;
    7767       
    78         ti = calloc(1, sizeof(tinput_t));
     68        ti = malloc(sizeof(tinput_t));
    7969        if (ti == NULL)
    8070                return NULL;
     
    8777void tinput_destroy(tinput_t *ti)
    8878{
    89         if (ti->prompt != NULL)
    90                 free(ti->prompt);
    9179        free(ti);
    92 }
    93 
    94 static 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);
    10280}
    10381
     
    11088        tinput_sel_get_bounds(ti, &sa, &sb);
    11189       
    112         tinput_console_set_lpos(ti, ti->text_coord + start);
    113         console_set_style(ti->console, STYLE_NORMAL);
     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);
    11493       
    11594        size_t p = start;
     
    122101       
    123102        if (p < sb) {
    124                 console_flush(ti->console);
    125                 console_set_style(ti->console, STYLE_SELECTED);
    126                
     103                fflush(stdout);
     104                console_set_style(fphone(stdout), STYLE_SELECTED);
    127105                memcpy(dbuf, ti->buffer + p,
    128106                    (sb - p) * sizeof(wchar_t));
     
    132110        }
    133111       
    134         console_flush(ti->console);
    135         console_set_style(ti->console, STYLE_NORMAL);
     112        fflush(stdout);
     113        console_set_style(fphone(stdout), STYLE_NORMAL);
    136114       
    137115        if (p < ti->nc) {
     
    145123                putchar(' ');
    146124       
    147         console_flush(ti->console);
     125        fflush(stdout);
    148126}
    149127
     
    155133static void tinput_position_caret(tinput_t *ti)
    156134{
    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. */
     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. */
    161140static void tinput_update_origin(tinput_t *ti)
    162141{
    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 
    176 static 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 
    183 static 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;
     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;
    198148}
    199149
     
    203153                return;
    204154       
    205         unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + 1;
     155        sysarg_t new_width = ti->col0 + ti->nc + 1;
    206156        if (new_width % ti->con_cols == 0) {
    207157                /* Advancing to new line. */
     
    234184                return;
    235185       
    236         unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + ilen;
    237         unsigned new_height = (new_width / ti->con_cols) + 1;
     186        sysarg_t new_width = ti->col0 + ti->nc + ilen;
     187        sysarg_t new_height = (new_width / ti->con_cols) + 1;
    238188        if (new_height >= ti->con_rows) {
    239189                /* Disallow text longer than 1 page for now. */
     
    560510}
    561511
    562 /** Compare two entries in array of completions. */
    563 static 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 
    571 static 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 */
    594 static 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 
    632 static 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 
    737512/** Initialize text input field.
    738513 *
     
    741516static void tinput_init(tinput_t *ti)
    742517{
    743         ti->console = console_init(stdin, stdout);
    744518        ti->hnum = 0;
    745519        ti->hpos = 0;
    746520        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  */
    756 int 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  */
    773 void tinput_set_compl_ops(tinput_t *ti, tinput_compl_ops_t *compl_ops)
    774 {
    775         ti->compl_ops = compl_ops;
    776521}
    777522
     
    788533int tinput_read(tinput_t *ti, char **dstr)
    789534{
    790         console_flush(ti->console);
    791         if (console_get_size(ti->console, &ti->con_cols, &ti->con_rows) != EOK)
     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)
    792540                return EIO;
    793541       
     
    799547        ti->exit_clui = false;
    800548       
    801         if (tinput_display(ti) != EOK)
    802                 return EIO;
    803        
    804549        while (!ti->done) {
    805                 console_flush(ti->console);
    806                
    807                 kbd_event_t ev;
    808                 if (!console_get_kbd_event(ti->console, &ev))
     550                fflush(stdout);
     551               
     552                console_event_t ev;
     553                if (!console_get_event(fphone(stdin), &ev))
    809554                        return EIO;
    810555               
     
    851596}
    852597
    853 static void tinput_key_ctrl(tinput_t *ti, kbd_event_t *ev)
     598static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev)
    854599{
    855600        switch (ev->key) {
     
    890635}
    891636
    892 static void tinput_key_ctrl_shift(tinput_t *ti, kbd_event_t *ev)
     637static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev)
    893638{
    894639        switch (ev->key) {
     
    910655}
    911656
    912 static void tinput_key_shift(tinput_t *ti, kbd_event_t *ev)
     657static void tinput_key_shift(tinput_t *ti, console_event_t *ev)
    913658{
    914659        switch (ev->key) {
     
    936681}
    937682
    938 static void tinput_key_unmod(tinput_t *ti, kbd_event_t *ev)
     683static void tinput_key_unmod(tinput_t *ti, console_event_t *ev)
    939684{
    940685        switch (ev->key) {
     
    967712                tinput_history_seek(ti, -1);
    968713                break;
    969         case KC_TAB:
    970                 tinput_text_complete(ti);
    971                 break;
    972714        default:
    973715                break;
Note: See TracChangeset for help on using the changeset viewer.