00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <str.h>
00032 #include <io/console.h>
00033 #include <io/keycode.h>
00034 #include <io/style.h>
00035 #include <io/color.h>
00036 #include <vfs/vfs.h>
00037 #include <clipboard.h>
00038 #include <macros.h>
00039 #include <errno.h>
00040 #include <assert.h>
00041 #include <bool.h>
00042 #include <tinput.h>
00043
00045 typedef enum {
00046 seek_backward = -1,
00047 seek_forward = 1
00048 } seek_dir_t;
00049
00050 static void tinput_init(tinput_t *);
00051 static void tinput_insert_string(tinput_t *, const char *);
00052 static void tinput_sel_get_bounds(tinput_t *, size_t *, size_t *);
00053 static bool tinput_sel_active(tinput_t *);
00054 static void tinput_sel_all(tinput_t *);
00055 static void tinput_sel_delete(tinput_t *);
00056 static void tinput_key_ctrl(tinput_t *, console_event_t *);
00057 static void tinput_key_shift(tinput_t *, console_event_t *);
00058 static void tinput_key_ctrl_shift(tinput_t *, console_event_t *);
00059 static void tinput_key_unmod(tinput_t *, console_event_t *);
00060 static void tinput_pre_seek(tinput_t *, bool);
00061 static void tinput_post_seek(tinput_t *, bool);
00062
00064 tinput_t *tinput_new(void)
00065 {
00066 tinput_t *ti;
00067
00068 ti = malloc(sizeof(tinput_t));
00069 if (ti == NULL)
00070 return NULL;
00071
00072 tinput_init(ti);
00073 return ti;
00074 }
00075
00077 void tinput_destroy(tinput_t *ti)
00078 {
00079 free(ti);
00080 }
00081
00082 static void tinput_display_tail(tinput_t *ti, size_t start, size_t pad)
00083 {
00084 wchar_t dbuf[INPUT_MAX_SIZE + 1];
00085
00086 size_t sa;
00087 size_t sb;
00088 tinput_sel_get_bounds(ti, &sa, &sb);
00089
00090 console_set_pos(fphone(stdout), (ti->col0 + start) % ti->con_cols,
00091 ti->row0 + (ti->col0 + start) / ti->con_cols);
00092 console_set_style(fphone(stdout), STYLE_NORMAL);
00093
00094 size_t p = start;
00095 if (p < sa) {
00096 memcpy(dbuf, ti->buffer + p, (sa - p) * sizeof(wchar_t));
00097 dbuf[sa - p] = '\0';
00098 printf("%ls", dbuf);
00099 p = sa;
00100 }
00101
00102 if (p < sb) {
00103 fflush(stdout);
00104 console_set_style(fphone(stdout), STYLE_SELECTED);
00105 memcpy(dbuf, ti->buffer + p,
00106 (sb - p) * sizeof(wchar_t));
00107 dbuf[sb - p] = '\0';
00108 printf("%ls", dbuf);
00109 p = sb;
00110 }
00111
00112 fflush(stdout);
00113 console_set_style(fphone(stdout), STYLE_NORMAL);
00114
00115 if (p < ti->nc) {
00116 memcpy(dbuf, ti->buffer + p,
00117 (ti->nc - p) * sizeof(wchar_t));
00118 dbuf[ti->nc - p] = '\0';
00119 printf("%ls", dbuf);
00120 }
00121
00122 for (p = 0; p < pad; p++)
00123 putchar(' ');
00124
00125 fflush(stdout);
00126 }
00127
00128 static char *tinput_get_str(tinput_t *ti)
00129 {
00130 return wstr_to_astr(ti->buffer);
00131 }
00132
00133 static void tinput_position_caret(tinput_t *ti)
00134 {
00135 console_set_pos(fphone(stdout), (ti->col0 + ti->pos) % ti->con_cols,
00136 ti->row0 + (ti->col0 + ti->pos) / ti->con_cols);
00137 }
00138
00140 static void tinput_update_origin(tinput_t *ti)
00141 {
00142 sysarg_t width = ti->col0 + ti->nc;
00143 sysarg_t rows = (width / ti->con_cols) + 1;
00144
00145
00146 if (ti->row0 + rows > ti->con_rows)
00147 ti->row0 = ti->con_rows - rows;
00148 }
00149
00150 static void tinput_insert_char(tinput_t *ti, wchar_t c)
00151 {
00152 if (ti->nc == INPUT_MAX_SIZE)
00153 return;
00154
00155 sysarg_t new_width = ti->col0 + ti->nc + 1;
00156 if (new_width % ti->con_cols == 0) {
00157
00158 sysarg_t new_height = (new_width / ti->con_cols) + 1;
00159 if (new_height >= ti->con_rows) {
00160
00161 return;
00162 }
00163 }
00164
00165 size_t i;
00166 for (i = ti->nc; i > ti->pos; i--)
00167 ti->buffer[i] = ti->buffer[i - 1];
00168
00169 ti->buffer[ti->pos] = c;
00170 ti->pos += 1;
00171 ti->nc += 1;
00172 ti->buffer[ti->nc] = '\0';
00173 ti->sel_start = ti->pos;
00174
00175 tinput_display_tail(ti, ti->pos - 1, 0);
00176 tinput_update_origin(ti);
00177 tinput_position_caret(ti);
00178 }
00179
00180 static void tinput_insert_string(tinput_t *ti, const char *str)
00181 {
00182 size_t ilen = min(str_length(str), INPUT_MAX_SIZE - ti->nc);
00183 if (ilen == 0)
00184 return;
00185
00186 sysarg_t new_width = ti->col0 + ti->nc + ilen;
00187 sysarg_t new_height = (new_width / ti->con_cols) + 1;
00188 if (new_height >= ti->con_rows) {
00189
00190 return;
00191 }
00192
00193 if (ti->nc > 0) {
00194 size_t i;
00195 for (i = ti->nc; i > ti->pos; i--)
00196 ti->buffer[i + ilen - 1] = ti->buffer[i - 1];
00197 }
00198
00199 size_t off = 0;
00200 size_t i = 0;
00201 while (i < ilen) {
00202 wchar_t c = str_decode(str, &off, STR_NO_LIMIT);
00203 if (c == '\0')
00204 break;
00205
00206
00207 if (c < 32)
00208 c = 32;
00209
00210 ti->buffer[ti->pos + i] = c;
00211 i++;
00212 }
00213
00214 ti->pos += ilen;
00215 ti->nc += ilen;
00216 ti->buffer[ti->nc] = '\0';
00217 ti->sel_start = ti->pos;
00218
00219 tinput_display_tail(ti, ti->pos - ilen, 0);
00220 tinput_update_origin(ti);
00221 tinput_position_caret(ti);
00222 }
00223
00224 static void tinput_backspace(tinput_t *ti)
00225 {
00226 if (tinput_sel_active(ti)) {
00227 tinput_sel_delete(ti);
00228 return;
00229 }
00230
00231 if (ti->pos == 0)
00232 return;
00233
00234 size_t i;
00235 for (i = ti->pos; i < ti->nc; i++)
00236 ti->buffer[i - 1] = ti->buffer[i];
00237
00238 ti->pos -= 1;
00239 ti->nc -= 1;
00240 ti->buffer[ti->nc] = '\0';
00241 ti->sel_start = ti->pos;
00242
00243 tinput_display_tail(ti, ti->pos, 1);
00244 tinput_position_caret(ti);
00245 }
00246
00247 static void tinput_delete(tinput_t *ti)
00248 {
00249 if (tinput_sel_active(ti)) {
00250 tinput_sel_delete(ti);
00251 return;
00252 }
00253
00254 if (ti->pos == ti->nc)
00255 return;
00256
00257 ti->pos += 1;
00258 ti->sel_start = ti->pos;
00259
00260 tinput_backspace(ti);
00261 }
00262
00263 static void tinput_seek_cell(tinput_t *ti, seek_dir_t dir, bool shift_held)
00264 {
00265 tinput_pre_seek(ti, shift_held);
00266
00267 if (dir == seek_forward) {
00268 if (ti->pos < ti->nc)
00269 ti->pos += 1;
00270 } else {
00271 if (ti->pos > 0)
00272 ti->pos -= 1;
00273 }
00274
00275 tinput_post_seek(ti, shift_held);
00276 }
00277
00278 static void tinput_seek_word(tinput_t *ti, seek_dir_t dir, bool shift_held)
00279 {
00280 tinput_pre_seek(ti, shift_held);
00281
00282 if (dir == seek_forward) {
00283 if (ti->pos == ti->nc)
00284 return;
00285
00286 while (true) {
00287 ti->pos += 1;
00288
00289 if (ti->pos == ti->nc)
00290 break;
00291
00292 if ((ti->buffer[ti->pos - 1] == ' ') &&
00293 (ti->buffer[ti->pos] != ' '))
00294 break;
00295 }
00296 } else {
00297 if (ti->pos == 0)
00298 return;
00299
00300 while (true) {
00301 ti->pos -= 1;
00302
00303 if (ti->pos == 0)
00304 break;
00305
00306 if (ti->buffer[ti->pos - 1] == ' ' &&
00307 ti->buffer[ti->pos] != ' ')
00308 break;
00309 }
00310
00311 }
00312
00313 tinput_post_seek(ti, shift_held);
00314 }
00315
00316 static void tinput_seek_vertical(tinput_t *ti, seek_dir_t dir, bool shift_held)
00317 {
00318 tinput_pre_seek(ti, shift_held);
00319
00320 if (dir == seek_forward) {
00321 if (ti->pos + ti->con_cols <= ti->nc)
00322 ti->pos = ti->pos + ti->con_cols;
00323 } else {
00324 if (ti->pos >= ti->con_cols)
00325 ti->pos = ti->pos - ti->con_cols;
00326 }
00327
00328 tinput_post_seek(ti, shift_held);
00329 }
00330
00331 static void tinput_seek_max(tinput_t *ti, seek_dir_t dir, bool shift_held)
00332 {
00333 tinput_pre_seek(ti, shift_held);
00334
00335 if (dir == seek_backward)
00336 ti->pos = 0;
00337 else
00338 ti->pos = ti->nc;
00339
00340 tinput_post_seek(ti, shift_held);
00341 }
00342
00343 static void tinput_pre_seek(tinput_t *ti, bool shift_held)
00344 {
00345 if ((tinput_sel_active(ti)) && (!shift_held)) {
00346
00347 ti->sel_start = ti->pos;
00348 tinput_display_tail(ti, 0, 0);
00349 tinput_position_caret(ti);
00350 }
00351 }
00352
00353 static void tinput_post_seek(tinput_t *ti, bool shift_held)
00354 {
00355 if (shift_held) {
00356
00357 tinput_display_tail(ti, 0, 0);
00358 } else {
00359
00360 ti->sel_start = ti->pos;
00361 }
00362
00363 tinput_position_caret(ti);
00364 }
00365
00366 static void tinput_history_insert(tinput_t *ti, char *str)
00367 {
00368 if (ti->hnum < HISTORY_LEN) {
00369 ti->hnum += 1;
00370 } else {
00371 if (ti->history[HISTORY_LEN] != NULL)
00372 free(ti->history[HISTORY_LEN]);
00373 }
00374
00375 size_t i;
00376 for (i = ti->hnum; i > 1; i--)
00377 ti->history[i] = ti->history[i - 1];
00378
00379 ti->history[1] = str_dup(str);
00380
00381 if (ti->history[0] != NULL) {
00382 free(ti->history[0]);
00383 ti->history[0] = NULL;
00384 }
00385 }
00386
00387 static void tinput_set_str(tinput_t *ti, char *str)
00388 {
00389 str_to_wstr(ti->buffer, INPUT_MAX_SIZE, str);
00390 ti->nc = wstr_length(ti->buffer);
00391 ti->pos = ti->nc;
00392 ti->sel_start = ti->pos;
00393 }
00394
00395 static void tinput_sel_get_bounds(tinput_t *ti, size_t *sa, size_t *sb)
00396 {
00397 if (ti->sel_start < ti->pos) {
00398 *sa = ti->sel_start;
00399 *sb = ti->pos;
00400 } else {
00401 *sa = ti->pos;
00402 *sb = ti->sel_start;
00403 }
00404 }
00405
00406 static bool tinput_sel_active(tinput_t *ti)
00407 {
00408 return (ti->sel_start != ti->pos);
00409 }
00410
00411 static void tinput_sel_all(tinput_t *ti)
00412 {
00413 ti->sel_start = 0;
00414 ti->pos = ti->nc;
00415 tinput_display_tail(ti, 0, 0);
00416 tinput_position_caret(ti);
00417 }
00418
00419 static void tinput_sel_delete(tinput_t *ti)
00420 {
00421 size_t sa;
00422 size_t sb;
00423
00424 tinput_sel_get_bounds(ti, &sa, &sb);
00425 if (sa == sb)
00426 return;
00427
00428 memmove(ti->buffer + sa, ti->buffer + sb,
00429 (ti->nc - sb) * sizeof(wchar_t));
00430
00431 ti->pos = ti->sel_start = sa;
00432 ti->nc -= (sb - sa);
00433 ti->buffer[ti->nc] = '\0';
00434
00435 tinput_display_tail(ti, sa, sb - sa);
00436 tinput_position_caret(ti);
00437 }
00438
00439 static void tinput_sel_copy_to_cb(tinput_t *ti)
00440 {
00441 size_t sa;
00442 size_t sb;
00443
00444 tinput_sel_get_bounds(ti, &sa, &sb);
00445
00446 char *str;
00447
00448 if (sb < ti->nc) {
00449 wchar_t tmp_c = ti->buffer[sb];
00450 ti->buffer[sb] = '\0';
00451 str = wstr_to_astr(ti->buffer + sa);
00452 ti->buffer[sb] = tmp_c;
00453 } else
00454 str = wstr_to_astr(ti->buffer + sa);
00455
00456 if (str == NULL)
00457 goto error;
00458
00459 if (clipboard_put_str(str) != EOK)
00460 goto error;
00461
00462 free(str);
00463 return;
00464
00465 error:
00466
00467 return;
00468 }
00469
00470 static void tinput_paste_from_cb(tinput_t *ti)
00471 {
00472 char *str;
00473 int rc = clipboard_get_str(&str);
00474
00475 if ((rc != EOK) || (str == NULL)) {
00476
00477 return;
00478 }
00479
00480 tinput_insert_string(ti, str);
00481 free(str);
00482 }
00483
00484 static void tinput_history_seek(tinput_t *ti, int offs)
00485 {
00486 if (offs >= 0) {
00487 if (ti->hpos + offs > ti->hnum)
00488 return;
00489 } else {
00490 if (ti->hpos < (size_t) -offs)
00491 return;
00492 }
00493
00494 if (ti->history[ti->hpos] != NULL) {
00495 free(ti->history[ti->hpos]);
00496 ti->history[ti->hpos] = NULL;
00497 }
00498
00499 ti->history[ti->hpos] = tinput_get_str(ti);
00500 ti->hpos += offs;
00501
00502 int pad = (int) ti->nc - str_length(ti->history[ti->hpos]);
00503 if (pad < 0)
00504 pad = 0;
00505
00506 tinput_set_str(ti, ti->history[ti->hpos]);
00507 tinput_display_tail(ti, 0, pad);
00508 tinput_update_origin(ti);
00509 tinput_position_caret(ti);
00510 }
00511
00516 static void tinput_init(tinput_t *ti)
00517 {
00518 ti->hnum = 0;
00519 ti->hpos = 0;
00520 ti->history[0] = NULL;
00521 }
00522
00533 int tinput_read(tinput_t *ti, char **dstr)
00534 {
00535 fflush(stdout);
00536 if (console_get_size(fphone(stdin), &ti->con_cols, &ti->con_rows) != EOK)
00537 return EIO;
00538
00539 if (console_get_pos(fphone(stdin), &ti->col0, &ti->row0) != EOK)
00540 return EIO;
00541
00542 ti->pos = 0;
00543 ti->sel_start = 0;
00544 ti->nc = 0;
00545 ti->buffer[0] = '\0';
00546 ti->done = false;
00547 ti->exit_clui = false;
00548
00549 while (!ti->done) {
00550 fflush(stdout);
00551
00552 console_event_t ev;
00553 if (!console_get_event(fphone(stdin), &ev))
00554 return EIO;
00555
00556 if (ev.type != KEY_PRESS)
00557 continue;
00558
00559 if (((ev.mods & KM_CTRL) != 0) &&
00560 ((ev.mods & (KM_ALT | KM_SHIFT)) == 0))
00561 tinput_key_ctrl(ti, &ev);
00562
00563 if (((ev.mods & KM_SHIFT) != 0) &&
00564 ((ev.mods & (KM_CTRL | KM_ALT)) == 0))
00565 tinput_key_shift(ti, &ev);
00566
00567 if (((ev.mods & KM_CTRL) != 0) &&
00568 ((ev.mods & KM_SHIFT) != 0) &&
00569 ((ev.mods & KM_ALT) == 0))
00570 tinput_key_ctrl_shift(ti, &ev);
00571
00572 if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
00573 tinput_key_unmod(ti, &ev);
00574
00575 if (ev.c >= ' ') {
00576 tinput_sel_delete(ti);
00577 tinput_insert_char(ti, ev.c);
00578 }
00579 }
00580
00581 if (ti->exit_clui)
00582 return ENOENT;
00583
00584 ti->pos = ti->nc;
00585 tinput_position_caret(ti);
00586 putchar('\n');
00587
00588 char *str = tinput_get_str(ti);
00589 if (str_cmp(str, "") != 0)
00590 tinput_history_insert(ti, str);
00591
00592 ti->hpos = 0;
00593
00594 *dstr = str;
00595 return EOK;
00596 }
00597
00598 static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev)
00599 {
00600 switch (ev->key) {
00601 case KC_LEFT:
00602 tinput_seek_word(ti, seek_backward, false);
00603 break;
00604 case KC_RIGHT:
00605 tinput_seek_word(ti, seek_forward, false);
00606 break;
00607 case KC_UP:
00608 tinput_seek_vertical(ti, seek_backward, false);
00609 break;
00610 case KC_DOWN:
00611 tinput_seek_vertical(ti, seek_forward, false);
00612 break;
00613 case KC_X:
00614 tinput_sel_copy_to_cb(ti);
00615 tinput_sel_delete(ti);
00616 break;
00617 case KC_C:
00618 tinput_sel_copy_to_cb(ti);
00619 break;
00620 case KC_V:
00621 tinput_sel_delete(ti);
00622 tinput_paste_from_cb(ti);
00623 break;
00624 case KC_A:
00625 tinput_sel_all(ti);
00626 break;
00627 case KC_Q:
00628
00629 ti->done = true;
00630 ti->exit_clui = true;
00631 break;
00632 default:
00633 break;
00634 }
00635 }
00636
00637 static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev)
00638 {
00639 switch (ev->key) {
00640 case KC_LEFT:
00641 tinput_seek_word(ti, seek_backward, true);
00642 break;
00643 case KC_RIGHT:
00644 tinput_seek_word(ti, seek_forward, true);
00645 break;
00646 case KC_UP:
00647 tinput_seek_vertical(ti, seek_backward, true);
00648 break;
00649 case KC_DOWN:
00650 tinput_seek_vertical(ti, seek_forward, true);
00651 break;
00652 default:
00653 break;
00654 }
00655 }
00656
00657 static void tinput_key_shift(tinput_t *ti, console_event_t *ev)
00658 {
00659 switch (ev->key) {
00660 case KC_LEFT:
00661 tinput_seek_cell(ti, seek_backward, true);
00662 break;
00663 case KC_RIGHT:
00664 tinput_seek_cell(ti, seek_forward, true);
00665 break;
00666 case KC_UP:
00667 tinput_seek_vertical(ti, seek_backward, true);
00668 break;
00669 case KC_DOWN:
00670 tinput_seek_vertical(ti, seek_forward, true);
00671 break;
00672 case KC_HOME:
00673 tinput_seek_max(ti, seek_backward, true);
00674 break;
00675 case KC_END:
00676 tinput_seek_max(ti, seek_forward, true);
00677 break;
00678 default:
00679 break;
00680 }
00681 }
00682
00683 static void tinput_key_unmod(tinput_t *ti, console_event_t *ev)
00684 {
00685 switch (ev->key) {
00686 case KC_ENTER:
00687 case KC_NENTER:
00688 ti->done = true;
00689 break;
00690 case KC_BACKSPACE:
00691 tinput_backspace(ti);
00692 break;
00693 case KC_DELETE:
00694 tinput_delete(ti);
00695 break;
00696 case KC_LEFT:
00697 tinput_seek_cell(ti, seek_backward, false);
00698 break;
00699 case KC_RIGHT:
00700 tinput_seek_cell(ti, seek_forward, false);
00701 break;
00702 case KC_HOME:
00703 tinput_seek_max(ti, seek_backward, false);
00704 break;
00705 case KC_END:
00706 tinput_seek_max(ti, seek_forward, false);
00707 break;
00708 case KC_UP:
00709 tinput_history_seek(ti, 1);
00710 break;
00711 case KC_DOWN:
00712 tinput_history_seek(ti, -1);
00713 break;
00714 default:
00715 break;
00716 }
00717 }