diff --git a/arch/amd64/hw/timer.c b/arch/amd64/hw/timer.c index 751272f..618d2ed 100644 --- a/arch/amd64/hw/timer.c +++ b/arch/amd64/hw/timer.c @@ -7,6 +7,8 @@ #include "arch/amd64/hw/idt.h" #include "arch/amd64/hw/io.h" #include "arch/amd64/cpu.h" +#include "sys/display.h" +#include "sys/console.h" #include "user/time.h" #include "sys/assert.h" #include "sys/thread.h" @@ -58,18 +60,12 @@ void timer_remove_sleep(struct thread *thr) { static uint32_t timer_tick(void *arg) { switch ((uint64_t) arg) { case TIMER_PIT: -// #if defined(VESA_ENABLE) -// ++int_timer_ticks; -// if (int_timer_ticks >= 300) { -// con_blink(); -// int_timer_ticks = 0; -// } -// if (!vesa_available) { -//#else -// { -//#endif -// amd64_con_sync_cursor(); -// } + ++int_timer_ticks; + if (int_timer_ticks >= 300) { + g_display_blink_state ^= 1; + int_timer_ticks = 0; + } + console_update_cursor(); // Each tick is approx. 1ms, so add 1ms to system time system_time += 1000000; break; diff --git a/include/sys/console.h b/include/sys/console.h index e237858..86cc3ef 100644 --- a/include/sys/console.h +++ b/include/sys/console.h @@ -24,7 +24,9 @@ struct console { // Enslave a TTY to a physical console void console_attach(struct console *con, struct chrdev *tty); +uint16_t console_buffer_at(uint16_t y, uint16_t x); void console_resize(struct console *con, uint16_t new_width, uint16_t new_height); +void console_update_cursor(void); void console_putc(struct console *con, struct chrdev *tty, int c); void console_type(struct console *con, int c); diff --git a/include/sys/display.h b/include/sys/display.h index 35f9c15..618ac2d 100644 --- a/include/sys/display.h +++ b/include/sys/display.h @@ -28,6 +28,8 @@ struct display { struct list_head list; }; +extern int g_display_blink_state; + void display_add(struct display *d); struct display *display_create(void); diff --git a/sys/char/tty.c b/sys/char/tty.c index 4f9058a..cf62b12 100644 --- a/sys/char/tty.c +++ b/sys/char/tty.c @@ -27,26 +27,26 @@ static int tty_ioctl(struct chrdev *tty, unsigned int cmd, void *arg); static const struct termios default_termios = TERMIOS_DEFAULT; -void tty_control_write(struct chrdev *tty, char c) { - struct tty_data *data = tty->dev_data; - _assert(data); - - switch (c) { - case 'd': - ring_signal(&tty->buffer, RING_SIGNAL_EOF); - break; - case '.': - //ring_signal(&tty->buffer, RING_SIGNAL_BRK); - thread_signal_pgid(data->fg_pgid, SIGUSR1); - break; - case 'c': - //ring_signal(&tty->buffer, RING_SIGNAL_BRK); - thread_signal_pgid(data->fg_pgid, SIGINT); - break; - default: - panic("Unhandled control to TTY: ^%c\n", c); - } -} +//void tty_control_write(struct chrdev *tty, char c) { +// struct tty_data *data = tty->dev_data; +// _assert(data); +// +// switch (c) { +// case 'd': +// ring_signal(&tty->buffer, RING_SIGNAL_EOF); +// break; +// case '.': +// //ring_signal(&tty->buffer, RING_SIGNAL_BRK); +// thread_signal_pgid(data->fg_pgid, SIGUSR1); +// break; +// case 'c': +// //ring_signal(&tty->buffer, RING_SIGNAL_BRK); +// thread_signal_pgid(data->fg_pgid, SIGINT); +// break; +// default: +// panic("Unhandled control to TTY: ^%c\n", c); +// } +//} void tty_data_write(struct chrdev *tty, char c) { _assert(tty && tty->type == CHRDEV_TTY); @@ -54,7 +54,6 @@ void tty_data_write(struct chrdev *tty, char c) { switch (c) { case '\n': - // TODO: this should also check ICANON if (tty->tc.c_lflag & ECHONL) { tty_putc(tty, c); } diff --git a/sys/console.c b/sys/console.c index 7a4602e..aef9ee4 100644 --- a/sys/console.c +++ b/sys/console.c @@ -10,13 +10,29 @@ #define ATTR_DEFAULT 0x1700 +#define ESC_ESC 1 +#define ESC_CSI 2 + +#define ATTR_BOLD 1 + static void console_flush(struct console *con, struct console_buffer *buf); static LIST_HEAD(g_consoles); struct console_buffer { + // Current attribute uint16_t attr; + // Extended attributes + uint16_t xattrs; + // Escape code processing + uint32_t esc_argv[8]; + char esc_letter; + size_t esc_argc; + int esc_mode; + // Cursor uint16_t y, x; + // Blink + uint16_t last_blink_y, last_blink_x; uint16_t data[0]; }; @@ -26,7 +42,11 @@ static struct console_buffer *console_buffer_create(uint16_t rows, uint16_t cols memsetw(buf->data, ATTR_DEFAULT, rows * cols); buf->y = 0; buf->x = 0; + buf->last_blink_x = 0; + buf->last_blink_y = 0; buf->attr = ATTR_DEFAULT; + buf->xattrs = 0; + buf->esc_mode = 0; return buf; } @@ -75,28 +95,223 @@ static void console_scroll_check(struct console *con, struct console_buffer *buf } } +static uint8_t color_map[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; +// I guess I could've implemented VT100 escape handling better, but +// this is all I invented so far +static void process_csi(struct console *con, struct console_buffer *buf) { + switch (buf->esc_letter) { + case 'm': + for (size_t i = 0; i < buf->esc_argc; ++i) { + uint32_t v = buf->esc_argv[i]; + switch (v / 10) { + case 0: + switch (v % 10) { + case 0: + // Reset + buf->attr = ATTR_DEFAULT; + buf->xattrs = 0; + break; + case 1: + // Bright + buf->xattrs |= ATTR_BOLD; + break; + case 2: + // Dim + buf->xattrs &= ~ATTR_BOLD; + break; + case 7: + // Reverse + buf->attr >>= 4; + buf->attr |= (buf->attr & 0xF0) << 8; + buf->attr &= 0xFF00; + break; + } + break; + case 3: + // Foreground + buf->attr &= ~0x0F00; + buf->attr |= (uint16_t) color_map[v % 10] << 8; + break; + case 4: + // Background + buf->attr &= ~0xF000; + buf->attr |= (uint16_t) color_map[v % 10] << 12; + break; + } + } + break; + case 'J': + switch (buf->esc_argv[0]) { + case 0: + // Erase lines down + //memsetw(con_buffer, attr, con_width * y); + break; + case 1: + // Erase lines up + //memsetw(&con_buffer[y * con_width], attr, con_width * (con_height - y)); + break; + case 2: + // Erase all + memsetw(buf->data, buf->attr, con->width_chars * con->height_chars); + console_flush(con, buf); + break; + } + break; + case 'f': + // Set cursor position + buf->y = (buf->esc_argv[0] - 1) % con->height_chars; + buf->x = (buf->esc_argv[1] - 1) % (con->width_chars - 1); + break; + default: + kdebug("\033[31mUnknown CSI sequence: %c\033[0m\n", buf->esc_letter); + break; + //case 'A': + // // Cursor up + // if (!esc_argv[0]) { + // esc_argv[0] = 1; + // } + + // if (esc_argv[0] >= y) { + // y = 0; + // } else { + // y -= esc_argv[0]; + // } + // break; + //case 'B': + // // Cursor down + // if (!esc_argv[0]) { + // esc_argv[0] = 1; + // } + + // if (esc_argv[0] + y >= (uint32_t) (con_height - 1)) { + // y = 22; + // } else { + // y += esc_argv[0]; + // } + // break; + //case 'C': + // // Forward + // if (!esc_argv[0]) { + // esc_argv[0] = 1; + // } + + // if (esc_argv[0] + x >= con_width) { + // x = 79; + // } else { + // x += esc_argv[0]; + // } + + // break; + //case 'D': + // // Backward + // if (!esc_argv[0]) { + // esc_argv[0] = 1; + // } + + // if (esc_argv[0] >= x) { + // x = 0; + // } else { + // x -= esc_argv[0]; + // } + + // break; + //case 'K': + // // Erase end of line + // memsetw(&con_buffer[y * con_width + x], attr, con_width - x); + // break; + } +} + static void _console_putc(struct console *con, struct console_buffer *buf, int c) { int act = buf == con->buf_active; - if (c == '\n') { - ++buf->y; - buf->x = 0; - console_scroll_check(con, buf); - } else if (c >= ' ') { - buf->data[buf->y * con->width_chars + buf->x] = c | buf->attr; - if (act) { - display_setc(con->display, buf->y, buf->x, c | buf->attr); + switch (buf->esc_mode) { + case ESC_CSI: + if (c >= '0' && c <= '9') { + buf->esc_argv[buf->esc_argc] *= 10; + buf->esc_argv[buf->esc_argc] += c - '0'; + } else if (c == ';') { + buf->esc_argv[++buf->esc_argc] = 0; + } else { + ++buf->esc_argc; + buf->esc_letter = c; + process_csi(con, buf); + buf->esc_mode = 0; + } + break; + case ESC_ESC: + if (c == '[') { + buf->esc_mode = ESC_CSI; + break; + } else { + buf->esc_mode = 0; + } + __attribute__((fallthrough)); + case 0: + if (c == '\033') { + buf->esc_mode = ESC_ESC; + buf->esc_argv[0] = 0; + buf->esc_argc = 0; + + return; } - ++buf->x; - if (buf->x >= con->width_chars) { + if (c == '\n') { ++buf->y; buf->x = 0; console_scroll_check(con, buf); + } else if (c >= ' ') { + buf->data[buf->y * con->width_chars + buf->x] = c | buf->attr; + if (act) { + uint16_t a = buf->attr; + if (buf->xattrs & ATTR_BOLD) { + a |= 0x8000; + } + display_setc(con->display, buf->y, buf->x, c | a); + } + + ++buf->x; + if (buf->x >= con->width_chars) { + ++buf->y; + buf->x = 0; + console_scroll_check(con, buf); + } } } } +void console_update_cursor(void) { + struct console *con; + static int prev_blink = 0; + + if (g_display_blink_state != prev_blink) { + list_for_each_entry(con, &g_consoles, list) { + if (con->display && con->buf_active) { + struct console_buffer *buf = con->buf_active; + uint16_t c = buf->data[buf->y * con->width_chars + buf->x]; + if (g_display_blink_state) { + // Swap attributes + c = ((c >> 4) & 0xF00) | + ((c & 0xF00) << 4) | + (c & 0xFF); + } + display_setc(con->display, buf->y, buf->x, c); + if (buf->last_blink_x != buf->x || buf->last_blink_y != buf->y) { + // Also redraw character at last blink position + display_setc(con->display, + buf->last_blink_y, + buf->last_blink_x, + buf->data[buf->last_blink_y * con->width_chars + + buf->last_blink_x]); + buf->last_blink_x = buf->x; + buf->last_blink_y = buf->y; + } + } + } + prev_blink = g_display_blink_state; + } +} + void console_putc(struct console *con, struct chrdev *tty, int c) { struct tty_data *data = tty->dev_data; _assert(data); diff --git a/sys/display.c b/sys/display.c index 2bdb4d6..51af072 100644 --- a/sys/display.c +++ b/sys/display.c @@ -1,11 +1,13 @@ #include "sys/font/psf.h" #include "sys/display.h" +#include "sys/console.h" #include "sys/debug.h" #include +int g_display_blink_state = 0; + static LIST_HEAD(displays); -static uint8_t color_map[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; static uint32_t rgb_map[16] = { 0x000000, 0x0000AA, @@ -44,7 +46,6 @@ void display_add(struct display *disp) { void display_setc(struct display *disp, uint16_t y, uint16_t x, uint16_t ch) { if (disp->flags & DISP_GRAPHIC) { - // TODO: attr conversion psf_draw(disp, y, x, ch & 0xFF, rgb_map[(ch >> 8) & 0xF], rgb_map[(ch >> 12) & 0xF]); } }