/* -*- mode: C; mode: fold -*- */
/*
Copyright (C) 2004-2017,2018 John E. Davis

This file is part of the S-Lang Library.

The S-Lang Library 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 2 of the
License, or (at your option) any later version.

The S-Lang Library 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/

/* This file is best edited with a folding editor */

#include "slinclud.h"

#if !defined(__WIN32__) && !defined(__IBMC__)
# include <dos.h>
#endif

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

int SLtt_Term_Cannot_Insert = 0;
int SLtt_Term_Cannot_Scroll = 0;
int SLtt_Ignore_Beep = 3;
int SLtt_Use_Ansi_Colors = 0;
int SLtt_Has_Status_Line = 0;
int SLtt_Screen_Rows = 25;
int SLtt_Screen_Cols = 80;
int SLtt_Msdos_Cheap_Video = 0;

void (*_pSLtt_color_changed_hook)(void);

/* This definition will need changing when SLsmg_Char_Type changes. */
#define SLSMG_CHAR_TO_USHORT(x) ((unsigned short)(x))

/*{{{ ------------- static local variables ---------- */

static int Attribute_Byte;
static int Scroll_r1 = 0, Scroll_r2 = 25;
static int Cursor_Row = 1, Cursor_Col = 1;
static int Current_Color;
static int IsColor = 1;
static int Blink_Killed = 1;	/* high intensity background enabled */

/* FIXME: sldisply.c allows up to SLTT_MAX_COLORS colors */
#define JMAX_COLORS	256
#define JNORMAL_COLOR	0
#define JNO_COLOR	-1

static unsigned char Color_Map [JMAX_COLORS] =
{
   0x7, 0x70, 0x70, 0x70, 0x70, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
   0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7
};

#define JMAX_COLOR_NAMES 16
static SLCONST char *Color_Names [JMAX_COLOR_NAMES] =
{
   "black", "blue", "green", "cyan",
     "red", "magenta", "brown", "lightgray",
     "gray", "brightblue", "brightgreen", "brightcyan",
     "brightred", "brightmagenta", "yellow", "white"
};

static void fixup_colors (void);

/*}}}*/

static void goto_rc_abs (int r, int c)
{
   SLtt_goto_rc (r - Scroll_r1, c);
}

#if defined(__BORLANDC__) && defined(__MSDOS__)
# define IBMPC_ASM_VIDEO	1
#endif

#if defined(__WATCOMC__) && !defined(__NT__) && !defined(__os2__)
# define WATCOM_VIDEO		1
#endif

#if defined (__GO32__)
# define GO32_VIDEO		1
#endif

#if defined (__EMX__)		/* EMX video does both DOS & OS/2 */
# define EMX_VIDEO		1
#else
# if defined(__os2__)
#  define OS2_VIDEO		1
# endif
#endif

#if defined (__WIN32__)
# define WIN32_VIDEO		1
#endif

/* The functions in these folds contain somewhat video system specific code
 * that if merged together into single functions will become a confusing
 * mess.
 */

#ifdef IBMPC_ASM_VIDEO /*{{{*/

# include <conio.h>
# include <bios.h>
# include <mem.h>

/* buffer to hold a line of character/attribute pairs */
#define MAXCOLS 256
static unsigned char Line_Buffer [MAXCOLS*2];

#define MK_SPACE_CHAR()	(((Attribute_Byte) << 8) | 0x20)

static unsigned char *Video_Base;
# define MK_SCREEN_POINTER(row,col)	((unsigned short *)\
					 (Video_Base +\
					  2 * (SLtt_Screen_Cols * (row)\
					       + (col))))
static int Video_Status_Port;

# define MONO_STATUS	0x3BA
# define CGA_STATUS	0x3DA
# define CGA_SETMODE	0x3D8

# define SNOW_CHECK \
  if (SLtt_Msdos_Cheap_Video)\
    { while ((inp (CGA_STATUS) & 0x08)); while (!(inp (CGA_STATUS) & 0x08)); }

void SLtt_write_string (SLFUTURE_CONST char *str)
{
   /* FIXME: Priority=medium
    * This should not go to stdout. */
   fputs (str, stdout);
}

/* row is with respect to the scrolling region. */
void SLtt_goto_rc (int r, int c)
{
   union REGS regs;

   r += Scroll_r1;

   if (r > SLtt_Screen_Rows - 1) r = SLtt_Screen_Rows - 1;
   if (c > SLtt_Screen_Cols - 1) c = SLtt_Screen_Cols - 1;

   Cursor_Row = r;
   Cursor_Col = c;

   regs.h.dh = r;
   regs.h.dl = c;
   regs.h.bh = 0;
   regs.h.ah = 2;
   int86 (0x10, &regs, &regs);
}

static void asm_video_getxy (void)
{
   asm  mov ah, 3
     asm  mov bh, 0
     asm  int 10h
     asm  xor ax, ax
     asm  mov al, dh
     asm  mov Cursor_Row, ax
     asm  xor ax, ax
     asm  mov al, dl
     asm  mov Cursor_Col, ax
}

void SLtt_begin_insert (void)
{
   unsigned short *p;
   int n;

   asm_video_getxy ();
   n = SLtt_Screen_Cols - Cursor_Col;
   p = MK_SCREEN_POINTER (Cursor_Row, SLtt_Screen_Cols - 1);

   SNOW_CHECK;
   asm  mov ax, ds
     asm  mov bx, di
     asm  mov dx, si

     asm  mov cx, n
     asm  les di, p
     asm  lds si, p
     asm  sub si, 2
     asm  std
     asm  rep movsw

     asm  mov ds, ax
     asm  mov di, bx
     asm  mov si, dx
}

void SLtt_end_insert (void)
{
}

void SLtt_delete_char (void)
{
   unsigned short *p;
   int n;

   asm_video_getxy ();
   n = SLtt_Screen_Cols - Cursor_Col - 1;
   p = MK_SCREEN_POINTER (Cursor_Row, Cursor_Col);

   SNOW_CHECK;
   asm  mov ax, ds
     asm  mov bx, si
     asm  mov dx, di

     asm  mov cx, n
     asm  les di, p
     asm  lds si, p
     asm  add si, 2
     asm  cld
     asm  rep movsw

     asm  mov ds, ax
     asm  mov si, bx
     asm  mov di, dx
}

void SLtt_erase_line (void)
{
   unsigned short w, *p;

   p = MK_SCREEN_POINTER (Cursor_Row, 0);
   Attribute_Byte = 0x07;

   w = MK_SPACE_CHAR ();

   SNOW_CHECK;
   asm  mov dx, di
     asm  mov ax, w
     asm  mov cx, SLtt_Screen_Cols
     asm  les di, p
     asm  cld
     asm  rep stosw
     asm  mov di, dx

     Current_Color = JNO_COLOR;		/* since we messed with attribute byte */
}

void SLtt_delete_nlines (int nlines)
{
   SLtt_normal_video ();

   /* This has the effect of pulling all lines below it up */
   asm  mov ax, nlines
     asm  mov ah, 6		/* int 6h */
     asm  xor cx, cx
     asm  mov ch, byte ptr Scroll_r1
     asm  mov dx, SLtt_Screen_Cols
     asm  dec dx
     asm  mov dh, byte ptr Scroll_r2
     asm  mov bh, byte ptr Attribute_Byte
     asm  int 10h
}

void SLtt_reverse_index (int nlines)
{
   SLtt_normal_video ();
   asm  xor cx, cx
     asm  mov ch, byte ptr Scroll_r1
     asm  mov dx, SLtt_Screen_Cols
     asm  dec dx
     asm  mov dh, byte ptr Scroll_r2
     asm  mov bh, byte ptr Attribute_Byte
     asm  mov ah, 7
     asm  mov al, byte ptr nlines
     asm  int 10h
}

static void asm_video_invert_region (int top_row, int bot_row)
{
   register unsigned short ch, sh;
   register unsigned short *pmin = MK_SCREEN_POINTER (top_row, 0);
   register unsigned short *pmax = MK_SCREEN_POINTER (bot_row, 0);

   while (pmin < pmax)
     {
	sh = *pmin;
	ch = sh;
	ch = ch ^ 0xFF00;
	*pmin = (ch & 0xFF00) | (sh & 0x00FF);
	pmin++;
     }
}

void SLtt_del_eol (void)
{
   unsigned short *p;
   unsigned short w;
   int n;

   n = SLtt_Screen_Cols - Cursor_Col;
   p = MK_SCREEN_POINTER (Cursor_Row, Cursor_Col);
   if (Current_Color != JNO_COLOR) SLtt_normal_video ();
   w = MK_SPACE_CHAR ();

   SNOW_CHECK;
   asm  mov dx, di
     asm  les di, p
     asm  mov ax, w
     asm  mov cx, n
     asm  cld
     asm  rep stosw

     asm  mov di, dx
}

