/* Copyright (c) 1992, 1995 John E. Davis
 * All rights reserved.
 * 
 * You may distribute under the terms of either the GNU General Public
 * License or the Perl Artistic License.
 */
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "slang.h"
#include "_slang.h"

#ifndef NO_STDLIB_H
#include <stdlib.h>
#endif

#ifndef NO_UNISTD_H
# include <unistd.h>
#endif

#ifdef __DECC
/* These get prototypes for write an sleep */
/* # include <unixio.h> */
# include <signal.h>
#endif

#include <termios.h>

#include <sys/ioctl.h>

#ifdef linux_unicode
#include <linux/kd.h>
#endif

/* Colors:  These definitions are used for the display.  However, the 
 * application only uses object handles which get mapped to this
 * internal representation.  The mapping is performed by the Color_Map
 * structure below. */

#define CHAR_MASK	0x000000FF
#define FG_MASK		0x0000FF00
#define BG_MASK		0x00FF0000
#define ATTR_MASK	0x1F000000
#define BGALL_MASK	0x0FFF0000

/* The 0x10000000 bit represents the alternate character set.  BGALL_MASK does
 * not include this attribute.
 */



#define GET_FG(color) ((color & FG_MASK) >> 8)
#define GET_BG(color) ((color & BG_MASK) >> 16)
#define MAKE_COLOR(fg, bg) (((fg) | ((bg) << 8)) << 8)

int SLtt_Screen_Cols;
int SLtt_Screen_Rows;
int SLtt_Term_Cannot_Insert;
int SLtt_Term_Cannot_Scroll;
int SLtt_Use_Ansi_Colors;
int SLtt_Blink_Mode = 1;
int SLtt_Use_Blink_For_ACS = 0;
int SLtt_Newline_Ok = 0;
int SLtt_Has_Alt_Charset = 0;
int SLtt_Unicode = 0;
int SLtt_DefaultUnicode = 0;

static int Automatic_Margins;
/* static int No_Move_In_Standout; */
static int Worthless_Highlight;

static int Linux_Console;

static int is_xterm;

/* It is crucial that JMAX_COLORS must be less than 128 since the high bit
 * is used to indicate a character from the ACS (alt char set).  The exception
 * to this rule is if SLtt_Use_Blink_For_ACS is true.  This means that of 
 * the highbit is set, we interpret that as a blink character.  This is 
 * exploited by DOSemu.
 */
#define JMAX_COLORS 256
#define JNORMAL_COLOR 0

typedef struct 
{
   SLtt_Char_Type fgbg;
   SLtt_Char_Type mono;
   char *custom_esc;
} Ansi_Color_Type;

#define RGB1(r, g, b)   ((r) | ((g) << 1) | ((b) << 2))
#define RGB(r, g, b, br, bg, bb)  ((RGB1(r, g, b) << 8) | (RGB1(br, bg, bb) << 16))

