/*
 *
 * Module       : curses.c
 *
 * Description  : AMIGA CURSES package.
 *
 * Author       : Simon Raybould  (sie@fulcrum.bt.co.uk)
 *
 * Date  V1.00a : 16th February 1990
 *       V1.10  : 30th July 1990
 *       V1.20a :  4th October 1990
 *       V1.20  :  7th November 1990
 *       V1.21  :  2nd December 1990 minor fixes
 *       V1.22  :  7th January 1991 bug fixes
 *
 */


#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <devices/audio.h>

#include "acurses.h"


/* Main background screen */
static struct NewScreen NewScreen = {
  0,                        /* LeftEdge */
  0,                        /* TopEdge */
  0,                        /* Width */
  0,                        /* Height */
  4,                        /* No. Bitplanes */
  0, 1,                     /* DetailPen, BlockPen */
  HIRES,                    /* ViewModes */
  CUSTOMSCREEN,             /* Screen type */
  (struct TextAttr *)NULL,  /* default font */
  "Curses screen",          /* Screen Title */
  (struct Gadget *)NULL,    /* Gadget list */
  (struct BitMap *)NULL     /* custom bitmap */
};

/* Main background window */
static struct NewWindow NewWindow = {
  0,                      /* Left edge */
  0,                      /* Top edge */
  0,                      /* Width */
  0,                      /* Height */
  -1, -1,                 /* Pens */
  RAWKEY,                 /* IDCMP flags */
  ACTIVATE | BORDERLESS,  /* Window flags */
  NULL,                   /* First gadget */
  NULL,                   /* Image data */
  NULL,                   /* Title */
  NULL,                   /* Pointer to screen structure */
  NULL,                   /* Super BitMap ? */
  0,0,0,0,                /* MIN/MAX sizing */
  CUSTOMSCREEN            /* Type of screen */
};

/*
 *  Make author appear when right mouse button is pressed.
 */

static struct Menu _CursesMenu = {
  NULL, 0, 0, 0, 0, 0,
  "  AMIGA CURSES by Simon J Raybould  (sie@fulcrum.bt.co.uk) V1.22  07.Jan.1991",
  NULL, 0, 0, 0, 0
};

/*
 * Should initialise all of these to NULL to be certain that
 * CleanExit() will function correctly. Most compilers will do this
 * anyway, but better safe than GURUed.
 */

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;

struct ConsoleDevice *ConsoleDevice = NULL;
static struct IOStdReq ioreq;
static struct Screen *CursesScreen = NULL;
static struct Window *CursesWindow = NULL;

static struct RastPort *RPort;
static struct ViewPort *VPort;

static unsigned char CursesFlags;  /* Global flags */
static short CursorCol = 0,CursorLine = 0,LCursorLine = -1,LCursorCol = -1;
static struct WindowState *HeadWindowList = (struct WindowState *)NULL;
static struct RefreshElement *HeadRefreshList=(struct RefreshElement *)NULL;
static struct IOAudio *AIOptr;
static struct MsgPort *port;
static ULONG device;
static BYTE *sound_data;
static UBYTE whichannel[] = { 1, 2, 4, 8 };

/*
 *  void internal functions
 */

static void CleanExit(), CleanUp(), DoEcho();
static void ZapCursor(), DrawCursor();
     
/* Define a blank mouse pointer */
static USHORT __chip MyMsPtr[] = { 0, 0, 0, 0 };
     
#define NCOLOURS  32
     
static UWORD ColourTable[] = {
  0x000, 0xfff, 0xff0, 0xf80, 0x00f, 0xf0f, 0x0ff, 0xfff, 
  0x620, 0xe50, 0x9f1, 0xeb0, 0x55f, 0x92f, 0x0f8, 0xccc, 
  0x000, 0xd22, 0x000, 0xabc, 0x444, 0x555, 0x666, 0x777, 
  0x888, 0x999, 0xaaa, 0xbbb, 0xccc, 0xddd, 0xeee, 0xfff
};

WINDOW *stdscr = (WINDOW *)NULL, *curscr = (WINDOW *)NULL;
int LINES=24, COLS=80;  /* Defaults */

/*
 *  Need to be global so that flushinp() can reset them !
 */
static unsigned char GetchRPos = 0, GetchWPos = 0;

/*
 *  Start of code.
 */

initscr()
{
  ULONG IBaseLock;
  char *Ptr, *getenv();
  int Tmp;
  
  /*
   *  It would be devestating if someone called initscr() twice
   *  so make the second call fail.
   */
  if(CursesFlags & CFLAG_INITSCR)
    return ERR;
  
  CursesFlags |= CFLAG_INITSCR;  /* Mark that we have called initscr() */
  
  if((IntuitionBase = (struct IntuitionBase *)
      OpenLibrary("intuition.library", 0)) == NULL) {
    fprintf(stderr, "Failed to open Intuition library");
    CleanExit(10);
  }
  IBaseLock = LockIBase(0L);
  NewScreen.Height = NewWindow.Height = IntuitionBase->ActiveScreen->Height;
  NewScreen.Width = NewWindow.Width = IntuitionBase->ActiveScreen->Width;
  UnlockIBase(IBaseLock);
  /* Set interlace if height >= 400 */
  if(NewScreen.Height>=400)
    NewScreen.ViewModes |= LACE;
  LINES = NewScreen.Height/8;
  COLS = NewScreen.Width/8;
  /* if LINES and/or COLS set as environment variables then use them */
  if((Ptr = getenv("LINES"))) {
    Tmp = atoi(Ptr);
    if(Tmp>0 && Tmp<=LINES)
      LINES = Tmp;
  }
  if((Ptr = getenv("COLS"))) {
    Tmp = atoi(Ptr);
    if(Tmp>0 && Tmp<=COLS)
      COLS = Tmp;
  }
  /* Open graphics library */
  if((GfxBase = (struct GfxBase *)
      OpenLibrary("graphics.library", 0))==NULL) {
    fprintf(stderr, "Failed to open Graphics library");
    CleanExit(10);
  }
  /*
   * must have the console.device opened to use RawKeyConvert()
   */
  
  if(OpenDevice("console.device",-1L,(struct IORequest *)&ioreq,0L))
    CleanExit(10);
  ConsoleDevice=(struct ConsoleDevice *)ioreq.io_Device;
  
  if((CursesScreen=(struct Screen *)OpenScreen(&NewScreen)) == NULL) {
    fprintf(stderr, "Failed to open Screen");
    CleanExit(10);
  }
  RPort = &(CursesScreen->RastPort);
  VPort = &(CursesScreen->ViewPort);
  LoadRGB4(VPort, ColourTable, NCOLOURS);
  SetDrMd(RPort, JAM2);
  SetAPen(RPort, 1);
  NewWindow.Screen = CursesScreen;  /* Must do this !! */
  if((CursesWindow=(struct Window *)OpenWindow(&NewWindow)) == NULL) {
    fprintf(stderr, "Failed to open Window\n");
    CleanExit(10);
  }
  SetMenuStrip(CursesWindow, &_CursesMenu);
  SetPointer(CursesWindow, MyMsPtr, 0, 0, 0, 0);  /*Remove mouse pointer*/
  /* Create stdscr and curscr */
  stdscr = newwin(LINES, COLS, 0, 0);
  curscr = newwin(LINES, COLS, 0, 0);  /* used for redraws */
  clearok(curscr, TRUE);  /* Clear curscr on every refresh */
  
  CursesFlags = CFLAG_ECHO | CFLAG_NLCR | CFLAG_CURSOR | CFLAG_INITSCR;
  return OK;
}