static unsigned short *asm_video_write (register unsigned char *pp,
					register unsigned char *p,
					register unsigned short *pos)
{
   int n = (int) (p - pp);	/* num of characters of PP to write */

   asm  push si
     asm  push ds
     asm  push di

   /* set up register for BOTH fast and slow */
     asm  mov bx, SLtt_Msdos_Cheap_Video

   /* These are the registers needed for both fast AND slow */
     asm  mov ah, byte ptr Attribute_Byte
     asm  mov cx, n
     asm  lds si, dword ptr pp
     asm  les di, dword ptr pos
     asm  cld

     asm  cmp bx, 0		       /* cheap video test */
     asm  je L_fast
     asm  mov bx, ax
     asm  mov dx, CGA_STATUS
     asm  jg L_slow_blank

   /* slow video */
     asm  cli

   /* wait for retrace */
     L_slow:
   asm  in al, dx
     asm  test al, 1
     asm  jnz L_slow

     L_slow1:
   asm  in al, dx
     asm  test al, 1
     asm  jz L_slow1

   /* move a character out */
     asm  mov ah, bh
     asm  lodsb
     asm  stosw
     asm  loop L_slow

     asm  sti
     asm  jmp done

/* -------------- slow video, vertical retace and pump --------------*/
     L_slow_blank:
   L_slow_blank_loop:
   asm  in al, dx
     asm  test al, 8
     asm  jnz L_slow_blank_loop

     L_slow_blank1:
   asm  in al, dx
     asm  test al, 8
     asm  jz L_slow_blank1
   /* write line */
     asm  mov ah, bh
     L_slow_blank2:
   asm  lodsb
     asm  stosw
     asm  loop L_slow_blank2

     asm jmp done
/*-------------- Fast video --------------*/

     L_fast:
   asm  lodsb
     asm  stosw
     asm  loop L_fast
     done:
   asm  pop di
     asm  pop ds
     asm  pop si
     return (pos + n);
}

static void write_attributes (SLsmg_Char_Type *src, unsigned int count)
{
   register unsigned char *p;
   register unsigned short pair;
   unsigned char ch, color;
   register unsigned short *pos;

   p = Line_Buffer;
   pos = MK_SCREEN_POINTER (Cursor_Row, 0);

   while (count--)
     {
	pair = SLSMG_CHAR_TO_USHORT(*src);	/* character/color pair */
	src++;
	ch = pair & 0xff;	/* character value */
	color = pair >> 8;	/* color value */
	if (color != Current_Color)	/* need a new color */
	  {
	     if (p != Line_Buffer)
	       {
		  pos = asm_video_write (Line_Buffer, p, pos);
		  p = Line_Buffer;
	       }
	     SLtt_reverse_video (color);	/* change color */
	  }
	*(p++) = ch;
     }
   pos = asm_video_write (Line_Buffer, p, pos);
}

void SLtt_cls (void)
{
   SLtt_normal_video ();

   asm  mov dx, SLtt_Screen_Cols
     asm  dec dx
     asm  mov ax, SLtt_Screen_Rows
     asm  dec ax
     asm  mov dh, al
     asm  xor cx, cx
     asm  xor ax, ax
     asm  mov ah, 7
     asm  mov bh, byte ptr Attribute_Byte
     asm  int 10h
}

void SLtt_putchar (char ch)
{
   unsigned short p, *pp;

   if (Current_Color) SLtt_normal_video ();
   asm_video_getxy ();		/* get current position */
   switch (ch)
     {
      case 7:			/* ^G - break */
	SLtt_beep (); break;
      case 8:			/* ^H - backspace */
	goto_rc_abs (Cursor_Row, Cursor_Col - 1); break;
      case 13:			/* ^M - carriage return */
	goto_rc_abs (Cursor_Row, 0); break;
      default:
	/* write character to screen */
	pp = MK_SCREEN_POINTER (Cursor_Row, Cursor_Col);
	p = (Attribute_Byte << 8) | (unsigned char) ch;
	SNOW_CHECK;
	*pp = p;
	goto_rc_abs (Cursor_Row, Cursor_Col + 1);
     }
}

void SLtt_get_screen_size (void)
{
   int w, h;

   h = 0;

   /* Get BIOS's screenwidth, this works on ALL displays. */
   w = *((int *)MK_FP(0x40, 0x4a));

   /* Use Ralf Brown test for EGA or greater */
   asm mov ah, 12h
   asm mov bl, 10h
   asm mov bh, 0xFF  /* EGA or greater will change this */
   asm int 10h
   asm cmp bh, 0xFF
   asm je L1
     /* if EGA or compatible: Get BIOS's number of rows. */
     h = *(char *)MK_FP(0x40, 0x84) + 1;
   /* scan_lines = *(int *) 0x485; */

   L1:
      if (h <= 0) h = 25;

   SLtt_Screen_Rows = h;
   SLtt_Screen_Cols = w;
}

void SLtt_get_terminfo (void)
{
   SLtt_get_screen_size ();
}

/*----------------------------------------------------------------------*\
 * Function:	int SLtt_init_video (void);
\*----------------------------------------------------------------------*/
int SLtt_init_video (void)
{
   unsigned char *p;

#ifdef HAS_SAVE_SCREEN
   save_screen ();
#endif

   Cursor_Row = Cursor_Col = 0;
   p = (unsigned char far *) 0x00400049L;
   if (*p == 7)
     {
	Video_Status_Port = MONO_STATUS;
	Video_Base = (unsigned char *) MK_FP (0xb000,0000);
	IsColor = 0;
     }
   else
     {
	Video_Status_Port = CGA_STATUS;
	Video_Base = (unsigned char *) MK_FP (0xb800,0000);
	IsColor = 1;
     }

   /* test for video adapter type.  Of primary interest is whether there is
    * snow or not.  Assume snow if the card is color and not EGA or greater.
    */

   /* Use Ralf Brown test for EGA or greater */
   asm  mov ah, 0x12
     asm  mov bl, 0x10
     asm  mov bh, 0xFF
     asm  int 10h
     asm  cmp bh, 0xFF
     asm  je L1

   /* (V)EGA */
     asm  xor bx, bx
     asm  mov SLtt_Msdos_Cheap_Video, bx
     asm  mov ax, Attribute_Byte
     asm  cmp ax, bx
     asm  jne L2
     asm  mov ax, 0x17
     asm  mov Attribute_Byte, ax
     asm  jmp L2

     L1:
   /* Not (V)EGA */
   asm  mov ah, 0x0F
     asm  int 10h
     asm  cmp al, 7
     asm  je L3
     asm  mov ax, 1
     asm  mov SLtt_Msdos_Cheap_Video, ax
     L3:
   asm  mov ax, Attribute_Byte
     asm  cmp ax, 0
     asm  jne L2
     asm  mov ax, 0x07
     asm  mov Attribute_Byte, ax
     L2:
   /* toggle the blink bit so we can use hi intensity background */
   if (IsColor && !SLtt_Msdos_Cheap_Video)
     {
	asm  mov ax, 0x1003
	  asm  mov bx, 0
	  asm  int 0x10
	  Blink_Killed = 1;
     }

   SLtt_Use_Ansi_Colors = IsColor;
   SLtt_get_screen_size ();
   SLtt_reset_scroll_region ();
   fixup_colors ();
   return 0;
}

void SLtt_beep (void)
{
   int audible;			/* audible bell */
   int special = 0;		/* first row to invert */
   int visual = 0;		/* final row to invert */

   if (!SLtt_Ignore_Beep) return;

   audible = (SLtt_Ignore_Beep & 1);
   if ( (SLtt_Ignore_Beep & 4) )
     {
	special = SLtt_Screen_Rows - 1;
	visual = special--;	/* only invert bottom status line */
     }
   else if ( (SLtt_Ignore_Beep & 2) )
     {
	visual = SLtt_Screen_Rows;
     }

   if (visual) asm_video_invert_region (special, visual);
   if (audible) sound (1500); delay (100); if (audible) nosound ();
   if (visual) asm_video_invert_region (special, visual);
}

#endif				       /* IBMPC_ASM_VIDEO */

/*}}}*/

#ifdef GO32_VIDEO /*{{{*/

# include <pc.h>
# define HAS_SAVE_SCREEN	1

# ifdef HAS_SAVE_SCREEN
static void *Saved_Screen_Buffer;
static int Saved_Cursor_Row;

