Port back CSI handling and cursor blink
This commit is contained in:
+8
-12
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+20
-21
@@ -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);
|
||||
}
|
||||
|
||||
+225
-10
@@ -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);
|
||||
|
||||
+3
-2
@@ -1,11 +1,13 @@
|
||||
#include "sys/font/psf.h"
|
||||
#include "sys/display.h"
|
||||
#include "sys/console.h"
|
||||
#include "sys/debug.h"
|
||||
#include <stddef.h>
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user