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
This commit is contained in:
Kevin Lange 2012-11-30 23:28:49 -08:00
parent c9448437dc
commit c6daef72a3
3 changed files with 189 additions and 100 deletions

View File

@ -1,8 +1,15 @@
toaru|toaruos framebuffer terminal, toaru|toaruos framebuffer terminal,
mc5i,
cols#128, lines#45, cols#128, lines#45,
colors#256, it#8, ncv#18, pairs#32767, npc, am, 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, home=\E[H, clear=\E[H\E[2J,
hpa=\E[%i%p1%dG,
cup=\E[%i%p1%d;%p2%dH, cup=\E[%i%p1%d;%p2%dH,
ed=\E[J, el=\E[K, 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, 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@, setb@,
setf@, setf@,
rev=\E[7m, sgr0=\E[0m, 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, 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, bold=\E[1m,
kcuu1=\E[A, kcuu1=\E[A,
kcud1=\E[B, kcud1=\E[B,
kcub1=\E[D,
kcuf1=\E[C, 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,

View File

@ -26,7 +26,6 @@ void *print_serial_stuff(void * garbage) {
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
char x = buf[i]; char x = buf[i];
if (x == 13) continue;
fputc(x, stdout); fputc(x, stdout);
} }
if (size) fflush(stdout); if (size) fflush(stdout);

View File

@ -41,6 +41,14 @@ int mk_wcwidth_cjk(wchar_t ucs);
#include "terminal-palette.h" #include "terminal-palette.h"
#include "terminal-font.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 */ /* A terminal cell represents a single character on screen */
typedef struct _terminal_cell { typedef struct _terminal_cell {
uint16_t c; /* codepoint */ uint16_t c; /* codepoint */
@ -96,6 +104,7 @@ size_t terminal_title_length = 0;
gfx_context_t * ctx; gfx_context_t * ctx;
volatile int needs_redraw = 1; volatile int needs_redraw = 1;
static void render_decors(); static void render_decors();
void term_clear();
/* Trigger to exit the terminal when the child process dies or /* Trigger to exit the terminal when the child process dies or
* we otherwise receive an exit signal */ * 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 ANSI_EXT_IOCTL 'z' /* These are special escapes only we support */
#define MAX_ARGS 1024
/* Returns the lower of two shorts */ /* Returns the lower of two shorts */
uint16_t min(uint16_t a, uint16_t b) { uint16_t min(uint16_t a, uint16_t b) {
return (a < b) ? a : b; return (a < b) ? a : b;
@ -184,6 +195,7 @@ int (*ansi_get_csr_x)(void) = NULL;
int (*ansi_get_csr_y)(void) = NULL; int (*ansi_get_csr_y)(void) = NULL;
void (*ansi_set_cell)(int,int,uint16_t) = NULL; void (*ansi_set_cell)(int,int,uint16_t) = NULL;
void (*ansi_cls)(int) = NULL; void (*ansi_cls)(int) = NULL;
void (*ansi_scroll)(int) = NULL;
/* XXX: Needs verification, but I'm pretty sure this never gets called */ /* XXX: Needs verification, but I'm pretty sure this never gets called */
void (*redraw_cursor)(void) = NULL; void (*redraw_cursor)(void) = NULL;
@ -252,7 +264,7 @@ ansi_put(
/* Woah, woah, let's see here. */ /* Woah, woah, let's see here. */
char * pch; /* tokenizer pointer */ char * pch; /* tokenizer pointer */
char * save; /* strtok_r 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 */ /* Get rid of the front of the buffer */
strtok_r(state.buffer,"[",&save); strtok_r(state.buffer,"[",&save);
pch = strtok_r(NULL,";",&save); pch = strtok_r(NULL,";",&save);
@ -261,9 +273,10 @@ ansi_put(
while (pch != NULL) { while (pch != NULL) {
argv[argc] = (char *)pch; argv[argc] = (char *)pch;
++argc; ++argc;
if (argc > MAX_ARGS)
break;
pch = strtok_r(NULL,";",&save); pch = strtok_r(NULL,";",&save);
} }
argv[argc] = NULL;
/* Alright, let's do this */ /* Alright, let's do this */
switch (c) { switch (c) {
case ANSI_EXT_IOCTL: case ANSI_EXT_IOCTL:
@ -403,9 +416,11 @@ ansi_put(
} }
break; break;
case ANSI_SHOW: case ANSI_SHOW:
if (!strcmp(argv[0], "?1049")) { if (argc > 0) {
ansi_cls(2); if (!strcmp(argv[0], "?1049")) {
ansi_set_csr(0,0); ansi_cls(2);
ansi_set_csr(0,0);
}
} }
break; break;
case ANSI_CUF: case ANSI_CUF:
@ -447,16 +462,16 @@ ansi_put(
case ANSI_CHA: case ANSI_CHA:
if (argc < 1) { if (argc < 1) {
ansi_set_csr(0,ansi_get_csr_y()); 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; break;
case ANSI_CUP: case ANSI_CUP:
if (argc < 2) { if (argc < 2) {
ansi_set_csr(0,0); 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; break;
case ANSI_ED: case ANSI_ED:
if (argc < 1) { if (argc < 1) {
@ -493,10 +508,28 @@ ansi_put(
input_buffer_stuff(out); input_buffer_stuff(out);
} }
break; 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': case 'X':
{ {
int how_many = 1; int how_many = 1;
if (argc >= 1) { if (argc > 0) {
how_many = atoi(argv[0]); how_many = atoi(argv[0]);
} }
for (int i = 0; i < how_many; ++i) { for (int i = 0; i < how_many; ++i) {
@ -535,7 +568,7 @@ ansi_put(
/* Tokenize on semicolons, like we always do */ /* Tokenize on semicolons, like we always do */
char * pch; /* tokenizer pointer */ char * pch; /* tokenizer pointer */
char * save; /* strtok_r 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 */ /* Get rid of the front of the buffer */
strtok_r(state.buffer,"]",&save); strtok_r(state.buffer,"]",&save);
pch = strtok_r(NULL,";",&save); pch = strtok_r(NULL,";",&save);
@ -544,19 +577,21 @@ ansi_put(
while (pch != NULL) { while (pch != NULL) {
argv[argc] = (char *)pch; argv[argc] = (char *)pch;
++argc; ++argc;
if (argc > MAX_ARGS) break;
pch = strtok_r(NULL,";",&save); pch = strtok_r(NULL,";",&save);
} }
argv[argc] = NULL;
/* Start testing the first argument for what command to use */ /* Start testing the first argument for what command to use */
if (!strcmp(argv[0], "1")) { if (argv[0]) {
if (argc > 1) { if (!strcmp(argv[0], "1")) {
int len = min(TERMINAL_TITLE_SIZE, strlen(argv[1])+1); if (argc > 1) {
memcpy(terminal_title, argv[1], len); int len = min(TERMINAL_TITLE_SIZE, strlen(argv[1])+1);
terminal_title[len-1] = '\0'; memcpy(terminal_title, argv[1], len);
terminal_title_length = len - 1; terminal_title[len-1] = '\0';
render_decors(); terminal_title_length = len - 1;
} render_decors();
} /* Currently, no other options */ }
} /* Currently, no other options */
}
/* Clear out the buffer */ /* Clear out the buffer */
state.buflen = 0; state.buflen = 0;
state.escape = 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 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 (*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_writer = writer;
ansi_set_color = setcolor; 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_set_cell = setcell;
ansi_cls = cls; ansi_cls = cls;
redraw_cursor = redraw_csr; redraw_cursor = redraw_csr;
ansi_scroll = scroll_term;
/* Terminal Defaults */ /* Terminal Defaults */
state.fg = DEFAULT_FG; /* Light grey */ 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 */ /* FreeType text rendering */
FT_Library library; 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; int y_max = y + bitmap->rows;
for (j = y, q = 0; j < y_max; j++, q++) { for (j = y, q = 0; j < y_max; j++, q++) {
for ( i = x, p = 0; i < x_max; i++, p++) { 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; return;
} }
int pen_x = x; int pen_x = x;
@ -870,32 +892,72 @@ void term_redraw_all() {
} }
} }
void term_term_scroll() { void term_scroll(int how_much) {
/* Shirt terminal cells one row up */ if (how_much >= term_height || -how_much >= term_height) {
memmove(term_buffer, (void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width), sizeof(t_cell) * term_width * (term_height - 1)); term_clear();
/* Reset the "new" row to clean cells */ return;
memset((void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width * (term_height - 1)), 0x0, sizeof(t_cell) * term_width); }
if (_vga_mode) { if (how_much == 0) {
/* In VGA mode, we can very quickly just redraw everything */ return;
term_redraw_all(); }
} else { if (how_much > 0) {
/* In graphical modes, we will shift the graphics buffer up as necessary */ /* Shift terminal cells one row up */
uintptr_t dst, src; memmove(term_buffer, (void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width), sizeof(t_cell) * term_width * (term_height - how_much));
size_t siz = char_height * (term_height - 1) * GFX_W(ctx) * GFX_B(ctx); /* Reset the "new" row to clean cells */
if (_windowed) { memset((void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width * (term_height - how_much)), 0x0, sizeof(t_cell) * term_width * how_much);
/* Windowed mode must take borders into account */ if (_vga_mode) {
dst = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * decor_top_height) * GFX_B(ctx); /* In VGA mode, we can very quickly just redraw everything */
src = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * (decor_top_height + char_height)) * GFX_B(ctx); term_redraw_all();
} else { } else {
/* While fullscreen mode does not */ /* In graphical modes, we will shift the graphics buffer up as necessary */
dst = (uintptr_t)ctx->backbuffer; uintptr_t dst, src;
src = (uintptr_t)ctx->backbuffer + (GFX_W(ctx) * char_height) * GFX_B(ctx); 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 */ } else {
memmove((void *)dst, (void *)src, siz); how_much = -how_much;
/* And redraw the new rows */ /* Shift terminal cells one row up */
for (uint16_t x = 0; x < term_width; ++x) { memmove((void *)((uintptr_t)term_buffer + sizeof(t_cell) * term_width), term_buffer, sizeof(t_cell) * term_width * (term_height - how_much));
cell_redraw(x, term_height - 1); /* 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; uint32_t unicode_state = 0;
int is_wide(uint32_t codepoint) { int is_wide(uint32_t codepoint) {
if (codepoint < 256) return 0;
return mk_wcwidth_cjk(codepoint) == 2; return mk_wcwidth_cjk(codepoint) == 2;
} }
@ -915,13 +978,19 @@ void term_write(char c) {
c = '?'; c = '?';
} }
if (c == '\n') { 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_x = 0;
++csr_y; ++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') { } else if (c == '\r') {
cell_redraw(csr_x,csr_y); cell_redraw(csr_x,csr_y);
csr_x = 0; csr_x = 0;
@ -929,10 +998,11 @@ void term_write(char c) {
if (csr_x > 0) { if (csr_x > 0) {
--csr_x; --csr_x;
} }
cell_set(csr_x, csr_y, ' ',current_fg, current_bg, state.flags);
cell_redraw(csr_x, csr_y); cell_redraw(csr_x, csr_y);
draw_cursor();
} else if (c == '\t') { } else if (c == '\t') {
csr_x = (csr_x + 8) & ~(8 - 1); csr_x += (8 - csr_x % 8);
draw_cursor();
} else { } else {
int wide = is_wide(codepoint); int wide = is_wide(codepoint);
uint8_t flags = state.flags; uint8_t flags = state.flags;
@ -958,7 +1028,7 @@ void term_write(char c) {
++csr_y; ++csr_y;
} }
if (csr_y == term_height) { if (csr_y == term_height) {
term_term_scroll(); term_scroll(1);
csr_y = term_height - 1; csr_y = term_height - 1;
} }
} else if (unicode_state == UTF8_REJECT) { } else if (unicode_state == UTF8_REJECT) {
@ -972,6 +1042,7 @@ term_set_csr(int x, int y) {
cell_redraw(csr_x,csr_y); cell_redraw(csr_x,csr_y);
csr_x = x; csr_x = x;
csr_y = y; csr_y = y;
draw_cursor();
} }
int int
@ -1026,7 +1097,7 @@ void term_redraw_cell(int x, int y) {
cell_redraw(x,y); cell_redraw(x,y);
} }
void term_term_clear(int i) { void term_clear(int i) {
if (i == 2) { if (i == 2) {
/* Oh dear */ /* Oh dear */
csr_x = 0; 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) { char * loadMemFont(char * name, char * ident, size_t * size) {
if (!_windowed) { if (!_windowed) {
FILE * f = fopen(name, "r"); FILE * f = fopen(name, "r");
@ -1277,7 +1326,7 @@ void reinit() {
} }
/* XXX: Transfer values, cursor location, etc.? */ /* XXX: Transfer values, cursor location, etc.? */
term_buffer = malloc(sizeof(t_cell) * term_width * term_height); 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_x = ctx->width / 2;
mouse_y = ctx->height / 2; mouse_y = ctx->height / 2;
@ -1290,6 +1339,23 @@ void reinit() {
ansi_print("\033[H\033[2J"); 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) { int main(int argc, char ** argv) {
_windowed = 1; _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"); putenv("TERM=toaru");
if (_windowed) { if (_windowed) {
@ -1546,6 +1616,9 @@ fail_mouse:
int r = read(ofd, buf, min(_stat.st_size, 1024)); int r = read(ofd, buf, min(_stat.st_size, 1024));
for (uint32_t i = 0; i < r; ++i) { for (uint32_t i = 0; i < r; ++i) {
ansi_put(buf[i]); ansi_put(buf[i]);
#if DEBUG_TERMINAL_WITH_SERIAL
serial_put(buf[i]);
#endif
} }
} }
} }