static void save_screen (void)
{
   int row, col;

   SLfree ((char *) Saved_Screen_Buffer);
   Saved_Screen_Buffer = NULL;

   Saved_Screen_Buffer = (short *) SLmalloc (sizeof (short) *
				   ScreenCols () * ScreenRows ());

   if (Saved_Screen_Buffer == NULL)
     return;

   ScreenRetrieve (Saved_Screen_Buffer);
   ScreenGetCursor (&row, &col);
   Saved_Cursor_Row = row;
}

static void restore_screen (void)
{
   if (Saved_Screen_Buffer == NULL) return;
   ScreenUpdate (Saved_Screen_Buffer);
   goto_rc_abs (Saved_Cursor_Row, 0);
}
#endif				       /* HAS_SAVE_SCREEN */

void SLtt_write_string (SLFUTURE_CONST char *str)
{
   while (Cursor_Col < SLtt_Screen_Cols)
     {
	char ch = *str++;

	if (ch == 0)
	  break;

	ScreenPutChar (ch, Attribute_Byte, Cursor_Col, Cursor_Row);
	Cursor_Col++;
     }
   goto_rc_abs (Cursor_Row, Cursor_Col);
}

void SLtt_goto_rc (int row, int col)
{
   row += Scroll_r1;
   if (row > SLtt_Screen_Rows) row = SLtt_Screen_Rows;
   if (col > SLtt_Screen_Cols) col = SLtt_Screen_Cols;

   ScreenSetCursor (row, col);

   Cursor_Row = row;
   Cursor_Col = col;
}

static void go32_video_getxy (void)
{
   ScreenGetCursor (&Cursor_Row, &Cursor_Col);
}

static void go32_video_deleol (int x)
{
   while (x < SLtt_Screen_Cols)
     ScreenPutChar (32, Attribute_Byte, x++, Cursor_Row);
}

void SLtt_begin_insert (void)
{
}

void SLtt_end_insert (void)
{
}

void SLtt_delete_char (void)
{
}

void SLtt_erase_line (void)
{
   Attribute_Byte = 0x07;
   go32_video_deleol (0);
   Current_Color = JNO_COLOR;		/* since we messed with attribute byte */
}

void SLtt_delete_nlines (int nlines)
{
   union REGS r;

   SLtt_normal_video ();

   r.x.ax = nlines;
   r.x.cx = 0;
   r.h.ah = 6;
   r.h.ch = Scroll_r1;
   r.h.dl = SLtt_Screen_Cols - 1;
   r.h.dh = Scroll_r2;
   r.h.bh = Attribute_Byte;
   int86 (0x10, &r, &r);
}

void SLtt_reverse_index (int nlines)
{
   union REGS r;

   SLtt_normal_video ();

   r.h.al = nlines;
   r.x.cx = 0;
   r.h.ah = 7;
   r.h.ch = Scroll_r1;
   r.h.dl = SLtt_Screen_Cols - 1;
   r.h.dh = Scroll_r2;
   r.h.bh = Attribute_Byte;
   int86 (0x10, &r, &r);
}

static void go32_video_invert_region (int top_row, int bot_row)
{
   unsigned char buf [2 * 180 * 80];         /* 180 cols x 80 rows */
   unsigned char *b, *bmax;

   b    = buf + 1 + 2 * SLtt_Screen_Cols * top_row;
   bmax = buf + 1 + 2 * SLtt_Screen_Cols * bot_row;

   ScreenRetrieve (buf);
   while (b < bmax)
     {
	*b ^= 0xFF;
	b += 2;
     }
   ScreenUpdate (buf);
}

void SLtt_beep (void)
{
   int audible;			/* audible bell */
   int special = 0;		/* first row to invert */
   int visual = 0;		/* final row to invert */

   if (!SLtt_Ignore_Beep) return;

   audible = (SLtt_Ignore_Beep & 1);
   if ( (SLtt_Ignore_Beep & 4) )
     {
	special = SLtt_Screen_Rows - 1;
	visual = special--;	/* only invert bottom status line */
     }
   else if ( (SLtt_Ignore_Beep & 2) )
     {
	visual = SLtt_Screen_Rows;
     }

   if (visual) go32_video_invert_region (special, visual);
   if (audible) sound (1500); delay (100); if (audible) nosound ();
   if (visual) go32_video_invert_region (special, visual);
}

void SLtt_del_eol (void)
{
   if (Current_Color != JNO_COLOR) SLtt_normal_video ();
   go32_video_deleol (Cursor_Col);
}

static void
write_attributes (SLsmg_Char_Type *src, unsigned int count)
{
   unsigned int n;

   /* write into a character/attribute pair */
   n = Cursor_Col;
   while (count)
     {
        SLtt_reverse_video (src->color); /* color change */
        ScreenPutChar (src->wchars[0] & 0xFF, Attribute_Byte, n, Cursor_Row);
	n++;
	src++;
	count--;
     }
}

/*----------------------------------------------------------------------*\
 * Function:	void SLtt_cls (void);
\*----------------------------------------------------------------------*/
void SLtt_cls (void)
{
   SLtt_normal_video ();
   SLtt_reset_scroll_region ();
   SLtt_goto_rc (0, 0);
   SLtt_delete_nlines (SLtt_Screen_Rows);
}

void SLtt_putchar (char ch)
{
   if (Current_Color) SLtt_normal_video ();

   go32_video_getxy ();		/* get current position */

   switch (ch)
     {
      case 7:			/* ^G - break */
	SLtt_beep (); break;
      case 8:			/* ^H - backspace */
	goto_rc_abs (Cursor_Row, Cursor_Col - 1); break;
      case 13:			/* ^M - carriage return */
	goto_rc_abs (Cursor_Row, 0); break;
      default:			/* write character to screen */
	ScreenPutChar ((int) ch, Attribute_Byte, Cursor_Col, Cursor_Row);
	goto_rc_abs (Cursor_Row, Cursor_Col + 1);
     }
}

void SLtt_get_screen_size (void)
{
   SLtt_Screen_Rows = ScreenRows ();
   SLtt_Screen_Cols = ScreenCols ();
}

void SLtt_get_terminfo (void)
{
   SLtt_get_screen_size ();
}

int SLtt_init_video (void)
{
#ifdef HAS_SAVE_SCREEN
   save_screen ();
#endif

   if (!Attribute_Byte) Attribute_Byte = 0x17;

   IsColor = 1;			/* is it really? */

   if (IsColor)
     {
	union REGS r;
	r.x.ax = 0x1003; r.x.bx = 0;
	int86 (0x10, &r, &r);
	Blink_Killed = 1;
     }

   Cursor_Row = Cursor_Col = 0;

   SLtt_Term_Cannot_Insert = 1;
   SLtt_reset_scroll_region ();
   SLtt_Use_Ansi_Colors = IsColor;
   fixup_colors ();
   return 0;
}

#endif				       /* GO32_VIDEO */

/*}}}*/

#ifdef EMX_VIDEO /*{{{*/

# define INCL_VIO
# define INCL_DOSPROCESS
# include <os2.h>
# include <os2emx.h>
# include <sys/video.h>

static VIOMODEINFO vioModeInfo;
/* buffer to hold a line of character/attribute pairs */
#define MAXCOLS 256
static unsigned char Line_Buffer [MAXCOLS*2];

void SLtt_write_string (SLFUTURE_CONST char *str)
{
   /* FIXME: Priority=medium
    * This should not go to stdout. */
   fputs (str, stdout);
}

void SLtt_goto_rc (int row, int col)
{
   row += Scroll_r1;
   if (row > SLtt_Screen_Rows) row = SLtt_Screen_Rows;
   if (col > SLtt_Screen_Cols) col = SLtt_Screen_Cols;
   v_gotoxy (col, row);
   Cursor_Row = row;
   Cursor_Col = col;
}

static void emx_video_getxy (void)
{
   v_getxy (&Cursor_Col, &Cursor_Row);
}

static void emx_video_deleol (int x)
{
   unsigned char *p, *pmax;
   int count = SLtt_Screen_Cols - x;
   unsigned char attr = Attribute_Byte;

   p = Line_Buffer;
   pmax = p + 2 * count;

   while (p < pmax)
     {
	*p++ = (unsigned char) ' ';
	*p++ = attr;
     }

   v_putline (Line_Buffer, x, Cursor_Row, count);
}

void SLtt_begin_insert (void)
{
   int n;

   emx_video_getxy ();

   n = SLtt_Screen_Cols - Cursor_Col;
   v_getline (Line_Buffer, Cursor_Col, Cursor_Row, n);
   v_putline (Line_Buffer, Cursor_Col+1, Cursor_Row, n - 1);
}

