/* 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);
}