/* Terminal video */ /* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Cette implementation est inspiree de la norme ANSI X3.64, qui definit des * sequences de controle pour les terminaux video. * Documentation : * - ANSI X3.64 : http://www.cs.usyd.edu.au/~tapted/ansi.html * - VT100 : http://vt100.net/docs/vt100-ug/ */ #include "vt_struct.h" #include "tty.h" #include "tty_struct.h" #include "video_driver.h" #include "tty_queue.h" #include #include #define DEF_FGCOLOR 7 /* blanc */ #define DEF_BGCOLOR 0 /* noir */ #define color(c) ({ \ register unsigned char _c = (c); \ ((_c & 001) << 2) | (_c & 002) | ((_c & 004) >> 2); \ }) /* Tables de conversion des caracteres. '\0' signifie que le caractere n'est * pas imprimable. */ const unsigned char latin1[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0" " !\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "`abcdefghijklmnopqrstuvwxyz{|}~\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250" "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376" "\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341" "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213" "\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230"; static void reset_attr_mode(struct attr_mode_struct *attr_mode) { attr_mode->intensity = 1; attr_mode->underscore = 0; attr_mode->blink = 0; attr_mode->reverse = 0; attr_mode->concealed = 0; attr_mode->fgcolor = DEF_FGCOLOR; attr_mode->bgcolor = DEF_BGCOLOR; } static void reset(struct vt_struct *vt, unsigned short line, unsigned short col) { /* affichage */ vt->saved_line = vt->line = line; vt->saved_col = vt->col = col; reset_attr_mode(&vt->attr_mode); vt->saved_attr_mode = vt->attr_mode; vt->saved_translation_table = vt->translation_table = latin1; /* etat */ vt->need_wrap = 0; vt->state = 0; /* forcera la reinitialisation de param, npar et ques */ /* modes */ vt->auto_wrap = 1; } static void respond_string(const char *str, struct tty_struct *tty) { while (*str) tty_put(tty, *str++); } /* n doit verifier 1 <= n <= 999. */ static void respond_num(unsigned short n, struct tty_struct *tty) { char buf[3]; unsigned int i; i = 3; do { buf[--i] = '0' + n % 10; n /= 10; } while (i && n); while (i < 3) tty_put(tty, buf[i++]); } /* Commandes de controle */ static void bel(struct vt_struct *vt) { vt->do_beep(); } static void bs(struct vt_struct *vt) { if (vt->col) { vt->col--; vt->need_wrap = 0; } } static void ht(struct vt_struct *vt) { vt->col = min((vt->col | 7) + 1, vt->columns - 1); } static void lf(struct vt_struct *vt) { if (vt->line + 1 < vt->lines) vt->line++; else { vt->driver->scroll(); vt->need_wrap = 0; } } static void cr(struct vt_struct *vt) { vt->col = 0; vt->need_wrap = 0; } static void del(struct vt_struct *vt) {} static void putc(struct vt_struct *vt, unsigned char c) { vt->driver->putc(vt->line * vt->columns + vt->col, c); if (vt->col + 1 < vt->columns) vt->col++; else vt->need_wrap = vt->auto_wrap; } static void save_cursor(struct vt_struct *vt) { vt->saved_line = vt->line; vt->saved_col = vt->col; vt->saved_attr_mode = vt->attr_mode; vt->saved_translation_table = vt->translation_table; } static void restore_cursor(struct vt_struct *vt) { vt->line = vt->saved_line; vt->col = vt->saved_col; vt->attr_mode = vt->saved_attr_mode; vt->translation_table = vt->saved_translation_table; vt->driver->set_attr(&vt->attr_mode); vt->need_wrap = 0; } static void reset_device(struct vt_struct *vt) { reset(vt, 0, 0); vt->driver->clear(0, vt->lines * vt->columns); vt->do_kb_reset(); } /* Si la position donnee par to_line et to_col est en dehors de l'ecran, le * curseur est place sur le bord le plus proche. */ static void move(struct vt_struct *vt, int to_line, int to_col) { if (to_line < 0) vt->line = 0; else if (to_line > vt->lines - 1) vt->line = vt->lines - 1; else vt->line = to_line; if (to_col < 0) vt->col = 0; else if (to_col > vt->columns - 1) vt->col = vt->columns - 1; else vt->col = to_col; vt->need_wrap = 0; } static void erase(struct vt_struct *vt, unsigned short start, unsigned short end) { vt->driver->clear(start, end); vt->need_wrap = 0; } static void report_device_code(struct vt_struct *vt, struct tty_struct *tty) { respond_string("\e[?1;0c", tty); /* VT100 */ tty_process_input_queue(tty); } static void report_device_status(struct vt_struct *vt, struct tty_struct *tty) { if (1) /* OK */ respond_string("\e[0n", tty); else /* Failure */ respond_string("\e[3n", tty); tty_process_input_queue(tty); } static void report_cursor_position(struct vt_struct *vt, struct tty_struct *tty) { tty_put(tty, '\e'); tty_put(tty, '['); respond_num(vt->line + 1, tty); tty_put(tty, ';'); respond_num(vt->col + 1, tty); tty_put(tty, 'R'); tty_process_input_queue(tty); } /* Export */ void vt_init(struct vt_struct *vt, unsigned short line, unsigned short col) { /* Inutile d'initialiser vt->lines et vt->columns. */ reset(vt, line, col); vt->do_kb_reset = NULL; /* a affecter par l'appelant */ vt->driver = NULL; /* a affecter par l'appelant */ vt->do_beep = NULL; /* a affecter par l'appelant */ } /* tty_queue est la file des caracteres a ecrire. * tty est le terminal sur lequel ecrire les reponses aux requetes. */ void vt_write(struct vt_struct *vt, struct tty_queue_struct *tty_queue, struct tty_struct *tty) { char c; unsigned char b; int i; /* recharge les dimensions de l'ecran */ vt->lines = vt->driver->get_lines(); vt->columns = vt->driver->get_columns(); vt->saved_line = vt->line = min(vt->line, vt->lines - 1); vt->saved_col = vt->col = min(vt->col, vt->columns - 1); vt->need_wrap &= vt->col == vt->columns - 1; while (tty_queue_pop(tty_queue, &c)) { /* Les caracteres de controle peuvent etre utilises a l'interieur des sequences d'echappement. */ switch (c) { case '\a': /* BEL */ bel(vt); continue; case '\b': /* BS */ bs(vt); continue; case '\t': /* HT */ ht(vt); continue; case '\n': /* LF */ case '\v': /* VT */ case '\f': /* FF */ lf(vt); continue; case '\r': /* CR */ cr(vt); continue; case 030: /* CAN */ case 032: /* SUB */ vt->state = 0; continue; case '\e': /* ESC */ vt->state = 1; continue; case 0177: /* DEL */ del(vt); continue; case '\x80' | '\e': vt->state = 2; continue; } switch (vt->state) { case 0: if ((b = vt->translation_table[(unsigned char)c])) { /* caracteres imprimables */ if (vt->need_wrap) { cr(vt); lf(vt); } putc(vt, b); } continue; case 1: /* sequence d'echappement */ switch (c) { case '[': vt->state = 2; continue; case '(': case ')': case '#': vt->state = 4; continue; } vt->state = 0; switch (c) { case '7': /* Save Cursor & Attrs */ save_cursor(vt); continue; case '8': /* Restore Cursor & Attrs */ restore_cursor(vt); continue; case 'c': /* Reset device */ reset_device(vt); continue; default: continue; } case 2: /* sequence de controle */ if (c == '[') { vt->state = 4; continue; } vt->state = 3; memset(vt->param, 0, 16 * sizeof (int)); vt->npar = 0; if ((vt->ques = c == '?')) continue; case 3: switch (c) { case '0' ... '9': vt->param[vt->npar] = vt->param[vt->npar] * 10 + (c - '0'); continue; case ';': if (vt->npar < 15) { vt->npar++; continue; } break; } vt->state = 0; switch (c) { case 'A': /* Cursor Up */ if (!vt->param[0]) vt->param[0] = 1; move(vt, vt->line - vt->param[0], vt->col); continue; case 'B': /* Cursor Down */ if (!vt->param[0]) vt->param[0] = 1; move(vt, vt->line + vt->param[0], vt->col); continue; case 'C': /* Cursor Forward */ if (!vt->param[0]) vt->param[0] = 1; move(vt, vt->line, vt->col + vt->param[0]); continue; case 'D': /* Cursor Backward */ if (!vt->param[0]) vt->param[0] = 1; move(vt, vt->line, vt->col - vt->param[0]); continue; case 'E': /* Cursor Next Line */ if (!vt->param[0]) vt->param[0] = 1; move(vt, vt->line + vt->param[0], 0); continue; case 'F': /* Cursor Preceding Line */ if (!vt->param[0]) vt->param[0] = 1; move(vt, vt->line - vt->param[0], 0); continue; case 'G': /* Cursor Horizontal Absolute */ if (vt->param[0]) vt->param[0]--; move(vt, vt->line, vt->param[0]); continue; case 'H': /* Cursor Home */ case 'f': /* Force Cursor Position */ if (vt->param[0]) vt->param[0]--; if (vt->param[1]) vt->param[1]--; move(vt, vt->param[0], vt->param[1]); continue; case 'J': switch (vt->param[0]) { case 0: /* Erase Down */ erase(vt, vt->line * vt->columns + vt->col, vt->lines * vt->columns); continue; case 1: /* Erase Up */ erase(vt, 0, vt->line * vt->columns + vt->col + 1); continue; case 2: /* Erase Screen */ erase(vt, 0, vt->lines * vt->columns); continue; default: continue; } case 'K': switch (vt->param[0]) { case 0: /* Erase End of Line */ erase(vt, vt->line * vt->columns + vt->col, (vt->line + 1) * vt->columns); continue; case 1: /* Erase Start of Line */ erase(vt, vt->line * vt->columns, vt->line * vt->columns + vt->col + 1); continue; case 2: /* Erase Line */ erase(vt, vt->line * vt->columns, (vt->line + 1) * vt->columns); continue; default: continue; } case 'c': /* Query Device Code */ report_device_code(vt, tty); continue; case 'h': for (i = 0; i <= vt->npar; i++) if (vt->ques) switch (vt->param[i]) { case 7: /* Enable Line Wrap */ vt->auto_wrap = 1; continue; } continue; case 'l': for (i = 0; i <= vt->npar; i++) if (vt->ques) switch (vt->param[i]) { case 7: /* Disable Line Wrap */ vt->auto_wrap = 0; continue; } continue; case 'm': /* Set Attribute Mode */ for (i = 0; i <= vt->npar; i++) switch (vt->param[i]) { case 0: /* Reset all attributes */ reset_attr_mode(&vt->attr_mode); continue; case 1: /* Bright */ vt->attr_mode.intensity = 2; continue; case 2: /* Dim */ vt->attr_mode.intensity = 0; continue; case 4: /* Underscore */ vt->attr_mode.underscore = 1; continue; case 5: /* Blink */ vt->attr_mode.blink = 1; continue; case 7: /* Reverse */ vt->attr_mode.reverse = 1; continue; case 8: /* Hidden */ vt->attr_mode.concealed = 1; continue; case 21: /* Bright off */ case 22: /* Dim Off */ vt->attr_mode.intensity = 1; continue; case 24: /* Underscore Off */ vt->attr_mode.underscore = 0; continue; case 25: /* Blink off */ vt->attr_mode.blink = 0; continue; case 27: /* Reverse off */ vt->attr_mode.reverse = 0; continue; case 28: /* Hidden off */ vt->attr_mode.concealed = 0; continue; case 30 ... 37: /* Foreground Color */ vt->attr_mode.fgcolor = color(vt->param[i] - 30); continue; case 39: /* Reset Foreground Color */ vt->attr_mode.fgcolor = DEF_FGCOLOR; continue; case 40 ... 47: /* Background Color */ vt->attr_mode.bgcolor = color(vt->param[i] - 40); continue; case 49: /* Reset Background Color */ vt->attr_mode.bgcolor = DEF_BGCOLOR; continue; } vt->driver->set_attr(&vt->attr_mode); continue; case 'n': switch (vt->param[0]) { case 5: /* Query Device Status */ report_device_status(vt, tty); continue; case 6: /* Query Cursor Position */ report_cursor_position(vt, tty); continue; default: continue; } case 's': /* Save Cursor */ save_cursor(vt); continue; case 'u': /* Unsave Cursor */ restore_cursor(vt); continue; default: continue; } case 4: /* ignore */ vt->state = 0; continue; } } vt->driver->set_cursor_pos(vt->line * vt->columns + vt->col); }