void SLtt_end_insert (void)
{
}

void SLtt_delete_char (void)
{
   int n;

   emx_video_getxy ();

   n = SLtt_Screen_Cols - Cursor_Col - 1;
   v_getline (Line_Buffer, Cursor_Col+1, Cursor_Row, n);
   v_putline (Line_Buffer, Cursor_Col, Cursor_Row, n);
}

void SLtt_erase_line (void)
{
   Attribute_Byte = 0x07;
   emx_video_deleol (0);
   Current_Color = JNO_COLOR;		/* since we messed with attribute byte */
}

void SLtt_delete_nlines (int nlines)
{
   SLtt_normal_video ();
   v_attrib (Attribute_Byte);
   v_scroll (0, Scroll_r1, SLtt_Screen_Cols-1, Scroll_r2, nlines, V_SCROLL_UP);
}

void SLtt_reverse_index (int nlines)
{
   SLtt_normal_video ();

   v_attrib (Attribute_Byte);
   v_scroll (0, Scroll_r1, SLtt_Screen_Cols-1, Scroll_r2, nlines,
	     V_SCROLL_DOWN);
}

static void emx_video_invert_region (int top_row, int bot_row)
{
   int row, col;

   for (row = top_row; row < bot_row; row++)
     {
	v_getline (Line_Buffer, 0, row, SLtt_Screen_Cols);
	for (col = 1; col < SLtt_Screen_Cols * 2; col += 2)
	  Line_Buffer [col] ^= 0xff;
	v_putline (Line_Buffer, 0, row, SLtt_Screen_Cols);
     }
}

void SLtt_beep (void)
{
   int audible;			/* audible bell */
   int special = 0;		/* first row to invert */
   int visual = 0;		/* final row to invert */

   if (!SLtt_Ignore_Beep) return;

   audible = (SLtt_Ignore_Beep & 1);
   if ( (SLtt_Ignore_Beep & 4) )
     {
	special = SLtt_Screen_Rows - 1;
	visual = special--;	/* only invert bottom status line */
     }
   else if ( (SLtt_Ignore_Beep & 2) )
     {
	visual = SLtt_Screen_Rows;
     }

   if (visual) emx_video_invert_region (special, visual);
   if (audible) /*sound (1500)*/; _sleep2 (100); if (audible) /* nosound () */;
   if (visual) emx_video_invert_region (special, visual);
}

void SLtt_del_eol (void)
{
   if (Current_Color != JNO_COLOR) SLtt_normal_video ();
   emx_video_deleol (Cursor_Col);
}

static void
write_attributes (SLsmg_Char_Type *src, unsigned int count)
{
   register unsigned char *p = Line_Buffer;
   int n = count;

   /* write into a character/attribute pair */
   while (n > 0)
     {
	/* UTF8 FIXME */
	if (src->nchars)
	  {
	     SLtt_reverse_video (src->color & SLSMG_COLOR_MASK);
	     *p++ = src->wchars[0];
	  }
	else *p++ = ' ';
	*p++ = Attribute_Byte;
	n--;
	src++;
     }
   v_putline (Line_Buffer, Cursor_Col, Cursor_Row, count);
}

void SLtt_cls (void)
{
   SLtt_normal_video ();
   SLtt_reset_scroll_region ();
   SLtt_goto_rc (0, 0);
   SLtt_delete_nlines (SLtt_Screen_Rows);
}

void SLtt_putchar (char ch)
{
   if (Current_Color) SLtt_normal_video ();

   emx_video_getxy ();		/* get current position */
   switch (ch)
     {
      case 7:			/* ^G - break */
	SLtt_beep (); break;
      case 8:			/* ^H - backspace */
	goto_rc_abs (Cursor_Row, Cursor_Col - 1); break;
      case 13:			/* ^M - carriage return */
	goto_rc_abs (Cursor_Row, 0); break;
      default:			/* write character to screen */
	v_putn (ch, 1);
	goto_rc_abs (Cursor_Row, Cursor_Col + 1);
     }
}

void SLtt_get_terminfo (void)
{
   SLtt_get_screen_size ();
}

void SLtt_get_screen_size (void)
{
   vioModeInfo.cb = sizeof(vioModeInfo);
   VioGetMode (&vioModeInfo, 0);
   SLtt_Screen_Cols = vioModeInfo.col;
   SLtt_Screen_Rows = vioModeInfo.row;
}

int SLtt_init_video (void)
{
   int OldCol, OldRow;
   PTIB   ptib;
   PPIB   ppib;
   USHORT args[3] = { 6, 2, 1 };

#ifdef HAS_SAVE_SCREEN
   save_screen ();
#endif

   Cursor_Row = Cursor_Col = 0;

   v_init ();
   if ( v_hardware () != V_MONOCHROME ) IsColor = 1; else IsColor = 0;

   v_getxy(&OldCol,&OldRow);
   v_gotoxy (0, 0);
   if (IsColor)
     {
	if (_osmode == OS2_MODE)
	  {
# if 0
	     /* Enable high-intensity background colors */
	     VIOINTENSITY RequestBlock;
	     RequestBlock.cb = sizeof (RequestBlock);
	     RequestBlock.type = 2; RequestBlock.fs = 1;
	     VioSetState (&RequestBlock, 0);	/* nop if !fullscreen */
# endif
	  }
     }

   DosGetInfoBlocks (&ptib, &ppib);
   if ((ppib->pib_ultype) == 2)        /* VIO */
     Blink_Killed = 1;
   else
     {                              /* Fullscreen */
        if (VioSetState (args, 0) == 0)
	  Blink_Killed = 1;
        else
	  Blink_Killed = 0;
     }

   if (!Attribute_Byte)
     {
	/* find the attribute currently under the cursor */
	v_getline (Line_Buffer, OldCol, OldRow, 1);
	Attribute_Byte = Line_Buffer[1];
	SLtt_set_color (JNORMAL_COLOR, NULL,
			Color_Names[(Attribute_Byte) & 0xf],
			Color_Names[(Attribute_Byte) >> 4]);
     }

   v_attrib (Attribute_Byte);

   fixup_colors ();

   SLtt_get_screen_size ();
   SLtt_Use_Ansi_Colors = IsColor;
   SLtt_reset_scroll_region ();
   return 0;
}

#endif				       /* EMX_VIDEO */

/*}}}*/

#ifdef WIN32_VIDEO /*{{{*/

# define UNICODE 1
#include <windows.h>

static HANDLE hStdout = INVALID_HANDLE_VALUE;

#define MAXCOLS 256
static CHAR_INFO Line_Buffer [MAXCOLS];

void SLtt_write_string (SLFUTURE_CONST char *str)
{
   DWORD bytes;
   int n, c;
   if (str == NULL) return;

   n = (int) strlen (str);
   c = n + Cursor_Col;
   if (c >= SLtt_Screen_Cols)
     n = SLtt_Screen_Cols - Cursor_Col;
   if (n < 0) n = 0;

   (void) WriteConsole (hStdout, str, (unsigned int) n, &bytes, NULL);

   goto_rc_abs (Cursor_Row, Cursor_Col + n);
}

void SLtt_goto_rc (int row, int col)
{
   COORD newPosition;

   row += Scroll_r1;
   if (row > SLtt_Screen_Rows) row = SLtt_Screen_Rows;
   if (col > SLtt_Screen_Cols) col = SLtt_Screen_Cols;
   newPosition.X = col;
   newPosition.Y = row;

   (void) SetConsoleCursorPosition(hStdout, newPosition);

   Cursor_Row = row;
   Cursor_Col = col;
}

static void win32_video_getxy (void)
{
   CONSOLE_SCREEN_BUFFER_INFO screenInfo;

   if (TRUE == GetConsoleScreenBufferInfo(hStdout, &screenInfo))
     {
	Cursor_Row = screenInfo.dwCursorPosition.Y;
	Cursor_Col = screenInfo.dwCursorPosition.X;
     }
}

static void win32_video_hscroll (int n)
{
   SMALL_RECT rc;
   COORD c;
   CHAR_INFO ci;
   WORD w = 227;
   DWORD d;

   win32_video_getxy ();

   rc.Left = Cursor_Col;
   rc.Right = SLtt_Screen_Cols;
   rc.Top = rc.Bottom = Cursor_Row;

   c.Y = Cursor_Row;
#if 1
   c.X = SLtt_Screen_Cols - 1;
   ReadConsoleOutputAttribute(hStdout, &w, 1, c, &d);
#else
   /* New region gets the current color */
   w = Attribute_Byte;
#endif
   c.X = Cursor_Col + n;

   ci.Char.AsciiChar = ' ';
   ci.Attributes = w;

   ScrollConsoleScreenBuffer(hStdout, &rc, &rc, c, &ci);
}

