diff options
Diffstat (limited to 'src/user/terminal/terminal.c')
-rw-r--r-- | src/user/terminal/terminal.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/user/terminal/terminal.c b/src/user/terminal/terminal.c new file mode 100644 index 0000000..7961fd5 --- /dev/null +++ b/src/user/terminal/terminal.c @@ -0,0 +1,284 @@ +#include <terminal/terminal.h> + +#include <libfont/fonts.h> + +#include <knob/format.h> +#include <knob/heap.h> + +struct term_list_entry { + struct terminal term; + struct term_list_entry *next; + struct term_list_entry *prev; +}; + +struct term_list_entry *last_term = 0; +struct terminal *active_term = 0; + +struct terminal *make_term(struct font_info *font, uint32_t cols, uint32_t rows) { + if (!font) + return 0; + + struct term_list_entry *next_entry = get_block(sizeof(struct term_list_entry)); + if (!next_entry) + return 0; + + char *const cb = get_block(cols * rows); + + if (!cb) { + free_block(next_entry); + return 0; + } + + const uint32_t w = cols * font->space_width; + const uint32_t h = rows * font->space_height; + + uint8_t *const pb = get_block(w * h); + if (!pb) { + free_block(next_entry); + free_block(cb); + return 0; + } + + _window_handle_t win = _new_window(w, h, pb); + if (!win) { + free_block(next_entry); + free_block(cb); + free_block(pb); + return 0; + } + + for (char *i = cb; i < cb + cols * rows; ++i) + *i = ' '; + for (uint8_t *i = pb; i < pb + w * h; ++i) + *i = 0x10; + + next_entry->term.window = win; + next_entry->term.pixbuf = pb; + next_entry->term.window_width = w; + next_entry->term.window_height = h; + + next_entry->term.font = font; + + next_entry->term.cols = cols; + next_entry->term.rows = rows; + next_entry->term.charbuf = cb; + + next_entry->term.cursor_y = 0; + next_entry->term.cursor_x = 0; + + next_entry->term.fg = 0x0f; + next_entry->term.bg = 0x10; + + next_entry->prev = last_term; + next_entry->next = 0; + + if (last_term) + last_term->next = next_entry; + last_term = next_entry; + + return (struct terminal *)next_entry; +} + +void del_term(struct terminal *term) { + _delete_window(term->window); + free_block(term->pixbuf); + free_block(term->charbuf); + + free_block(term);//coincides with the term_list_entry + if (active_term == term) + active_term = 0; + + for (struct term_list_entry *i = last_term; i; i = i->prev) + if (i == (struct term_list_entry *)term) { + if (i->prev) + i->prev->next = i->next; + if (i->next) + i->next->prev = i->prev; + if (i == last_term) + last_term = i->prev; + return; + } +} + +static void draw_char(uint32_t y, uint32_t x, bool inverted) { + put_char(active_term->font, active_term->charbuf[y * active_term->cols + x], active_term->pixbuf + (y * active_term->cols * active_term->font->space_height + x) * active_term->font->space_width, active_term->window_width, inverted ? active_term->fg : active_term->bg, inverted ? active_term->bg : active_term->fg); +} + +static void draw_cursor() { + draw_char(active_term->cursor_y, active_term->cursor_x, true); +} + +void paint_term() { + _paint_window(active_term->window); +} + +void move_cursor(uint32_t new_y, uint32_t new_x) { + draw_char(active_term->cursor_y, active_term->cursor_x, false); + active_term->cursor_y = new_y; + active_term->cursor_x = new_x; + draw_cursor(); +} + +static void redraw_term() { + for (uint32_t y = 0; y < active_term->rows; ++y) + for (uint32_t x = 0; x < active_term->cols; ++x) + draw_char(y, x, false); + draw_cursor(); +} + +void set_color(uint8_t fg, uint8_t bg) { + active_term->fg = fg; + active_term->bg = bg; + redraw_term(); +} + +void clear_term() { + for (char *i = active_term->charbuf, *const e = i + active_term->cols * active_term->rows; i != e; ++i) + *i = ' '; + for (uint8_t *i = active_term->pixbuf, *const e = i + active_term->window_width * active_term->window_height; i != e; ++i) + *i = active_term->bg; + move_cursor(0, 0); +} + +void cursor_up() { + draw_char(active_term->cursor_y, active_term->cursor_x, false); + if (active_term->cursor_y) + --active_term->cursor_y; + else + //eventually, maybe scroll back through a longer terminal buffer + active_term->cursor_x = 0; + draw_cursor(); +} + +void cursor_left() { + draw_char(active_term->cursor_y, active_term->cursor_x, false); + if (active_term->cursor_x) { + --active_term->cursor_x; + draw_cursor(); + } + else { + active_term->cursor_x = active_term->cols - 1; + cursor_up(); + } +} + +void cursor_down() { + draw_char(active_term->cursor_y, active_term->cursor_x, false); + + const uint32_t rows = active_term->rows; + + if (++active_term->cursor_y == rows) { + --active_term->cursor_y; + + char *const cb = active_term->charbuf; + uint8_t *const pb = active_term->pixbuf; + const uint32_t cols = active_term->cols; + const uint32_t fw = active_term->font->space_width; + const uint32_t fh = active_term->font->space_height; + + char *to; + for (to = cb; to < cb + (rows - 1) * cols; ++to) + *to = *(to + cols); + for (; to < cb + rows * cols; ++to) + *to = ' '; + uint8_t *pto; + for (pto = pb; pto < pb + cols * fw * (rows - 1) * fh; ++pto) + *pto = *(pto + cols * fw * fh); + for (; pto < pb + cols * fw * rows * fh; ++pto) + *pto = active_term->bg; + } + + draw_cursor(); +} + +void cursor_right() { + draw_char(active_term->cursor_y, active_term->cursor_x, false); + + if (++active_term->cursor_x == active_term->cols) + term_newline(); + else + draw_cursor(); +} + +void term_newline() { + draw_char(active_term->cursor_y, active_term->cursor_x, false); + active_term->cursor_x = 0; + cursor_down(); +} + +void term_add_char(char ch) { + if (ch == '\n') { + term_newline(); + return; + } +//char *const debug_msg = format("charbuf[%u] = '%c'", active_term->cursor_y * active_term->cols + active_term->cursor_x, ch); +//_system_log(debug_msg); +//free_block(debug_msg); + active_term->charbuf[active_term->cursor_y * active_term->cols + active_term->cursor_x] = ch; + cursor_right(); +} + +void term_add_sz_no_ww(const char *sz) { + while (*sz) + term_add_char(*(sz++)); +} + +#define MIN_TAB 3 +#define TAB_STEP 4 + +void term_add_sz(const char *sz) { + //TODO: special hyphen handling + const char *word = sz; + while (1) + if (!*sz || (*sz == ' ') || (*sz == '\n') || (*sz == '\t')) { + const uint32_t len = sz - word; + if ((active_term->cursor_x + len > active_term->cols) && (len <= active_term->cols) && active_term->cols) + term_newline(); + for (const char *i = word; i < sz; ++i) + term_add_char(*i); + while ((*sz == ' ') || (*sz == '\n') || (*sz == '\t')) { + if (*sz == '\n') + term_newline(); + else if (*sz == '\t') { + for (uint8_t i = 0; i < MIN_TAB; ++i) + cursor_right(); + while (active_term->cursor_x % TAB_STEP) + cursor_right(); + } + ++sz; + } + if (!*sz) + return; + if (active_term->cursor_x) + term_add_char(' '); + word = sz; + } + else + ++sz; +} + +void term_addf_no_ww_v(const char *fmt, va_list args) { + char *const msg = format_v(fmt, args); + term_add_sz_no_ww(msg); + free_block(msg); +} + +void term_addf_no_ww(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + term_addf_no_ww_v(fmt, args); + va_end(args); +} + +void term_addf_v(const char *fmt, va_list args) { + char *const msg = format_v(fmt, args); + term_add_sz(msg); + free_block(msg); +} + +void term_addf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + term_addf_v(fmt, args); + va_end(args); +}
\ No newline at end of file |