/*
 *  Close the screen and libraries.
 */

endwin()                  /* called from main prior to exit. */
{
  void ZapWindows();
  
  if(!(CursesFlags & CFLAG_INITSCR)) {  /* haven't called initscr() */
    return ERR;
  }
  ZapWindows(HeadWindowList);
  CleanUp();
  CursesFlags &= ~CFLAG_INITSCR;  /* Mark that we have called endwin() */
  return OK;
}

/* Recursive routine to zap all windows and release data structures */
static void ZapWindows(WinStat)
     struct WindowState *WinStat;
{
  if(!WinStat)
    return;
  if(WinStat->Next)
    ZapWindows(WinStat->Next);  /* Recurse */
  delwin(&WinStat->Window);
}

static void CleanExit(RetCode)
     int RetCode;
{
  CleanUp();
  exit(RetCode);
}

static void CleanUp()
{
  if(CursesWindow)
    CloseWindow(CursesWindow);
  if(CursesScreen)
    CloseScreen(CursesScreen);
  if(GfxBase)
    CloseLibrary((struct Library *)GfxBase);
  if(IntuitionBase)
    CloseLibrary((struct Library *)IntuitionBase);
}

init_color(n, r, g, b)
     short n;
     unsigned char r, g, b;
{
  if(n<0 || n>15 || r>1000 || g>1000 || b>1000)
    return ERR;
  /* If 0 then leave, else subtract 1 */
  if(r) r--;
  if(g) g--;
  if(g) g--;
  
  SetRGB4(VPort, n, r*16/1000, g*16/1000, b*16/1000);
  return OK;
}

start_color()
{
  return OK;  /* No initialisation required to get colours going */
}

has_colors()
{
  return TRUE;  /* Yes baby we have colours on this bitch */
}

/*
 *  static because not implemented yet.
 */
static color_content(color, r, g, b)
     short color, *r, *g, *b;
{
  return OK;
}

waddstr(WinPtr, Str)
     WINDOW *WinPtr;
     char *Str;
{
  struct WindowState *WinStat, *PWinStat = NULL;
  short TmpAttrs;
  
  if(!*Str)
    return OK;
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  if(WinStat->ParentWin)
    if(!(PWinStat = (struct WindowState *)WinStat->ParentWin->_WinStat))
      return ERR;
  
  WinStat->LnArry[WinPtr->_cury].Touched = TRUE;
  WinStat->LnArry[WinPtr->_cury].StartCol = min(WinStat->LnArry[WinPtr->_cury].StartCol, WinPtr->_curx);
  if(PWinStat) {
    PWinStat->LnArry[WinPtr->_cury + WinPtr->_begy].Touched = TRUE;
    PWinStat->LnArry[WinPtr->_cury + WinPtr->_begy].StartCol = min(PWinStat->LnArry[WinPtr->_cury + WinPtr->_begy].StartCol, WinPtr->_curx + WinPtr->_begx);
  }
  while(*Str) {
    switch(*Str) {
    case '\t':
      do {
	WinStat->LnArry[WinPtr->_cury].Line[WinPtr->_curx] = ' ';
	WinStat->LnArry[WinPtr->_cury].ATTRS[WinPtr->_curx++] = WinPtr->_attrs;
      } while(WinPtr->_curx % 8);
      break;
    case '\n':
      WinStat->LnArry[WinPtr->_cury].EndCol = max(WinStat->LnArry[WinPtr->_cury].EndCol, WinPtr->_curx - 1);
      TmpAttrs = WinPtr->_attrs;
      wattrset(WinPtr, 0); /* better to call wattrset in case I change attrs */
      wclrtoeol(WinPtr);
      wattrset(WinPtr, TmpAttrs);
      if(PWinStat)
	PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].EndCol=max(PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].EndCol,WinPtr->_curx+WinPtr->_begx-1);
      WinPtr->_curx = 0;
      WinPtr->_cury++;
      if(WinPtr->_cury > WinStat->ScrollBot) {
	if(WinPtr->_scroll)
	  scroll(WinPtr);
	WinPtr->_cury = WinStat->ScrollBot;
      }
      if(*(Str + 1)) {  /* If there is more then touch this line too */
	WinStat->LnArry[WinPtr->_cury].Touched = TRUE;
	WinStat->LnArry[WinPtr->_cury].StartCol = min(WinStat->LnArry[WinPtr->_cury].StartCol, WinPtr->_curx);
	if(PWinStat) {
	  PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].Touched = TRUE;
	  PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].StartCol = min(PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].StartCol, WinPtr->_curx+WinPtr->_begx);
	}
      }
      break;
    default:
      WinStat->LnArry[WinPtr->_cury].Line[WinPtr->_curx] = *Str;
      WinStat->LnArry[WinPtr->_cury].ATTRS[WinPtr->_curx] = WinPtr->_attrs;
      WinPtr->_curx++;
      break;
    }
    /* If hit right edge of window then increment _cury */
    if(WinPtr->_curx > WinPtr-> _maxx) {
      WinStat->LnArry[WinPtr->_cury].EndCol = max(WinStat->LnArry[WinPtr->_cury].EndCol, WinPtr->_curx - 1);
      WinPtr->_curx = 0;
      WinPtr->_cury++;
      if(WinPtr->_cury > WinStat->ScrollBot) {
	if(WinPtr->_scroll)
	  scroll(WinPtr);
	WinPtr->_cury = WinStat->ScrollBot;
      }
      if(WinPtr->_cury > WinPtr->_maxy)
	WinPtr->_cury = WinPtr->_maxy;
      if(*(Str + 1)) {  /* If there is more then touch this line too */
	WinStat->LnArry[WinPtr->_cury].Touched = TRUE;
	WinStat->LnArry[WinPtr->_cury].StartCol = min(WinStat->LnArry[WinPtr->_cury].StartCol, WinPtr->_curx);
	if(PWinStat) {
	  PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].Touched = TRUE;
	  PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].StartCol = min(PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].StartCol, WinPtr->_curx+WinPtr->_begx);
	}
      }
    }
    Str++;
  }
  WinStat->LnArry[WinPtr->_cury].EndCol = max(WinStat->LnArry[WinPtr->_cury].EndCol, WinPtr->_curx - 1);
  if(PWinStat)
    PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].EndCol = max(PWinStat->LnArry[WinPtr->_cury+WinPtr->_begy].EndCol, WinPtr->_curx+WinPtr->_begx-1);
  return OK;
}