static Ansi_Color_Type Ansi_Color_Map[JMAX_COLORS] = 
{
   {RGB(1, 1, 1, 0, 0, 0), 0x00000000, NULL},
   {RGB(0, 0, 1, 0, 0, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 0, 0, 0), SLTT_REV_MASK, NULL},
   {RGB(1, 0, 0, 0, 0, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 0, 0, 1), SLTT_REV_MASK, NULL},
   {RGB(1, 0, 0, 0, 0, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 0, 1, 0, 1, 0), SLTT_REV_MASK, NULL},
   {RGB(1, 0, 0, 0, 1, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 0, 1, 1, 0, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 0, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 1, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(1, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(1, 0, 1, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 0, 0, 0, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL}
};


/* This is the string to use to use when outputting color information.
 */
#ifdef M_UNIX
/* work around for sco console bug that can't handle combined sequences */
  static char *Color_Escape_Sequence = "\033[3%dm\033[4%dm";
#else
  static char *Color_Escape_Sequence = "\033[3%d;4%dm";
#endif

char *SLtt_Graphics_Char_Pairs;	       /* ac termcap string -- def is vt100 */

   
/* 1 if terminal lacks the ability to do into insert mode or into delete
   mode. Currently controlled by S-Lang but later perhaps termcap. */

static char *UnderLine_Vid_Str;
static char *Blink_Vid_Str;
static char *Bold_Vid_Str;
static char *Ins_Mode_Str; /* = "\033[4h"; */   /* ins mode (im) */
static char *Eins_Mode_Str; /* = "\033[4l"; */  /* end ins mode (ei) */
static char *Scroll_R_Str; /* = "\033[%d;%dr"; */ /* scroll region */
static char *Cls_Str; /* = "\033[2J\033[H"; */  /* cl termcap STR  for ansi terminals */
static char *Rev_Vid_Str; /* = "\033[7m"; */    /* mr,so termcap string */
static char *Norm_Vid_Str; /* = "\033[m"; */   /* me,se termcap string */
static char *Del_Eol_Str; /* = "\033[K"; */	       /* ce */
static char *Del_Char_Str; /* = "\033[P"; */   /* dc */
static char *Del_N_Lines_Str; /* = "\033[%dM"; */  /* DL */
static char *Add_N_Lines_Str; /* = "\033[%dL"; */  /* AL */
static char *Rev_Scroll_Str;
static char *Curs_Up_Str;
static char *Curs_F_Str;    /* RI termcap string */
static char *Cursor_Visible_Str;    /* vs termcap string */
static char *Cursor_Invisible_Str;    /* vi termcap string */

static char *Start_Alt_Chars_Str;  /* as */
static char *End_Alt_Chars_Str;   /* ae */
static char *Enable_Alt_Char_Set;  /* eA */

static char *Term_Init_Str;
static char *Term_Reset_Str;

/* static int Len_Curs_F_Str = 5; */

/* cm string has %i%d since termcap numbers columns from 0 */
/* char *CURS_POS_STR = "\033[%d;%df";  ansi-- hor and vert pos */
static char *Curs_Pos_Str; /* = "\033[%i%d;%dH";*/   /* cm termcap string */


/* scrolling region */
static int Scroll_r1 = 0, Scroll_r2 = 23;
static int Cursor_r, Cursor_c;	       /* 0 based */

/* current attributes --- initialized to impossible value */
static SLtt_Char_Type Current_Fgbg = 0xFFFFFFFF;

static int Cursor_Set;		       /* 1 if cursor position known, 0
					* if not.  -1 if only row is known
					*/


#define MAX_OUTPUT_BUFFER_SIZE 4096

static unsigned char Output_Buffer[MAX_OUTPUT_BUFFER_SIZE];
static unsigned char *Output_Bufferp = Output_Buffer;

unsigned long SLtt_Num_Chars_Output;

int SLtt_flush_output (void)
{
   int nwrite = 0;
   int n = (int) (Output_Bufferp - Output_Buffer);
   
   SLtt_Num_Chars_Output += n;
   
   while (n > 0)
     {
	nwrite = write (fileno(stdout), (char *) Output_Buffer + nwrite, n);
	if (nwrite == -1)
	  {
	     nwrite = 0;
#ifdef EAGAIN
	     if (errno == EAGAIN) continue;
#endif
#ifdef EINTR
	     if (errno == EINTR) continue;
#endif
	     break;
	  }
	n -= nwrite;
     }
   Output_Bufferp = Output_Buffer;
   return n;
}


int SLtt_Baud_Rate;
static void tt_write(char *str, int n)
{
   static unsigned long last_time;
   static int total;
   unsigned long now;
   int ndiff;
   
   if ((str == NULL) || (n <= 0)) return;
   total += n;
   
   while (1)
     {
	ndiff = MAX_OUTPUT_BUFFER_SIZE - (int) (Output_Bufferp - Output_Buffer);
	if (ndiff < n)
	  {
	     MEMCPY ((char *) Output_Bufferp, (char *) str, ndiff);
	     Output_Bufferp += ndiff;
	     SLtt_flush_output ();
	     n -= ndiff;
	     str += ndiff;
	  }
	else 
	  {
	     MEMCPY ((char *) Output_Bufferp, str, n);
	     Output_Bufferp += n;
	     break;
	  }
     }
   
   if (((SLtt_Baud_Rate > 150) && (SLtt_Baud_Rate <= 9600))
       && (10 * total > SLtt_Baud_Rate))
     {
	total = 0;
	if ((now = (unsigned long) time(NULL)) - last_time <= 1)
	  {
	     SLtt_flush_output ();
	     sleep((unsigned) 1);
	  }
	last_time = now;
     }
}


void SLtt_write_string(char *str)
{
   if (str != NULL) tt_write(str, strlen(str));
}


void SLtt_putchar(char ch)
{
   SLtt_normal_video ();
   if (Cursor_Set == 1)
     {
	if (ch >= ' ') Cursor_c++;
	else if (ch == '\b') Cursor_c--;
	else if (ch == '\r') Cursor_c = 0;
	else Cursor_Set = 0;
	
	if ((Cursor_c + 1 == SLtt_Screen_Cols) 
	    && Automatic_Margins) Cursor_Set = 0;
     }
   
   if (Output_Bufferp < Output_Buffer + MAX_OUTPUT_BUFFER_SIZE)
     {
	*Output_Bufferp++ = (unsigned char) ch;
     }
   else tt_write (&ch, 1);
}

/* this is supposed to be fast--- also handles 
   termcap: %d, &i, %., %+, %r strings as well as terminfo stuff */
static int tt_sprintf(char *buf, char *fmt, int x, int y)
{
   register unsigned char *f = (unsigned char *) fmt, *b, ch;
   int offset = 0, tinfo = 0;
   int stack[10];
   int i = 0, z;
   stack[0] = y; stack[1] = x; i = 2;
   
   b = (unsigned char *) buf;
   if (fmt != NULL) while ((ch = *f++) != 0)
     {
	if (ch != '%') *b++ = ch;
	else 
	  {
	     ch = *f++;
	     if (tinfo)
	       {
		  if ((ch <= '3') && (ch >= '0'))
		    {
		       /* map it to termcap.  Since this is terminfo,
			* it must be one of:
			*   %2d, %3d, %02d, %03d
			* 
			* I am assuming that a terminal that understands
			* %2d form will also understand the %02d form.  These
			* only differ by a space padding the field.
			*/
		       
		       /* skip the 'd'-- hope it is there */
		       if (ch == '0') 
			 {
			    ch = *f;
			    f += 2;
			 }
		       else f++;
		    }
	       }
	     
	     if (ch == 'p')
	       {
		  tinfo = 1;
		  ch = *f++;
		  if (ch == '1') stack[i++] = x; else stack[i++] = y;
	       }
	     else if (ch == '\'')   /* 'x' */
	       {
		  stack[i++] = *f++;
		  f++;
	       }
	     else if ((ch == 'd') || (ch == '2') || (ch == '3'))
	       {
		  z = stack[--i];
		  z += offset;
		  if (z >= 100)
		    {
		       *b++ = z / 100 + '0';
		       z = z % 100;
		       goto ten;
		    }
		  else if (ch == 3) 
		    {
		       *b++ = '0';
		       ch = '2';
		    }
		  
		  if (z >= 10)
		    {
		       ten:
		       *b++ = z / 10 + '0';
		       z = z % 10;
		    }
		  else if (ch == 2) *b++ = '0';
		  
		  *b++ = z + '0';
	       }
	     else if (ch == 'i') 
	       {
		  offset = 1;
	       }
	     else if (ch == '+')
	       {
		  if (tinfo) 
		    {
		       z = stack[--i];
		       stack[i-1] += z;
		    }
		  else
		    {
		       ch = *f++;
		       if ((unsigned char) ch == 128) ch = 0;
		       ch = ch + (unsigned char) stack[--i];
		       if (ch == '\n') ch++;
		       *b++ = ch;
		    }
	       }
	     else if (ch == 'r')
	       {
		  stack[0] = x;
		  stack[1] = y;
	       }
	     else if ((ch == '.') || (ch == 'c'))
	       {
		  ch = (unsigned char) stack[--i];
		  if (ch == '\n') ch++;
		  *b++ = ch;
	       }
	     else *b++ = ch;
	  }
     }
   *b = 0;
   return((int) (b - (unsigned char *) buf));
}

static void tt_printf(char *fmt, int x, int y)
{
   char buf[256];
   int n;
   if (fmt == NULL) return;
   n = tt_sprintf(buf, fmt, x, y);
   tt_write(buf, n);
}


void SLtt_set_scroll_region (int r1, int r2)
{
   Scroll_r1 = r1;
   Scroll_r2 = r2;
   tt_printf (Scroll_R_Str, Scroll_r1, Scroll_r2);
   Cursor_Set = 0;
}

void SLtt_reset_scroll_region (void)
{
    SLtt_set_scroll_region(0, SLtt_Screen_Rows - 1);
}

int SLtt_set_cursor_visibility (int show)
{
   if (show) 
     {
	if (Cursor_Visible_Str != NULL) SLtt_write_string (Cursor_Visible_Str);
	else return -1;
	return 0;
     }
   if (Cursor_Invisible_Str != NULL) SLtt_write_string (Cursor_Invisible_Str);
   else return -1;
   return 0;
}

   
/* the goto_rc function moves to row relative to scrolling region */
void SLtt_goto_rc(int r, int c)
{
   char *s = NULL;
   int n;
   char buf[6];
   
   if (c < 0)
     {
	c = -c - 1;
	Cursor_Set = 0;
     }
   
   /* if (No_Move_In_Standout && Current_Fgbg) SLtt_normal_video (); */
   r += Scroll_r1;
   
   if ((Cursor_Set > 0) || ((Cursor_Set < 0) && !Automatic_Margins))
     {
	n = r - Cursor_r;
	if ((n == -1) && (Cursor_Set > 0) && (Cursor_c == c) 
	    && (Curs_Up_Str != NULL))
	  {
	     s = Curs_Up_Str;
	  }
	else if ((n >= 0) && (n <= 4))
	  {
	     if ((n == 0) && (Cursor_Set == 1) 
		 && ((c > 1) || (c == Cursor_c)))
	       {
		  if (Cursor_c == c) return;
		  if (Cursor_c == c + 1) 
		    {
		       s = buf;
		       *s++ = '\b'; *s = 0;
		       s = buf;
		    }
	       }
	     else if (c == 0)
	       {
		  s = buf;
		  if ((Cursor_Set != 1) || (Cursor_c != 0)) *s++ = '\r';
		  while (n--) *s++ = '\n';
#ifdef VMS
		  /* Need to add this after \n to start a new record.  Sheesh. */
		  *s++ = '\r';
#endif
		  *s = 0;
		  s = buf;
	       }
	     /* Will fail on VMS */
#ifndef VMS
	     else if (SLtt_Newline_Ok && (Cursor_Set == 1) && 
		      (Cursor_c >= c) && (c + 3 > Cursor_c))
	       {
		  s = buf;
		  while (n--) *s++ = '\n';
		  n = Cursor_c - c;
		  while (n--) *s++ = '\b';
		  *s = 0;
		  s = buf;
	       }
#endif
	  }
     }
   if (s != NULL) SLtt_write_string(s);
   else tt_printf(Curs_Pos_Str, r, c);
   Cursor_c = c; Cursor_r = r;
   Cursor_Set = 1;
}

void SLtt_begin_insert (void)
{
   SLtt_write_string(Ins_Mode_Str);
}

void SLtt_end_insert (void)
{
   SLtt_write_string(Eins_Mode_Str);
}

void SLtt_delete_char (void)
{
   SLtt_normal_video ();
   SLtt_write_string(Del_Char_Str);
}

void SLtt_erase_line (void)
{
   char *s;
   
   Current_Fgbg = 0xFFFFFFFF;
   SLtt_write_string("\r");
   Cursor_Set = 1; Cursor_c = 0;
   if (SLtt_Use_Ansi_Colors) s = "\033[0m";
   else s = Norm_Vid_Str;
   SLtt_write_string(s);
   SLtt_del_eol();
}

void SLtt_delete_nlines (int n)
{
   int r1, curs;
   char buf[132];
   if (!n) return;
   SLtt_normal_video ();
   if (Del_N_Lines_Str != NULL) tt_printf(Del_N_Lines_Str,n, 0);
   else
   /* get a new terminal */
     {
	r1 = Scroll_r1;
	curs = Cursor_r;
	SLtt_set_scroll_region(curs, Scroll_r2);
	SLtt_goto_rc(Scroll_r2 - Scroll_r1, 0);
	MEMSET(buf, '\n', n);
	tt_write(buf, n);
	/* while (n--) tt_putchar('\n'); */
	SLtt_set_scroll_region(r1, Scroll_r2);
	SLtt_goto_rc(curs, 0);
     }
}

void SLtt_cls (void)
{
   SLtt_normal_video();
   SLtt_reset_scroll_region ();
   SLtt_write_string(Cls_Str);
}

void SLtt_reverse_index (int n)
{
   if (!n) return;
   
   SLtt_normal_video();
   if (Add_N_Lines_Str != NULL) tt_printf(Add_N_Lines_Str,n, 0);
   else
     {
	while(n--) SLtt_write_string(Rev_Scroll_Str);
     }
}


int SLtt_Ignore_Beep = 3;
static char *Visible_Bell_Str;

void SLtt_beep (void)
{
   if (SLtt_Ignore_Beep & 0x1) SLtt_putchar('\007');
   
   if (SLtt_Ignore_Beep & 0x2)
     {
	if (Visible_Bell_Str != NULL) SLtt_write_string (Visible_Bell_Str);
#ifdef linux
	else if (Linux_Console)
	  {
	     SLtt_write_string ("\033[?5h");
	     SLtt_flush_output ();
	     usleep (50000);
	     SLtt_write_string ("\033[?5l");
	  }
#endif
     }
   SLtt_flush_output ();
}

void SLtt_del_eol (void)
{
   if (Current_Fgbg != 0xFFFFFFFF) SLtt_normal_video ();
   SLtt_write_string(Del_Eol_Str);
}

typedef struct
{
   char *name;
   SLtt_Char_Type color;
}
Color_Def_Type;

static Color_Def_Type Color_Defs[16] =
{
   {"black",		0x00000000},
   {"red",		0x00000100},
   {"green",		0x00000200},
   {"brown",		0x00000300},
   {"blue",		0x00000400},
   {"magenta",		0x00000500},
   {"cyan",		0x00000600},
   {"lightgray",	0x00000700},
   {"gray",		0x00000800},
   {"brightred",	0x00000900},
   {"brightgreen",	0x00000A00},
   {"yellow",		0x00000B00},
   {"brightblue",	0x00000C00},
   {"brightmagenta",	0x00000D00},
   {"brightcyan",	0x00000E00},
   {"white",		0x00000F00}
};

void SLtt_set_mono (int obj, char *what, SLtt_Char_Type mask)
{
   if ((obj < 0) || (obj >= JMAX_COLORS))
     {
	return;
     }   
   Ansi_Color_Map[obj].mono = mask & ATTR_MASK;
}



static unsigned char FgBg_Stats[JMAX_COLORS];

void SLtt_set_color_object (int obj, SLtt_Char_Type attr)
{
   char *cust_esc;
   
   if ((obj < 0) || (obj >= JMAX_COLORS)) return;
     
   cust_esc = Ansi_Color_Map[obj].custom_esc;
   if (cust_esc != NULL) 
     {
	SLFREE (cust_esc);
	FgBg_Stats[(Ansi_Color_Map[obj].fgbg >> 8) & 0x7F] -= 1;
	Ansi_Color_Map[obj].custom_esc = NULL;
     }
   
   Ansi_Color_Map[obj].fgbg = attr;
}

void SLtt_add_color_attribute (int obj, SLtt_Char_Type attr)
{
   if ((obj < 0) || (obj >= JMAX_COLORS)) return;
     
   Ansi_Color_Map[obj].fgbg |= (attr & ATTR_MASK);
}


void SLtt_set_color (int obj, char *what, char *fg, char *bg)
{
   int i;
   SLtt_Char_Type f = 0xFFFFFFFF, g = 0xFFFFFFFF;
   SLtt_Char_Type attr = 0;
   
   if ((obj < 0) || (obj >= JMAX_COLORS))
     {
	return;
     }
   
   i = 0; while (i < 16)
     {
	if (!strcmp(fg, Color_Defs[i].name))
	  {
	     f = Color_Defs[i].color;
	     if (f & 0x800)
	       {
		  f &= 0x700;
		  attr = SLTT_BOLD_MASK;
	       }
	     
	     break;
	  }
	i++;
     }
   i = 0; while (i < 16)
     {
	if (!strcmp(bg, Color_Defs[i].name))
	  {
	     g = Color_Defs[i].color;
	     g &= 0x700;
	     g = g << 8;
	     break;
	  }
	i++;
     }
   
   if ((f == 0xFFFFFFFF) || (g == 0xFFFFFFFF) 
       || ((f == g) && (attr == 0)))
     return;

   SLtt_set_color_object (obj, f | g | attr);
}

void SLtt_set_color_esc (int obj, char *esc)
{
   char *cust_esc;
   SLtt_Char_Type fgbg = 0;
   int i;
   
   if ((obj < 0) || (obj >= JMAX_COLORS))
     {
	return;
     }
   
   cust_esc = Ansi_Color_Map[obj].custom_esc;
   if (cust_esc != NULL) 
     {
	SLFREE (cust_esc);
	FgBg_Stats[(Ansi_Color_Map[obj].fgbg >> 8) & 0x7F] -= 1;
     }
   
   cust_esc = (char *) SLMALLOC (strlen(esc) + 1);
   if (cust_esc != NULL) strcpy (cust_esc, esc);
   
   Ansi_Color_Map[obj].custom_esc = cust_esc;
   if (cust_esc == NULL) fgbg = 0;
   else
     {
	/* The whole point of this is to generate a unique fgbg */
	for (i = 0; i < JMAX_COLORS; i++)
	  {
	     if (FgBg_Stats[i] == 0) fgbg = i;
	     
	     if (obj == i) continue;
	     if ((Ansi_Color_Map[i].custom_esc) == NULL) continue;
	     if (!strcmp (Ansi_Color_Map[i].custom_esc, cust_esc))
	       {
		  fgbg = (Ansi_Color_Map[i].fgbg >> 8) & 0x7F;
		  break;
	       }
	  }
	FgBg_Stats[fgbg] += 1;
     }
   
   fgbg |= 0x80;
   Ansi_Color_Map[obj].fgbg = (fgbg | (fgbg << 8)) << 8;
}



void SLtt_set_alt_char_set (int i)
{
   static int last_i;
   if (SLtt_Has_Alt_Charset == 0) return;
   if (i == last_i) return;
   SLtt_write_string (i ? Start_Alt_Chars_Str : End_Alt_Chars_Str );
   last_i = i;
}

static void write_attributes (SLtt_Char_Type fgbg)
{
   int bg0, fg0;
   
   if (Worthless_Highlight) return;
   if (fgbg == Current_Fgbg) return;
   
   /* Before spitting out colors, fix attributes */
   if ((fgbg & ATTR_MASK) != (Current_Fgbg & ATTR_MASK))
     {
	if (Current_Fgbg & ATTR_MASK)
	  {
             SLtt_write_string(Norm_Vid_Str);
	     /* In case normal video turns off ALL attributes: */
	     if (fgbg & SLTT_ALTC_MASK)
	       Current_Fgbg &= ~SLTT_ALTC_MASK;
	     SLtt_set_alt_char_set (0);
	  }
	
	if ((fgbg & SLTT_ALTC_MASK) 
	    != (Current_Fgbg & SLTT_ALTC_MASK))
	  {
	     SLtt_set_alt_char_set ((int) (fgbg & SLTT_ALTC_MASK));
	  }
	
	if (fgbg & SLTT_ULINE_MASK) SLtt_write_string (UnderLine_Vid_Str);
	if (fgbg & SLTT_BOLD_MASK) SLtt_bold_video ();
	if (fgbg & SLTT_REV_MASK) SLtt_write_string (Rev_Vid_Str);
	if (fgbg & SLTT_BLINK_MASK)
	  {
	     /* Someday Linux will have a blink mode that set high intensity
	      * background.  Lets be prepared.
	      */
	     if (SLtt_Blink_Mode) SLtt_write_string (Blink_Vid_Str);
	  }
     }
   
   if (SLtt_Use_Ansi_Colors)
     {
	fg0 = (int) GET_FG(fgbg);
	bg0 = (int) GET_BG(fgbg);
	tt_printf(Color_Escape_Sequence, fg0, bg0);
     }
   Current_Fgbg = fgbg;
}

static int Video_Initialized;

void SLtt_reverse_video (int color)
{
   SLtt_Char_Type fgbg;
   char *esc;
   
   if (Worthless_Highlight) return;
   if ((color < 0) || (color >= JMAX_COLORS)) return;
   
   if (Video_Initialized == 0)
     {
	if (color == JNORMAL_COLOR)
	  {
	     SLtt_write_string (Norm_Vid_Str);
	  }
	else SLtt_write_string (Rev_Vid_Str);
	Current_Fgbg = 0xFFFFFFFF;
	return;
     }
   
   if (SLtt_Use_Ansi_Colors) 
     {
	fgbg = Ansi_Color_Map[color].fgbg;
	if ((esc = Ansi_Color_Map[color].custom_esc) != NULL)
	  {
	     if (fgbg != Current_Fgbg)
	       {
		  Current_Fgbg = fgbg;
		  SLtt_write_string (esc);
		  return;
	       }
	  }
     }
   else fgbg = Ansi_Color_Map[color].mono;

   if (fgbg == Current_Fgbg) return;
   write_attributes (fgbg);
}




void SLtt_normal_video (void)
{
   SLtt_reverse_video(JNORMAL_COLOR);
}

void SLtt_narrow_width (void)
{
    SLtt_write_string("\033[?3l");
}

void SLtt_wide_width (void)
{
    SLtt_write_string("\033[?3h");
}

/* Highest bit represents the character set. */
#ifdef linux_unicode
#define COLOR_MASK 0x7F0000
#else
#define COLOR_MASK 0x7F00
#endif

#ifdef linux_unicode
#define COLOR_OF(x) (((unsigned int)(x) & COLOR_MASK) >> 16)
#else
#define COLOR_OF(x) (((unsigned int)(x) & COLOR_MASK) >> 8)
#endif
/*
#define COLOR_EQS(a, b) \
   (Ansi_Color_Map[COLOR_OF(a)].fgbg == Ansi_Color_Map[COLOR_OF(b)].fgbg)
*/

#define COLOR_EQS(a, b) \
   (SLtt_Use_Ansi_Colors \
    ? (Ansi_Color_Map[COLOR_OF(a)].fgbg == Ansi_Color_Map[COLOR_OF(b)].fgbg)\
    :  (Ansi_Color_Map[COLOR_OF(a)].mono == Ansi_Color_Map[COLOR_OF(b)].mono))


#define CHAR_EQS(a, b) (((a) == (b))\
			|| ((((a) & ~COLOR_MASK) == ((b) & ~COLOR_MASK))\
			    && COLOR_EQS((a), (b))))

#ifdef linux_unicode
/* Kernel doesn't support 32bit Unicode, so there is no reason to convert it */
static char *SLtt_Unicode_UTF (unsigned short ch, char *p)
{
   if (ch < 0x80)
     *p++ = (unsigned char) ch;
   else if (ch < 0x800)
     {
       *p++ = (unsigned char) (0xC0 + (ch >> 6));
       *p++ = (unsigned char) 0x80 + (ch & 0x3f);
     }
   else
     {
       *p++ = (unsigned char) (0xE0 + (ch >> 12));
       *p++ = (unsigned char) 0x80 + ((ch >> 6) & 0x3f);
       *p++ = (unsigned char) 0x80 + (ch & 0x3f);
     }
   return p;
}
#endif

static void send_attr_str(SLang_Char_Type *s)
{
   unsigned char out[256], *p;
#ifdef linux_unicode
   unsigned char *q;
   unsigned short ch;
#else
   unsigned char ch;
#endif
   register SLtt_Char_Type attr;
   register SLang_Char_Type sh;
   int color, last_color = -1;
   
   p = out;
   while (0 != (sh = *s++))
     {
#ifdef linux_unicode
        ch = sh & 0xFFFF;
        color = ((int) sh & 0xFF0000) >> 16;
#else
	ch = sh & 0xFF;
	color = ((int) sh & 0xFF00) >> 8;
#endif	  
	if (color != last_color)
	  {
	     if (SLtt_Use_Ansi_Colors) attr = Ansi_Color_Map[color & 0x7F].fgbg;
	     else attr = Ansi_Color_Map[color & 0x7F].mono;
	     
	     if (color & 0x80) /* alternate char set */
	       {
		  if (SLtt_Use_Blink_For_ACS)
		    {
		       if (SLtt_Blink_Mode) attr |= SLTT_BLINK_MASK;
		    }
		  else attr |= SLTT_ALTC_MASK;   
	       }
	     
	     if (attr != Current_Fgbg)
	       {
		  if ((ch != ' ') ||
		      /* it is a space so only consider it different if it
		       * has different attributes.
		       */
		      (attr & BGALL_MASK) != (Current_Fgbg & BGALL_MASK))
		    {
		       if (p != out)
			 {
			    *p = 0;
			    SLtt_write_string((char *) out);
			    Cursor_c += (int) (p - out);
			    p = out;
			 }
		       
		       if (SLtt_Use_Ansi_Colors && (NULL != Ansi_Color_Map[color & 0x7F].custom_esc))
			 {
			    SLtt_write_string (Ansi_Color_Map[color & 0x7F].custom_esc);
                           /* Just in case the custom escape sequence screwed up
                            * the alt character set state...
                            */
                           if ((attr & SLTT_ALTC_MASK) != (Current_Fgbg & SLTT_ALTC_MASK))
                             SLtt_set_alt_char_set ((int) (attr & SLTT_ALTC_MASK));
			    Current_Fgbg = attr;
			 }
		       else write_attributes (attr);
		       
		       last_color = color;
		    }
	       }
	  }
#ifdef linux_unicode
	if (SLtt_Unicode)
	  {
	    if (p > out + sizeof (out) - 6)
	      {
		*p = 0;
		SLtt_write_string((char *) out);
		Cursor_c += (int) (p - out);
		p = out;
	      }
	    q = SLtt_Unicode_UTF (ch, p);
	    Cursor_c -= (int) (q - p - 1);
	    p = q;
	  }
	else
#endif	  
	  *p++ = (unsigned char) ch;
     }
   *p = 0;
   if (p != out) SLtt_write_string((char *) out);
   Cursor_c += (int) (p - out);
}

static void forward_cursor (int n, int row)
{
   char buf[30];
   
   
   if (n <= 4) 
     {
	Cursor_c += n;
	SLtt_normal_video ();
	MEMSET (buf, ' ', n);
	tt_write (buf, n);
     }
   else if (Curs_F_Str != NULL)
     {
	Cursor_c += n;
	n = tt_sprintf(buf, Curs_F_Str, n, 0);
	tt_write(buf, n);
     }
   else SLtt_goto_rc (row, Cursor_c + n);
}

#ifdef linux_unicode
#define SPACE_CHAR (0x20 | (JNORMAL_COLOR << 16))
#else
#define SPACE_CHAR (0x20 | (JNORMAL_COLOR << 8))
#endif

void SLtt_smart_puts(SLang_Char_Type *neww, SLang_Char_Type *oldd, int len, int row)
{
   register SLang_Char_Type *p, *q, *qmax, *pmax, *buf;
   SLang_Char_Type buffer[256];
   int n_spaces;
   SLang_Char_Type *space_match, *last_buffered_match;
   
   q = oldd; p = neww;
   qmax = oldd + len;
   pmax = p + len;
   
   /* Find out where to begin --- while they match, we are ok */
   for (;;)
     {
	if (q == qmax) return;
	if (!CHAR_EQS(*q, *p)) break;
	q++; p++;
     }

   /*position the cursor */
   SLtt_goto_rc (row, (int) (p - neww));

   /* Find where the last non-blank character on old/new screen is */
   
   while (qmax > q)
     {
	qmax--;
	if (!CHAR_EQS(*qmax, SPACE_CHAR)) 
	  {
	     qmax++;
	     break;
	  }
     }
   
   while (pmax > p)
     {
	pmax--;
	if (!CHAR_EQS(*pmax, SPACE_CHAR))
	  {
	     pmax++;
	     break;
	  }
     }
   
   
   last_buffered_match = buf = buffer;		       /* buffer is empty */
   
   /* loop using overwrite then skip algorithm until done */
   while (1)
     {
	/* while they do not match and we do not hit a space, buffer them up */
	n_spaces = 0;
	while (p < pmax)
	  {
	     if (CHAR_EQS(*q,SPACE_CHAR) && CHAR_EQS(*p, SPACE_CHAR))
	       {
		  /* If *q is not a space, we would have to overwrite it.  
		   * However, if *q is a space, then while *p is also one, 
		   * we only need to skip over the blank field.
		   */
		  space_match = p;
		  p++; q++;
		  while ((p < pmax) 
			 && CHAR_EQS(*q,SPACE_CHAR) 
			 && CHAR_EQS(*p, SPACE_CHAR))
		    {
		       p++;
		       q++;
		    }
		  n_spaces = (int) (p - space_match);
		  break;
	       }
	     if (CHAR_EQS(*q, *p)) break;
	     *buf++ = *p++;
	     q++;
	  }
	*buf = 0;
	
	if (buf != buffer) send_attr_str (buffer);
	buf = buffer;
	
	if (n_spaces && (p < pmax)) 
	  {
	     forward_cursor (n_spaces, row);
	  }
	
	/* Now we overwrote what we could and cursor is placed at position 
	 * of a possible match of new and old.  If this is the case, skip 
	 * some more.
	 */
	
	while ((p < pmax) && CHAR_EQS(*p, *q))
	  {
	     *buf++ = *p++;
	     q++;
	  }
	
	last_buffered_match = buf;
	if (p >= pmax) break;
	
	/* jump to new position is it is greater than 5 otherwise
	 * let it sit in the buffer and output it later.
	 */
	if ((int) (buf - buffer) >= 5) 
	  {
	     forward_cursor (buf - buffer, row);
	     last_buffered_match = buf = buffer;
	  }
     }
   if (buf != buffer)
     {
	if (q < qmax)
	  {
	     if ((buf == last_buffered_match) 
		 && ((int) (buf - buffer) >= 5))
	       {
		  forward_cursor (buf - buffer, row);
	       }
	     else
	       {
		  *buf = 0;
		  send_attr_str (buffer);
	       }
	  }
     }
   if (q < qmax) SLtt_del_eol ();
   if (Automatic_Margins && (Cursor_c + 1 >= SLtt_Screen_Cols)) Cursor_Set = 0;
}  


/* termcap stuff */

#ifndef unix
#define unix
#endif

#ifdef unix
#define EXTERN extern

#ifndef USE_TERMCAP
static char *Tbuf;
static char *Tstr_Buf;

#define tgetstr SLtt_tigetstr
#define tgetent SLtt_tigetent
#define TGETNUM(x) SLtt_tigetnum((x), &Tbuf)
#define TGETFLAG(x) SLtt_tigetflag((x), &Tbuf)

#else

EXTERN char *tgetstr(char *, char **);
EXTERN int tgetent(char *, char *);
EXTERN int tgetnum(char *);
EXTERN int tgetflag(char *);
static char Tstr_Buf[1024];
static char Tbuf[4096];
#define TGETNUM tgetnum
#define TGETFLAG tgetflag
#endif

static char *my_tgetstr(char *what, char **p)
{
   register char *w, *w1;
   char *wsave;
   what = tgetstr(what, p);
   if (what != NULL)
     {
	/* lose pad info --- with today's technology, term is a loser if
	   it is really needed */
	while ((*what == '.') || 
	       ((*what >= '0') && (*what <= '9'))) what++;
	if (*what == '*') what++;	
	
	/* lose terminfo padding--- looks like $<...> */
        w = what;
	while (*w) if ((*w++ == '$') && (*w == '<'))
	  {
	     w1 = w - 1;
	     while (*w && (*w != '>')) w++;
	     if (*w == 0) break;
	     w++;
	     wsave = w1;
	     while ((*w1++ = *w++) != 0);
	     w = wsave;
	  }
	if (*what == 0) what = NULL; 
     }
   return(what);
}

char *SLtt_tgetstr (char *s)
{
#ifdef USE_TERMCAP
   static
#endif
     char *p = Tstr_Buf;
   return my_tgetstr (s, &p);
}

int SLtt_tgetnum (char *s)
{
   return TGETNUM (s);
}
int SLtt_tgetflag (char *s)
{
   return TGETFLAG (s);
}


static int Vt100_Like = 0;

void SLtt_get_terminfo (void)
{
   char *term, *t, ch;
   static int been_here;

#ifdef TIOCGWINSZ
   struct winsize size;

   if (ioctl (0, TIOCGWINSZ, &size) == 0){
       if (size.ws_row && size.ws_col){
	   SLtt_Screen_Cols = size.ws_col;
	   SLtt_Screen_Rows = size.ws_row;
       }
   }
#endif
   
   if (been_here)
       return;
   else
       been_here = 1;
   
   SLtt_Use_Ansi_Colors = (NULL != getenv ("COLORTERM"));

   if (NULL == (term = (char *) getenv("TERM")))
     {
	SLang_exit_error("TERM environment variable needs set.");
     }
   
   Linux_Console = (!strncmp (term, "linux", 5)
#ifdef linux
       || (!strncmp(term, "con", 3))
#endif
      );
   
   t = term;
   
   if (strcmp(t, "vt52") && (*t++ == 'v') && (*t++ == 't')
       && (ch = *t, (ch >= '1') && (ch <= '9'))) Vt100_Like = 1; 

#ifndef USE_TERMCAP
   if (NULL == (Tbuf = tgetent (term))) 
     {
	char err_buf[512];
	/* Special cases. */
	if (Vt100_Like
	    || Linux_Console
	    || !strcmp (term, "screen"))
	  {
	     int vt102 = 1;
	     if (!strcmp (term, "vt100")) vt102 = 0;

	     if (!(SLtt_Screen_Cols || SLtt_Screen_Rows)){
		 SLtt_Screen_Cols = 80;
		 SLtt_Screen_Rows = 24;
	     }
	     if (Linux_Console) SLtt_Screen_Rows++;
   	     SLtt_set_term_vtxxx (&vt102);
	     return;
	  }
	sprintf (err_buf, "Unknown terminal: %s\n\
Check the TERM environment variable.\n\
Also make sure that the terminal is defined in the terminfo database.\n\
Alternatively, set the TERMCAP environment variable to the desired\n\
termcap entry.", term);
	SLang_exit_error(err_buf);
     }
   Tstr_Buf = Tbuf;
#else				       /* USE_TERMCAP */
   if (1 != tgetent(Tbuf, term)) SLang_exit_error("Unknown terminal.");
#endif				       /* NOT USE_TERMCAP */
   
   if ((NULL == (Cls_Str = SLtt_tgetstr("cl"))) 
       || (NULL == (Curs_Pos_Str = SLtt_tgetstr("cm"))))
     {
	SLang_exit_error("Terminal not powerful enough for SLang.");
     }
   
   if ((NULL == (Ins_Mode_Str = SLtt_tgetstr("im")))
       || ( NULL == (Eins_Mode_Str = SLtt_tgetstr("ei")))
       || ( NULL == (Del_Char_Str = SLtt_tgetstr("dc"))))
     SLtt_Term_Cannot_Insert = 1;
   
   Visible_Bell_Str = SLtt_tgetstr ("vb");
   Curs_Up_Str = SLtt_tgetstr ("up");
   Rev_Scroll_Str = SLtt_tgetstr("sr");
   Del_N_Lines_Str = SLtt_tgetstr("DL");
   Add_N_Lines_Str = SLtt_tgetstr("AL");
   
   /* Actually these are used to initialize terminals that use cursor 
    * addressing.  Hard to believe.
    */
   Term_Init_Str = SLtt_tgetstr ("ti");
   Term_Reset_Str = SLtt_tgetstr ("te");
   
   /* Make up for defective termcap/terminfo databases */
   if ((Vt100_Like && (term[2] != '1'))
       || Linux_Console
       )
     {
	if (Del_N_Lines_Str == NULL) Del_N_Lines_Str = "\033[%dM";
	if (Add_N_Lines_Str == NULL) Add_N_Lines_Str = "\033[%dL";
     }
   
   Scroll_R_Str = SLtt_tgetstr("cs");
   
   if ((SLtt_Screen_Rows == 0) || (SLtt_Screen_Cols == 0))
     {
	char *envptr;
	if ((SLtt_Screen_Cols = TGETNUM("co")) <= 0) SLtt_Screen_Cols = 80;
	if ((SLtt_Screen_Rows = TGETNUM("li")) <= 0) SLtt_Screen_Rows = 24;
	envptr = getenv ("LINES");
	if (envptr != NULL)
	  {
	     if ((SLtt_Screen_Rows = atoi (envptr)) <= 0) 
	       SLtt_Screen_Rows = 24;
	  }
	
	envptr = getenv ("COLUMNS");
	if (envptr != NULL)
	  {
	     if ((SLtt_Screen_Cols = atoi (envptr)) <= 0) 
	       SLtt_Screen_Cols = 80;
	  }
     }
   
   is_xterm = (!strncmp (term, "xterm", 5) || !strcmp (term, "vs100"));
   
   if ((Scroll_R_Str == NULL) 
       || (((NULL == Del_N_Lines_Str) || (NULL == Add_N_Lines_Str))
	   && (NULL == Rev_Scroll_Str)))
     {
	if (is_xterm)
	  {
	     /* Defective termcap mode!!!! */
	     SLtt_set_term_vtxxx (&is_xterm);
	  }
	else SLtt_Term_Cannot_Scroll = 1;
     }
   
   Del_Eol_Str = SLtt_tgetstr("ce");

   Rev_Vid_Str = SLtt_tgetstr("mr");
   if (Rev_Vid_Str == NULL) Rev_Vid_Str = SLtt_tgetstr("so");
   
   Bold_Vid_Str = SLtt_tgetstr("md");
   Blink_Vid_Str = SLtt_tgetstr("mb");
   UnderLine_Vid_Str = SLtt_tgetstr("us");
   
   Start_Alt_Chars_Str = SLtt_tgetstr ("as");   /* smacs */
   End_Alt_Chars_Str = SLtt_tgetstr ("ae");   /* rmacs */
   Enable_Alt_Char_Set = SLtt_tgetstr ("eA");   /* enacs */
   SLtt_Graphics_Char_Pairs = SLtt_tgetstr ("ac");
   
   /* I have had it with defective console/linux terminfo entries provided 
    * by ncurses.  They are not reliable.  I cannot track every ncurses 
    * release and fudge my stuff to work with defective stuff.  For that
    * reason, I am hardcoding some things.
    */
   if (Linux_Console)
     {
#if 0
       char *lgcp = "l\332m\300k\277j\331u\264t\303v\301w\302q\304x\263n\053o\176s\137`\004a\260f\370g\361~\011,\020+\021.\031-\030h\261i\0250\333";

       if ((SLtt_Graphics_Char_Pairs == NULL)
           || (0 == strncmp (SLtt_Graphics_Char_Pairs, lgcp, 8)))
         {
            Start_Alt_Chars_Str = "\033(B\033)U\016";
            End_Alt_Chars_Str = "\033(B\033)0\017";
            Enable_Alt_Char_Set = NULL;
         }
#else
       char *lgcp = "`\004a\261f\370g\361h\260j\331k\277l\332m\300n\305o\302q\304r\362s_t\303u\264v\301w\302x\263y\371z\372{\373|\374}\375~";

       if ((SLtt_Graphics_Char_Pairs == NULL)
           || (0 == strncmp (SLtt_Graphics_Char_Pairs, lgcp, 16)))
         {
            SLtt_Graphics_Char_Pairs = lgcp;
            Start_Alt_Chars_Str = "\033[11m";
            End_Alt_Chars_Str = "\033[10m";
            Enable_Alt_Char_Set = NULL;
         }
#endif
     }
   else if (NULL == SLtt_Graphics_Char_Pairs)
     {
	/* make up for defective termcap/terminfo */
	if (Vt100_Like)
	  {
	     Start_Alt_Chars_Str = "\016";
	     End_Alt_Chars_Str = "\017";
	     Enable_Alt_Char_Set = "\033)0";
	  }
     }
   
   if (is_xterm)
     {
	Start_Alt_Chars_Str = "\016";
	End_Alt_Chars_Str = "\017";
	Enable_Alt_Char_Set = "\033(B\033)0";
     }
   
   if ((SLtt_Graphics_Char_Pairs == NULL) && 
       ((Start_Alt_Chars_Str == NULL) || (End_Alt_Chars_Str == NULL)))
     {
	SLtt_Has_Alt_Charset = 0;
	Enable_Alt_Char_Set = NULL;
     }
   else SLtt_Has_Alt_Charset = 1;
   
       
   
   if (NULL == (Norm_Vid_Str = SLtt_tgetstr("me"))) 
     {
	Norm_Vid_Str = SLtt_tgetstr("se");
     }
   
   Cursor_Invisible_Str = SLtt_tgetstr("vi");
   Cursor_Visible_Str = SLtt_tgetstr("vs");
   
   Curs_F_Str = SLtt_tgetstr("RI");
   
#if 0
   if (NULL != Curs_F_Str)
     {
	 Len_Curs_F_Str = strlen(Curs_F_Str);
     }
   else Len_Curs_F_Str = strlen(Curs_Pos_Str);
#endif
   
   Automatic_Margins = TGETFLAG ("am");
   /* No_Move_In_Standout = !TGETFLAG ("ms"); */
   Worthless_Highlight = TGETFLAG ("xs");
   
   if (Worthless_Highlight == 0)
     {
	Worthless_Highlight = (TGETNUM ("sg") > 0);
     }
}

#endif
/* Unix */

/* specific to vtxxx only */
void SLtt_enable_cursor_keys (void)
{
#ifdef unix
   if (Vt100_Like ||
#ifdef linux
       Linux_Console ||
#endif
       is_xterm)
#endif
   SLtt_write_string("\033=\033[?1l");
}

#ifdef VMS
void SLtt_get_terminfo ()
{
   int zero = 0;
   SLtt_set_term_vtxxx(&zero);
   Start_Alt_Chars_Str = "\016";
   End_Alt_Chars_Str = "\017";
   SLtt_Has_Alt_Charset = 1;
   SLtt_Graphics_Char_Pairs = "aaffgghhjjkkllmmnnooqqssttuuvvwwxx";
   Enable_Alt_Char_Set = "\033(B\033)0";    
}
#endif

/* This sets term for vt102 terminals it parameter vt100 is 0.  If vt100
 * is non-zero, set terminal appropriate for a only vt100  
 * (no add line capability). */
							   
void SLtt_set_term_vtxxx(int *vt100)
{
   Norm_Vid_Str = "\033[m";
   
   Scroll_R_Str = "\033[%i%d;%dr"; 
   Cls_Str = "\033[2J\033[H";
   Rev_Vid_Str = "\033[7m";
   Bold_Vid_Str = "\033[1m";
   Blink_Vid_Str = "\033[5m";
   UnderLine_Vid_Str = "\033[4m";
   Del_Eol_Str = "\033[K";
   Rev_Scroll_Str = "\033M";
   Curs_F_Str = "\033[%dC";
   /* Len_Curs_F_Str = 5; */
   Curs_Pos_Str = "\033[%i%d;%dH";
   if (*vt100 == 0)
     {
	Ins_Mode_Str = "\033[4h";
	Eins_Mode_Str = "\033[4l";
	Del_Char_Str =  "\033[P";
	Del_N_Lines_Str = "\033[%dM";
	Add_N_Lines_Str = "\033[%dL";
	SLtt_Term_Cannot_Insert = 0;
     }
   else
     {
	Del_N_Lines_Str = NULL;
	Add_N_Lines_Str = NULL;
	SLtt_Term_Cannot_Insert = 1;
     }
   SLtt_Term_Cannot_Scroll = 0;
   /* No_Move_In_Standout = 0; */
}

#ifdef linux_unicode
static unsigned short unifix[] = {
SLUNI_DHLINE_CHAR, SLUNI_HLINE_CHAR,
SLUNI_DVLINE_CHAR, SLUNI_VLINE_CHAR,
SLUNI_DULCORN_CHAR, SLUNI_ULCORN_CHAR,
SLUNI_DURCORN_CHAR, SLUNI_URCORN_CHAR,
SLUNI_DLLCORN_CHAR, SLUNI_LLCORN_CHAR,
SLUNI_DLRCORN_CHAR, SLUNI_LRCORN_CHAR,
SLUNI_DRTEE_CHAR, SLUNI_RTEE_CHAR,
SLUNI_DLTEE_CHAR, SLUNI_LTEE_CHAR,
SLUNI_DUTEE_CHAR, SLUNI_UTEE_CHAR,
SLUNI_DDTEE_CHAR, SLUNI_DTEE_CHAR,
SLUNI_DCROSS_CHAR, SLUNI_CROSS_CHAR,
SLUNI_HLINE_CHAR, '|',
SLUNI_VLINE_CHAR, '-',
SLUNI_ULCORN_CHAR, '+',
SLUNI_URCORN_CHAR, '+',
SLUNI_LLCORN_CHAR, '+',
SLUNI_LRCORN_CHAR, '+',
SLUNI_RTEE_CHAR, '+',
SLUNI_LTEE_CHAR, '+',
SLUNI_UTEE_CHAR, '+',
SLUNI_DTEE_CHAR, '+',
SLUNI_CROSS_CHAR, '+',
SLUNI_DSRTEE_CHAR, SLUNI_RTEE_CHAR,
SLUNI_DSLTEE_CHAR, SLUNI_LTEE_CHAR,
SLUNI_DSUTEE_CHAR, SLUNI_UTEE_CHAR,
SLUNI_DSDTEE_CHAR, SLUNI_DTEE_CHAR,
SLUNI_SDRTEE_CHAR, SLUNI_RTEE_CHAR,
SLUNI_SDLTEE_CHAR, SLUNI_LTEE_CHAR,
SLUNI_SDUTEE_CHAR, SLUNI_UTEE_CHAR,
SLUNI_SDDTEE_CHAR, SLUNI_DTEE_CHAR,
SLUNI_CKBRD_CHAR, 'o'
};

unsigned short SLtt_fix_unicode (unsigned short ch)
{
   int i;
   
   for (i = 0; i < sizeof (unifix) / sizeof (unifix [0]); i += 2)
      if (ch == unifix [i])
          return unifix [i + 1];
   return ch;
}

int SLtt_unicode_init (void)
{
   struct unipair *list = NULL;
   struct unimapdesc descr;
   int i, j;
   
   descr.entry_ct = 0;
   descr.entries = 0;
   if (ioctl(0, GIO_UNIMAP, (unsigned long) &descr))
      {
          if (errno != ENOMEM || descr.entry_ct == 0)
            {
               SLtt_Unicode = 0;
               return 0;
            }
          descr.entries = list = (struct unipair *) SLMALLOC(descr.entry_ct * 
               sizeof (struct unipair));
          if (ioctl (0, GIO_UNIMAP, (unsigned long) &descr))
            {
               SLFREE(list);
               SLtt_Unicode = 0;
               return 0;
            }
      }
   for (i = 0; i < descr.entry_ct; i++)
      for (j = 0; j < sizeof (unifix) / sizeof (unifix [0]); j += 2)
         if (list [i].unicode == unifix [j])
            {
               unifix [j + 1] = unifix [j];
               break;
            }
   for (i = 0; i < sizeof (unifix) / sizeof (unifix [0]); i += 2)
     {
        if (unifix [i] == unifix [i + 1] || unifix [i + 1] < 127)
           continue;
        for (j = 0; j < sizeof (unifix) / sizeof (unifix [0]); j += 2)
           if (unifix [j] == unifix [i + 1])
              {
                 unifix [i + 1] = unifix [j + 1];
                 break;
              }
     }
   if (list)
      SLFREE(list);
   return 1;
}
#else /*linux unicode */
unsigned short SLtt_fix_unicode (unsigned short ch)
{
   return ch;
}
#endif

void SLtt_init_video (void)
{
   static int last_i;
   /*   send_string_to_term("\033[?6h"); */
   /* relative origin mode */
   last_i = 0;
   SLtt_write_string (Term_Init_Str);
   SLtt_reset_scroll_region();
   SLtt_end_insert();
   SLtt_write_string (Enable_Alt_Char_Set);
#ifdef linux_unicode
   if (!SLtt_Unicode)
     {
        char *term = (char *) getenv ("TERM");
   
        if (term && (!strncmp(term, "con", 3) || !strncmp (term, "linux", 5)))
          {
             term = (char *) getenv ("LC_CTYPE");
             if (!term) term = (char *) getenv ("LANG");
             if (term && strchr (term, '8') && 
                 (strstr (term, "UTF") || strstr (term, "utf")))
               {
                  SLtt_Unicode = 1;
                  SLtt_DefaultUnicode = 1;
               }
          }
     }
   if (SLtt_Unicode)
     {
        if (SLtt_unicode_init ())
            SLtt_write_string ("\033%8");
     }
#endif   
   Video_Initialized = 1;
}

void SLtt_want_unicode (void)
{
#ifdef linux_unicode
   char *term = (char *) getenv ("TERM");
   
   if (term)
      if (!strncmp(term, "con", 3) || !strncmp (term, "linux", 5))
         SLtt_Unicode = 1;
#endif
}

void SLtt_reset_video (void)
{
   SLtt_goto_rc (SLtt_Screen_Rows - 1, 0);
   Cursor_Set = 0;
   SLtt_normal_video ();	       /* MSKermit requires this  */
   SLtt_write_string(Norm_Vid_Str);
   Current_Fgbg = 0xFFFFFFFF;
   SLtt_set_alt_char_set (0);
   SLtt_write_string ("\n");
   SLtt_del_eol ();
#ifdef linux_unicode
   if (SLtt_Unicode && !SLtt_DefaultUnicode)
     {
        SLtt_write_string ("\033%@");
     }
#endif   
   SLtt_write_string (Term_Reset_Str);
   SLtt_flush_output ();
   Video_Initialized = 0;
}

void SLtt_bold_video (void)
{
   SLtt_write_string (Bold_Vid_Str);
}

#if 0
/* Midnight Commandes does not use this */
int SLtt_set_mouse_mode (int mode, int force)
{
   char *term;
   
   if (force == 0)
     {
       if (NULL == (term = (char *) getenv("TERM"))) return -1;
       if (strncmp ("xterm", term, 5))
         return -1;
     }
   
   if (mode)
     SLtt_write_string ("\033[?9h");
   else
     SLtt_write_string ("\033[?9l");
   
   return 0;
}
#endif
