diff --git a/kernel/include/kclib/stdio.h b/kernel/include/kclib/stdio.h index 8811d8f9..102d0029 100644 --- a/kernel/include/kclib/stdio.h +++ b/kernel/include/kclib/stdio.h @@ -1,8 +1,6 @@ #pragma once -#include #include -#include #include #include diff --git a/kernel/include/kernel/con/ansi.h b/kernel/include/kernel/con/ansi.h new file mode 100644 index 00000000..68d06a20 --- /dev/null +++ b/kernel/include/kernel/con/ansi.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef enum { ANSI_INCOMPLETE, ANSI_INVALID, ANSI_VALID } ansi_status_t; + +ansi_status_t add_to_ansi_parser_buf (unsigned char c); + +const char* get_ansi_buffer (void); +void clear_ansi_buffer (void); +void init_ansi_buffer (console_t** console); diff --git a/kernel/include/kernel/con/con.h b/kernel/include/kernel/con/con.h new file mode 100644 index 00000000..d9e98e60 --- /dev/null +++ b/kernel/include/kernel/con/con.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +#define TAB_WIDTH 8 + +bool con_update_cache_set (void); +bool con_update_cache_clear (void); +void con_update_upd (bool cached); +int con_update (void); + +void con_scrollup (size_t howmuch); +void con_scrolldown (size_t howmuch); + +int add_char (unsigned char c); +void init_con (size_t screen_width, size_t screen_height, size_t x_padding, size_t y_padding, + size_t char_spacing, size_t line_padding, size_t font_multiplier); diff --git a/kernel/include/kernel/con/con_ds.h b/kernel/include/kernel/con/con_ds.h new file mode 100644 index 00000000..68746305 --- /dev/null +++ b/kernel/include/kernel/con/con_ds.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +#define CON_IDX_GEN(x, y) (((uint64_t)(y) << 32) | (x)) +#define CON_IDX_X(idx) (idx & 0xFFFFFFFF) +#define CON_IDX_Y(idx) ((idx & (0xFFFFFFFFull << 32)) >> 32) +#define CON_COL_RGB(color) ((color.red << 16) | (color.green << 8) | color.blue) + +typedef uint64_t idx_t; + +typedef struct __attribute__ ((packed)) { + uint8_t red; + uint8_t green; + uint8_t blue; +} console_color_t; + +typedef struct __attribute__ ((packed)) { + unsigned char character; + console_color_t color; +} console_char_t; + +typedef struct __attribute__ ((packed)) { + console_char_t* chars; + size_t width; + uint8_t dirty; +} console_line_t; + +typedef struct { + size_t glyph_height, glyph_width; + size_t line_spacing, char_spacing; + size_t xpad, ypad; + size_t width, height; + size_t font_size; +} console_parameters_t; + +typedef struct __attribute__ ((packed)) { + console_line_t** display; + deque* scrollback; + deque* scrollfront; + idx_t idx; + console_color_t current_color; + console_parameters_t params; +} console_t; + +int console_create (console_t** console, console_parameters_t* params); +int console_delete (console_t** console); + +int console_putchar (console_t** console, unsigned char c); +int console_setcolor (console_t** console, uint8_t red, uint8_t green, uint8_t blue); +int console_goto (console_t** console, uint32_t x, uint32_t y); + +int console_scrollup (console_t** console, size_t howmuch); +int console_scrolldown (console_t** console, size_t howmuch); +int console_clearscrollback (console_t** console); + +idx_t console_getidx (console_t** console); +console_color_t console_getcolor (console_t** console); +console_parameters_t console_getparams (console_t** console); + +int write_to_gfx (console_t** console); diff --git a/kernel/include/kernel/console.h b/kernel/include/kernel/console.h deleted file mode 100644 index 8e38c8ae..00000000 --- a/kernel/include/kernel/console.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include -#include - -#define TAB_WIDTH 8 - -void set_update_on_putch (bool); -bool get_update_on_putch (void); -void set_idx (size_t); -size_t get_idx (void); - -void init_console (size_t, size_t, size_t, size_t, size_t, size_t, size_t); -void set_color (uint32_t); -void update (void); - -void putchar (unsigned char); -void putstr (const char*, size_t); diff --git a/kernel/src/kclib/stdio.c b/kernel/src/kclib/stdio.c index 5f0468f6..b5f7012b 100644 --- a/kernel/src/kclib/stdio.c +++ b/kernel/src/kclib/stdio.c @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include @@ -164,10 +164,9 @@ static void kvsprintf (char* buf, const char* fmt, va_list* list) { buf[kvsprintf_idx] = '\0'; } -static void kvprintf (const char* fmt, va_list* list) { - kv_core_printf (fmt, list, putchar); - update (); -} +static void kvprintf_putc (unsigned char c) { add_char (c); } + +static void kvprintf (const char* fmt, va_list* list) { kv_core_printf (fmt, list, kvprintf_putc); } void ksprintf (char* buf, const char* fmt, ...) { va_list ap; diff --git a/kernel/src/kernel/con/ansi.c b/kernel/src/kernel/con/ansi.c new file mode 100644 index 00000000..38ad1e4d --- /dev/null +++ b/kernel/src/kernel/con/ansi.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include + +#define ANSI_BUFFER_SIZE 100 +#define ANSI_VALID_CMDS "ABCDEFGHJKSTm" +#define ANSI_MAX_PARAMS 8 + +static char* ansi_buffer = nullptr; +static size_t ansi_idx = 0; +static console_t** ansi_console = nullptr; + +static ansi_status_t try_parse_ansi (void) { + size_t idx = 0; + if (ansi_buffer[idx] != '[') return ANSI_INVALID; + idx++; + + int64_t params[ANSI_MAX_PARAMS]; + int param_count = 0; + params[0] = -1; + + while (1) { + if (ansi_buffer[idx] >= '0' && ansi_buffer[idx] <= '9') { + if (params[param_count] == -1) params[param_count] = 0; + params[param_count] = params[param_count] * 10 + (ansi_buffer[idx] - '0'); + idx++; + } else if (ansi_buffer[idx] == ';') { + if (++param_count >= ANSI_MAX_PARAMS) return ANSI_INVALID; + params[param_count] = -1; + idx++; + } else { + break; + } + } + + char cmd = ansi_buffer[idx]; + if (cmd == '\0') return ANSI_INCOMPLETE; + if (!kstrchr (ANSI_VALID_CMDS, cmd)) return ANSI_INVALID; + + int64_t a = (params[0] == -1) ? 1 : params[0]; + idx_t cur = console_getidx (ansi_console); + int64_t new_x, new_y; + + console_parameters_t p = console_getparams (ansi_console); + + switch (cmd) { + case 'A': /* Cursor Up */ + new_y = (int64_t)CON_IDX_Y (cur) - a; + if (new_y < 0) new_y = 0; + console_goto (ansi_console, CON_IDX_X (cur), (uint32_t)new_y); + break; + + case 'B': /* Cursor Down */ + new_y = (int64_t)CON_IDX_Y (cur) + a; + if (new_y >= (int64_t)p.height) new_y = (int64_t)p.height - 1; + console_goto (ansi_console, CON_IDX_X (cur), (uint32_t)new_y); + break; + + case 'C': /* Cursor Forward */ + new_x = (int64_t)CON_IDX_X (cur) + a; + if (new_x >= (int64_t)p.width) new_x = (int64_t)p.width - 1; + console_goto (ansi_console, (uint32_t)new_x, CON_IDX_Y (cur)); + break; + + case 'D': /* Cursor Back */ + new_x = (int64_t)CON_IDX_X (cur) - a; + if (new_x < 0) new_x = 0; + console_goto (ansi_console, (uint32_t)new_x, CON_IDX_Y (cur)); + break; + + case 'E': /* Cursor Next Line */ + new_y = (int64_t)CON_IDX_Y (cur) + a; + if (new_y >= (int64_t)p.height) new_y = (int64_t)p.height - 1; + console_goto (ansi_console, 0, (uint32_t)new_y); + break; + + case 'F': /* Cursor Previous Line */ + new_y = (int64_t)CON_IDX_Y (cur) - a; + if (new_y < 0) new_y = 0; + console_goto (ansi_console, 0, (uint32_t)new_y); + break; + + case 'G': /* Cursor Horizontal Absolute (1-based column) */ + new_x = a - 1; + if (new_x < 0) new_x = 0; + if (new_x >= (int64_t)p.width) new_x = (int64_t)p.width - 1; + console_goto (ansi_console, (uint32_t)new_x, CON_IDX_Y (cur)); + break; + + case 'H': { /* Cursor Position: ESC[row;col H (1-based, default 1;1) */ + int64_t row = (params[0] == -1 ? 1 : params[0]) - 1; + int64_t col = (param_count >= 1 && params[1] != -1 ? params[1] : 1) - 1; + if (row < 0) row = 0; + if (col < 0) col = 0; + if (row >= (int64_t)p.height) row = (int64_t)p.height - 1; + if (col >= (int64_t)p.width) col = (int64_t)p.width - 1; + console_goto (ansi_console, (uint32_t)col, (uint32_t)row); + break; + } + + case 'J': { /* Erase in Display (default 0) */ + int64_t mode = (params[0] == -1) ? 0 : params[0]; + int64_t sx = CON_IDX_X (cur), sy = CON_IDX_Y (cur); + int64_t ex = (int64_t)p.width - 1, ey = (int64_t)p.height - 1; + bool con_flag = con_update_cache_clear (); + + if (mode == 3) { + console_clearscrollback (ansi_console); + con_update_upd (con_flag); + break; + } + + if (mode == 1) { + sx = 0; + sy = 0; + ex = CON_IDX_X (cur); + ey = CON_IDX_Y (cur); + } else if (mode == 2) { + sx = 0; + sy = 0; + } + /* mode == 0: from cursor to end of screen (sx/sy already set) */ + for (int64_t y = sy; y <= ey; y++) { + int64_t lx = (y == sy) ? sx : 0; + int64_t rx = (y == ey) ? ex : (int64_t)p.width - 1; + for (int64_t x = lx; x <= rx; x++) { + console_goto (ansi_console, (uint32_t)x, (uint32_t)y); + console_putchar (ansi_console, 0); + } + } + console_goto (ansi_console, CON_IDX_X (cur), CON_IDX_Y (cur)); + con_update_upd (con_flag); + break; + } + + case 'K': { /* Erase in Line (default 0) */ + int64_t mode = (params[0] == -1) ? 0 : params[0]; + int64_t lx, rx; + bool con_flag = con_update_cache_clear (); + + if (mode == 0) { + lx = CON_IDX_X (cur); + rx = (int64_t)p.width - 1; + } else if (mode == 1) { + lx = 0; + rx = CON_IDX_X (cur); + } else { + lx = 0; + rx = (int64_t)p.width - 1; + } + for (int64_t x = lx; x <= rx; x++) { + console_goto (ansi_console, (uint32_t)x, CON_IDX_Y (cur)); + console_putchar (ansi_console, 0); + } + console_goto (ansi_console, CON_IDX_X (cur), CON_IDX_Y (cur)); + con_update_upd (con_flag); + break; + } + + case 'S': /* Scroll Up */ + console_scrollup (ansi_console, (size_t)a); + break; + + case 'T': /* Scroll Down */ + console_scrolldown (ansi_console, (size_t)a); + break; + + case 'm': /* SGR */ + if (params[0] == -1 || params[0] == 0) { + console_setcolor (ansi_console, 0xFF, 0xFF, 0xFF); + } else if (params[0] == 38 && param_count >= 4 && params[1] == 2) { + uint8_t r = (uint8_t)(params[2] < 0 ? 0 : params[2] > 255 ? 255 : params[2]); + uint8_t g = (uint8_t)(params[3] < 0 ? 0 : params[3] > 255 ? 255 : params[3]); + uint8_t b = (uint8_t)(params[4] < 0 ? 0 : params[4] > 255 ? 255 : params[4]); + console_setcolor (ansi_console, r, g, b); + } else if (params[0] >= 30 && params[0] <= 37) { + static const uint8_t ansi_cols[8][3] = { + {0, 0, 0}, /* 30 black */ + {170, 0, 0}, /* 31 red */ + {0, 170, 0}, /* 32 green */ + {170, 170, 0}, /* 33 yellow */ + {0, 0, 170}, /* 34 blue */ + {170, 0, 170}, /* 35 magenta */ + {0, 170, 170}, /* 36 cyan */ + {170, 170, 170}, /* 37 white */ + }; + int ci = (int)(params[0] - 30); + console_setcolor (ansi_console, ansi_cols[ci][0], ansi_cols[ci][1], ansi_cols[ci][2]); + } + break; + } + + clear_ansi_buffer (); + return ANSI_VALID; +} + +ansi_status_t add_to_ansi_parser_buf (unsigned char c) { + ansi_buffer[ansi_idx++] = c; + return try_parse_ansi (); +} + +const char* get_ansi_buffer (void) { return (const char*)ansi_buffer; } + +void clear_ansi_buffer (void) { + kmemset (ansi_buffer, 0, ANSI_BUFFER_SIZE); + ansi_idx = 0; +} + +void init_ansi_buffer (console_t** console) { + ansi_console = console; + ansi_buffer = kmalloc (ANSI_BUFFER_SIZE); +} \ No newline at end of file diff --git a/kernel/src/kernel/con/con.c b/kernel/src/kernel/con/con.c new file mode 100644 index 00000000..22ff2e3b --- /dev/null +++ b/kernel/src/kernel/con/con.c @@ -0,0 +1,98 @@ +#include +#include +#include + +static console_t* console = nullptr; +static bool update_flag = true; + +static bool in_esc = false; + +bool con_update_cache_set (void) { + bool cached = update_flag; + update_flag = true; + return cached; +} + +bool con_update_cache_clear (void) { + bool cached = update_flag; + update_flag = false; + return cached; +} + +void con_update_upd (bool cached) { update_flag = cached; } + +int con_update (void) { return write_to_gfx (&console); } + +void con_scrollup (size_t howmuch) { + console_scrollup (&console, howmuch); + write_to_gfx (&console); +} + +void con_scrolldown (size_t howmuch) { + console_scrolldown (&console, howmuch); + write_to_gfx (&console); +} + +int add_char (unsigned char c) { + int error = 0; + console_parameters_t params = console_getparams (&console); + if (in_esc) { + ansi_status_t status = add_to_ansi_parser_buf (c); + if (status == ANSI_INVALID) { + const char* buf = get_ansi_buffer (); + for (size_t i = 0; buf[i]; i++) + console_putchar (&console, (unsigned char)buf[i]); + clear_ansi_buffer (); + in_esc = false; + } else if (status == ANSI_VALID) { + in_esc = false; + } + } else if (c == '\x7F') { + idx_t idx = console_getidx (&console); + if (CON_IDX_X (idx) == 0) + idx = CON_IDX_GEN (params.width - 1, CON_IDX_Y (idx) - 1); + else + idx = CON_IDX_GEN (CON_IDX_X (idx) - 1, CON_IDX_Y (idx)); + + console_goto (&console, CON_IDX_X (idx), CON_IDX_Y (idx)); + error = console_putchar (&console, 0); + console_goto (&console, CON_IDX_X (idx), CON_IDX_Y (idx)); + } else if (c == '\r') { + idx_t idx = console_getidx (&console); + console_goto (&console, 0, CON_IDX_Y (idx)); + } else if (c == '\t') { + idx_t idx = console_getidx (&console); + size_t x_next_tabstop = TAB_WIDTH * ((CON_IDX_X (idx) + TAB_WIDTH - 1) / TAB_WIDTH); + if (x_next_tabstop >= params.width) + if (CON_IDX_Y (idx) + 1 == params.height) + console_putchar (&console, '\n'); + else + console_goto (&console, 0, CON_IDX_Y (idx) + 1); + else + console_goto (&console, x_next_tabstop, CON_IDX_Y (idx)); + } else if (c == '\033') { + in_esc = true; + } else { + error = console_putchar (&console, c); + } + + if (update_flag) write_to_gfx (&console); + return error; +} + +void init_con (size_t screen_width, size_t screen_height, size_t x_padding, size_t y_padding, + size_t char_spacing, size_t line_padding, size_t font_multiplier) { + console_parameters_t params = { + .glyph_height = 8, + .glyph_width = 5, + .line_spacing = line_padding, + .char_spacing = char_spacing, + .xpad = x_padding, + .ypad = y_padding, + .width = (screen_width - 2 * x_padding) / ((5 + char_spacing) * font_multiplier), + .height = (screen_height - 2 * y_padding) / ((8 + line_padding) * font_multiplier), + .font_size = font_multiplier}; + + init_ansi_buffer (&console); + console_create (&console, ¶ms); +}; diff --git a/kernel/src/kernel/con/con_ds.c b/kernel/src/kernel/con/con_ds.c new file mode 100644 index 00000000..0453d795 --- /dev/null +++ b/kernel/src/kernel/con/con_ds.c @@ -0,0 +1,346 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CON_SCROLLBACK_LIMIT 10000 + +/*! + * Increment idx_t index. + * @param idx index to be incremented + * @param width width of the console + * @return incremented idx_t index + */ +static inline idx_t increment_idx (idx_t idx, size_t width) { + return CON_IDX_GEN (CON_IDX_X (idx) == width - 1 ? 0 : CON_IDX_X (idx) + 1, + CON_IDX_Y (idx) + (CON_IDX_X (idx) == width - 1)); +} + +/*! + * Allocate and initialise a new console object. + * @param console pointer to console_t* which will hold the reference to created console + * @param params initialisation parameters for the console + * @return 0 if successful, else -EINVAL or -ENOMEM + */ +int console_create (console_t** console, console_parameters_t* params) { + if (!console || !params) return -EINVAL; + + console_t* new_console = kmalloc (sizeof (console_t)); + if (!new_console) return -ENOMEM; + + kmemset (new_console, 0, sizeof (console_t)); + + new_console->scrollback = deque_create (0); + new_console->scrollfront = deque_create (0); + if (!new_console->scrollback || !new_console->scrollfront) return -ENOMEM; + + new_console->display = kmalloc (params->height * sizeof (console_line_t*)); + if (!new_console->display) return -ENOMEM; + + for (size_t i = 0; i < params->height; i++) { + new_console->display[i] = kmalloc (sizeof (console_line_t)); + if (!new_console->display[i]) return -ENOMEM; + + new_console->display[i]->chars = kmalloc (params->width * sizeof (console_char_t)); + if (!new_console->display[i]->chars) return -ENOMEM; + kmemset (new_console->display[i]->chars, 0, params->width * sizeof (console_char_t)); + + new_console->display[i]->dirty = 0; + new_console->display[i]->width = params->width; + } + + new_console->current_color.red = 0xFF; + new_console->current_color.green = 0xFF; + new_console->current_color.blue = 0xFF; + new_console->params = *params; + + *console = new_console; + + return 0; +} + +/*! + * Deallocate an existing console object. + * @param console pointer to console_t* which holds the reference to console + * @return 0 if successful, else -EINVAL + */ +int console_delete (console_t** console) { + if (!console || !(*console)) return -EINVAL; + + while (deque_size ((*console)->scrollback)) { + console_line_t* popped = nullptr; + int error = deque_pop_front ((*console)->scrollback, (deque_elem*)&popped); + if (error) break; + + kfree (popped->chars); + kfree (popped); + } + + while (deque_size ((*console)->scrollfront)) { + console_line_t* popped = nullptr; + int error = deque_pop_front ((*console)->scrollfront, (deque_elem*)&popped); + if (error) break; + + kfree (popped->chars); + kfree (popped); + } + + deque_destroy ((*console)->scrollback); + deque_destroy ((*console)->scrollfront); + + for (size_t i = 0; i < (*console)->params.height; i++) { + kfree ((*console)->display[i]->chars); + kfree ((*console)->display[i]); + } + + kfree ((*console)->display); + kfree (*console); + *console = nullptr; + + return 0; +} + +/*! + * Write a character at the current cursor position. Increments idx to the sequentially next value. + * + * In the case that character \n is supplied, no update is made to actual console charaters, instead + * idx is directly set to (0, height), potentially falling through to the following case. This is + * the canonical way of adding new lines to the console. + * + * In the case that after incrementing, the condition idx.y >= height holds true, new lines are + * added at the bottom and the console is scrolled to accomodate until the condition is no longer + * true. In the case that after addition of a new line, the scrollback exceeds CON_SCROLLBACK_LIMIT, + * the oldest line is freed until the condition is no longer true. + * + * This function does not call write_to_gfx. + * + * @param console pointer to console_t* which holds the reference to console + * @param c character to put at index + * @return 0 if successful, else -EINVAL, -ENOMEM or other errors + */ +int console_putchar (console_t** console, unsigned char c) { + if (!console || !(*console)) return -EINVAL; + + // clear the scrollfront so we are always printing on the newest line + console_scrolldown (console, deque_size ((*console)->scrollfront)); + + if (c == '\n') { + (*console)->idx = CON_IDX_GEN (0, CON_IDX_Y ((*console)->idx) + 1); + } else { + console_char_t* target = + &(*console)->display[CON_IDX_Y ((*console)->idx)]->chars[CON_IDX_X ((*console)->idx)]; + target->character = c; + target->color = (*console)->current_color; + (*console)->display[CON_IDX_Y ((*console)->idx)]->dirty = 1; + (*console)->idx = increment_idx ((*console)->idx, (*console)->params.width); + } + + while (CON_IDX_Y ((*console)->idx) >= (*console)->params.height) { + console_line_t* new_line = kmalloc (sizeof (console_line_t)); + if (!new_line) return -ENOMEM; + + new_line->chars = kmalloc ((*console)->params.width * sizeof (console_char_t)); + if (!new_line->chars) return -ENOMEM; + kmemset (new_line->chars, 0, (*console)->params.width * sizeof (console_char_t)); + + new_line->dirty = 0; + new_line->width = (*console)->params.width; + + int error = deque_push_front ((*console)->scrollfront, (deque_elem)new_line); + if (error) return error; + + console_scrolldown (console, 1); + (*console)->idx = + CON_IDX_GEN (CON_IDX_X ((*console)->idx), CON_IDX_Y ((*console)->idx) - 1); + } + + while (deque_size ((*console)->scrollback) > CON_SCROLLBACK_LIMIT) { + console_line_t* buf = nullptr; + int error = deque_pop_back ((*console)->scrollback, (deque_elem*)&buf); + if (error == -INTERNAL_EEMPQ) + break; + else if (error != 0) + return error; + + kfree (buf->chars); + kfree (buf); + } + + return 0; +} + +/*! + * Set the color that will be used by putchar for following characters. + * @param console pointer to console_t* which holds the reference to console + * @param red 8-bit value for red channel + * @param green 8-bit value for green channel + * @param blue 8-but value for blue channel + * @return 0 if successful, else -EINVAL + */ +int console_setcolor (console_t** console, uint8_t red, uint8_t green, uint8_t blue) { + if (!console || !(*console)) return -EINVAL; + (*console)->current_color.red = red; + (*console)->current_color.green = green; + (*console)->current_color.blue = blue; + return 0; +} + +/*! + * Move the cursor to another position. + * @param console pointer to console_t* which holds the reference to console + * @param x x-index to seek (left-right) + * @param y y-index to seek (top-down) + * @return 0 if successful, else -EINVAL + */ +int console_goto (console_t** console, uint32_t x, uint32_t y) { + if (!console || !(*console)) return -EINVAL; + (*console)->idx = CON_IDX_GEN (x, y); + return 0; +} + +/*! + * Scroll the whole console to reveal a line from the scrollback deque. + * + * If there are fewer lines in the scrollback than requested by the howmuch parameter, no new lines + * will be created and scrolling will terminate once the scrollback deque is empty. + * + * @param console pointer to console_t* which holds the reference to console + * @param howmuch how many lines to scroll + * @return 0 if successful, else -EINVAL or other errors + */ +int console_scrollup (console_t** console, size_t howmuch) { + if (!console || !(*console)) return -EINVAL; + + for (size_t i = 0; i < howmuch; i++) { + console_line_t* popped = nullptr; + int error = deque_pop_front ((*console)->scrollback, (deque_elem*)&popped); + if (error == -INTERNAL_EEMPQ) + break; + else if (error != 0) + return error; + + error = deque_push_front ((*console)->scrollfront, + (deque_elem)(*console)->display[(*console)->params.height - 1]); + if (error) return error; + + for (size_t j = (*console)->params.height - 1; j > 0; j--) { + (*console)->display[j] = (*console)->display[j - 1]; + (*console)->display[j]->dirty = 1; + } + + (*console)->display[0] = popped; + (*console)->display[0]->dirty = 1; + } + + return 0; +} + +/*! + * Scroll the whole console to reveal a line from the scrollfront deque. + * + * If there are fewer lines in the scrollfront than requested by the howmuch parameter, no new lines + * will be created and scrolling will terminate once the scrollfront deque is empty. + * + * @param console pointer to console_t* which holds the reference to console + * @param howmuch how many lines to scroll + * @return 0 if successful, else -EINVAL or other errors + */ +int console_scrolldown (console_t** console, size_t howmuch) { + if (!console || !(*console)) return -EINVAL; + + for (size_t i = 0; i < howmuch; i++) { + console_line_t* popped = nullptr; + int error = deque_pop_front ((*console)->scrollfront, (deque_elem*)&popped); + if (error == -INTERNAL_EEMPQ) + break; + else if (error != 0) + return error; + + error = deque_push_front ((*console)->scrollback, (deque_elem)(*console)->display[0]); + if (error) return error; + + for (size_t j = 0; j < (*console)->params.height - 1; j++) { + (*console)->display[j] = (*console)->display[j + 1]; + (*console)->display[j]->dirty = 1; + } + + (*console)->display[(*console)->params.height - 1] = popped; + (*console)->display[(*console)->params.height - 1]->dirty = 1; + } + + return 0; +} + +/*! + * Free the scrollback deque. + * @param console pointer to console_t* which holds the reference to console + * @return 0 if successful, else -EINVAL or other errors + */ +int console_clearscrollback (console_t** console) { + if (!console || !(*console)) return -EINVAL; + + console_line_t* popped = nullptr; + do { + int error = deque_pop_front ((*console)->scrollback, (deque_elem*)&popped); + if (error == -INTERNAL_EEMPQ) + break; + else if (error != 0) + return error; + kfree (popped->chars); + kfree (popped); + } while (1); + + return 0; +} + +/*! + * Get the position of the cursor. Behavior undefined if console parameter is invalid + * @param console pointer to console_t* which holds the reference to console + * @return index + */ +idx_t console_getidx (console_t** console) { return (*console)->idx; } + +/*! + * Get the color that will be used when printing the following characters. Behavior undefined if + * console parameter is invalid + * @param console pointer to console_t* which holds the reference to console + * @return color + */ +console_color_t console_getcolor (console_t** console) { return (*console)->current_color; } + +/*! + * Get the parameters of the console. Behavior undefined if console parameter is invalid + * @param console pointer to console_t* which holds the reference to console + * @return parameters + */ +console_parameters_t console_getparams (console_t** console) { return (*console)->params; } + +/*! + * Update the screenbuffer to match the contents of the console structure's internal display. + * @param console pointer to console_t* which holds the reference to console + * @return 0 if successful, else -EINVAL + */ +int write_to_gfx (console_t** console) { + if (!console || !(*console)) return -EINVAL; + + console_parameters_t* params = &(*console)->params; + for (size_t i = 0; i < params->height; i++) { + if (!(*console)->display[i]->dirty) continue; + console_char_t* target = (*console)->display[i]->chars; + + for (size_t j = 0; j < params->width; j++) { + renderGlyph (glyph (target[j].character), 8, 5, + params->xpad + (params->font_size * j * (5 + params->char_spacing)), + params->ypad + (params->font_size * i * (8 + params->line_spacing)), + params->font_size, CON_COL_RGB (target[j].color)); + } + + (*console)->display[i]->dirty = 0; + } + + return 0; +} diff --git a/kernel/src/kernel/console.c b/kernel/src/kernel/console.c deleted file mode 100644 index f8ebebe3..00000000 --- a/kernel/src/kernel/console.c +++ /dev/null @@ -1,152 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#define CONSOLE_HEIGHT 80 -#define CONSOLE_WIDTH 140 - -bool PUTCH_UPDATE = false; - -unsigned char bufferstr[CONSOLE_HEIGHT * CONSOLE_WIDTH] = { - 0}; // TODO: replace this with malloc when implemented -uint32_t colorbufferstr[CONSOLE_HEIGHT * CONSOLE_WIDTH] = { - 0xffffff}; // TODO: replace this with malloc when implemented -unsigned char* buffer = bufferstr; -uint32_t* colorbuffer = colorbufferstr; -size_t x, y, xs, ys, xsp, ysp, xc, yc, fs; -size_t idx = 0; - -uint32_t color = 0xeeeeee; - -/*! -Set PUTCH_UPDATE - -@param set value to set to -*/ -void set_update_on_putch (bool set) { PUTCH_UPDATE = set; } - -/*! -Get PUTCH_UPDATE - -@return PUTCH_UPDATE -*/ -bool get_update_on_putch () { return PUTCH_UPDATE; } - -/*! -Set idx - -@param set value to set to -*/ -void set_idx (size_t set) { idx = set; } - -/*! -Get idx - -@return idx -*/ -size_t get_idx () { return idx; } - -/*! -Initialise the console with the default (classic) font - -@param x_screen screen width -@param y_screen screen height -@param x_pad padding around sides -@param y_pad padding around top and bottom -@param x_spc spacing between characters -@param y_spc spacing between lines -@param font_size font size -*/ -void init_console (size_t x_screen, size_t y_screen, size_t x_pad, size_t y_pad, size_t x_spc, - size_t y_spc, size_t font_size) { - x = x_screen - 2 * x_pad; - y = y_screen - 2 * y_pad; - - xc = (x / ((5 + x_spc) * font_size) > CONSOLE_WIDTH) ? CONSOLE_WIDTH - : x / ((5 + x_spc) * font_size); - yc = (y / ((8 + y_spc) * font_size) > CONSOLE_HEIGHT) ? CONSOLE_HEIGHT - : y / ((8 + y_spc) * font_size); - - xsp = x_spc; - ysp = y_spc; - - xs = x_pad; - ys = y_pad; - - fs = font_size; - - kmemset (buffer, 32, xc * yc); -} - -/*! -Set the screen color. - -@param c RGB color -*/ -void set_color (uint32_t c) { color = c; } - -/*! -Update the display. -*/ -void update () { - for (size_t pos = 0; pos < xc * yc; pos++) { - size_t i = pos / xc; - size_t j = pos % xc; - renderGlyph (glyph (buffer[pos]), 8, 5, xs + (fs * j * (5 + xsp)), - ys + (fs * i * (8 + ysp)), fs, colorbuffer[pos]); - } -} - -/*! -Register a character to the screen buffer. - -@param rc character to register -@param index position to print it on -*/ -static void registerChar (unsigned char rc, int index) { - buffer[index] = rc; - colorbuffer[index] = color; -} - -/*! -Print a single character to the screen. - -@param rc character to print -*/ -void putchar (unsigned char rc) { - switch (rc) { - case '\n': - idx = ((idx / xc) + 1) * xc; - if (idx >= xc * yc) idx -= xc * yc; - return; - case '\r': - idx -= idx % xc; - return; - case '\b': - idx--; - return; - case '\x7F': - idx--; - registerChar (0, idx); - return; - } - registerChar (rc, idx); - idx++; - if (PUTCH_UPDATE) update (); -} - -/*! -Print a string to the screen. - -@param str string to print -@param len length of string -*/ -void putstr (const char* str, size_t len) { - for (size_t i = 0; i < len; i++) - putchar (str[i]); - update (); -} diff --git a/kernel/src/kernel/entry.c b/kernel/src/kernel/entry.c index 7fcf9fef..44eff6c2 100644 --- a/kernel/src/kernel/entry.c +++ b/kernel/src/kernel/entry.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -56,33 +56,26 @@ static void hcf (void) { } static void print_info (void) { - drawBorder (20); - set_color (0x44eeaa); + kprintf ("\033[38;2;68;238;170mCOS 0.0%d", 7); + kprintf ("\033[38;2;221;238;204m\n\nHello, World!\n\n"); + kprintf ("\033[38;2;136;170;238mSystem info:\n"); - kprintf ("COS 0.0%d", 7); - - set_color (0xddeecc); - - kprintf ("\n\nHello, World!\n\n"); - - set_color (0x88aaee); - kprintf ("System info:\n"); if (bootinfo_req.response != nullptr) { - set_color (0x888888); - kprintf ("Bootloader: %s %s", bootinfo_req.response->name, bootinfo_req.response->version); + kprintf ("\033[38;2;200;200;200mBootloader: %s %s", bootinfo_req.response->name, + bootinfo_req.response->version); } else kprintf ("\nDid not receive bootloader info from bootloader.\n"); if (boottime_req.response != nullptr) { - set_color (0x888888); - kprintf ("\nSystem booted at time %ld.\n", boottime_req.response->boot_time); + kprintf ("\n\033[38;2;200;200;200mSystem booted at time %ld.\n", + boottime_req.response->boot_time); } else kprintf ("\nDid not receive boot time from Limine.\n"); } __attribute__ ((noreturn)) void _start_stage2 (void) { init_graphics (framebuffer); - init_console (framebuffer->width, framebuffer->height, 40, 40, 1, 1, 2); + init_con (framebuffer->width, framebuffer->height, 20, 20, 1, 1, 2); init_kb (); init_acpi (rsdp); diff --git a/kernel/src/kernel/fs/chardev/chardev.c b/kernel/src/kernel/fs/chardev/chardev.c index 32db773b..51095364 100644 --- a/kernel/src/kernel/fs/chardev/chardev.c +++ b/kernel/src/kernel/fs/chardev/chardev.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -13,14 +13,13 @@ inode* tty1_ptr = nullptr; static int stdout_write (inode* node, file* f, void* buf, size_t len) { (void)node, (void)f; // args not used - bool stdio_buf = get_update_on_putch (); - set_update_on_putch (false); + bool stdio_buf = con_update_cache_clear (); for (size_t i = 0; i < len; i++) - putchar (((char*)buf)[i]); + add_char (((char*)buf)[i]); - update (); - set_update_on_putch (stdio_buf); + con_update (); + con_update_upd (stdio_buf); return len; } @@ -37,6 +36,7 @@ static int stdin_read (inode* node, file* f, void* buffer, size_t size) { (void)node, (void)f; // args not used char* cbuffer = (char*)buffer; size_t bytes_read = 0; + bool stdio_buf = con_update_cache_set (); for (size_t i = 0; i < size; i++) { unsigned char c = 255; while ((c = pop_next_char ()) == 255) @@ -45,8 +45,7 @@ static int stdin_read (inode* node, file* f, void* buffer, size_t size) { if (i > 0) { i -= 2; bytes_read--; - putchar ('\x7F'); - update (); + add_char ('\x7F'); } else { i--; } @@ -55,13 +54,13 @@ static int stdin_read (inode* node, file* f, void* buffer, size_t size) { } else if (c < 0x80) { cbuffer[i] = c; bytes_read++; - putchar (c); - update (); + add_char (c); if (c == '\n') break; } else { i--; } } + con_update_upd (stdio_buf); return (int)bytes_read; } diff --git a/kernel/src/kernel/hw/keyboard.c b/kernel/src/kernel/hw/keyboard.c index 67d45369..df4b28c3 100644 --- a/kernel/src/kernel/hw/keyboard.c +++ b/kernel/src/kernel/hw/keyboard.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -60,7 +61,11 @@ static void kb_handler (registers_t* registers) { if (kb_read_status_register ().out_buffer_full) { unsigned char scancode = inb (kb_ps2_data_port); unsigned char processed = map_keypress (kb_statemachine, scancode); - if (processed != 0) { + if (processed == kb_action_pgup) { + con_scrollup (3); + } else if (processed == kb_action_pgdn) { + con_scrolldown (3); + } else if (processed != 0) { push_charqueue (kb_keypress_charqueue, processed); if (kb_tty_handler) kb_tty_handler (processed); } diff --git a/user/cosh/include/builtin.h b/user/cosh/include/builtin.h index aa3e17c1..11f90a73 100644 --- a/user/cosh/include/builtin.h +++ b/user/cosh/include/builtin.h @@ -5,6 +5,7 @@ int dispatch_builtin (size_t argc, char** argv); int builtin_chdir (int argc, char** argv); +int builtin_clear (int argc, char** argv); int builtin_echo (int argc, char** argv); int builtin_eval (int argc, char** argv); int builtin_exit (int argc, char** argv); diff --git a/user/cosh/src/builtin/clear.c b/user/cosh/src/builtin/clear.c new file mode 100644 index 00000000..c4995243 --- /dev/null +++ b/user/cosh/src/builtin/clear.c @@ -0,0 +1,8 @@ +#include +#include + +int builtin_clear (int argc, char** argv) { + (void)argc, (void)argv; + printf ("\033[2J\033[3J\033[H"); + return 0; +} diff --git a/user/cosh/src/builtin/dispatch_builtin.c b/user/cosh/src/builtin/dispatch_builtin.c index 2c5b2f57..2395998a 100644 --- a/user/cosh/src/builtin/dispatch_builtin.c +++ b/user/cosh/src/builtin/dispatch_builtin.c @@ -33,6 +33,8 @@ int dispatch_builtin (size_t argc, char** argv) { return builtin_source (argc, argv); else if (strcmp (argv[0], "test") == 0 || strcmp (argv[0], "[") == 0) return builtin_test (argc, argv); + else if (strcmp (argv[0], "clear") == 0) + return builtin_clear (argc, argv); return -1; } diff --git a/user/cosh/src/repl_main.c b/user/cosh/src/repl_main.c index 7188e4ff..ec626b29 100644 --- a/user/cosh/src/repl_main.c +++ b/user/cosh/src/repl_main.c @@ -122,14 +122,14 @@ int repl_loop (void) { if (!pathbuf || !cmdbuf) return -1; getcwd (pathbuf, 100); - printf ("root@cos %s > ", pathbuf); + printf ("\033[37mroot@cos \033[36m%s\033[37m > ", pathbuf); if (fgets (cmdbuf, 1000, stdin) == nullptr) return -1; cmdbuf[strcspn (cmdbuf, "\n")] = '\0'; if (cmdbuf[0] == '\0') return 0; last_exit = run_line (cmdbuf); - if (last_exit != 0) printf ("exited with non-zero status: %i\n", last_exit); + if (last_exit != 0) printf ("\033[31mexited with non-zero status: %i\n", last_exit); return 0; }