static void win32_video_deleol (int x)
{
   DWORD d;
   COORD c;

   c.X = x;
   c.Y = Cursor_Row;

   x = SLtt_Screen_Cols - x;
   FillConsoleOutputCharacter(hStdout, ' ', x, c, &d);
   FillConsoleOutputAttribute(hStdout, (char)Attribute_Byte, x, c, &d);
}

static void win32_video_vscroll (int n)
{
   SMALL_RECT rc, clip_rc;
   COORD c;
   CHAR_INFO ci;

   SLtt_normal_video();

   /* ScrollConsoleScreenBuffer appears to have a bug when
    * Scroll_r1 == Scroll_r2.  Sigh.
    */
   if (Scroll_r2 == Scroll_r1)
     {
	SLtt_goto_rc (0, 0);
	win32_video_deleol (0);
	return;
     }

   rc.Left = clip_rc.Left = 0;
   rc.Right = clip_rc.Right = SLtt_Screen_Cols - 1;
   rc.Top = clip_rc.Top = Scroll_r1;
   rc.Bottom = clip_rc.Bottom = Scroll_r2;

   c.X = 0;
   c.Y = Scroll_r1 + n;

   ci.Char.AsciiChar = ' ';
   ci.Attributes = Attribute_Byte;

   ScrollConsoleScreenBuffer(hStdout, &rc, &clip_rc, c, &ci);
}

void SLtt_begin_insert (void)
{
   win32_video_hscroll (1);
}

void SLtt_end_insert (void)
{
}

void SLtt_delete_char (void)
{
   win32_video_hscroll (-1);
}

void SLtt_erase_line (void)
{
   Attribute_Byte = 0x7;
   win32_video_deleol (0);
   Current_Color = JNO_COLOR;
}

void SLtt_delete_nlines (int nlines)
{
   win32_video_vscroll (-nlines);
}

void SLtt_reverse_index (int nlines)
{
   win32_video_vscroll (nlines);
}

static void win32_invert_region (int top_row, int bot_row)
{
   (void) top_row; (void) bot_row;
}

void SLtt_beep (void)
{
   int audible;			/* audible bell */
   int special = 0;		/* first row to invert */
   int visual = 0;		/* final row to invert */

   if (!SLtt_Ignore_Beep) return;

   audible = (SLtt_Ignore_Beep & 1);

   if ( (SLtt_Ignore_Beep & 4) )
     {
	special = SLtt_Screen_Rows - 1;
	visual = special--;	/* only invert bottom status line */
     }
   else if ( (SLtt_Ignore_Beep & 2) )
     {
	visual = SLtt_Screen_Rows;
     }

   if (visual) win32_invert_region (special, visual);
   if (audible) Beep (1500, 100); else Sleep (100);
   if (visual) win32_invert_region (special, visual);
}

void SLtt_del_eol (void)
{
   if (Current_Color != JNO_COLOR)
     SLtt_normal_video ();
   win32_video_deleol (Cursor_Col);
}

static void
write_attributes (SLsmg_Char_Type *src, unsigned int count)
{
   COORD coord, c;
   CHAR_INFO *p;
   unsigned int n;
   SMALL_RECT rc;

   /* write into a character/attribute pair */
   n = count;
   p = Line_Buffer;
   while (n)
     {
	/* UTF8 FIXME */
	if (src->nchars)
	  {
	     SLtt_reverse_video (src->color & SLSMG_COLOR_MASK);
	     if (_pSLtt_UTF8_Mode)
	       p->Char.UnicodeChar = src->wchars[0];
	     else
	       p->Char.AsciiChar = src->wchars[0];
	  }
	else
	  {
	     if (_pSLtt_UTF8_Mode)
	       p->Char.UnicodeChar = 0;
	     else
	       p->Char.AsciiChar = 0;
	  }
	p->Attributes = Attribute_Byte;
	p++;
	n--;
	src++;
     }

   c.X = count;
   c.Y = 1;
   coord.X = coord.Y = 0;
   rc.Left = Cursor_Col;
   rc.Right = Cursor_Col + count - 1;
   rc.Top = rc.Bottom = Cursor_Row;
   WriteConsoleOutput(hStdout, Line_Buffer, c, coord, &rc);
}

void SLtt_cls (void)
{
   DWORD bytes;
   COORD coord;
   char ch;

   SLtt_normal_video ();
   /* clear the WIN32 screen in one shot */
   coord.X = 0;
   coord.Y = 0;

   ch = ' ';

   (void) FillConsoleOutputCharacter(hStdout,
				     ch,
				     SLtt_Screen_Cols * SLtt_Screen_Rows,
				     coord,
				     &bytes);

     /* now set screen to the current attribute */
   ch = Attribute_Byte;
   (void) FillConsoleOutputAttribute(hStdout,
				     ch,
				     SLtt_Screen_Cols * SLtt_Screen_Rows,
				     coord,
				     &bytes);
}

void SLtt_putchar (char ch)
{
   DWORD bytes;
   WORD attr;
   COORD c;
   WCHAR wch;

   wch = (unsigned char) ch;
   if (Current_Color) SLtt_normal_video ();
   win32_video_getxy ();
   switch (wch)
     {
      case 7:			/* ^G - break */
	SLtt_beep (); break;
      case 8:			/* ^H - backspace */
	goto_rc_abs (Cursor_Row, Cursor_Col - 1); break;
      case 13:			/* ^M - carriage return */
	goto_rc_abs (Cursor_Row, 0); break;
      default:			/* write character to screen */
	c.X = Cursor_Col;
	c.Y = Cursor_Row;
	attr = Attribute_Byte;
	WriteConsoleOutputCharacter(hStdout, &wch, 1, c, &bytes);
	WriteConsoleOutputAttribute(hStdout, &attr, 1, c, &bytes);
	goto_rc_abs (Cursor_Row, Cursor_Col + 1);
     }
}

void SLtt_get_screen_size (void)
{
   CONSOLE_SCREEN_BUFFER_INFO csbi;
   HANDLE h;

   h = hStdout;
   if (h == INVALID_HANDLE_VALUE)
     h = GetStdHandle (STD_OUTPUT_HANDLE);

   if ((h == INVALID_HANDLE_VALUE)
       || (FALSE == GetConsoleScreenBufferInfo(h, &csbi)))
     {
	/* FIXME: Is it a good idea to exit? If the console is hosed, would
	 * the error message be seen?
	 */
	SLang_exit_error ("Unable to determine the screen size");
	return;
     }
#if 0
   SLtt_Screen_Rows = csbi.dwSize.Y;
   SLtt_Screen_Cols = csbi.dwSize.X;
#else
   SLtt_Screen_Rows = (csbi.srWindow.Bottom - csbi.srWindow.Top) + 1;
   SLtt_Screen_Cols = (csbi.srWindow.Right - csbi.srWindow.Left) + 1;
#endif
}

void SLtt_get_terminfo (void)
{
   SLtt_get_screen_size ();
}

static int win32_resize (void)
{
   SMALL_RECT windowRect;

   SLtt_get_screen_size ();

   windowRect.Left = 0;
   windowRect.Top = 0;
   windowRect.Right = SLtt_Screen_Cols - 1;
   windowRect.Bottom = SLtt_Screen_Rows - 1;

   if (FALSE == SetConsoleWindowInfo(hStdout, TRUE, &windowRect))
     return -1;

   return 0;
}

static int Save_Code_Page = 0;
static int win32_init (void)
{
   SECURITY_ATTRIBUTES sec;

   memset ((char *) &sec, 0, sizeof(SECURITY_ATTRIBUTES));
   sec.nLength = sizeof (SECURITY_ATTRIBUTES);
   sec.bInheritHandle = FALSE;

   hStdout = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
				       FILE_SHARE_READ|FILE_SHARE_WRITE,
				       &sec,
				       CONSOLE_TEXTMODE_BUFFER,
				       0);

   if (hStdout == INVALID_HANDLE_VALUE)
     return -1;

   if ((FALSE == SetConsoleActiveScreenBuffer(hStdout))
       || (FALSE == SetConsoleMode(hStdout, 0))
       || (-1 == win32_resize ()))
     {
	SLtt_reset_video ();
	return -1;
     }

   Save_Code_Page = GetConsoleOutputCP ();
   if (_pSLtt_UTF8_Mode)
     {
	(void) SetConsoleOutputCP (65001);
	(void) fprintf (stderr, "CodePage = %d\n", GetConsoleOutputCP ());
     }
   return 0;
}

