summaryrefslogtreecommitdiff
path: root/src/user/terminal
diff options
context:
space:
mode:
Diffstat (limited to 'src/user/terminal')
-rw-r--r--src/user/terminal/readline.c104
-rw-r--r--src/user/terminal/terminal.c284
2 files changed, 388 insertions, 0 deletions
diff --git a/src/user/terminal/readline.c b/src/user/terminal/readline.c
new file mode 100644
index 0000000..37ef54b
--- /dev/null
+++ b/src/user/terminal/readline.c
@@ -0,0 +1,104 @@
+#include <terminal/terminal.h>
+
+#include <knob/format.h>
+#include <knob/heap.h>
+#include <knob/key.h>
+
+#include <pland/syscall.h>
+
+#include <stdint.h>
+
+//returns length of string without null terminator
+//max_length doesn't include null terminator
+uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) {
+ uint32_t i = 0;
+ uint32_t l = 0;
+
+ term_add_sz_no_ww(prompt);
+ paint_term();
+
+ const _window_handle_t handle = active_term->window;
+ struct window_action action;
+ while (1) {
+ _get_win_action(handle, &action);
+ switch (action.action_type) {
+ case NOT_READY:
+ _wait_for_action();
+ _yield_task();
+ continue;
+ case KEY_DOWN:
+ //;char *const debug_msg = format("got key 0x%2x, 0x%3x", action.as_key.key_id, action.as_key.modifiers);
+ // _system_log(debug_msg);
+ // free_block(debug_msg);
+ switch (action.as_key.key_id) {
+ case KEY_DELETE:
+ if (i != l) {
+ cursor_right();
+ ++i;
+ }
+ case KEY_BSPACE:
+ if (!i)
+ continue;
+ --l;
+ --i;
+ for (uint8_t j = i; j < l; ++j)
+ sz[j] = sz[j + 1];
+ sz[l] = '\0';
+ cursor_left();
+ uint32_t cursor_backup_x = active_term->cursor_x;
+ uint32_t cursor_backup_y = active_term->cursor_y;
+ term_add_sz_no_ww(sz + i);
+ term_add_char(' ');
+ move_cursor(cursor_backup_y, cursor_backup_x);
+ paint_term();
+ continue;
+ case KEY_ENTER:
+ term_newline();
+ paint_term();
+ sz[l] = '\0';
+ return l;
+ case KEY_HOME:
+ case KEY_UP_ARROW:
+ for (; i; --i)
+ cursor_left();
+ paint_term();
+ continue;
+ case KEY_END:
+ case KEY_DOWN_ARROW:
+ for (; i != l; ++i)
+ cursor_right();
+ paint_term();
+ continue;
+ case KEY_LEFT_ARROW:
+ if (i) {
+ cursor_left();
+ paint_term();
+ --i;
+ }
+ continue;
+ case KEY_RIGHT_ARROW:
+ if (i != l) {
+ cursor_right();
+ paint_term();
+ ++i;
+ }
+ continue;
+ default:
+ if (i == max_length)
+ continue;
+ char ch = key_to_char(action.as_key);
+ if (ch) {
+ term_add_char(ch);
+ paint_term();
+ sz[i] = ch;
+ if (i == l)
+ ++l;
+ ++i;
+ }
+ continue;
+ }
+ default:
+ continue;
+ }
+ }
+} \ No newline at end of file
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