diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index dca2a4d8b..bf1a5cf97 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -136,14 +136,6 @@ c3_w pid_w; // pid of checkpoint process } u3_save; - /* u3_ubuf: unix tty i/o buffer. - */ - typedef struct _u3_ubuf { - struct _u3_ubuf* nex_u; - c3_w len_w; - c3_y hun_y[0]; // bytes to send - } u3_ubuf; - /* u3_utat: unix terminal state. */ typedef struct { @@ -153,9 +145,10 @@ } siz; struct { - c3_w* lin_w; // current line (utf32) - c3_w len_w; // length of current line - c3_w sap_w; // escape chars in current line + c3_y* lin_y; // current line (utf8) + c3_w byt_w; // utf8 line-length + c3_w wor_w; // utf32 line-length + c3_w sap_w; // escape chars in line c3_w cus_w; // cursor position } mir; @@ -190,25 +183,26 @@ /* u2_utfo: unix terminfo strings. */ typedef struct { + // disabled, currently unused + // + // struct { + // uv_buf_t kcuu1_u; // key_up + // uv_buf_t kcud1_u; // key_down + // uv_buf_t kcub1_u; // key_back + // uv_buf_t kcuf1_u; // key_forward + // } inn; struct { - const c3_y* kcuu1_y; // key_up - const c3_y* kcud1_y; // key_down - const c3_y* kcub1_y; // key_back - const c3_y* kcuf1_y; // key_forward - c3_w max_w; // maximum input sequence length - } inn; - struct { - const c3_y* clear_y; // clear_screen - const c3_y* el_y; // clr_bol clear to beginning - // const c3_y* el1_y; // clr_eol clear to end - const c3_y* ed_y; // clear to end of screen - const c3_y* bel_y; // bel sound bell - const c3_y* cub1_y; // parm_left - const c3_y* cuf1_y; // parm_right - const c3_y* cuu1_y; // parm_up - const c3_y* cud1_y; // parm_down - // const c3_y* cub_y; // parm_left_cursor #num - // const c3_y* cuf_y; // parm_right_cursor #num + uv_buf_t clear_u; // clear_screen + uv_buf_t el_u; // clr_bol clear to beginning + // uv_buf_t el1_u; // clr_eol clear to end + uv_buf_t ed_u; // clear to end of screen + uv_buf_t bel_u; // bel sound bell + uv_buf_t cub1_u; // parm_left + uv_buf_t cuf1_u; // parm_right + uv_buf_t cuu1_u; // parm_up + uv_buf_t cud1_u; // parm_down + // uv_buf_t cub_u; // parm_left_cursor #num + // uv_buf_t cuf_u; // parm_right_cursor #num } out; } u3_utfo; diff --git a/pkg/urbit/vere/io/term.c b/pkg/urbit/vere/io/term.c index d1da4c966..53dd79459 100644 --- a/pkg/urbit/vere/io/term.c +++ b/pkg/urbit/vere/io/term.c @@ -13,6 +13,14 @@ #include "all.h" #include "vere/vere.h" +// macros for string literal args/buffers +// +// since (sizeof(s) - 1) is used for vector length, parameters +// must be appropriately typed. use with care! +// +#define TERM_LIT(s) sizeof(s) - 1, (const c3_y*)(s) +#define TERM_LIT_BUF(s) uv_buf_init(s, sizeof(s) - 1) + static u3_utty* _term_main(); static void _term_read_cb(uv_stream_t* tcp_u, ssize_t siz_i, @@ -143,62 +151,36 @@ u3_term_log_init(void) uv_pipe_open(&(uty_u->pop_u), uty_u->fid_i); } - // Load terminfo strings. + // configure output escape sequences + // + // our requirements are minimal here, so we bypass terminfo + // and simply use constant sequences. // { - c3_w len_w; - - uty_u->ufo_u.inn.max_w = 0; - - // escape sequences we use - // (as reported by the terminfo database we bundled) - // - { - uty_u->ufo_u.out.clear_y = (const c3_y*)"\033[H\033[2J"; - uty_u->ufo_u.out.el_y = (const c3_y*)"\033[K"; - // uty_u->ufo_u.out.el1_y = (const c3_y*)"\033[1K"; - uty_u->ufo_u.out.ed_y = (const c3_y*)"\033[J"; - uty_u->ufo_u.out.bel_y = (const c3_y*)"\x7"; - uty_u->ufo_u.out.cub1_y = (const c3_y*)"\x8"; - uty_u->ufo_u.out.cuf1_y = (const c3_y*)"\033[C"; - uty_u->ufo_u.out.cuu1_y = (const c3_y*)"\033[A"; - uty_u->ufo_u.out.cud1_y = (const c3_y*)"\xa"; - // uty_u->ufo_u.out.cub_y = (const c3_y*)"\033[%p1%dD"; - // uty_u->ufo_u.out.cuf_y = (const c3_y*)"\033[%p1%dC"; - } - - // NB: terminfo reports the wrong sequence for arrow keys on xterms. - // - { - uty_u->ufo_u.inn.kcuu1_y = (const c3_y*)"\033[A"; // terminfo reports "\033OA" - uty_u->ufo_u.inn.kcud1_y = (const c3_y*)"\033[B"; // terminfo reports "\033OB" - uty_u->ufo_u.inn.kcuf1_y = (const c3_y*)"\033[C"; // terminfo reports "\033OC" - uty_u->ufo_u.inn.kcub1_y = (const c3_y*)"\033[D"; // terminfo reports "\033OD" - } - - uty_u->ufo_u.inn.max_w = 0; - if ( (len_w = strlen((c3_c*)uty_u->ufo_u.inn.kcuu1_y)) > - uty_u->ufo_u.inn.max_w ) - { - uty_u->ufo_u.inn.max_w = len_w; - } - if ( (len_w = strlen((c3_c*)uty_u->ufo_u.inn.kcud1_y)) > - uty_u->ufo_u.inn.max_w ) - { - uty_u->ufo_u.inn.max_w = len_w; - } - if ( (len_w = strlen((c3_c*)uty_u->ufo_u.inn.kcub1_y)) > - uty_u->ufo_u.inn.max_w ) - { - uty_u->ufo_u.inn.max_w = len_w; - } - if ( (len_w = strlen((c3_c*)uty_u->ufo_u.inn.kcuf1_y)) > - uty_u->ufo_u.inn.max_w ) - { - uty_u->ufo_u.inn.max_w = len_w; - } + uty_u->ufo_u.out.clear_u = TERM_LIT_BUF("\033[H\033[2J"); + uty_u->ufo_u.out.el_u = TERM_LIT_BUF("\033[K"); + // uty_u->ufo_u.out.el1_u = TERM_LIT_BUF("\033[1K"); + uty_u->ufo_u.out.ed_u = TERM_LIT_BUF("\033[J"); + uty_u->ufo_u.out.bel_u = TERM_LIT_BUF("\x7"); + uty_u->ufo_u.out.cub1_u = TERM_LIT_BUF("\x8"); + uty_u->ufo_u.out.cuf1_u = TERM_LIT_BUF("\033[C"); + uty_u->ufo_u.out.cuu1_u = TERM_LIT_BUF("\033[A"); + uty_u->ufo_u.out.cud1_u = TERM_LIT_BUF("\xa"); + // uty_u->ufo_u.out.cub_u = TERM_LIT_BUF("\033[%p1%dD"); + // uty_u->ufo_u.out.cuf_u = TERM_LIT_BUF("\033[%p1%dC"); } + // configure input escape sequences + // + // NB: terminfo reports the wrong sequence for arrow keys on xterms. + // disabled, currently unused + // { + // uty_u->ufo_u.inn.kcuu1_u = TERM_LIT_BUF("\033[A"); // terminfo reports "\033OA" + // uty_u->ufo_u.inn.kcud1_u = TERM_LIT_BUF("\033[B"); // terminfo reports "\033OB" + // uty_u->ufo_u.inn.kcuf1_u = TERM_LIT_BUF("\033[C"); // terminfo reports "\033OC" + // uty_u->ufo_u.inn.kcub1_u = TERM_LIT_BUF("\033[D"); // terminfo reports "\033OD" + // } + // Load old terminal state to restore. // { @@ -229,8 +211,10 @@ u3_term_log_init(void) // Initialize mirror and accumulator state. // { - uty_u->tat_u.mir.lin_w = 0; - uty_u->tat_u.mir.len_w = 0; + uty_u->tat_u.mir.lin_y = 0; + uty_u->tat_u.mir.byt_w = 0; + uty_u->tat_u.mir.wor_w = 0; + uty_u->tat_u.mir.sap_w = 0; uty_u->tat_u.mir.cus_w = 0; uty_u->tat_u.esc.ape = c3n; @@ -307,7 +291,9 @@ u3_term_log_exit(void) } } - uv_close((uv_handle_t*)&u3_Host.uty_u->pop_u, 0); + if ( u3_Host.uty_u ) { + uv_close((uv_handle_t*)&u3_Host.uty_u->pop_u, 0); + } } /* _term_tcsetattr(): tcsetattr w/retry on EINTR. @@ -331,25 +317,13 @@ _term_tcsetattr(c3_i fil_i, c3_i act_i, const struct termios* tms_u) return ret_i; } -/* _term_it_buf(): create a data buffer. -*/ -static u3_ubuf* -_term_it_buf(c3_w len_w, const c3_y* hun_y) -{ - u3_ubuf* buf_u = c3_malloc(len_w + sizeof(*buf_u)); - - buf_u->len_w = len_w; - memcpy(buf_u->hun_y, hun_y, len_w); - - buf_u->nex_u = 0; - return buf_u; -} - -/* _term_write_cb(): general write callback. +/* _term_it_write_cb(): general write callback. */ static void -_term_write_cb(uv_write_t* wri_u, c3_i sas_i) +_term_it_write_cb(uv_write_t* wri_u, c3_i sas_i) { + // write failure is logged, but otherwise ignored. + // if ( 0 != sas_i ) { u3l_log("term: write: %s\n", uv_strerror(sas_i)); } @@ -358,87 +332,102 @@ _term_write_cb(uv_write_t* wri_u, c3_i sas_i) c3_free(wri_u); } -/* _term_it_write_buf(): write buffer uv style. +/* _term_it_write(): write libuv buffer, freeing pointer. */ static void -_term_it_write_buf(u3_utty* uty_u, uv_buf_t buf_u) +_term_it_write(u3_utty* uty_u, + uv_buf_t* buf_u, + void* ptr_v) { - uv_write_t* wri_u = c3_malloc(sizeof(uv_write_t)); - wri_u->data = buf_u.base; - - c3_w ret_w; - if ( 0 != (ret_w = uv_write(wri_u, - (uv_stream_t*)&(uty_u->pop_u), - &buf_u, 1, - _term_write_cb)) ) - { - u3l_log("term: write: %s\n", uv_strerror(ret_w)); - } -} - -/* _term_it_write_old(): write buffer, transferring pointer. -*/ -static void -_term_it_write_old(u3_utty* uty_u, - u3_ubuf* old_u) -{ - uv_buf_t buf_u; - - // XX extra copy here due to old code. Use hbod as base directly. + // work off a local copy of the buffer, in case we need + // to manipulate the length/pointer // - { - c3_y* buf_y = c3_malloc(old_u->len_w); + uv_buf_t fub_u = { .base = buf_u->base, .len = buf_u->len }; + uv_stream_t* han_u = (uv_stream_t*)&(uty_u->pop_u); + c3_i ret_i; - memcpy(buf_y, old_u->hun_y, old_u->len_w); - buf_u = uv_buf_init((c3_c*)buf_y, old_u->len_w); + // try to write synchronously + // + while ( 1 ) { + ret_i = uv_try_write(han_u, &fub_u, 1); - c3_free(old_u); + if ( (ret_i > 0) && (ret_i < fub_u.len) ) { + fub_u.len -= ret_i; + fub_u.base += ret_i; + continue; + } + else { + break; + } + } + + // cue an async write if necessary + // + if ( UV_EAGAIN == ret_i ) { + uv_write_t* wri_u = c3_malloc(sizeof(*wri_u)); + wri_u->data = ptr_v; + + // invoke callback manually on error + // + if ( (ret_i = uv_write(wri_u, han_u, &fub_u, 1, _term_it_write_cb)) ) { + _term_it_write_cb(wri_u, ret_i); + } + } + else { + // synchronous write failure is logged, but otherwise ignored + // + if ( ret_i < 0 ) { + u3l_log("term: write: %s\n", uv_strerror(ret_i)); + } + + c3_free(ptr_v); } - _term_it_write_buf(uty_u, buf_u); } -/* _term_it_write_bytes(): write bytes, retaining pointer. +/* _term_it_dump_buf(): write static buffer. */ static void -_term_it_write_bytes(u3_utty* uty_u, - c3_w len_w, - const c3_y* hun_y) +_term_it_dump_buf(u3_utty* uty_u, + uv_buf_t* buf_u) { - _term_it_write_old(uty_u, _term_it_buf(len_w, hun_y)); + _term_it_write(uty_u, buf_u, 0); } -/* _term_it_write_txt(): write null-terminated string, retaining pointer. +/* _term_it_dump(): write static vector. */ static void -_term_it_write_txt(u3_utty* uty_u, - const c3_y* hun_y) +_term_it_dump(u3_utty* uty_u, + c3_w len_w, + const c3_y* hun_y) { - _term_it_write_bytes(uty_u, strlen((const c3_c*)hun_y), hun_y); + uv_buf_t buf_u = uv_buf_init((c3_c*)hun_y, len_w); + _term_it_dump_buf(uty_u, &buf_u); } -/* _term_it_write_str(): write null-terminated string, retaining pointer. +/* _term_it_send(): write dynamic vector, freeing pointer. */ static void -_term_it_write_str(u3_utty* uty_u, - const c3_c* str_c) +_term_it_send(u3_utty* uty_u, + c3_w len_w, + const c3_y* hun_y) { - _term_it_write_txt(uty_u, (const c3_y*) str_c); + uv_buf_t buf_u = uv_buf_init((c3_c*)hun_y, len_w); + _term_it_write(uty_u, &buf_u, (void*)hun_y); } -/* _term_it_show_wide(): show wide text, retaining. +/* _term_it_send_cord(): write a cord. */ static void -_term_it_show_wide(u3_utty* uty_u, c3_w len_w, c3_w* txt_w) +_term_it_send_cord(u3_utty* uty_u, + u3_atom txt) { - u3_noun wad = u3i_words(len_w, txt_w); - u3_noun txt = u3do("tuft", wad); - c3_c* txt_c = u3r_string(txt); + c3_w len_w = u3r_met(3, txt); + c3_y* hun_y = c3_malloc(len_w); + u3r_bytes(0, len_w, hun_y, txt); + + _term_it_send(uty_u, len_w, hun_y); - _term_it_write_str(uty_u, txt_c); - c3_free(txt_c); u3z(txt); - - uty_u->tat_u.mir.cus_w += len_w; } /* _term_it_show_clear(): clear to the beginning of the current line. @@ -447,10 +436,10 @@ static void _term_it_show_clear(u3_utty* uty_u) { if ( uty_u->tat_u.siz.col_l ) { - _term_it_write_str(uty_u, "\r"); - _term_it_write_txt(uty_u, uty_u->ufo_u.out.el_y); + _term_it_dump(uty_u, TERM_LIT("\r")); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.el_u); - uty_u->tat_u.mir.len_w = 0; + uty_u->tat_u.mir.wor_w = 0; uty_u->tat_u.mir.cus_w = 0; } } @@ -460,7 +449,7 @@ _term_it_show_clear(u3_utty* uty_u) static void _term_it_show_blank(u3_utty* uty_u) { - _term_it_write_txt(uty_u, uty_u->ufo_u.out.clear_y); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.clear_u); } /* _term_it_show_cursor(): set current line, transferring pointer. @@ -468,42 +457,55 @@ _term_it_show_blank(u3_utty* uty_u) static void _term_it_show_cursor(u3_utty* uty_u, c3_w cur_w) { + c3_w cus_w = uty_u->tat_u.mir.cus_w; + c3_w dif_w; + //NOTE assumes all styled text precedes the cursor. drum enforces this. // - cur_w = cur_w + uty_u->tat_u.mir.sap_w; + cur_w += uty_u->tat_u.mir.sap_w; - if ( cur_w < uty_u->tat_u.mir.cus_w ) { - c3_w dif_w = (uty_u->tat_u.mir.cus_w - cur_w); + if ( cur_w < cus_w ) { + dif_w = cus_w - cur_w; while ( dif_w-- ) { - _term_it_write_txt(uty_u, uty_u->ufo_u.out.cub1_y); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.cub1_u); } } - else if ( cur_w > uty_u->tat_u.mir.cus_w ) { - c3_w dif_w = (cur_w - uty_u->tat_u.mir.cus_w); + else if ( cur_w > cus_w ) { + dif_w = cur_w - cus_w; while ( dif_w-- ) { - _term_it_write_txt(uty_u, uty_u->ufo_u.out.cuf1_y); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.cuf1_u); } } + uty_u->tat_u.mir.cus_w = cur_w; } -/* _term_it_show_line(): set current line +/* _term_it_show_line(): render current line. */ static void -_term_it_show_line(u3_utty* uty_u, c3_w* lin_w, c3_w len_w, c3_w sap_w) +_term_it_show_line(u3_utty* uty_u, c3_w wor_w, c3_w sap_w) { - _term_it_show_wide(uty_u, len_w, lin_w); + u3_utat* tat_u = &uty_u->tat_u; - if ( lin_w != uty_u->tat_u.mir.lin_w ) { - if ( uty_u->tat_u.mir.lin_w ) { - c3_free(uty_u->tat_u.mir.lin_w); - } - uty_u->tat_u.mir.lin_w = lin_w; + // we have to reallocate the current line on write, + // or we have a data race if a) the write is async, + // and b) a new output line arrives before the write completes. + // + { + c3_w len_w = tat_u->mir.byt_w; + c3_y* hun_y = c3_malloc(len_w); + memcpy(hun_y, tat_u->mir.lin_y, len_w); + + _term_it_send(uty_u, len_w, hun_y); } - uty_u->tat_u.mir.len_w = len_w; - uty_u->tat_u.mir.sap_w = sap_w; + + // XX refactor to avoid updating state + // + tat_u->mir.cus_w += wor_w; + tat_u->mir.wor_w = wor_w; + tat_u->mir.sap_w = sap_w; } /* _term_it_refresh_line(): refresh current line. @@ -511,25 +513,81 @@ _term_it_show_line(u3_utty* uty_u, c3_w* lin_w, c3_w len_w, c3_w sap_w) static void _term_it_refresh_line(u3_utty* uty_u) { - c3_w len_w = uty_u->tat_u.mir.len_w; - c3_w sap_w = uty_u->tat_u.mir.sap_w; - c3_w cus_w = uty_u->tat_u.mir.cus_w; + u3_utat* tat_u = &uty_u->tat_u; + c3_w wor_w = tat_u->mir.wor_w; + c3_w sap_w = tat_u->mir.sap_w; + c3_w cus_w = tat_u->mir.cus_w; _term_it_show_clear(uty_u); - _term_it_show_line(uty_u, uty_u->tat_u.mir.lin_w, len_w, sap_w); + _term_it_show_line(uty_u, wor_w, sap_w); _term_it_show_cursor(uty_u, cus_w); } +/* _term_it_set_line(): set current line. +*/ +static void +_term_it_set_line(u3_utty* uty_u, + c3_w* lin_w, + c3_w wor_w, + c3_w sap_w) +{ + u3_utat* tat_u = &uty_u->tat_u; + c3_y* hun_y = (c3_y*)lin_w; + c3_w byt_w = 0; + + // convert lin_w in-place from utf-32 to utf-8 + // + // (this is just a hand-translation of +tuft) + // XX refactor for use here and in a jet + // + { + c3_w car_w, i_w; + + for ( i_w = 0; i_w < wor_w; i_w++ ) { + car_w = lin_w[i_w]; + + if ( 0x7f >= car_w ) { + hun_y[byt_w++] = car_w; + } + else if ( 0x7ff >= car_w ) { + hun_y[byt_w++] = 0xc0 ^ ((car_w >> 6) & 0x1f); + hun_y[byt_w++] = 0x80 ^ (car_w & 0x3f); + } + else if ( 0xffff >= car_w ) { + hun_y[byt_w++] = 0xe0 ^ ((car_w >> 12) & 0xf); + hun_y[byt_w++] = 0x80 ^ ((car_w >> 6) & 0x3f); + hun_y[byt_w++] = 0x80 ^ (car_w & 0x3f); + } + else { + hun_y[byt_w++] = 0xf0 ^ ((car_w >> 18) & 0x7); + hun_y[byt_w++] = 0x80 ^ ((car_w >> 12) & 0x3f); + hun_y[byt_w++] = 0x80 ^ ((car_w >> 6) & 0x3f); + hun_y[byt_w++] = 0x80 ^ (car_w & 0x3f); + } + } + } + + c3_free(tat_u->mir.lin_y); + tat_u->mir.lin_y = hun_y; + tat_u->mir.byt_w = byt_w; + tat_u->mir.wor_w = wor_w; + tat_u->mir.sap_w = sap_w; + + _term_it_show_line(uty_u, wor_w, sap_w); +} + /* _term_it_show_more(): new current line. */ static void _term_it_show_more(u3_utty* uty_u) { if ( c3y == u3_Host.ops_u.tem ) { - _term_it_write_str(uty_u, "\n"); - } else { - _term_it_write_str(uty_u, "\r\n"); + _term_it_dump(uty_u, TERM_LIT("\n")); } + else { + _term_it_dump(uty_u, TERM_LIT("\r\n")); + } + uty_u->tat_u.mir.cus_w = 0; } @@ -658,7 +716,7 @@ _term_io_suck_char(u3_utty* uty_u, c3_y cay_y) if ( c3y == tat_u->esc.bra ) { switch ( cay_y ) { default: { - _term_it_write_txt(uty_u, uty_u->ufo_u.out.bel_y); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.bel_u); break; } case 'A': _term_io_belt(uty_u, u3nc(c3__aro, 'u')); break; @@ -687,7 +745,7 @@ _term_io_suck_char(u3_utty* uty_u, c3_y cay_y) else { tat_u->esc.ape = c3n; - _term_it_write_txt(uty_u, uty_u->ufo_u.out.bel_y); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.bel_u); } } } @@ -698,10 +756,9 @@ _term_io_suck_char(u3_utty* uty_u, c3_y cay_y) u3_noun huv = u3i_bytes(tat_u->fut.wid_w, tat_u->fut.syb_y); u3_noun wug; - // u3l_log("muck-utf8 len %d\n", tat_u->fut.len_w); - // u3l_log("muck-utf8 %x\n", huv); + // XX implement directly here and jet + // wug = u3do("taft", huv); - // u3l_log("muck-utf32 %x\n", tat_u->fut.len_w); tat_u->fut.len_w = tat_u->fut.wid_w = 0; _term_io_belt(uty_u, u3nt(c3__txt, wug, u3_nul)); @@ -712,7 +769,7 @@ _term_io_suck_char(u3_utty* uty_u, c3_y cay_y) _term_io_belt(uty_u, u3nt(c3__txt, cay_y, u3_nul)); } else if ( 0 == cay_y ) { - _term_it_write_txt(uty_u, uty_u->ufo_u.out.bel_y); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.bel_u); } else if ( 8 == cay_y || 127 == cay_y ) { _term_io_belt(uty_u, u3nc(c3__bac, u3_nul)); @@ -800,27 +857,104 @@ _term_read_cb(uv_stream_t* tcp_u, c3_free(buf_u->base); } -/* _term_spin_write_str(): write null-terminated string +/* _term_spin_step(): advance spinner state and (re-)render. */ static void -_term_spin_write_str(u3_utty* uty_u, - const c3_c* str_c) +_term_spin_step(u3_utty* uty_u) { - // c3_i fid_i = uv_fileno(&uty_u->pop_u); - c3_i fid_i = uty_u->pop_u.io_watcher.fd; // XX old libuv - c3_w len_w = strlen(str_c); + u3_utat* tat_u = &uty_u->tat_u; + c3_w bac_w; - if ( len_w != write(fid_i, str_c, len_w) ) { - // ignore, we just tryin + // calculate backoff from end of line, or bail out + // + { + c3_w cus_w = tat_u->mir.cus_w; + c3_l col_l = tat_u->siz.col_l; + + if ( cus_w >= col_l ) { // shenanigans! + return; + } + + bac_w = col_l - 1 - cus_w; } -} -/* _term_spin_move_left(): move the cursor left -*/ -static void -_term_spin_move_left(u3_utty* uty_u) -{ - _term_spin_write_str(uty_u, (const c3_c*)uty_u->ufo_u.out.cub1_y); + c3_d lag_d = tat_u->sun_u.eve_d++; + const c3_c daz_c[] = "|/-\\"; + // | + « + why + » + \0 + c3_c buf_c[1 + 2 + 4 + 2 + 1]; + c3_c* cur_c = buf_c; + c3_w sol_w = 1; // spinner length (utf-32) + + // set spinner char + // + *cur_c++ = daz_c[lag_d % (sizeof(daz_c) - 1)]; + + // if we have a spinner, add it between brackets + // + if ( tat_u->sun_u.why_c[0] ) { + *cur_c++ = '\xc2'; + *cur_c++ = '\xab'; + sol_w++; + + { + c3_c* why_c = tat_u->sun_u.why_c; + *cur_c++ = *why_c++; + *cur_c++ = *why_c++; + *cur_c++ = *why_c++; + *cur_c++ = *why_c; + // XX assumes one glyph per char + // + sol_w += 4; + } + + *cur_c++ = '\xc2'; + *cur_c++ = '\xbb'; + sol_w++; + } + + *cur_c = '\0'; + + // write spinner, adjusting cursor as needed + // + // NB: we simply bail out if anything goes wrong + // + { + uv_buf_t lef_u = uty_u->ufo_u.out.cub1_u; + c3_i fid_i; + + if ( uv_fileno((uv_handle_t*)&uty_u->pop_u, &fid_i) ) { + return; + } + + // One-time cursor backoff. + // + if ( c3n == tat_u->sun_u.diz_o ) { + c3_w i_w; + + for ( i_w = bac_w; i_w < sol_w; i_w++ ) { + if ( lef_u.len != write(fid_i, lef_u.base, lef_u.len) ) { + return; + } + } + + tat_u->sun_u.diz_o = c3y; + } + + { + c3_w len_w = cur_c - buf_c; + if ( len_w != write(fid_i, buf_c, len_w) ) { + return; + } + } + + // Cursor stays on spinner. + // + while ( sol_w-- ) { + if ( lef_u.len != write(fid_i, lef_u.base, lef_u.len) ) { + return; + } + } + } } /* _term_spin_timer_cb(): render spinner @@ -829,61 +963,7 @@ static void _term_spin_timer_cb(uv_timer_t* tim_u) { u3_utty* uty_u = tim_u->data; - u3_utat* tat_u = &uty_u->tat_u; - - c3_w cus_w = tat_u->mir.cus_w; - c3_l col_l = tat_u->siz.col_l; - - if ( cus_w >= col_l ) { // shenanigans! - return; - } - - c3_w bac_w = col_l - 1 - cus_w; // backoff from end of line - c3_d lag_d = tat_u->sun_u.eve_d++; - - const c3_c daz_c[] = "|/-\\"; - const c3_c dal_c[] = "\xc2\xab"; - const c3_c dar_c[] = "\xc2\xbb"; - - c3_c buf_c[1 + 2 + 4 + 2 + 1]; - // | + « + why + » + \0 - - c3_c* cur_c = buf_c; - - *cur_c++ = daz_c[lag_d % strlen(daz_c)]; - c3_w sol_w = 1; // spinner length (utf-32) - - if ( tat_u->sun_u.why_c[0] ) { - strncpy(cur_c, dal_c, 2); - cur_c += 2; - sol_w += 1; // length of dal_c (utf-32) - - strncpy(cur_c, tat_u->sun_u.why_c, 4); - cur_c += 4; - sol_w += 4; // XX assumed utf-8 - - strncpy(cur_c, dar_c, 2); - cur_c += 2; - sol_w += 1; // length of dar_c (utf-32) - } - - *cur_c = '\0'; - - // One-time cursor backoff. - if ( c3n == tat_u->sun_u.diz_o ) { - c3_w i_w; - for ( i_w = bac_w; i_w < sol_w; i_w++ ) { - _term_spin_move_left(uty_u); - } - } - - _term_spin_write_str(uty_u, buf_c); - tat_u->sun_u.diz_o = c3y; - - // Cursor stays on spinner. - while ( sol_w-- ) { - _term_spin_move_left(uty_u); - } + _term_spin_step(uty_u); } #define _SPIN_COOL_US 500UL // spinner activation delay when cool @@ -1081,7 +1161,7 @@ _term_it_put_deco(c3_w* lin_w, */ static void _term_it_show_stub(u3_utty* uty_u, - u3_noun tub) + u3_noun tub) { c3_w tuc_w = u3qb_lent(tub); @@ -1198,11 +1278,33 @@ _term_it_show_stub(u3_utty* uty_u, } } - _term_it_show_line(uty_u, lin_w, i_w, sap_w); + _term_it_set_line(uty_u, lin_w, i_w, sap_w); u3z(tub); } +/* _term_it_show_tour(): send utf32 to terminal. +*/ +static void +_term_it_show_tour(u3_utty* uty_u, + u3_noun lin) +{ + c3_w len_w = u3qb_lent(lin); + c3_w* lin_w = c3_malloc( sizeof(c3_w) * len_w ); + + { + c3_w i_w; + + for ( i_w = 0; u3_nul != lin; i_w++, lin = u3t(lin) ) { + lin_w[i_w] = u3r_word(0, u3h(lin)); + } + } + + _term_it_set_line(uty_u, lin_w, len_w, 0); + + u3z(lin); +} + /* _term_ef_blit(): send blit to terminal. */ static void @@ -1214,7 +1316,7 @@ _term_ef_blit(u3_utty* uty_u, case c3__bel: { if ( c3n == u3_Host.ops_u.tem ) { - _term_it_write_txt(uty_u, uty_u->ufo_u.out.bel_y); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.bel_u); } } break; @@ -1239,24 +1341,10 @@ _term_ef_blit(u3_utty* uty_u, } break; case c3__lin: { - u3_noun lin = u3t(blt); - c3_w len_w = u3kb_lent(u3k(lin)); - c3_w* lin_w = c3_malloc( sizeof(c3_w) * len_w ); - - { - c3_w i_w; - - for ( i_w = 0; u3_nul != lin; i_w++, lin = u3t(lin) ) { - lin_w[i_w] = u3r_word(0, u3h(lin)); - } - } - if ( c3n == u3_Host.ops_u.tem ) { _term_it_show_clear(uty_u); - _term_it_show_line(uty_u, lin_w, len_w, 0); - } else { - _term_it_show_line(uty_u, lin_w, len_w, 0); } + _term_it_show_tour(uty_u, u3k(u3t(blt))); } break; case c3__mor: { @@ -1264,36 +1352,38 @@ _term_ef_blit(u3_utty* uty_u, } break; case c3__sav: { - _term_it_save(u3k(u3h(u3t(blt))), u3k(u3t(u3t(blt)))); + u3_noun pax, dat; + u3x_cell(u3t(blt), &pax, &dat); + + _term_it_save(u3k(pax), u3k(dat)); } break; case c3__sag: { - u3_noun pib = u3k(u3t(u3t(blt))); - u3_noun jam; + u3_noun pax, dat; + u3x_cell(u3t(blt), &pax, &dat); - jam = u3ke_jam(pib); - - _term_it_save(u3k(u3h(u3t(blt))), jam); + _term_it_save(u3k(pax), u3qe_jam(dat)); } break; case c3__url: { - if ( c3n == u3ud(u3t(blt)) ) { - break; - } else { - c3_c* txt_c = u3r_string(u3t(blt)); + u3_noun txt = u3t(blt); + // XX check u3_Host.ops_u.tem ? + // XX this looks to be broken, + // multiple calls to _show_clear will discard the mirror state + // + if ( c3y == u3a_is_atom(txt) ) { _term_it_show_clear(uty_u); - _term_it_write_str(uty_u, txt_c); - c3_free(txt_c); + + _term_it_send_cord(uty_u, u3k(txt)); _term_it_show_more(uty_u); _term_it_refresh_line(uty_u); } - } + } break; } - u3z(blt); - return; + u3z(blt); } /* u3_term_io_hija(): hijack console for fprintf, returning FILE*. @@ -1329,8 +1419,10 @@ u3_term_io_hija(void) c3_assert(!"hija-fcntl"); } _write(uty_u->fid_i, "\r", 1); - _write(uty_u->fid_i, uty_u->ufo_u.out.el_y, - strlen((c3_c*) uty_u->ufo_u.out.el_y)); + { + uv_buf_t* buf_u = &uty_u->ufo_u.out.el_u; + _write(uty_u->fid_i, buf_u->base, buf_u->len); + } } return stdout; }