View of xos/drivers/vt.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
/* 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 <util.h>
#include <string.h>
 
#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);
}