#include "term.h" #include #include #include #include #include #include extern font_t uf5x7; struct tcell { char chr[4]; int fg; int bg; }; static struct tcell tgrid[UNS_TERM_ROWS][UNS_TERM_COLS]; static void tgrid_set(int row, int col, int fg, int bg, char c[4]) { tgrid[row][col].chr[0] = c[0]; tgrid[row][col].chr[1] = c[1]; tgrid[row][col].chr[2] = c[2]; tgrid[row][col].chr[3] = c[3]; tgrid[row][col].fg = fg; tgrid[row][col].bg = bg; } static int tgrid_sets(int *row, int *col, int fg, int bg, const char *s) { int i = 0; while (s[i] != '\0') { // boundary check if (*row >= UNS_TERM_ROWS) return i; if (*col >= UNS_TERM_COLS) return i; // detect utf-8 char length unsigned char lb = s[i]; int charlen = 0; if ((lb & 0x80) == 0) // lead bit is zero, must be a single ascii charlen = 1; else if ((lb & 0xE0) == 0xC0) // 110x xxxx charlen = 2; else if ((lb & 0xF0) == 0xE0) // 1110 xxxx charlen = 3; else if ((lb & 0xF8) == 0xF0) // 1111 0xxx charlen = 4; char unichar[4]; int j; for (j = 0; j < charlen; j++) unichar[j] = s[i + j]; for (; j < 4; j++) unichar[j] = '\0'; i += charlen; const int char_is_newline = (strncmp(unichar, "\n", 4) == 0); if (char_is_newline) { // clear line end for (; *col < UNS_TERM_COLS; *col = *col + 1) tgrid_set(*row, *col, C_WHITE, C_BLACK, " \0\0"); } else { tgrid_set(*row, *col, fg, bg, unichar); *col = *col + 1; } } return i; } int term_writeat(int row, int col, int fg, int bg, const char *s) { return tgrid_sets(&row, &col, fg, bg, s); } void tgrid_display(void) { dfont(&uf5x7); int y = 3; for (int row = 0; row < UNS_TERM_ROWS; row++) { int x = 2; for (int col = 0; col < UNS_TERM_COLS; col++) { const struct tcell cell = tgrid[row][col]; // unpack unicode char buf[5]; buf[0] = cell.chr[0]; buf[1] = cell.chr[1]; buf[2] = cell.chr[2]; buf[3] = cell.chr[3]; buf[4] = '\0'; drect(x - 1, y - 1, x + 6, y + 8, cell.bg); dtext_opt(x, y, cell.fg, C_NONE, DTEXT_LEFT, DTEXT_TOP, (char *)&buf, -1); x += 1 + 5; } y += 1 + 7 + 2; } } void term_scroll_down(void) { // clear last line for (int i = 0; i < UNS_TERM_COLS; i++) { tgrid[UNS_TERM_ROWS - 1][i].chr[0] = ' '; tgrid[UNS_TERM_ROWS - 1][i].chr[1] = '\0'; tgrid[UNS_TERM_ROWS - 1][i].chr[2] = '\0'; tgrid[UNS_TERM_ROWS - 1][i].chr[3] = '\0'; tgrid[UNS_TERM_ROWS - 1][i].fg = C_WHITE; tgrid[UNS_TERM_ROWS - 1][i].bg = C_BLACK; } for (int i = 1; i < UNS_TERM_ROWS - 1; i++) { memcpy(tgrid[i], tgrid[i + 1], UNS_TERM_COLS * sizeof(struct tcell)); } } static int term_print_opt(const char *str, int fg, int bg) { static int row = 1; static int col = 0; int i = 0; while (str[i] != '\0') { // handle cursor overflow (column) if (col >= UNS_TERM_COLS) { col = 0; row++; } // handle cursor overflow (row) if (row >= UNS_TERM_ROWS - 1) { term_scroll_down(); row = UNS_TERM_ROWS - 2; } i += tgrid_sets(&row, &col, fg, bg, str + i); } return i; } int term_print(const char *str) { return term_print_opt(str, C_WHITE, C_BLACK); } int term_printf(const char *restrict format, ...) { va_list argp; va_start(argp, format); char buf[1024]; const int n = vsnprintf(buf, sizeof(buf), format, argp); term_print(buf); va_end(argp); return n; } int term_eprint(const char *str) { return term_print_opt(str, C_RED, C_BLACK); } int term_eprintf(const char *restrict format, ...) { va_list argp; va_start(argp, format); char buf[1024]; const int n = vsnprintf(buf, sizeof(buf), format, argp); term_eprint(buf); va_end(argp); return n; } int term_kprint(const char *str) { int t = rtc_ticks(); char kstr[256]; sprintf(kstr, "[%5d:%03d] %s\n", t / 128, t % 128, str); const int ret = term_print_opt(kstr, C_RED | C_GREEN, C_BLACK); return ret; } int term_kprintf(const char *restrict format, ...) { va_list argp; va_start(argp, format); char buf[1024]; const int n = vsnprintf(buf, sizeof(buf), format, argp); term_kprint(buf); va_end(argp); return n; }