#include "vt.h"
#include <dev/display.h>
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 {
while (r < kbd_buffer_filled && c < len) {
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;
}