From c6daef72a3e81d7bb05d70a344bde7f67ff5353d Mon Sep 17 00:00:00 2001 From: Kevin Lange Date: Fri, 30 Nov 2012 23:28:49 -0800 Subject: [PATCH] Major terminal improvements. * Speed improvements * Crash fixers * Significant overhaul to escape handling based on extensive use of the new serial console and a remote connection to a Linux box. * Updated included terminfo file to accurately reflect terminal behavior. Some fun facts: * vim runs great on a remote console (though the serial line slows a lot of things down, it still runs rather smoothly) * going to fix GNU screen, maybe in next commit, dunno * tested with htop, less, and a bunch of other stuff --- toaru.terminfo | 25 +++- userspace/serial-console.c | 1 - userspace/terminal.c | 263 +++++++++++++++++++++++-------------- 3 files changed, 189 insertions(+), 100 deletions(-) diff --git a/toaru.terminfo b/toaru.terminfo index e4f504be..7a0a2f69 100644 --- a/toaru.terminfo +++ b/toaru.terminfo @@ -1,8 +1,15 @@ toaru|toaruos framebuffer terminal, + mc5i, cols#128, lines#45, colors#256, it#8, ncv#18, pairs#32767, npc, am, - ind=^J, cr=^M, cud1=^J, nel=^J, ht=^I, + ind=^J, cr=^M, + nel=^J, ht=^I, + cud1=^J, + cuf1=\E[C, + cuu1=\E[A, + cub1=^H, home=\E[H, clear=\E[H\E[2J, + hpa=\E[%i%p1%dG, cup=\E[%i%p1%d;%p2%dH, ed=\E[J, el=\E[K, setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, @@ -10,11 +17,21 @@ toaru|toaruos framebuffer terminal, setb@, setf@, rev=\E[7m, sgr0=\E[0m, - smso=\E[7m, rmso=\E[27m, + smso=\E[7m, rmso=\E[m, sgr=\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m, - op=\E[39;39m, + op=\E[39;49m, bold=\E[1m, kcuu1=\E[A, kcud1=\E[B, - kcub1=\E[D, kcuf1=\E[C, + kcub1=\E[D, + kent=\E0M, + cuu=\E[%p1%dA, + cud=\E[%p1%dB, + cuf=\E[%p1%dC, + cub=\E[%p1%dD, + indn=\E[%p1%dS, + rin=\E[%p1%dT, + ri=\E[T, + sc=\E[s, + rc=\E[u, diff --git a/userspace/serial-console.c b/userspace/serial-console.c index 743a4edf..a90370df 100644 --- a/userspace/serial-console.c +++ b/userspace/serial-console.c @@ -26,7 +26,6 @@ void *print_serial_stuff(void * garbage) { for (int i = 0; i < size; ++i) { char x = buf[i]; - if (x == 13) continue; fputc(x, stdout); } if (size) fflush(stdout); diff --git a/userspace/terminal.c b/userspace/terminal.c index 75411cbb..aabd70ce 100644 --- a/userspace/terminal.c +++ b/userspace/terminal.c @@ -41,6 +41,14 @@ int mk_wcwidth_cjk(wchar_t ucs); #include "terminal-palette.h" #include "terminal-font.h" +/* + * If you're working on updating the terminal's handling of escapes, + * switch this option to 1 to have it print an escaped bit of output + * to the serial line, so you can examine exactly which codes were + * processed. + */ +#define DEBUG_TERMINAL_WITH_SERIAL 0 + /* A terminal cell represents a single character on screen */ typedef struct _terminal_cell { uint16_t c; /* codepoint */ @@ -96,6 +104,7 @@ size_t terminal_title_length = 0; gfx_context_t * ctx; volatile int needs_redraw = 1; static void render_decors(); +void term_clear(); /* Trigger to exit the terminal when the child process dies or * we otherwise receive an exit signal */ @@ -145,6 +154,8 @@ volatile int exit_application = 0; #define ANSI_EXT_IOCTL 'z' /* These are special escapes only we support */ +#define MAX_ARGS 1024 + /* Returns the lower of two shorts */ uint16_t min(uint16_t a, uint16_t b) { return (a < b) ? a : b; @@ -184,6 +195,7 @@ int (*ansi_get_csr_x)(void) = NULL; int (*ansi_get_csr_y)(void) = NULL; void (*ansi_set_cell)(int,int,uint16_t) = NULL; void (*ansi_cls)(int) = NULL; +void (*ansi_scroll)(int) = NULL; /* XXX: Needs verification, but I'm pretty sure this never gets called */ void (*redraw_cursor)(void) = NULL; @@ -252,7 +264,7 @@ ansi_put( /* Woah, woah, let's see here. */ char * pch; /* tokenizer pointer */ char * save; /* strtok_r pointer */ - char * argv[1024]; /* escape arguments */ + char * argv[MAX_ARGS]; /* escape arguments */ /* Get rid of the front of the buffer */ strtok_r(state.buffer,"[",&save); pch = strtok_r(NULL,";",&save); @@ -261,9 +273,10 @@ ansi_put( while (pch != NULL) { argv[argc] = (char *)pch; ++argc; + if (argc > MAX_ARGS) + break; pch = strtok_r(NULL,";",&save); } - argv[argc] = NULL; /* Alright, let's do this */ switch (c) { case ANSI_EXT_IOCTL: @@ -403,9 +416,11 @@ ansi_put( } break; case ANSI_SHOW: - if (!strcmp(argv[0], "?1049")) { - ansi_cls(2); - ansi_set_csr(0,0); + if (argc > 0) { + if (!strcmp(argv[0], "?1049")) { + ansi_cls(2); + ansi_set_csr(0,0); + } } break; case ANSI_CUF: @@ -447,16 +462,16 @@ ansi_put( case ANSI_CHA: if (argc < 1) { ansi_set_csr(0,ansi_get_csr_y()); - break; + } else { + ansi_set_csr(min(max(atoi(argv[0]), 1), state.width) - 1, ansi_get_csr_y()); } - ansi_set_csr(min(max(atoi(argv[0]), 1), state.width) - 1, ansi_get_csr_y()); break; case ANSI_CUP: if (argc < 2) { ansi_set_csr(0,0); - break; + } else { + ansi_set_csr(min(max(atoi(argv[1]), 1), state.width) - 1, min(max(atoi(argv[0]), 1), state.height) - 1); } - ansi_set_csr(min(max(atoi(argv[1]), 1), state.width) - 1, min(max(atoi(argv[0]), 1), state.height) - 1); break; case ANSI_ED: if (argc < 1) { @@ -493,10 +508,28 @@ ansi_put( input_buffer_stuff(out); } break; + case ANSI_SU: + { + int how_many = 1; + if (argc > 0) { + how_many = atoi(argv[0]); + } + ansi_scroll(how_many); + } + break; + case ANSI_SD: + { + int how_many = 1; + if (argc > 0) { + how_many = atoi(argv[0]); + } + ansi_scroll(-how_many); + } + break; case 'X': { int how_many = 1; - if (argc >= 1) { + if (argc > 0) { how_many = atoi(argv[0]); } for (int i = 0; i < how_many; ++i) { @@ -535,7 +568,7 @@ ansi_put( /* Tokenize on semicolons, like we always do */ char * pch; /* tokenizer pointer */ char * save; /* strtok_r pointer */ - char * argv[1024]; /* escape arguments */ + char * argv[MAX_ARGS]; /* escape arguments */ /* Get rid of the front of the buffer */ strtok_r(state.buffer,"]",&save); pch = strtok_r(NULL,";",&save); @@ -544,19 +577,21 @@ ansi_put( while (pch != NULL) { argv[argc] = (char *)pch; ++argc; + if (argc > MAX_ARGS) break; pch = strtok_r(NULL,";",&save); } - argv[argc] = NULL; /* Start testing the first argument for what command to use */ - if (!strcmp(argv[0], "1")) { - if (argc > 1) { - int len = min(TERMINAL_TITLE_SIZE, strlen(argv[1])+1); - memcpy(terminal_title, argv[1], len); - terminal_title[len-1] = '\0'; - terminal_title_length = len - 1; - render_decors(); - } - } /* Currently, no other options */ + if (argv[0]) { + if (!strcmp(argv[0], "1")) { + if (argc > 1) { + int len = min(TERMINAL_TITLE_SIZE, strlen(argv[1])+1); + memcpy(terminal_title, argv[1], len); + terminal_title[len-1] = '\0'; + terminal_title_length = len - 1; + render_decors(); + } + } /* Currently, no other options */ + } /* Clear out the buffer */ state.buflen = 0; state.escape = 0; @@ -571,7 +606,7 @@ ansi_put( void ansi_init(void (*writer)(char), int w, int y, void (*setcolor)(unsigned char, unsigned char), void (*setcsr)(int,int), int (*getcsrx)(void), int (*getcsry)(void), void (*setcell)(int,int,uint16_t), - void (*cls)(int), void (*redraw_csr)(void)) { + void (*cls)(int), void (*redraw_csr)(void), void (*scroll_term)(int)) { ansi_writer = writer; ansi_set_color = setcolor; @@ -581,6 +616,7 @@ void ansi_init(void (*writer)(char), int w, int y, void (*setcolor)(unsigned cha ansi_set_cell = setcell; ansi_cls = cls; redraw_cursor = redraw_csr; + ansi_scroll = scroll_term; /* Terminal Defaults */ state.fg = DEFAULT_FG; /* Light grey */ @@ -624,21 +660,6 @@ static inline void term_set_point(uint16_t x, uint16_t y, uint32_t color ) { } } -static inline void term_set_point_alpha(uint16_t x, uint16_t y, uint32_t color, uint8_t alpha) { - if (_windowed) { - GFX(ctx, (x+decor_left_width),(y+decor_top_height)) = color | (alpha * 0x1000000); - } else { - if (ctx->depth == 32) { - GFX(ctx, x,y) = color | 0xFF000000; - } else if (ctx->depth == 24) { - ctx->backbuffer[((y) * ctx->width + x) * 3 + 2] = _RED(color); - ctx->backbuffer[((y) * ctx->width + x) * 3 + 1] = _GRE(color); - ctx->backbuffer[((y) * ctx->width + x) * 3 + 0] = _BLU(color); - } - } -} - - /* FreeType text rendering */ FT_Library library; @@ -656,7 +677,8 @@ void drawChar(FT_Bitmap * bitmap, int x, int y, uint32_t fg, uint32_t bg) { int y_max = y + bitmap->rows; for (j = y, q = 0; j < y_max; j++, q++) { for ( i = x, p = 0; i < x_max; i++, p++) { - term_set_point(i,j, alpha_blend(bg, fg, rgb(bitmap->buffer[q * bitmap->width + p],0,0))); + uint32_t tmp = (fg & 0xFFFFFF) | 0x1000000 * bitmap->buffer[q * bitmap->width + p]; + term_set_point(i,j, alpha_blend_rgba(bg, tmp)); } } } @@ -729,7 +751,7 @@ term_write_char( } } } - if (val < 32) { + if (val < 32 || val == ' ') { return; } int pen_x = x; @@ -870,32 +892,72 @@ void term_redraw_all() { } } -void term_term_scroll() { - /* Shirt terminal cells one row up */ - memmove(term_buffer, (void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width), sizeof(t_cell) * term_width * (term_height - 1)); - /* Reset the "new" row to clean cells */ - memset((void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width * (term_height - 1)), 0x0, sizeof(t_cell) * term_width); - if (_vga_mode) { - /* In VGA mode, we can very quickly just redraw everything */ - term_redraw_all(); - } else { - /* In graphical modes, we will shift the graphics buffer up as necessary */ - uintptr_t dst, src; - size_t siz = char_height * (term_height - 1) * GFX_W(ctx) * GFX_B(ctx); - if (_windowed) { - /* Windowed mode must take borders into account */ - dst = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * decor_top_height) * GFX_B(ctx); - src = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * (decor_top_height + char_height)) * GFX_B(ctx); +void term_scroll(int how_much) { + if (how_much >= term_height || -how_much >= term_height) { + term_clear(); + return; + } + if (how_much == 0) { + return; + } + if (how_much > 0) { + /* Shift terminal cells one row up */ + memmove(term_buffer, (void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width), sizeof(t_cell) * term_width * (term_height - how_much)); + /* Reset the "new" row to clean cells */ + memset((void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width * (term_height - how_much)), 0x0, sizeof(t_cell) * term_width * how_much); + if (_vga_mode) { + /* In VGA mode, we can very quickly just redraw everything */ + term_redraw_all(); } else { - /* While fullscreen mode does not */ - dst = (uintptr_t)ctx->backbuffer; - src = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * char_height) * GFX_B(ctx); + /* In graphical modes, we will shift the graphics buffer up as necessary */ + uintptr_t dst, src; + size_t siz = char_height * (term_height - how_much) * GFX_W(ctx) * GFX_B(ctx); + if (_windowed) { + /* Windowed mode must take borders into account */ + dst = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * decor_top_height) * GFX_B(ctx); + src = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * (decor_top_height + char_height * how_much)) * GFX_B(ctx); + } else { + /* While fullscreen mode does not */ + dst = (uintptr_t)ctx->backbuffer; + src = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * char_height * how_much) * GFX_B(ctx); + } + /* Perform the shift */ + memmove((void *)dst, (void *)src, siz); + /* And redraw the new rows */ + for (int i = 0; i < how_much; ++i) { + for (uint16_t x = 0; x < term_width; ++x) { + cell_redraw(x, term_height - how_much); + } + } } - /* Perform the shift */ - memmove((void *)dst, (void *)src, siz); - /* And redraw the new rows */ - for (uint16_t x = 0; x < term_width; ++x) { - cell_redraw(x, term_height - 1); + } else { + how_much = -how_much; + /* Shift terminal cells one row up */ + memmove((void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width), term_buffer, sizeof(t_cell) * term_width * (term_height - how_much)); + /* Reset the "new" row to clean cells */ + memset(term_buffer, 0x0, sizeof(t_cell) * term_width * how_much); + if (_vga_mode) { + /* In VGA mode, we can very quickly just redraw everything */ + term_redraw_all(); + } else { + uintptr_t dst, src; + size_t siz = char_height * (term_height - how_much) * GFX_W(ctx) * GFX_B(ctx); + if (_windowed) { + src = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * decor_top_height) * GFX_B(ctx); + dst = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * (decor_top_height + char_height * how_much)) * GFX_B(ctx); + } else { + /* While fullscreen mode does not */ + src = (uintptr_t)ctx->backbuffer; + dst = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * char_height * how_much) * GFX_B(ctx); + } + /* Perform the shift */ + memmove((void *)dst, (void *)src, siz); + /* And redraw the new rows */ + for (int i = 0; i < how_much; ++i) { + for (uint16_t x = 0; x < term_width; ++x) { + cell_redraw(x, i); + } + } } } } @@ -904,6 +966,7 @@ uint32_t codepoint; uint32_t unicode_state = 0; int is_wide(uint32_t codepoint) { + if (codepoint < 256) return 0; return mk_wcwidth_cjk(codepoint) == 2; } @@ -915,13 +978,19 @@ void term_write(char c) { c = '?'; } if (c == '\n') { - for (uint16_t i = csr_x; i < term_width; ++i) { - /* I like this behaviour */ - cell_set(i, csr_y, ' ',current_fg, current_bg, state.flags); - cell_redraw(i, csr_y); - } csr_x = 0; ++csr_y; + draw_cursor(); + } else if (c == '\007') { + /* bell */ + for (int i = 0; i < term_height; ++i) { + for (int j = 0; j < term_width; ++j) { + cell_redraw_inverted(j, i); + } + } + /* XXX: sleep */ + for (int i = 0; i < 10; ++i) syscall_yield(); + term_redraw_all(); } else if (c == '\r') { cell_redraw(csr_x,csr_y); csr_x = 0; @@ -929,10 +998,11 @@ void term_write(char c) { if (csr_x > 0) { --csr_x; } - cell_set(csr_x, csr_y, ' ',current_fg, current_bg, state.flags); cell_redraw(csr_x, csr_y); + draw_cursor(); } else if (c == '\t') { - csr_x = (csr_x + 8) & ~(8 - 1); + csr_x += (8 - csr_x % 8); + draw_cursor(); } else { int wide = is_wide(codepoint); uint8_t flags = state.flags; @@ -958,7 +1028,7 @@ void term_write(char c) { ++csr_y; } if (csr_y == term_height) { - term_term_scroll(); + term_scroll(1); csr_y = term_height - 1; } } else if (unicode_state == UTF8_REJECT) { @@ -972,6 +1042,7 @@ term_set_csr(int x, int y) { cell_redraw(csr_x,csr_y); csr_x = x; csr_y = y; + draw_cursor(); } int @@ -1026,7 +1097,7 @@ void term_redraw_cell(int x, int y) { cell_redraw(x,y); } -void term_term_clear(int i) { +void term_clear(int i) { if (i == 2) { /* Oh dear */ csr_x = 0; @@ -1057,28 +1128,6 @@ void term_term_clear(int i) { } } -void cat(char * file) { - FILE * f = fopen(file, "rb"); - if (!f) { - ansi_print("Failed to open file, so skipping that part.\n"); - return; - } - - size_t len = 0; - fseek(f, 0, SEEK_END); - len = ftell(f); - fseek(f, 0, SEEK_SET); - - char * buffer = (char *)malloc(sizeof(char) * len); - fread(buffer, 1, len, f); - fclose(f); - for (size_t i = 0; i < len; ++i) { - ansi_put(buffer[i]); - } - - free(buffer); -} - char * loadMemFont(char * name, char * ident, size_t * size) { if (!_windowed) { FILE * f = fopen(name, "r"); @@ -1277,7 +1326,7 @@ void reinit() { } /* XXX: Transfer values, cursor location, etc.? */ term_buffer = malloc(sizeof(t_cell) * term_width * term_height); - ansi_init(&term_write, term_width, term_height, &term_set_colors, &term_set_csr, &term_get_csr_x, &term_get_csr_y, &term_set_cell, &term_term_clear, &term_redraw_cursor); + ansi_init(&term_write, term_width, term_height, &term_set_colors, &term_set_csr, &term_get_csr_x, &term_get_csr_y, &term_set_cell, &term_clear, &term_redraw_cursor, &term_scroll); mouse_x = ctx->width / 2; mouse_y = ctx->height / 2; @@ -1290,6 +1339,23 @@ void reinit() { ansi_print("\033[H\033[2J"); } +#if DEBUG_TERMINAL_WITH_SERIAL +DEFN_SYSCALL1(serial, 44, int); +int serial_fd; +void serial_put(uint8_t c) { + if (c == '\033') { + char out[3] = {'\\', 'E', 0}; + write(serial_fd, out, 2); + } else if (c < 32) { + char out[5] = {'\\', '0' + ((c / 8) / 8) % 8, '0' + (c / 8) % 8, '0' + c % 8, 0}; + write(serial_fd, out, 4); + } else { + char out[2] = {c, 0}; + write(serial_fd, out, 1); + } +} +#endif + int main(int argc, char ** argv) { _windowed = 1; @@ -1360,6 +1426,10 @@ int main(int argc, char ** argv) { } } +#if DEBUG_TERMINAL_WITH_SERIAL + serial_fd = syscall_serial(0x3F8); +#endif + putenv("TERM=toaru"); if (_windowed) { @@ -1546,6 +1616,9 @@ fail_mouse: int r = read(ofd, buf, min(_stat.st_size, 1024)); for (uint32_t i = 0; i < r; ++i) { ansi_put(buf[i]); +#if DEBUG_TERMINAL_WITH_SERIAL + serial_put(buf[i]); +#endif } } }