int SLtt_init_video (void)
{
   SLtt_reset_video ();

   if (-1 == win32_init ())
     return -1;

   /* It is possible for SLtt_init_video to be called after suspension.
    * For all I know, the window size may have changed.  So, resize it
    * now.
    */

   Cursor_Row = Cursor_Col = 0;
   SLtt_Use_Ansi_Colors = IsColor = 1;
   Blink_Killed = 1;

   SLtt_reset_scroll_region ();
   goto_rc_abs (0, 0);
   fixup_colors ();

   return 0;
}

int SLtt_reset_video (void)
{
   if (hStdout == INVALID_HANDLE_VALUE)
     return 0;

   SLtt_reset_scroll_region ();
   SLtt_goto_rc (SLtt_Screen_Rows - 1, 0);
   Attribute_Byte = 0x7;
   Current_Color = JNO_COLOR;
   SLtt_del_eol ();
   (void) CloseHandle (hStdout);

   hStdout = GetStdHandle (STD_OUTPUT_HANDLE);
   if (hStdout != INVALID_HANDLE_VALUE)
     (void) SetConsoleActiveScreenBuffer(hStdout);

   hStdout = INVALID_HANDLE_VALUE;
   (void) SetConsoleOutputCP (Save_Code_Page);
   return 0;
}

#endif

/*}}}*/

#ifdef OS2_VIDEO /*{{{*/

# define INCL_BASE
# define INCL_NOPM
# define INCL_VIO
# define INCL_KBD

# define INCL_DOSPROCESS

# include <os2.h>
# ifndef __IBMC__
#  include <dos.h>
# endif
/* this is how to make a space character */
#define MK_SPACE_CHAR()	(((Attribute_Byte) << 8) | 0x20)

/* buffer to hold a line of character/attribute pairs */
#define MAXCOLS 256
static unsigned char Line_Buffer [MAXCOLS*2];

void SLtt_write_string (SLFUTURE_CONST char *str)
{
   /* FIXME: Priority=medium
    * This should not go to stdout. */
   fputs (str, stdout);
}

void SLtt_goto_rc (int row, int col)
{
   row += Scroll_r1;
   VioSetCurPos (row, col, 0);
   Cursor_Row = row;
   Cursor_Col = col;
}

static void os2_video_getxy (void)
{
   USHORT r, c;

   VioGetCurPos (&r, &c, 0);
   Cursor_Row = r;
   Cursor_Col = c;
}

void SLtt_begin_insert (void)
{
   USHORT n;

   os2_video_getxy ();
   n = SLtt_Screen_Cols - Cursor_Col;

   n = 2 * (n - 1);
   VioReadCellStr ((PCH)Line_Buffer, &n, Cursor_Row, Cursor_Col, 0);
   VioWrtCellStr ((PCH)Line_Buffer, n, Cursor_Row, Cursor_Col + 1, 0);
}

void SLtt_end_insert (void)
{
}

void SLtt_delete_char (void)
{
   USHORT n;

   os2_video_getxy ();
   n = SLtt_Screen_Cols - Cursor_Col - 1;

   n *= 2;
   VioReadCellStr ((PCH)Line_Buffer, &n, Cursor_Row, Cursor_Col + 1, 0);
   VioWrtCellStr ((PCH)Line_Buffer, n, Cursor_Row, Cursor_Col, 0);
}

void SLtt_erase_line (void)
{
   USHORT w;

   Attribute_Byte = 0x07;
   w = MK_SPACE_CHAR ();

   VioWrtNCell ((BYTE*)&w, SLtt_Screen_Cols, Cursor_Row, 0, 0);

   Current_Color = JNO_COLOR;		/* since we messed with attribute byte */
}

void SLtt_delete_nlines (int nlines)
{
   SLtt_normal_video ();

   Line_Buffer[0] = ' '; Line_Buffer[1] = Attribute_Byte;
   VioScrollUp (Scroll_r1, 0, Scroll_r2, SLtt_Screen_Cols-1,
		nlines, (PCH) Line_Buffer, 0);
}

void SLtt_reverse_index (int nlines)
{
   SLtt_normal_video ();

   Line_Buffer[0] = ' '; Line_Buffer[1] = Attribute_Byte;
   VioScrollDn (Scroll_r1, 0, Scroll_r2, SLtt_Screen_Cols-1,
		nlines, (PCH) Line_Buffer, 0);
}

static void os2_video_invert_region (int top_row, int bot_row)
{
   int row, col;
   USHORT length = SLtt_Screen_Cols * 2;

   for (row = top_row; row < bot_row; row++)
     {
	VioReadCellStr ((PCH)Line_Buffer, &length, row, 0, 0);
	for (col = 1; col < length; col += 2)
	  Line_Buffer [col] ^= 0xff;
	VioWrtCellStr ((PCH)Line_Buffer, length, row, 0, 0);
     }
}

void SLtt_beep (void)
{
   int audible;			/* audible bell */
   int special = 0;		/* first row to invert */
   int visual = 0;		/* final row to invert */

   if (!SLtt_Ignore_Beep) return;

   audible = (SLtt_Ignore_Beep & 1);

   if ( (SLtt_Ignore_Beep & 4) )
     {
	special = SLtt_Screen_Rows - 1;
	visual = special--;	/* only invert bottom status line */
     }
   else if ( (SLtt_Ignore_Beep & 2) )
     {
	visual = SLtt_Screen_Rows;
     }

   if (visual) os2_video_invert_region (special, visual);
   if (audible) DosBeep (1500, 100); else DosSleep (100);
   if (visual) os2_video_invert_region (special, visual);
}

void SLtt_del_eol (void)
{
   USHORT w;
   if (Current_Color != JNO_COLOR) SLtt_normal_video ();

   w = MK_SPACE_CHAR ();

   VioWrtNCell ((BYTE*)&w, (SLtt_Screen_Cols - Cursor_Col),
		Cursor_Row, Cursor_Col, 0);
}

static void
write_attributes (SLsmg_Char_Type *src, unsigned int count)
{
   register unsigned char *p = Line_Buffer;
   register unsigned short pair;
   int n = count;

   /* write into a character/attribute pair */
   while (n-- > 0)
     {
	pair = SLSMG_CHAR_TO_USHORT(*src);/* character/color pair */
	src++;
	SLtt_reverse_video (pair >> 8);	/* color change */
	*(p++) = pair & 0xff;		/* character byte */
	*(p++) = Attribute_Byte;	/* attribute byte */
     }

   VioWrtCellStr ((PCH)Line_Buffer, (USHORT)(2 * count),
		  (USHORT)Cursor_Row, (USHORT)Cursor_Col, 0);
}

void SLtt_cls (void)
{
   SLtt_normal_video ();
   Line_Buffer [0] = ' '; Line_Buffer [1] = Attribute_Byte;
   VioScrollUp (0, 0, -1, -1, -1, (PCH)Line_Buffer, 0);
}

void SLtt_putchar (char ch)
{
   unsigned short p, *pp;

   if (Current_Color) SLtt_normal_video ();
   os2_video_getxy ();		/* get current position */

   switch (ch)
     {
      case 7:			/* ^G - break */
	SLtt_beep (); break;
      case 8:			/* ^H - backspace */
	goto_rc_abs (Cursor_Row, Cursor_Col - 1); break;
      case 13:			/* ^M - carriage return */
	goto_rc_abs (Cursor_Row, 0); break;
      default:			/* write character to screen */
 	VioWrtCharStrAtt (&ch, 1, Cursor_Row, Cursor_Col,
 			  (BYTE*)&Attribute_Byte, 0);
	goto_rc_abs (Cursor_Row, Cursor_Col + 1);
     }
}

void SLtt_get_screen_size (void)
{
#ifdef __os2__
# ifdef __IBMC__
   VIOMODEINFO vioModeInfo;
# endif
   vioModeInfo.cb = sizeof(vioModeInfo);
   VioGetMode (&vioModeInfo, 0);
   SLtt_Screen_Cols = vioModeInfo.col;
   SLtt_Screen_Rows = vioModeInfo.row;
#endif
}

void SLtt_get_terminfo (void)
{
   SLtt_get_screen_size ();
}