waddch(WinPtr, c)
     WINDOW *WinPtr;
     char c;
{
  char *str = " ";
  
  *str = c;
  return waddstr(WinPtr, str);
}

winsch(WinPtr, c)
     WINDOW *WinPtr;
     char c;
{
  int i;
  struct WindowState *WinStat;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  /* shuffle line along to the right */
  for (i = WinPtr->_maxx; i > WinPtr->_curx; i--)
    WinStat->LnArry[WinPtr->_cury].Line[i] = WinStat->LnArry[WinPtr->_cury].Line[i-1];
  WinStat->LnArry[WinPtr->_cury].Line[i] = c;
  WinStat->LnArry[WinPtr->_cury].StartCol = min(WinStat->LnArry[WinPtr->_cury].StartCol, WinPtr->_curx);
  WinStat->LnArry[WinPtr->_cury].EndCol = WinPtr->_maxx;
  WinStat->LnArry[WinPtr->_cury].Touched = TRUE;
  
  return OK;
}

wdelch(WinPtr)
     WINDOW *WinPtr;
{
  int i;
  struct WindowState *WinStat;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  /* shuffle line along to the left */
  for (i = WinPtr->_curx; i < WinPtr->_maxx; i++)
    WinStat->LnArry[WinPtr->_cury].Line[i] = WinStat->LnArry[WinPtr->_cury].Line[i+1];
  WinStat->LnArry[WinPtr->_cury].Line[i] = ' ';  /* Blank last char */
  WinStat->LnArry[WinPtr->_cury].StartCol = min(WinStat->LnArry[WinPtr->_cury].StartCol, WinPtr->_curx);
  WinStat->LnArry[WinPtr->_cury].EndCol = WinPtr->_maxx;
  WinStat->LnArry[WinPtr->_cury].Touched = TRUE;
  
  return OK;
}

wclear(WinPtr)
     WINDOW *WinPtr;
{
  int Line, Col;
  struct WindowState *WinStat;
  
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  for(Line=0; Line<WinStat->NLines; Line++) {
    memset(WinStat->LnArry[Line].Line, ' ', WinPtr->_maxx + 1);
    memset(WinStat->LnArry[Line].LRLine, ' ', WinPtr->_maxx + 1);
    for(Col = 0; Col <= WinPtr->_maxx; Col++) {
      WinStat->LnArry[Line].ATTRS[Col] = WinPtr->_attrs;
      WinStat->LnArry[Line].LRATTRS[Col] = WinPtr->_attrs;
    }
    WinStat->LnArry[Line].Touched = FALSE;
    WinStat->LnArry[Line].StartCol = WinPtr->_maxx;
    WinStat->LnArry[Line].EndCol = 0;
  }
  WinPtr->_curx = 0;
  WinPtr->_cury = 0;
  WinPtr->_cls = TRUE;
  return OK;
}

werase(WinPtr)
     WINDOW *WinPtr;
{
  int Line, Col;
  struct WindowState *WinStat;
  
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  /* Blank screen image */
  for(Line=0; Line<WinStat->NLines; Line++) {
    memset(WinStat->LnArry[Line].Line, ' ', WinPtr->_maxx + 1);
    for(Col = 0; Col <= WinPtr->_maxx; Col++)
      WinStat->LnArry[Line].ATTRS[Col] = WinPtr->_attrs;
    WinStat->LnArry[Line].Touched = TRUE;
    WinStat->LnArry[Line].StartCol = 0;
    WinStat->LnArry[Line].EndCol = WinPtr->_maxx;
  }
  WinPtr->_curx = 0;
  WinPtr->_cury = 0;
  return OK;
}

clearok(WinPtr, flag)
     WINDOW *WinPtr;
     int flag;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  WinPtr->_clear = (flag) ? TRUE : FALSE;
  
  return OK;
}

wclrtoeol(WinPtr)
     WINDOW *WinPtr;
{
  short x, y;
  int len;
  char Buffer[100];
  
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  x = WinPtr->_curx;
  y = WinPtr->_cury;
  
  len = WinPtr->_maxx - WinPtr->_curx + 1;
  memset(Buffer, ' ', len);
  Buffer[len] = '\0';
  
  waddstr(WinPtr, Buffer);
  wmove(WinPtr, y, x);
  
  return OK;
}

wclrtobot(WinPtr)
     WINDOW *WinPtr;
{
  short x, y;
  int i;
  
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  x = WinPtr->_curx;
  y = WinPtr->_cury;
  
  wclrtoeol(WinPtr);
  for(i = WinPtr->_cury + 1; i <= WinPtr->_maxy; i++) {
    wmove(WinPtr, i, 0);
    wclrtoeol(WinPtr);
  }
  wmove(WinPtr, y, x);
  
  return OK;
}

static int GetNextChar(WinPtr)
     WINDOW *WinPtr;
{
  static unsigned char buffer[RAWBUFSIZ], BufPos = 0, NumChars = 0;
  int Class, i;
  struct IntuiMessage *Message;
  static struct InputEvent ievent = { NULL, IECLASS_RAWKEY, 0, 0, 0 };
  
  if(BufPos < NumChars)  /* If we still hav some chars then return next */
    return (int)buffer[BufPos++];
  
  while (BufPos == NumChars) {
    /* Get message if there is one allready queued */
    Message = (struct IntuiMessage *)GetMsg(CursesWindow->UserPort);
    if(!Message) {
      /* Nuffin yet */
      if(WinPtr->_nodelay)  /* If non-blocking return ERR */
	return ERR;
      else {    /* Wait for character */
	Wait(1<<CursesWindow->UserPort->mp_SigBit);
	Message = (struct IntuiMessage *)GetMsg(CursesWindow->UserPort);
      }
    }
    if(!Message)  /* Try again */
      continue;
    
    Class = Message->Class;
    switch(Class) {
    case RAWKEY:
      BufPos = 0;
      ievent.ie_Code = Message->Code;
      ievent.ie_Qualifier = Message->Qualifier;
      ievent.ie_position.ie_addr = *((APTR*)Message->IAddress);
      NumChars = RawKeyConvert(&ievent, buffer, RAWBUFSIZ, 0L);
      ReplyMsg((struct Message *)Message);
      if(!NumChars)  /* If no characters then try again */
	break;
      if(CursesFlags & CFLAG_ECHO)
	for(i=0; i<NumChars; i++)
	  DoEcho(WinPtr, buffer[i]);
      /* Translate id keypad set to TRUE */
      if(WinPtr->_use_keypad) {
	switch(NumChars) {
	case 1:
	  NumChars = 0;  /* Translation will use up all chars */
	  return (int)buffer[0];
	case 2:    /* ARROW KEY */
	  NumChars = 0;  /* Translation will use up all chars */
	  if(buffer[0] != 155)
	    return -1;
	  switch(buffer[1]) {
	  case 65: return KEY_UP;
	  case 66: return KEY_DOWN;
	  case 67: return KEY_RIGHT;
	  case 68: return KEY_LEFT;
	  default: return -1;
	  }
	case 3:    /* FUNCTION KEY */
	  NumChars = 0;  /* Translation will use up all chars */
	  if(buffer[0] != 155)
	    return -1;
	  if(buffer[2] != 126)
	    return -1;
	  if(buffer[1] == 63)
	    return KEY_HELP;
	  return KEY_F0 + (buffer[1] - 48);  /* KEY_F0 = F1 */
	default:
	  NumChars = 0;  /* Translation will use up all chars */
	  return -1;
	}
      }
      break;
    default:
      ReplyMsg((struct Message *)Message);
      break;
    }
  }
  return (int)buffer[BufPos++];
}

