#include "vt.h" #include vt *ke_vt = 0, *home_vt = 0; static char *kb_cmd_esc[] = { /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 */ 0, 0, 0, 0, 0, 0, 0, 0, "\x1b[B", 0, /* 30 */ "\x1b[D", 0, "\x1b[C", 0, "\x1b[A", 0, 0, 0, 0, 0, /* 40 */ 0, 0, 0, 0, 0, 0, "\x1b[B", "\x1b[A", "\x1b[D", "\x1b[C", /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 */ 0, 0, 0, 0 }; vt::vt(node* parent, int ww, int hh) : node(parent, FT_TERMINAL) { w = ww; h = hh; fgcolor = TC_LIGHTGRAY; bgcolor = TC_BLACK; output = 0; cursor_visible = true; keyboard_echo = false; csr_l = csr_c = 0; kbd_buffer_filled = 0; kbd_waiter = 0; text = 0; if (w != 0 && h != 0) { text = (vt_char*)kmalloc(w * h * sizeof(vt_char)); clear(); } } void vt::put_at(int l, int c, int ch) { text[l * w + c].fgcolor = fgcolor; text[l * w + c].bgcolor = bgcolor; text[l * w + c].ch = ch; if (output != 0) { if (output->connected_vt != this) { output = 0; } else { output->text_put(l, c, ch, fgcolor, bgcolor); } } } void vt::scroll(int n) { if (output != 0 && output->connected_vt == this) { output->text_scroll(n, fgcolor, bgcolor); } if (n > 0) { for (int j = 0; j < w * (h-n); j++) { text[j] = text[j + w]; } for (int j = w * (h-n); j < w * h; j++) { text[j].fgcolor = fgcolor; text[j].bgcolor = bgcolor; text[j].ch = ' '; } } else if (n < 0) { //TODO } csr_l -= n; } void vt::put(int c) { if (text == 0) return; if (c == '\b' && csr_c != 0) { // backspace csr_c--; put_at(csr_l, csr_c, ' '); } else if (c == '\t') { // tab int csr_nc = (csr_c + 8) & ~(8 - 1); for (int i = csr_c; i < csr_nc && i < w; i++) put_at(csr_l, i, ' '); csr_c = csr_nc; } else if (c == '\r') { // carriage return csr_c = 0; } else if (c == '\n') { // new line csr_c = 0; csr_l++; } else if (c == '\f') { // form feed (new page) clear(); } else if (c >= ' ') { put_at(csr_l, csr_c, c); csr_c++; } if (csr_c >= w) { csr_c = 0; csr_l++; } if (csr_l == h) { scroll(1); } if (output != 0 && output->connected_vt == this) output->text_setcsr(csr_l, csr_c, cursor_visible); } void vt::clear() { for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { put_at(j, i, ' '); } } csr_c = csr_l = 0; } void vt::writeHex(uint32_t v) { int i; put('0'); put('x'); char hexdigits[] = "0123456789abcdef"; for (i = 0; i < 8; i++) { put(hexdigits[v >> 28]); v = v << 4; } } void vt::writeDec(int v) { if (v == 0) { put('0'); return; } if (v < 0) { put ('-'); v = -v; } char numbers[] = "0123456789"; while (v > 0) { int order = 1, no = 1; while (v / order > 0) order *= 10; order /= 10; put(numbers[v / order]); v = v - (v / order * order); while (v / no > 0) no *= 10; while (no < order) { put('0'); no *= 10; } } } // *************** INTERACTION void vt::outputTo(display *display) { output = display; if (output == 0) return; output->connected_vt = this; if (output->text_w() != w || output->text_h() != h) { int ow = w, oh = h; vt_char *old_text = text; w = output->text_w(); h = output->text_h(); text = (vt_char*)kmalloc(w * h * sizeof(vt_char)); for (int c = 0; c < w; c++) { for (int l = 0; l < h; l++) { if (c < ow && l < oh) { put_at(l, c, old_text[l * ow + c].ch); } else { put_at(l, c, ' '); } } } if (old_text != 0) kfree(old_text); if (csr_c >= w) csr_c = w; if (csr_l >= h) csr_l = h; } else { for (int c = 0; c < w; c++) { for (int l = 0; l < h; l++) { output->text_put(l, c, text[l*w+c].ch, text[l*w+c].fgcolor, text[l*w+c].bgcolor); } } } output->text_setcsr(csr_l, csr_c, cursor_visible); } void vt::keyboardInput(keypress kp, keyboard* from) { if (kp.command == KB_RSUPER || kp.command == KB_LSUPER) { if (this == home_vt) return; // go to home terminal home_vt->outputTo(output); from->outputTo(home_vt); output = 0; return; } // convert to sequence of chars int n = 0; char b[8]; if (kp.character >= 0x80) kp.character = '?'; // todo some day : unicode if ((kp.command == 0 || kp.command == KB_ENTER || kp.command == KB_TAB || kp.command == KB_BACKSPACE) && kp.character != 0 && kp.pressed) { b[n++] = (char)kp.character; } else if (kp.command == KB_CMD_ALT && kp.character >= 64 && kp.character <= 126 && kp.pressed) { b[n++] = 27; b[n++] = kp.character; } else if (kb_cmd_esc[kp.command & 0x3F] != 0 && kp.pressed) { int i; for (i = 0; kb_cmd_esc[kp.command & 0x3F][i] != 0; i++) { b[n++] = kb_cmd_esc[kp.command & 0x3F][i]; } } if (n == 0) return; if (kbd_buffer_filled + n > KBD_BUFFER_SIZE) goto wake_up; for (int i = 0; i < n; i++) { kbd_buffer[kbd_buffer_filled++] = b[i]; } wake_up: if (kbd_waiter != 0) { thread *w = kbd_waiter; kbd_waiter = 0; w->wakeUp(); } } // ********************* SYSCALLS int vt::write(size_t offset, size_t len, char* buffer) { // ignore offset // TODO: ANSI escape sequences // todo some day : unicode for (unsigned i = 0; i < len; i++) { if (buffer[i] == 27) { i++; if (buffer[i++] == '[') { int n = -1, m = -1; while (buffer[i] >= '0' && buffer[i] <= '9') { if (n == -1) n = 0; n *= 10; n += buffer[i] - '0'; i++; } if (buffer[i] == ';') { i++; while (buffer[i] >= '0' && buffer[i] <= '9') { if (m == -1) m = 0; m *= 10; m += buffer[i] - '0'; i++; } } if (buffer[i] == 'A') { // move cursor up if (n == -1) n = 1; csr_l -= n; } else if (buffer[i] == 'B') { // move cursor down if (n == -1) n = 1; csr_l += n; } else if (buffer[i] == 'C') { // move cursor forward if (n == -1) n = 1; csr_c += n; } else if (buffer[i] == 'D') { // move cursor back if (n == -1) n = 1; csr_c -= n; } else if (buffer[i] == 'E') { // move cursor n lines down, to beginning of line if (n == -1) n = 1; csr_l += n; csr_c = 0; } else if (buffer[i] == 'F') { // move cursor to beginning of line, n lines up if (n == -1) n = 1; csr_l -= n; csr_c = 0; } else if (buffer[i] == 'H' || buffer[i] == 'f') { // set cursor position if (n == -1) n = 1; if (m == -1) m = 1; csr_l = n - 1; csr_c = m - 1; } else if (buffer[i] == 'J' || buffer[i] == 'K') { // erase data / erase line if (n == -1) n = 0; int start = 0, end = 0; if (n == 0) { start = csr_c; end = w; } else if (n == 1) { start = 0; end = csr_c; } else if (n == 2) { start = 0; end = w; } for (int p = start; p < end; p++) put_at(csr_l, p, ' '); if (buffer[i] == 'J') { // erase data if (n == 0) { start = csr_l + 1; end = h; } else if (n == 1) { start = 0; end = csr_l; } else if (n == 2) { start = 0; end = h; } for (int l = start; l < end; l++) { for (int c = 0; c < w; c++) put_at(l, c, ' '); } } } else if (buffer[i] == 'S') { // scroll up if (n == -1) n = 1; scroll(n); } else if (buffer[i] == 'T') { // scroll down if (n == -1) n = 1; scroll(-n); } else if (buffer[i] == 'm') { // select graphic rendition if (n == -1) n = 0; if (n == 0) { fgcolor = 7; bgcolor = 0; } else if (n == 1) { fgcolor |= TC_MAKE_LIGHT; } else if (n == 22) { fgcolor &= TC_MAKE_DARK; } else if (n >= 30 && n <= 37) { fgcolor = (fgcolor & TC_MAKE_LIGHT) | (n - 30); } else if (n >= 40 && n <= 47) { bgcolor = (n - 40); } else { // others to do. } } else if (buffer[i] == 'n') { // device status report kbd_buffer[kbd_buffer_filled++] = 27; kbd_buffer[kbd_buffer_filled++] = '['; if (csr_l >= 100) kbd_buffer[kbd_buffer_filled++] = ((csr_l / 100) % 10) + '0'; if (csr_l >= 10) kbd_buffer[kbd_buffer_filled++] = ((csr_l / 10) % 10) + '0'; kbd_buffer[kbd_buffer_filled++] = (csr_l % 10) + '0'; kbd_buffer[kbd_buffer_filled++] = ';'; if (csr_c >= 100) kbd_buffer[kbd_buffer_filled++] = ((csr_c / 100) % 10) + '0'; if (csr_c >= 10) kbd_buffer[kbd_buffer_filled++] = ((csr_c / 10) % 10) + '0'; kbd_buffer[kbd_buffer_filled++] = (csr_c % 10) + '0'; kbd_buffer[kbd_buffer_filled++] = 'R'; } else if (buffer[i] == 's') { // save cursor position save_csr_l = csr_l; save_csr_c = csr_c; } else if (buffer[i] == 'p') { // restore cursor position csr_l = save_csr_l; csr_c = save_csr_c; } else if (buffer[i] == 'e') { // CUSTOM : enable keyboard echo keyboard_echo = true; } else if (buffer[i] == 'h') { // CUSTOM : disable keyboard echo keyboard_echo = false; } else if (buffer[i] == '?') { if (buffer[++i] == '2' && buffer[++i] == '5') { if (buffer[++i] == 'l') cursor_visible = false; else cursor_visible = true; } } else { } if (csr_l < 0) csr_l = 0; if (csr_c < 0) csr_c = 0; if (csr_l >= h) csr_l = h - 1; if (csr_c >= w) csr_c = w - 1; if (output != 0 && output->connected_vt == this) output->text_setcsr(csr_l, csr_c, cursor_visible); } else { // ignore it. } } else { put(buffer[i]); } } return len; } int vt::read(size_t offset, size_t len, char* buffer) { // ignore offset unsigned c = 0; while (true) { bool end = false; if (kbd_buffer_filled > 0) { int r = 0; if (keyboard_echo) { for (; r < kbd_buffer_filled; r++) { if (kbd_buffer[r] == '\b') { if (c > 0) { c--; put('\b'); } } else if (kbd_buffer[r] == '\n') { buffer[c++] = '\n'; c++; put('\n'); end = true; break; } else if (kbd_buffer[r] >= ' ') { buffer[c++] = kbd_buffer[r]; put(buffer[c]); } if (c == len) break; } } else { for (; r < kbd_buffer_filled && c < len; r++) { buffer[c++] = kbd_buffer[r]; } end = true; } memcpy(kbd_buffer + r, kbd_buffer, (kbd_buffer_filled - r)); kbd_buffer_filled -= r; } if (end || c == len) return c; if (kbd_waiter != 0) return c; kbd_waiter = current_thread; thread_goInactive(); } } size_t vt::get_size() { return ((w << 16) + h); } int vt::link(node* to, int mode) { if (mode == LM_OUTPUT_TO) { display *d = to->as_display(); if (d != 0) { outputTo(d); return 0; } } return E_NOT_IMPLEMENTED; }