int SLtt_init_video (void)
{
   VIOINTENSITY RequestBlock;
   PTIB   ptib;
   PPIB   ppib;
   USHORT args[3] = { 6, 2, 1 };

   Cursor_Row = Cursor_Col = 0;

   IsColor = 1;			/* is it really? */

   /* Enable high-intensity background colors */
   RequestBlock.cb = sizeof (RequestBlock);
   RequestBlock.type = 2; RequestBlock.fs = 1;
   VioSetState (&RequestBlock, 0);	/* nop if !fullscreen */

   DosGetInfoBlocks (&ptib, &ppib);
   if ((ppib->pib_ultype) == 2)        /* VIO */
     Blink_Killed = 1;
   else
     {                              /* Fullscreen */
        if (VioSetState (args, 0) == 0)
	  Blink_Killed = 1;
        else
	  Blink_Killed = 0;
     }

   if (!Attribute_Byte)
     {
	/* find the attribute currently under the cursor */
	USHORT len, r, c;

	len = 2;
	VioGetCurPos (&r, &c, 0);
	VioReadCellStr ((PCH)Line_Buffer, &len, r, c, 0);
	Attribute_Byte = Line_Buffer[1];

	SLtt_set_color (JNORMAL_COLOR, NULL,
			Color_Names[(Attribute_Byte) & 0xf],
			Color_Names[(Attribute_Byte) >> 4]);
     }

   SLtt_Use_Ansi_Colors = IsColor;
   SLtt_get_screen_size ();
   SLtt_reset_scroll_region ();
   fixup_colors ();

   return 0;
}

#endif				       /* OS2_VIDEO */
/*}}}*/

#ifdef WATCOM_VIDEO /*{{{*/

# include <graph.h>
# define int86	int386		/* simplify code writing */

#include <dos.h>

/* this is how to make a space character */
#define MK_SPACE_CHAR()	(((Attribute_Byte) << 8) | 0x20)

/* buffer to hold a line of character/attribute pairs */
#define MAXCOLS 256
static unsigned char Line_Buffer [MAXCOLS*2];

/* define for direct to memory screen writes */
static unsigned char *Video_Base;
#define MK_SCREEN_POINTER(row,col) \
  ((unsigned short *)(Video_Base + 2 * (SLtt_Screen_Cols * (row) + (col))))

#define ScreenPrimary	(0xb800 << 4)
#define ScreenSize	(SLtt_Screen_Cols * SLtt_Screen_Rows)
#define ScreenSetCursor(x,y) _settextposition (x+1,y+1)

void ScreenGetCursor (int *x, int *y)
{
   struct rccoord rc = _gettextposition ();
   *x = rc.row - 1;
   *y = rc.col - 1;
}

void ScreenRetrieve (unsigned char *dest)
{
   memcpy (dest, (unsigned char *) ScreenPrimary, 2 * ScreenSize);
}

void ScreenUpdate (unsigned char *src)
{
   memcpy ((unsigned char *) ScreenPrimary, src, 2 * ScreenSize);
}

void SLtt_write_string (SLFUTURE_CONST char *str)
{
   /* FIXME: Priority=medium
    * This should not go to stdout. */
   fputs (str, stdout);
}

void SLtt_goto_rc (int row, int col)
{
   row += Scroll_r1;
   if (row > SLtt_Screen_Rows) row = SLtt_Screen_Rows;
   if (col > SLtt_Screen_Cols) col = SLtt_Screen_Cols;
   ScreenSetCursor(row, col);
   Cursor_Row = row;
   Cursor_Col = col;
}

static void watcom_video_getxy (void)
{
   ScreenGetCursor (&Cursor_Row, &Cursor_Col);
}

void SLtt_begin_insert (void)
{
   unsigned short *p;
   unsigned short *pmin;
   int n;

   watcom_video_getxy ();
   n = SLtt_Screen_Cols - Cursor_Col;

   p = MK_SCREEN_POINTER (Cursor_Row, SLtt_Screen_Cols - 1);
   pmin = MK_SCREEN_POINTER (Cursor_Row, Cursor_Col);

   while (p-- > pmin) *(p + 1) = *p;
}

void SLtt_end_insert (void)
{
}

void SLtt_delete_char (void)
{
   unsigned short *p;
   register unsigned short *p1;
   int n;

   watcom_video_getxy ();
   n = SLtt_Screen_Cols - Cursor_Col - 1;

   p = MK_SCREEN_POINTER (Cursor_Row, Cursor_Col);
   while (n--)
     {
	p1 = p + 1;
	*p = *p1;
	p++;
     }
}

void SLtt_erase_line (void)
{
   unsigned short w;
   unsigned short *p = MK_SCREEN_POINTER (Cursor_Row, 0);
   register unsigned short *pmax = p + SLtt_Screen_Cols;

   Attribute_Byte = 0x07;
   w = MK_SPACE_CHAR ();
   while (p < pmax) *p++ = w;
   Current_Color = JNO_COLOR;		/* since we messed with attribute byte */
}

void SLtt_delete_nlines (int nlines)
{
   union REGS r;

   SLtt_normal_video ();
   r.x.eax = nlines;
   r.x.ecx = 0;
   r.h.ah = 6;
   r.h.ch = Scroll_r1;
   r.h.dl = SLtt_Screen_Cols - 1;
   r.h.dh = Scroll_r2;
   r.h.bh = Attribute_Byte;
   int86 (0x10, &r, &r);
}

void SLtt_reverse_index (int nlines)
{
   union REGS r;

   SLtt_normal_video ();
   r.h.al = nlines;
   r.x.ecx = 0;
   r.h.ah = 7;
   r.h.ch = Scroll_r1;
   r.h.dl = SLtt_Screen_Cols - 1;
   r.h.dh = Scroll_r2;
   r.h.bh = Attribute_Byte;
   int86 (0x10, &r, &r);
}

static void watcom_video_invert_region (int top_row, int bot_row)
{
   unsigned char buf [2 * 180 * 80];         /* 180 cols x 80 rows */
   unsigned char *b, *bmax;

   b    = buf + 1 + 2 * SLtt_Screen_Cols * top_row;
   bmax = buf + 1 + 2 * SLtt_Screen_Cols * bot_row;
   ScreenRetrieve (buf);
   while (b < bmax)
     {
	*b ^= 0xFF;
	b += 2;
     }
   ScreenUpdate (buf);
}

void SLtt_beep (void)
{
   int audible;			/* audible bell */
   int special = 0;		/* first row to invert */
   int visual = 0;		/* final row to invert */

   if (!SLtt_Ignore_Beep) return;

   audible = (SLtt_Ignore_Beep & 1);
   if ( (SLtt_Ignore_Beep & 4) )
     {
	special = SLtt_Screen_Rows - 1;
	visual = special--;	/* only invert bottom status line */
     }
   else if ( (SLtt_Ignore_Beep & 2) )
     {
	visual = SLtt_Screen_Rows;
     }

   if (visual) watcom_video_invert_region (special, visual);
   if (audible) sound (1500); delay (100); if (audible) nosound ();
   if (visual) watcom_video_invert_region (special, visual);
}

void SLtt_del_eol (void)
{
   unsigned short *p, *pmax;
   unsigned short w;
   int n;

   n = SLtt_Screen_Cols - Cursor_Col;
   p = MK_SCREEN_POINTER (Cursor_Row, Cursor_Col);
   pmax = p + n;

   if (Current_Color != JNO_COLOR) SLtt_normal_video ();

   w = MK_SPACE_CHAR ();
   while (p < pmax) *p++ = w;
}

static void
write_attributes (SLsmg_Char_Type *src, unsigned int count)
{
   register unsigned short pair;
   register unsigned short *pos = MK_SCREEN_POINTER (Cursor_Row, 0);

   /* write into a character/attribute pair */
   while (count--)
     {
	pair = SLSMG_CHAR_TO_USHORT(*src);/* character/color pair */
	src++;
	SLtt_reverse_video (pair >> 8);	/* color change */
	*(pos++) = ((unsigned short) Attribute_Byte << 8) | (pair & 0xff);
     }
}

void SLtt_cls (void)
{
   SLtt_normal_video ();
   SLtt_reset_scroll_region ();
   SLtt_goto_rc (0, 0);
   SLtt_delete_nlines (SLtt_Screen_Rows);
}

void SLtt_putchar (char ch)
{
   unsigned short p, *pp;

   if (Current_Color) SLtt_normal_video ();
   watcom_video_getxy ();
   switch (ch)
     {
      case 7:			/* ^G - break */
	SLtt_beep (); break;
      case 8:			/* ^H - backspace */
	goto_rc_abs (Cursor_Row, Cursor_Col - 1); break;
      case 13:			/* ^M - carriage return */
	goto_rc_abs (Cursor_Row, 0); break;
      default:			/* write character to screen */
	p = (Attribute_Byte << 8) | (unsigned char) ch;
	pp = MK_SCREEN_POINTER (Cursor_Row, Cursor_Col);
	*pp = p;
	goto_rc_abs (Cursor_Row, Cursor_Col + 1);
     }
}