flushinp()
{
  GetchRPos = 0;
  GetchWPos = 0;
  return OK;
}

wgetch(WinPtr)
     WINDOW *WinPtr;
{
  static int buffer[256];  /* Cyclic buffer */
  static unsigned char forward = FALSE;
  int Ret;
  
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  while(GetchRPos == GetchWPos || (!(CursesFlags & CFLAG_CBREAK) && !forward)) {
    if(WinPtr->_nodelay)
      return GetNextChar(WinPtr);
    if((Ret = GetNextChar(WinPtr)) >= 0)
      buffer[GetchWPos++] = Ret;
    if(Ret == (int)'\r')
      forward = TRUE;
  }
  if(buffer[GetchRPos] == '\r') {
    buffer[GetchRPos] = '\n';
    forward = FALSE;
  }
  return((int)buffer[GetchRPos++]);
}

wgetstr(WinPtr, ptr)
     WINDOW *WinPtr;
     char *ptr;
{
  char done = FALSE, *BuffStart;
  unsigned char TempFlag;  /* Used to restore flags after */
  
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  BuffStart = ptr;
  
  /* Will need to be in CBREAK mode for this */
  TempFlag = CursesFlags;
  CursesFlags |= CFLAG_CBREAK;
  while(!done) {
    switch(*ptr = wgetch(WinPtr)) {
    case -1:  /* wgetch() returned ERROR */
      *ptr = '\0';
      CursesFlags = TempFlag;
      return -1;
    case '\n':
    case '\r':
      *ptr = '\0';
      done = TRUE;
      break;
    case '\b':
      if(--ptr < BuffStart)  /* Don't move before start */
	ptr = BuffStart;
      else if(CursesFlags & CFLAG_ECHO) {
	/* Do BS SP BS processing */
	mvcur(CursorLine, CursorCol, CursorLine, CursorCol - 1);  /* BS */
	DoEcho(WinPtr, ' ');          /* SP */
	mvcur(CursorLine, CursorCol, CursorLine, CursorCol - 1);  /* BS */
      }
      break;
    default:
      ptr++;
      break;
    }
  }
  CursesFlags = TempFlag;
  return 0;
}

winch(WinPtr)
     WINDOW *WinPtr;
{
  struct WindowState *WinStat;
  
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  return (int)WinStat->LnArry[WinPtr->_cury].Line[WinPtr->_curx];
}

static void DoEcho(WinPtr, c)
     WINDOW *WinPtr;
     char c;
{
  short x, y;
  struct WindowState *WinStat;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return;
  
  if(c == BS || c == CR)        /* Don't echo Backspace or Return */
    return;
  
  x = CursorCol * 8;
  y = 6 + CursorLine * 8;
  ZapCursor();
  SetDrMd(RPort, JAM2);
  SetAPen(RPort, WinPtr->_attrs & 0x0f);
  Move(RPort, x, y);
  Text(RPort, &c, 1);
  DrawCursor();
  /* Update curscr */
  if(WinPtr != curscr) {
    wmove(curscr, CursorLine, CursorCol);
    waddch(curscr, c);
  }
  /* Update Line structure */
  WinStat->LnArry[CursorLine-WinPtr->_begy].Line[CursorCol-WinPtr->_begx] = c;
  WinStat->LnArry[CursorLine-WinPtr->_begy].LRLine[CursorCol-WinPtr->_begx] = c;
  WinStat->LnArry[CursorLine-WinPtr->_begy].ATTRS[CursorCol-WinPtr->_begx] = WinPtr->_attrs;
  WinStat->LnArry[CursorLine-WinPtr->_begy].LRATTRS[CursorCol-WinPtr->_begx] = WinPtr->_attrs;
  /* Move current position one to the right */
  if(++WinPtr->_curx > WinPtr->_maxx)
    WinPtr->_curx = WinPtr->_maxx;
  mvcur(CursorLine, CursorCol, CursorLine, CursorCol + 1);
}

wmove(WinPtr, Line, Col)
     WINDOW *WinPtr;
     short Line, Col;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  if(Line<0 || Line>WinPtr->_maxy)
    return ERR;
  if(Col<0 || Col>WinPtr->_maxx)
    return ERR;
  WinPtr -> _cury = Line;
  WinPtr -> _curx = Col;
  WinPtr -> _flags |= CWF_MOVED;
  return OK;
}

mvcur(CurLine, CurCol, NewLine, NewCol)
     int CurLine, CurCol, NewLine, NewCol;
{
  /* Could check CurLine and CurCol but this would make it fail too often */
  ZapCursor();
  CursorLine = NewLine;
  CursorCol = NewCol;
  DrawCursor();
  return OK;
}

printw(fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
     char *fmt;
     double A0, A1, A2, A3, A4, A5, A6, A7, A8, A9;
{
  int Ret;
  char buffer[BUFSIZ];
  
  Ret = sprintf(buffer, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
  waddstr(stdscr, buffer);
  return Ret;
}

wprintw(WinPtr, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
     WINDOW *WinPtr;
     char *fmt;
     double A0, A1, A2, A3, A4, A5, A6, A7, A8, A9;
{
  int Ret;
  char buffer[BUFSIZ];
  
  Ret = sprintf(buffer, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
  waddstr(WinPtr, buffer);
  return Ret;
}

mvprintw(Line, Col, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
     short Line, Col;
     char *fmt;
     double A0, A1, A2, A3, A4, A5, A6, A7, A8, A9;
{
  int Ret;
  char buffer[BUFSIZ];
  
  Ret = sprintf(buffer, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
  wmove(stdscr, Line, Col);
  waddstr(stdscr, buffer);
  return Ret;
}

mvwprintw(WinPtr, Line, Col, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
     WINDOW *WinPtr;
     short Line, Col;
     char *fmt;
     double A0, A1, A2, A3, A4, A5, A6, A7, A8, A9;
{
  int Ret;
  char buffer[BUFSIZ];
  
  Ret = sprintf(buffer, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
  wmove(WinPtr, Line, Col);
  waddstr(WinPtr, buffer);
  return Ret;
}

wrefresh(WinPtr)
     WINDOW *WinPtr;
{
  int i, j;
  unsigned long style;
  short Line;
  struct WindowState *WinStat;
  char Buffer[BUFSIZ];
  void Optimise();
  
  if(WinPtr == curscr)
    touchwin(WinPtr);
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  ZapCursor();
  /*
   *  It is possible for no printing since last refresh, but for
   *  a move to have been done...
   */
  if(WinPtr->_flags & CWF_MOVED) {
    WinPtr->_flags &= ~CWF_MOVED;
    CursorLine = WinPtr->_cury + WinPtr->_begy;
    CursorCol = WinPtr->_curx + WinPtr->_begx;
  }
  /*
   *  If clearok has been called, then clear on every refresh.
   */
  if(WinPtr->_clear || WinPtr->_cls) {
    WinPtr->_cls = FALSE;
    SetAPen(RPort, 0);
    SetDrMd(RPort, JAM2);
    RectFill(RPort, (WinPtr->_begx * 8), (WinPtr->_begy * 8),
	     ((WinPtr->_begx + WinPtr->_maxx) * 8) + 7,
	     ((WinPtr->_begy + WinPtr->_maxy) * 8 + 7));
  }
  if(CursesFlags & CFLAG_CURSOR) {
    CursorLine = WinPtr->_cury + WinPtr->_begy;
    CursorCol = WinPtr->_curx + WinPtr->_begx;
  }
  for(Line=0; Line<WinStat->NLines; Line++) {
    /* if clearok set then must refresh everything */
    if(WinPtr->_clear) {
      memset(WinStat->LnArry[Line].LRLine, ' ', WinPtr->_maxx+1);
      WinStat->LnArry[Line].Touched = TRUE;
      WinStat->LnArry[Line].StartCol = 0;
      WinStat->LnArry[Line].EndCol = WinPtr->_maxx;
    }
    Optimise(&WinStat->LnArry[Line]);
    if(WinStat->LnArry[Line].Touched) {
      j = WinStat->LnArry[Line].StartCol;
      for(i=WinStat->LnArry[Line].StartCol + 1; i<=WinStat->LnArry[Line].EndCol; i++) {
	if(WinStat->LnArry[Line].ATTRS[i] != WinStat->LnArry[Line].ATTRS[j]) {
	  /* Print what we've got */
	  SetAPen(RPort, WinStat->LnArry[Line].ATTRS[j] & 017);
	  if(WinStat->LnArry[Line].ATTRS[j] & (A_REVERSE | A_STANDOUT))
	    SetDrMd(RPort, JAM2|INVERSVID);
	  else
	    SetDrMd(RPort, JAM2);
	  style = FS_NORMAL;
	  if(WinStat->LnArry[Line].ATTRS[j] & A_BOLD)
	    style |= FSF_BOLD;
	  if(WinStat->LnArry[Line].ATTRS[j] & A_UNDERLINE)
	    style |= FSF_UNDERLINED;
	  SetSoftStyle(RPort, style, ~0L);
	  Move(RPort, (j+WinPtr->_begx) * 8, 6 + (Line+WinPtr->_begy) * 8);
	  Text(RPort, &WinStat->LnArry[Line].Line[j], i-j);
	  /*
	   *  Update the record of the current screen state.
	   */
	  if(WinPtr != curscr) {
	    wmove(curscr, Line+WinPtr->_begy, j+WinPtr->_begx);
	    memcpy(Buffer, &WinStat->LnArry[Line].Line[j], i-j);
	    Buffer[i-j] = '\0';
	    waddstr(curscr, Buffer);
	  }
	  j = i;
	}
      }
      if(j < i) {
	SetAPen(RPort, WinStat->LnArry[Line].ATTRS[j] & 017);
	if(WinStat->LnArry[Line].ATTRS[j] & (A_STANDOUT | A_REVERSE))
	  SetDrMd(RPort, JAM2|INVERSVID);
	else
	  SetDrMd(RPort, JAM2);
	style = FS_NORMAL;
	if(WinStat->LnArry[Line].ATTRS[j] & A_BOLD)
	  style |= FSF_BOLD;
	if(WinStat->LnArry[Line].ATTRS[j] & A_UNDERLINE)
	  style |= FSF_UNDERLINED;
	SetSoftStyle(RPort, style, ~0L);
	Move(RPort, (j+WinPtr->_begx) * 8, 6 + (Line+WinPtr->_begy) * 8);
	Text(RPort, &(WinStat->LnArry[Line].Line[j]), i-j);
	/*
	 *  Update the record of the current screen state.
	 */
	if(WinPtr != curscr) {
	  wmove(curscr, Line+WinPtr->_begy, j+WinPtr->_begx);
	  memcpy(Buffer, &WinStat->LnArry[Line].Line[j], i-j);
	  Buffer[i-j] = '\0';
	  waddstr(curscr, Buffer);
	}
      }
      WinStat->LnArry[Line].Touched = FALSE;
      WinStat->LnArry[Line].StartCol = WinPtr->_maxx;
      WinStat->LnArry[Line].EndCol = 0;
    }
    /*
     *  Copy line and attrs to LR for Optimise code
     */
    memcpy(WinStat->LnArry[Line].LRLine, WinStat->LnArry[Line].Line, WinPtr->_maxx+1);
    memcpy(WinStat->LnArry[Line].LRATTRS, WinStat->LnArry[Line].ATTRS, sizeof(short) * (WinPtr->_maxx+1));
  }
  DrawCursor();
  return OK;
}

static void ToggleCursor(Line, Col)
{
  SetDrMd(RPort, JAM2 | INVERSVID | COMPLEMENT);
  Move(RPort, Col*8, 6 + Line*8);
  Text(RPort, " ", 1);
}

static void
  ZapCursor()
{
  /* If there was a cursor then blank it */
  if(LCursorCol >= 0 && LCursorLine >= 0)
    ToggleCursor(LCursorLine, LCursorCol);
  
  LCursorCol = LCursorLine = -1;
}

static void DrawCursor()
{
  /* Draw cursor */
  if(CursesFlags & CFLAG_CURSOR)
    ToggleCursor(CursorLine, CursorCol);
  
  if(CursesFlags & CFLAG_CURSOR) {
    LCursorCol = CursorCol;
    LCursorLine = CursorLine;
  } else {
    LCursorCol = -1;
    LCursorLine = -1;
  }
}

scanw(fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
     char *fmt;
     long A0, A1, A2, A3, A4, A5, A6, A7, A8, A9;
{
  char buffer[BUFSIZ];
  
  wgetstr(stdscr, buffer);
  return sscanf(buffer, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
}

wscanw(WinPtr, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
     WINDOW *WinPtr;
     char *fmt;
     long A0, A1, A2, A3, A4, A5, A6, A7, A8, A9;
{
  char buffer[BUFSIZ];
  
  wgetstr(WinPtr, buffer);
  return sscanf(buffer, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
}

mvscanw(Line, Col, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
     short Line, Col;
     char *fmt;
     long A0, A1, A2, A3, A4, A5, A6, A7, A8, A9;
{
  char buffer[BUFSIZ];
  
  wmove(stdscr, Line, Col);
  wgetstr(stdscr, buffer);
  return sscanf(buffer, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
}

mvwscanw(WinPtr, Line, Col, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
     WINDOW *WinPtr;
     short Line, Col;
     char *fmt;
     long A0, A1, A2, A3, A4, A5, A6, A7, A8, A9;
{
  char buffer[BUFSIZ];
  
  wmove(WinPtr, Line, Col);
  wgetstr(WinPtr, buffer);
  return sscanf(buffer, fmt, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
}

wstandout(WinPtr)
     WINDOW *WinPtr;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  WinPtr->_attrs |= A_STANDOUT;
}

wstandend(WinPtr)
     WINDOW *WinPtr;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  WinPtr->_attrs &= ~A_STANDOUT;
}

wattrset(WinPtr, attr)
     WINDOW *WinPtr;
     short attr;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  /*
   *  Older code may inadvertently reset the attributes and set the
   *  forground colour to 0, in this case, set it to white.
   */
  if(!(attr & 017))
    attr |= COLOR_WHITE;
  
  WinPtr->_attrs = attr;
  return OK;
}

wattron(WinPtr, attr)
     WINDOW *WinPtr;
     short attr;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  /* If attributes contain a colour change then mask off old colour */
  if(attr & 017)
    WinPtr->_attrs &= ~017;
  
  WinPtr->_attrs |= attr;
  return OK;
}

wattroff(WinPtr, attr)
     WINDOW *WinPtr;
     short attr;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  WinPtr->_attrs &= ~attr;
  return OK;
}

cbreak()
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  CursesFlags |= CFLAG_CBREAK;
  return OK;
}

nocbreak()
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  CursesFlags &= ~CFLAG_CBREAK;
  return OK;
}

raw()
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  CursesFlags |= CFLAG_CBREAK;
  return OK;
}

noraw()
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  CursesFlags &= ~CFLAG_CBREAK;
  return OK;
}

idlok(WinPtr, flag)
     WINDOW *WinPtr;
     int flag;
{
  /* This function is to enable hardware insert/delete line */
  return OK;
}

winsertln(WinPtr)
     WINDOW *WinPtr;
{
  Scroll(WinPtr, WinPtr->_cury, WinPtr->_maxy, SCROLL_DOWN);
  return OK;
}

wdeleteln(WinPtr)
     WINDOW *WinPtr;
{
  Scroll(WinPtr, WinPtr->_cury, WinPtr->_maxy, SCROLL_UP);
  return OK;
}

nodelay(WinPtr, flag)
     WINDOW *WinPtr;
     int flag;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  WinPtr->_nodelay = flag;
  return OK;
}

echo()
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  CursesFlags |= CFLAG_ECHO;
  return OK;
}

noecho()
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  CursesFlags &= ~CFLAG_ECHO;
  return OK;
}

keypad(WinPtr, flag)
     WINDOW *WinPtr;
     char flag;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  WinPtr->_use_keypad = flag?TRUE:FALSE;
  
  return OK;
}

beep()
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  AIOptr = (struct IOAudio *) AllocMem(sizeof(struct IOAudio), MEMF_CHIP|MEMF_PUBLIC);
  if(!AIOptr)
    exit(1);
  
  port = (struct MsgPort *)CreatePort(0, 0);
  if(!port) {
    FreeMem(AIOptr, sizeof(struct IOAudio));
    return ERR;
  }
  
  AIOptr->ioa_Request.io_Message.mn_ReplyPort = port;
  AIOptr->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
  AIOptr->ioa_Request.io_Command = ADCMD_ALLOCATE;
  AIOptr->ioa_Request.io_Flags = ADIOF_NOWAIT;
  AIOptr->ioa_AllocKey = 0;
  AIOptr->ioa_Data = whichannel;
  AIOptr->ioa_Length = sizeof(whichannel);
  
  device = OpenDevice("audio.device", 0L, (struct IORequest *)AIOptr, 0L);
  if(device) {
    printf("Curses beep() - Can't open Audio Device\n");
    FreeMem(AIOptr, sizeof(struct IOAudio));
    return ERR;
  }
  
  sound_data = (UBYTE *) AllocMem(SOUNDLENGTH, MEMF_CHIP|MEMF_PUBLIC);
  if(!sound_data) {
    FreeMem(AIOptr, sizeof(struct IOAudio));
    return ERR;
  }
  
  sound_data[0]=127;
  sound_data[1]=-127;
  
  AIOptr->ioa_Request.io_Message.mn_ReplyPort = port;
  AIOptr->ioa_Request.io_Command = CMD_WRITE;
  AIOptr->ioa_Request.io_Flags = ADIOF_PERVOL;
  AIOptr->ioa_Data = sound_data;
  AIOptr->ioa_Cycles = 200;
  AIOptr->ioa_Length = SOUNDLENGTH;
  AIOptr->ioa_Period = 2000;
  AIOptr->ioa_Volume = 64;
  
  BeginIO((struct IORequest *)AIOptr);
  WaitIO((struct IORequest *)AIOptr);
  
  FreeMem(sound_data, SOUNDLENGTH);
  DeletePort(port);
  CloseDevice((struct IORequest *)AIOptr);
  FreeMem(AIOptr, sizeof(struct IOAudio));
}

flash()
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  DisplayBeep(CursesScreen);
}

leaveok(WinPtr, flag)
     WINDOW *WinPtr;
     int flag;
{
  if(!(CursesFlags & CFLAG_INITSCR))  /* Haven't called initscr() */
    return ERR;
  
  if(flag) {
    CursorCol = CursorLine = -1;
    CursesFlags &= ~CFLAG_CURSOR;
  } else {
    CursesFlags |= CFLAG_CURSOR;
  }
  return OK;
}

resetty()
{
  return OK;
}

savetty()
{
  return OK;
}

resetterm()
{
  return OK;
}

fixterm()
{
  return OK;
}

saveterm()
{
  return OK;
}

baudrate()
{
  return 9600;
}

nl()
{
  CursesFlags |= CFLAG_NLCR;
  return OK;
}

nonl()
{
  CursesFlags &= ~CFLAG_NLCR;
  return OK;
}

crmode()
{
  return (cbreak());
}

nocrmode()
{
  return (nocbreak());
}

box(WinPtr, vert, hor)
     WINDOW *WinPtr;
     char vert, hor;
{
  int i;
  short CurY, CurX;
  
  CurY = WinPtr->_cury;
  CurX = WinPtr->_curx;
  
  if(vert < 32 || vert > 126)
    vert = '|';
  if(hor < 32 || hor > 126)
    hor = '-';
  
  for(i=0; i<=WinPtr->_maxx; i++) {
    mvwaddch(WinPtr, 0, i, hor);  /* Top horizontal */
    mvwaddch(WinPtr, WinPtr->_maxy, i, hor);  /* Bottom horizontal */
  }
  for(i=1; i<WinPtr->_maxy; i++) {
    mvwaddch(WinPtr, i, 0, vert);  /* Left vertical */
    mvwaddch(WinPtr, i, WinPtr->_maxx, vert);  /* Right vertical */
  }
  WinPtr -> _cury = CurY;
  WinPtr -> _curx = CurX;
  return OK;
}

WINDOW *subwin(Orig, NLines, NCols, StartLine, StartCol)
     WINDOW *Orig;
     unsigned int NLines, NCols, StartLine, StartCol;
{
  WINDOW *WinPtr, *CreatWindow();
  
  if(!(WinPtr = CreatWindow(NLines, NCols, StartLine, StartCol, Orig))) {
    printf("WARNING - subwin() failed, returning stdscr !!\n");
    return stdscr;  /* Failed */
  }
  return WinPtr;
}

WINDOW *newwin(NLines, NCols, StartLine, StartCol)
     unsigned int NLines, NCols, StartLine, StartCol;
{
  WINDOW *WinPtr, *CreatWindow();
  
  if(!(WinPtr = CreatWindow(NLines, NCols, StartLine, StartCol, NULL))) {
    printf("WARNING - newwin() failed, returning stdscr !!\n");
    return stdscr;  /* Failed */
  }
  wclear(WinPtr);
  return WinPtr;
}

/* Orig is NULL and StartCol/Line are not used for newwin() calls */
static WINDOW *CreatWindow(NLines, NCols, StartLine, StartCol, Orig)
     int NLines, NCols, StartLine, StartCol;
     WINDOW *Orig;
{
  int Line, j;
  struct WindowState *NewWinStat, *TmpWinPtr, *OrgWinStat = NULL;
  char *malloc();
  
  /* If either are zero then make them max */
  if(!NLines)
    NLines = LINES - StartLine;
  if(!NCols)
    NCols = COLS - StartCol;
  
  if(NLines>LINES || NCols>COLS || StartLine>LINES || StartCol>COLS)
    return (struct WINDOW *)NULL;
  
  if(StartLine < 0)
    StartLine = 0;
  if(StartCol < 0)
    StartCol = 0;
  if(Orig)
    OrgWinStat = (struct WindowState *)Orig->_WinStat;
  
  /* Create a new WinStat structure */
  if((NewWinStat=(struct WindowState *)malloc(sizeof(struct WindowState)))
     == (struct WindowState *)NULL) {
    fprintf(stderr, "CreatWindow() - Not enough memory\n");
    return (struct WINDOW *)NULL;
  }
  NewWinStat->ParentWin = Orig;
  NewWinStat->Next = (struct WindowState *)NULL;
  NewWinStat->Prev = (struct WindowState *)NULL;
  NewWinStat->ScrollTop = 0;
  NewWinStat->ScrollBot = NLines - 1;
  NewWinStat->NLines = NLines;
  /* Allocate space for LnArry[] */
  if(!(NewWinStat->LnArry = (struct LineElement *)malloc(sizeof(struct LineElement)*LINES))) {
    fprintf(stderr, "CreatWindow() - Not enough memory\n");
    return (struct WINDOW *)NULL;
  }
  /* Allocate space for Line, LRLine e.t.c */
  for(Line = 0; Line < NLines; Line++) {
    if(OrgWinStat) {  /* If this is a subwindow */
      /* Set up pointers into parent window */
      NewWinStat->LnArry[Line].Line =
	&OrgWinStat->LnArry[Line+StartLine].Line[StartCol];
      NewWinStat->LnArry[Line].LRLine =
	&OrgWinStat->LnArry[Line+StartLine].LRLine[StartCol];
      NewWinStat->LnArry[Line].ATTRS =
	&OrgWinStat->LnArry[Line+StartLine].ATTRS[StartCol];
      NewWinStat->LnArry[Line].LRATTRS =
	&OrgWinStat->LnArry[Line+StartLine].LRATTRS[StartCol];
    } else {  /* New window, allocate space for lines */
      /* malloc lines and ATTRS */
      if((NewWinStat->LnArry[Line].Line = malloc(NCols)) == NULL) {
	fprintf(stderr, "CreatWindow() - Not enough memory\n");
	return (struct WINDOW *)NULL;
      }
      if((NewWinStat->LnArry[Line].LRLine = malloc(NCols)) == NULL) {
	fprintf(stderr, "CreatWindow() - Not enough memory\n");
	return (struct WINDOW *)NULL;
      }
      if((NewWinStat->LnArry[Line].ATTRS =
	  (short *)malloc(NCols*sizeof(short))) == NULL) {
	fprintf(stderr, "CreatWindow() - Not enough memory\n");
	return (struct WINDOW *)NULL;
      }
      if((NewWinStat->LnArry[Line].LRATTRS =
	  (short *)malloc(NCols*sizeof(short))) == NULL) {
	fprintf(stderr, "CreatWindow() - Not enough memory\n");
	return (struct WINDOW *)NULL;
      }
      /* The optimiser will untouch any lines not used */
      memset(NewWinStat->LnArry[Line].LRLine, ' ', NCols);
      for(j=0; j<NCols; j++)
	NewWinStat->LnArry[Line].LRATTRS[j] = A_NORMAL | COLOR_WHITE;
    }
    NewWinStat->LnArry[Line].Touched = FALSE;
    NewWinStat->LnArry[Line].StartCol = NCols;
    NewWinStat->LnArry[Line].EndCol = 0;
  }
  /* Add to Window Stat list */
  if(HeadWindowList) {
    TmpWinPtr = HeadWindowList;
    while(TmpWinPtr->Next)
      TmpWinPtr = TmpWinPtr->Next;
    TmpWinPtr->Next = NewWinStat;
    NewWinStat->Prev = TmpWinPtr;
  } else {
    /* This is the first window i.e. stdscr */
    HeadWindowList = NewWinStat;
  }
  /* Initialise state of the window structure */
  NewWinStat->Window._cury = 0;
  NewWinStat->Window._curx = 0;
  NewWinStat->Window._maxy = NLines - 1;
  NewWinStat->Window._maxx = NCols - 1;
  NewWinStat->Window._begy = StartLine;
  NewWinStat->Window._begx = StartCol;
  NewWinStat->Window._flags = 0;
  NewWinStat->Window._attrs = A_NORMAL | COLOR_WHITE;
  NewWinStat->Window._clear = FALSE;
  NewWinStat->Window._cls = TRUE;
  NewWinStat->Window._scroll = FALSE;
  NewWinStat->Window._use_keypad = 0;
  NewWinStat->Window._use_meta = 0;
  NewWinStat->Window._nodelay = 0;
  NewWinStat->Window._WinStat = (char *)NewWinStat;
  
  return &NewWinStat->Window;
}

touchwin(WinPtr)
     WINDOW *WinPtr;
{
  struct WindowState *WinStat;
  int Line;
  
  if(!(CursesFlags & CFLAG_INITSCR))
    return ERR;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  for(Line=0; Line<WinStat->NLines; Line++) {
    WinStat->LnArry[Line].Touched = TRUE;
    /* Mark whole line as refreshable */
    WinStat->LnArry[Line].StartCol = 0;
    WinStat->LnArry[Line].EndCol = WinPtr->_maxx;
    /* Dump optimisation */
    memset(WinStat->LnArry[Line].LRLine, 0, WinPtr->_maxx+1);
  }
  return OK;
}

delwin(WinPtr)
     WINDOW *WinPtr;
{
  struct WindowState *WinStat;
  int LineNo;
  
  if(!(CursesFlags & CFLAG_INITSCR))
    return ERR;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  if(!WinStat->ParentWin) {
    /* If it's a real window, free up Line, LRLine, ATTRS, LRATTRS */
    for(LineNo=0; LineNo<WinStat->NLines; LineNo++) {
      free(WinStat->LnArry[LineNo].Line);
      free(WinStat->LnArry[LineNo].LRLine);
      free(WinStat->LnArry[LineNo].ATTRS);
      free(WinStat->LnArry[LineNo].LRATTRS);
    }
  }
  /* Free up LnArry[] */
  free(WinStat->LnArry);
  /* Remove the winstat from the list */
  if(WinStat == HeadWindowList) { /* if this is first window (stdscr) */
    HeadWindowList = WinStat->Next;
    if(HeadWindowList)
      HeadWindowList->Prev = (struct WindowState *)NULL;
  } else {
    if(WinStat->Next)
      WinStat->Next->Prev = WinStat->Prev;
    if(WinStat->Prev)
      WinStat->Prev->Next = WinStat->Next;
  }
  /* Free the winstat */
  free(WinStat);
  return OK;
}


mvwin(WinPtr, NewLine, NewCol)
     WINDOW *WinPtr;
     short NewLine, NewCol;
{
  if(!(CursesFlags & CFLAG_INITSCR))
    return ERR;
  
  WinPtr->_begx = NewCol;
  WinPtr->_begy = NewLine;
  if(touchwin(WinPtr) == ERR)
    return ERR;
  return OK;
}

scroll(WinPtr)
     WINDOW *WinPtr;
{
  struct WindowState *WinStat;
  
  if(!(CursesFlags & CFLAG_INITSCR))
    return ERR;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  Scroll(WinPtr, WinStat->ScrollTop, WinStat->ScrollBot, SCROLL_UP);
  return OK;
}

static Scroll(WinPtr, Top, Bottom, Direction)
     WINDOW *WinPtr;
     int Top, Bottom, Direction;
{
  int Step, SLine, DLine, Col;
  char *TLine, *TLRLine;
  short *TATTRS, *TLRATTRS;
  struct WindowState *WinStat;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  
  /* Store pointers to line that will be lost */
  if(Direction == SCROLL_UP) {
    Step = +1;
    DLine = Top;
    SLine = Top + Step;
  } else {
    Step = -1;
    DLine = Bottom;
    SLine = Bottom + Step;
  }
  TLine = WinStat->LnArry[DLine].Line;
  TLRLine = WinStat->LnArry[DLine].LRLine;
  TATTRS = WinStat->LnArry[DLine].ATTRS;
  TLRATTRS = WinStat->LnArry[DLine].LRATTRS;
  /* Move the lines */
  for(;;) {
    if((Direction == SCROLL_UP) && (DLine >= Bottom))
      break;  /* Done */
    if((Direction == SCROLL_DOWN) && (DLine <= Top))
      break;  /* Done */
    WinStat->LnArry[DLine].Line = WinStat->LnArry[SLine].Line;
    WinStat->LnArry[DLine].LRLine = WinStat->LnArry[SLine].LRLine;
    WinStat->LnArry[DLine].ATTRS = WinStat->LnArry[SLine].ATTRS;
    WinStat->LnArry[DLine].LRATTRS = WinStat->LnArry[SLine].LRATTRS;
    for(Col=0; Col <= WinStat->Window._maxx; Col++) {
      WinStat->LnArry[DLine].ATTRS[Col] = WinStat->Window._attrs;
      WinStat->LnArry[DLine].LRATTRS[Col] = 0;
    }
    SLine += Step;
    DLine += Step;
  }
  /* Blank the Temp line */
  memset(TLine, ' ', WinStat->Window._maxx+1);
  memset(TLRLine, 0, WinStat->Window._maxx+1);
  for(Col=0; Col <= WinStat->Window._maxx; Col++) {
    TATTRS[Col] = WinStat->Window._attrs;
    TLRATTRS[Col] = 0;
  }
  /* move in temp line */
  WinStat->LnArry[DLine].Line = TLine;
  WinStat->LnArry[DLine].LRLine = TLRLine;
  WinStat->LnArry[DLine].ATTRS = TATTRS;
  WinStat->LnArry[DLine].LRATTRS = TLRATTRS;
  
  return OK;
}

wsetscrreg(WinPtr, top, bottom)
     WINDOW *WinPtr;
     short top, bottom;
{
  struct WindowState *WinStat;
  
  if(!(WinStat = (struct WindowState *)WinPtr->_WinStat))
    return ERR;
  WinStat->ScrollTop = top;
  WinStat->ScrollBot = bottom;
  return OK;
}

scrollok(WinPtr, flag)
     WINDOW *WinPtr;
     int flag;
{
  WinPtr->_scroll = (flag) ? TRUE : FALSE;
  return OK;
}

/*
 *  Simple implementation of wnoutrefresh() and doupdate()
 *
 * Date: 8th Oct 1990
 *
 * Author: SJR
 *
 */

wnoutrefresh(WinPtr)
     WINDOW *WinPtr;
{
  struct RefreshElement *NewRefEl;
  
  if(!(NewRefEl = (struct RefreshElement *)
       malloc(sizeof(struct RefreshElement))))
    return ERR;
  
  /* Fill the new element in */
  NewRefEl->Next = (struct RefreshElement *)NULL;
  NewRefEl->WinPtr = WinPtr;
  
  /* Add to start of refresh list */
  if(HeadRefreshList)
    NewRefEl->Next = HeadRefreshList;
  HeadRefreshList = NewRefEl;
}

doupdate()
{
  struct RefreshElement *ElPtr;
  void ZapRElList();
  
  ElPtr = HeadRefreshList;
  while(ElPtr) {
    if(wrefresh(ElPtr->WinPtr) == ERR)
      return ERR;
    ElPtr = ElPtr->Next;
  }
  ZapRElList(HeadRefreshList);
  HeadRefreshList = (struct RefreshElement *)NULL;
  
  return OK;
}

static void ZapRElList(ElPtr)
     struct RefreshElement *ElPtr;
{
  if(!ElPtr)
    return;
  if(ElPtr->Next)
    ZapRElList(ElPtr->Next);  /* Recurse my boy */
  free(ElPtr);
}

static void Optimise(LinePtr)
     struct LineElement *LinePtr;
{
  int i;
  
  if(!LinePtr->Touched)
    return;
  for(i=LinePtr->StartCol; i<=LinePtr->EndCol; i++) {
    if((LinePtr->Line[i] != LinePtr->LRLine[i]) ||
       (LinePtr->ATTRS[i] != LinePtr->LRATTRS[i]))
      break;
    LinePtr->StartCol++;
  }
  for(i=LinePtr->EndCol; i>=LinePtr->StartCol; i--) {
    if((LinePtr->Line[i] != LinePtr->LRLine[i]) ||
       (LinePtr->ATTRS[i] != LinePtr->LRATTRS[i]))
      break;
    LinePtr->EndCol--;
  }
  if(LinePtr->StartCol > LinePtr->EndCol)
    LinePtr->Touched = FALSE;
}