void SLtt_get_screen_size (void)
{
   struct videoconfig vc;
   _getvideoconfig(&vc);

   SLtt_Screen_Rows = vc.numtextrows;
   SLtt_Screen_Cols = vc.numtextcols;
}

void SLtt_get_terminfo (void)
{
   SLtt_get_screen_size ();
}

int SLtt_init_video (void)
{
#ifdef HAS_SAVE_SCREEN
   save_screen ();
#endif

   Cursor_Row = Cursor_Col = 0;
   Video_Base = (unsigned char *) ScreenPrimary;
   if (!Attribute_Byte) Attribute_Byte = 0x17;
   IsColor = 1;			/* is it really? */

   if (IsColor)
     {
	union REGS r;
	r.x.eax = 0x1003; r.x.ebx = 0;
	int86 (0x10, &r, &r);
	Blink_Killed = 1;
     }

   SLtt_Use_Ansi_Colors = IsColor;
   SLtt_get_screen_size ();
   SLtt_reset_scroll_region ();
   fixup_colors ();

   return 0;
}

#endif				       /* WATCOM_VIDEO */

/*}}}*/

/* -------------------------------------------------------------------------*\
 * The rest of the functions are, for the most part, independent of a specific
 * video system.
\* ------------------------------------------------------------------------ */

/*----------------------------------------------------------------------*\
 * Function:	void SLtt_set_scroll_region (int r1, int r2);
 *
 * define a scroll region of top_row to bottom_row
\*----------------------------------------------------------------------*/
void SLtt_set_scroll_region (int top_row, int bottom_row)
{
   Scroll_r1 = top_row;
   Scroll_r2 = bottom_row;
}

/*----------------------------------------------------------------------*\
 * Function:	void SLtt_reset_scroll_region (void);
 *
 * reset the scrol region to be the entire screen,
 * ie, SLtt_set_scroll_region (0, SLtt_Screen_Rows);
\*----------------------------------------------------------------------*/
void SLtt_reset_scroll_region (void)
{
   Scroll_r1 = 0;
   Scroll_r2 = SLtt_Screen_Rows - 1;
}

/*----------------------------------------------------------------------*\
 * Function:	int SLtt_flush_output (void);
\*----------------------------------------------------------------------*/
int SLtt_flush_output (void)
{
#if defined(WIN32_VIDEO)
   return 0;
#else
   /* FIXME: Priority=medium
    * This should not go to stdout. */
   fflush (stdout);
   return 0;
#endif
}

int SLtt_set_cursor_visibility (int show)
{
#if defined(WIN32_VIDEO)
   CONSOLE_CURSOR_INFO c;

   if (0 == GetConsoleCursorInfo (hStdout, &c))
     return -1;
   c.bVisible = (show ? TRUE: FALSE);
   if (0 == SetConsoleCursorInfo (hStdout, &c))
     return -1;
   return 0;
#else
   (void) show;
   return -1;
#endif
}

/*----------------------------------------------------------------------*\
 * Function:	void SLtt_reverse_video (int color);
 *
 * set Attribute_Byte corresponding to COLOR.
 * Use Current_Color to remember the color which was set.
 * convert from the COLOR number to the attribute value.
\*----------------------------------------------------------------------*/
void SLtt_reverse_video (int color)
{
   if ((color >= JMAX_COLORS) || (color < 0))
     return;

   Attribute_Byte = Color_Map [color];
   Current_Color = color;
}

/*----------------------------------------------------------------------*\
 * Function:	void SLtt_normal_video (void);
 *
 * reset the attributes for normal video
\*----------------------------------------------------------------------*/
void SLtt_normal_video (void)
{
   SLtt_reverse_video (JNORMAL_COLOR);
}

/*----------------------------------------------------------------------*\
 * Function:	void SLtt_smart_puts (SLsmg_Char_Type *new_string,
 *				      SLsmg_Char_Type *old_string,
 *				      int len, int row);
 *
 * puts NEW_STRING, which has length LEN, at row ROW.  NEW_STRING contains
 * characters/colors packed in the form value = ((color << 8) | (ch));
 *
 * the puts tries to avoid overwriting the same characters/colors
 *
 * OLD_STRING is not used, maintained for compatibility with other systems
\*----------------------------------------------------------------------*/
void SLtt_smart_puts (SLsmg_Char_Type *new_string,
		      SLsmg_Char_Type *old_string,
		      int len, int row)
{
   (void) old_string;
   Cursor_Row = row;
   Cursor_Col = 0;
   /* fprintf (stderr, "smart_puts called: len=%d, row=%d\n", len, row); */
   /* return; */
   write_attributes (new_string, len);
}

/*----------------------------------------------------------------------*\
 * Function:	int SLtt_reset_video (void);
\*----------------------------------------------------------------------*/
#ifndef WIN32_VIDEO
int SLtt_reset_video (void)
{
   SLtt_reset_scroll_region ();
   SLtt_goto_rc (SLtt_Screen_Rows - 1, 0);
#ifdef HAS_SAVE_SCREEN
   restore_screen ();
#endif
   Attribute_Byte = 0x07;
   Current_Color = JNO_COLOR;
   SLtt_del_eol ();
   return 0;
}
#endif

/* FIXME: This does nothing with the attributes */
static char *parse_color_and_attributes (char *f, char *buf, size_t buflen)
{
   SLCONST char *s;
   size_t len;

   s = strchr (f, ';');
   if (s == NULL) return f;

   len = s - f;
   if (len >= buflen) len = buflen-1;
   strncpy (buf, f, len);
   buf[len] = 0;

   return buf;
}

/*----------------------------------------------------------------------*\
 * Function:	void SLtt_set_color (int obj, char *what, char *fg, char *bg);
 *
 * set foreground and background colors of OBJ to the attributes which
 * correspond to the names FG and BG, respectively.
 *
 * WHAT is the name corresponding to the object OBJ, but is not used in
 * this routine.
\*----------------------------------------------------------------------*/
int SLtt_set_color (int obj, SLFUTURE_CONST char *what, SLFUTURE_CONST char *fg, SLFUTURE_CONST char *bg)
{
   int i, b = 0, f = 7;
   char fgbuf[32], bgbuf[32];

   (void) what;

   if ((obj < 0) || (obj >= JMAX_COLORS))
     return -1;

   fg = parse_color_and_attributes ((char *)fg, fgbuf, sizeof(fgbuf));
   bg = parse_color_and_attributes ((char *)bg, bgbuf, sizeof(bgbuf));

   for (i = 0; i < JMAX_COLOR_NAMES; i++ )
     {
	if (!strcmp (fg, Color_Names [i]))
	  {
	     f = i;
	     break;
	  }
     }

   for (i = 0; i < JMAX_COLOR_NAMES; i++)
     {
	if (!strcmp (bg, Color_Names [i]))
	  {
	     if (Blink_Killed) b = i; else b = i & 0x7;
	     break;
	  }
     }
   if (f == b) return 0;

   Color_Map [obj] = (b << 4) | f;

    /* if we're setting the normal color, and the attribute byte hasn't
     been set yet, set it to the new color */
   if ((obj == 0) && (Attribute_Byte == 0))
     SLtt_reverse_video (0);

   if (_pSLtt_color_changed_hook != NULL)
     (*_pSLtt_color_changed_hook)();

   return 0;
}

static void fixup_colors (void)
{
   unsigned int i;

   if (Blink_Killed)
     return;

   for (i = 0; i < JMAX_COLORS; i++)
     Color_Map[i] &= 0x7F;

   SLtt_normal_video ();
}

/* FIXME!!! Add mono support.
 * The following functions have not been fully implemented.
 */
int SLtt_set_mono (int obj_unused, SLFUTURE_CONST char *unused, SLtt_Char_Type c_unused)
{
   (void) obj_unused;
   (void) unused;
   (void) c_unused;
   return 0;
}

#if 0
void SLtt_add_color_attribute (int obj, SLtt_Char_Type attr)
{
   (void) obj;
   (void) attr;
}
#endif

int SLtt_utf8_enable (int mode)
{
   /* FIXME: UTF-8 mode is disabled until the drivers support UTF-8 */
   mode = 0;

   if (mode == -1)
     mode = _pSLtt_UTF8_Mode;

   return _pSLtt_UTF8_Mode = mode;
}

int SLtt_is_utf8_mode (void)
{
   return _pSLtt_UTF8_Mode;
}

