/*
Copyright (C) 1998-99 Free Software Foundation, Inc.

Author: Frank Heckenbach <frank@pascal.gnu.de>

Support routines for the CRT unit

This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
the Free Software Foundation, version 2.

This 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 Library General Public
License for more details.

You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.
*/

#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "crtc.h"

#ifdef USE_PDCURSES
#ifdef XCURSES
#include <xcurses.h>
char *XCursesProgramName;
#else
#include <curses.h>
#endif
#define REVERSE_WORKS FALSE
#define INVIS_WORKS   TRUE
#define HAVE_RAW_OUTPUT
#define FKEYSH   13
#define FKEYCTRL 25
#define FKEYALT  37
#endif

#ifdef USE_NCURSES
#include <ncurses.h>
#define REVERSE_WORKS TRUE
#define INVIS_WORKS   FALSE
#undef  HAVE_RAW_OUTPUT
#define FKEYSH   11
#define FKEYCTRL 21
#define FKEYALT  31
#endif

#ifndef A_ALTCHARSET
#define A_ALTCHARSET 0
#endif

typedef unsigned char Char;
typedef unsigned char Boolean;
typedef unsigned char TTextAttr;
typedef unsigned int  TKey;
typedef enum { CursorHidden, CursorNormal, CursorFat, CursorBlock } TCursorShape;
typedef struct { int x, y; } TPoint;
typedef struct { Char Ch; TTextAttr Attr; } TCharAttr;

#define MAXLENGTH 4096
Char line_buf [MAXLENGTH], *line_buf_pos;
int line_buf_count = 0;

extern void crt_int_handler  (int sig);
extern void crt_term_handler (int sig);

extern Boolean      Checkbreak;
extern Boolean      Checkeof;
extern Boolean      Pccharset;
extern Boolean      Usecontrolchars;
extern Boolean      Visualbell;
extern int          Shiftstate;
extern TPoint       Screensize;
extern TTextAttr    Textattr;
extern unsigned int Lastmode, Windmin, Windmax;

#define WindXMin (Windmin & 0xff)
#define WindXMax (Windmax & 0xff)
#define WindYMin (Windmin >> 8)
#define WindYMax (Windmax >> 8)

TCursorShape    crt_CursorShape;
TTextAttr       crt_NormAttr;
Boolean         crt_ColorFlag, crt_HasColors, crt_linux_console, crt_LastCheckBreak = - 1;
unsigned        crt_LastWindMin, crt_LastWindMax;
int             crt_inited = 0;
int             crt_update_level = 0;
TKey            crt_unget_key_buf = 0;
int             crt_key_buf = 0;
Char            crt_fkey_buf = 0;
chtype          crt_attrs [8] [8], crt_monoattrs [8] [8];
Boolean         crt_pending_update = FALSE;
WINDOW         *crt_w = 0;
int             crt_colors [8] = { COLOR_BLACK, COLOR_BLUE,    COLOR_GREEN,  COLOR_CYAN,
                                   COLOR_RED,   COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE };

typedef struct
{
  int curses_key;
  Char crt_key;
} tkeytable;

static tkeytable keytable [] =
{
  { KEY_BACKSPACE, chBkSp },
  { KEY_SUSPEND,   26     },
  #if !defined(USE_PDCURSES) || defined(XCURSES)
  { '\n',          chCR   },
  { '\r',          chLF   },
  #endif
  #ifdef USE_PDCURSES
  { PADSTAR,       '*'    },
  { PADMINUS,      '-'    },
  { PADPLUS,       '+'    },
  { CTL_ENTER,     chLF   },
  { CTL_PADSTAR,   chLF   },
  { CTL_PADMINUS,  chCR   },
  { CTL_PADPLUS,   11     },
  #endif
  { 0,             0      }
};

static tkeytable fkeytable [] =
{
  { KEY_BTAB,      ksShTab     },
  { KEY_LEFT,      ksLeft      },
  { KEY_RIGHT,     ksRight     },
  { KEY_UP,        ksUp        },
  { KEY_DOWN,      ksDown      },
  { KEY_A3,        ksPgUp      },
  { KEY_PPAGE,     ksPgUp      },
  { KEY_C3,        ksPgDn      },
  { KEY_NPAGE,     ksPgDn      },
  { KEY_A1,        ksHome      },
  { KEY_HOME,      ksHome      },
  { KEY_C1,        ksEnd       },
  { KEY_END,       ksEnd       },
  { KEY_IC,        ksIns       },
  { KEY_DC,        ksDel       },
  { KEY_B2,        ksCenter    },
  { KEY_SLEFT,     ksCtrlLeft  },
  { KEY_SRIGHT,    ksCtrlRight },
  { KEY_SHOME,     ksCtrlHome  },
  { KEY_SEND,      ksCtrlEnd   },
  { KEY_SDC,       ksCtrlDel   },
  { KEY_SIC,       ksCtrlIns   },
  #ifdef USE_PDCURSES
  { CTL_TAB,       ksCtrlTab   },
  { ALT_TAB,       ksAltTab    },
  { KEY_A2,        ksUp        },
  { KEY_B1,        ksLeft      },
  { KEY_B3,        ksRight     },
  { KEY_C2,        ksDown      },
  { CTL_LEFT,      ksCtrlLeft  },
  { CTL_RIGHT,     ksCtrlRight },
  { CTL_UP,        ksCtrlUp    },
  { CTL_DOWN,      ksCtrlDown  },
  { CTL_PGUP,      ksCtrlPgUp  },
  { CTL_PGDN,      ksCtrlPgDn  },
  { CTL_HOME,      ksCtrlHome  },
  { CTL_END,       ksCtrlEnd   },
  { CTL_INS,       ksCtrlIns   },
  { CTL_PADSTOP,   ksCtrlDel   },
  { CTL_PADCENTER, ksCtrlCentr },
  { ALT_LEFT,      ksAltLeft   },
  { ALT_RIGHT,     ksAltRight  },
  { ALT_UP,        ksAltUp     },
  { ALT_DOWN,      ksAltDown   },
  { ALT_PGUP,      ksAltPgUp   },
  { ALT_PGDN,      ksAltPgDn   },
  { ALT_HOME,      ksAltHome   },
  { ALT_END,       ksAltEnd    },
  { ALT_INS,       ksAltIns    },
  { ALT_DEL,       ksAltDel    },
  { ALT_ENTER,     ksAltEnter  },
  { ALT_PADENTER,  ksAltEnter  },
  { ALT_PADSTAR,   ksAltPStar  },
  { ALT_PADMINUS,  ksAltPMinus },
  { ALT_PADPLUS,   ksAltPPlus  },
  { ALT_ESC,       ksAltEsc    },
  { ALT_BKSP,      ksAltBkSp   },
  { ALT_MINUS,     ksAltMinus  },
  { ALT_EQUAL,     ksAltEqual  },
  { ALT_LBRACKET,  ksAltLBrack },
  { ALT_RBRACKET,  ksAltRBrack },
  { ALT_SEMICOLON, ksAltSemic  },
  { ALT_FQUOTE,    ksAltFQuote },
  { ALT_BQUOTE,    ksAltBQuote },
  { ALT_COMMA,     ksAltComma  },
  { ALT_STOP,      ksAltStop   },
  { ALT_FSLASH,    ksAltFSlash },
  { ALT_BSLASH,    ksAltBSlash },
  { ALT_A,         ksAltA      },
  { ALT_B,         ksAltB      },
  { ALT_C,         ksAltC      },
  { ALT_D,         ksAltD      },
  { ALT_E,         ksAltE      },
  { ALT_F,         ksAltF      },
  { ALT_G,         ksAltG      },
  { ALT_H,         ksAltH      },
  { ALT_I,         ksAltI      },
  { ALT_J,         ksAltJ      },
  { ALT_K,         ksAltK      },
  { ALT_L,         ksAltL      },
  { ALT_M,         ksAltM      },
  { ALT_N,         ksAltN      },
  { ALT_O,         ksAltO      },
  { ALT_P,         ksAltP      },
  { ALT_Q,         ksAltQ      },
  { ALT_R,         ksAltR      },
  { ALT_S,         ksAltS      },
  { ALT_T,         ksAltT      },
  { ALT_U,         ksAltU      },
  { ALT_V,         ksAltV      },
  { ALT_W,         ksAltW      },
  { ALT_X,         ksAltX      },
  { ALT_Y,         ksAltY      },
  { ALT_Z,         ksAltZ      },
  { ALT_0,         ksAlt0      },
  { ALT_1,         ksAlt1      },
  { ALT_2,         ksAlt2      },
  { ALT_3,         ksAlt3      },
  { ALT_4,         ksAlt4      },
  { ALT_5,         ksAlt5      },
  { ALT_6,         ksAlt6      },
  { ALT_7,         ksAlt7      },
  { ALT_8,         ksAlt8      },
  { ALT_9,         ksAlt9      },
  { KEY_F (11),    ksF11       },
  { KEY_F (12),    ksF12       },
  { KEY_F (23),    ksShF11     },
  { KEY_F (24),    ksShF12     },
  { KEY_F (35),    ksCtrlF11   },
  { KEY_F (36),    ksCtrlF12   },
  { KEY_F (47),    ksAltF11    },
  { KEY_F (48),    ksAltF12    },
  #endif
  { 0,             ksUnknown   }
};

static tkeytable shiftkeytable [] =
{
  { ksIns, ksShIns },
  { ksDel, ksShDel },
  { 0,     0       }
};

static tkeytable ctrlkeytable [] =
{
  { ksLeft,   ksCtrlLeft  },
  { ksRight,  ksCtrlRight },
  { ksUp,     ksCtrlUp    },
  { ksDown,   ksCtrlDown  },
  { ksPgUp,   ksCtrlPgUp  },
  { ksPgDn,   ksCtrlPgDn  },
  { ksHome,   ksCtrlHome  },
  { ksEnd,    ksCtrlEnd   },
  { ksIns,    ksCtrlIns   },
  { ksDel,    ksCtrlDel   },
  { ksCenter, ksCtrlCentr },
  { 0,        0           }
};

static tkeytable altkeytable [] =
{
  { ksLeft,   ksAltLeft  },
  { ksRight,  ksAltRight },
  { ksUp,     ksAltUp    },
  { ksDown,   ksAltDown  },
  { ksPgUp,   ksAltPgUp  },
  { ksPgDn,   ksAltPgDn  },
  { ksHome,   ksAltHome  },
  { ksEnd,    ksAltEnd   },
  { ksIns,    ksAltIns   },
  { ksDel,    ksAltDel   },
  { 0,        0          }
};

#ifndef USE_PDCURSES
static tkeytable esckeytable [] =
{
  { KEY_BACKSPACE, ksAltBkSp   },
  { chBkSp,        ksAltBkSp   },
  { chTab,         ksAltTab    },
  { chLF,          ksAltEnter  },
  { chCR,          ksAltEnter  },
  { chEsc,         ksAltEsc    },
  { ' ',           ksAltSpace  },
  { '-',           ksAltMinus  },
  { '=',           ksAltEqual  },
  { '[',           ksAltLBrack },
  { ']',           ksAltRBrack },
  { ';',           ksAltSemic  },
  { '\'',          ksAltFQuote },
  { '`',           ksAltBQuote },
  { ',',           ksAltComma  },
  { '.',           ksAltStop   },
  { '/',           ksAltFSlash },
  { '\\',          ksAltBSlash },
  { 'A',           ksAltA      },
  { 'B',           ksAltB      },
  { 'C',           ksAltC      },
  { 'D',           ksAltD      },
  { 'E',           ksAltE      },
  { 'F',           ksAltF      },
  { 'G',           ksAltG      },
  { 'H',           ksAltH      },
  { 'I',           ksAltI      },
  { 'J',           ksAltJ      },
  { 'K',           ksAltK      },
  { 'L',           ksAltL      },
  { 'M',           ksAltM      },
  { 'N',           ksAltN      },
  { 'O',           ksAltO      },
  { 'P',           ksAltP      },
  { 'Q',           ksAltQ      },
  { 'R',           ksAltR      },
  { 'S',           ksAltS      },
  { 'T',           ksAltT      },
  { 'U',           ksAltU      },
  { 'V',           ksAltV      },
  { 'W',           ksAltW      },
  { 'X',           ksAltX      },
  { 'Y',           ksAltY      },
  { 'Z',           ksAltZ      },
  { 'a',           ksAltA      },
  { 'b',           ksAltB      },
  { 'c',           ksAltC      },
  { 'd',           ksAltD      },
  { 'e',           ksAltE      },
  { 'f',           ksAltF      },
  { 'g',           ksAltG      },
  { 'h',           ksAltH      },
  { 'i',           ksAltI      },
  { 'j',           ksAltJ      },
  { 'k',           ksAltK      },
  { 'l',           ksAltL      },
  { 'm',           ksAltM      },
  { 'n',           ksAltN      },
  { 'o',           ksAltO      },
  { 'p',           ksAltP      },
  { 'q',           ksAltQ      },
  { 'r',           ksAltR      },
  { 's',           ksAltS      },
  { 't',           ksAltT      },
  { 'u',           ksAltU      },
  { 'v',           ksAltV      },
  { 'w',           ksAltW      },
  { 'x',           ksAltX      },
  { 'y',           ksAltY      },
  { 'z',           ksAltZ      },
  { '0',           ksAlt0      },
  { '1',           ksAlt1      },
  { '2',           ksAlt2      },
  { '3',           ksAlt3      },
  { '4',           ksAlt4      },
  { '5',           ksAlt5      },
  { '6',           ksAlt6      },
  { '7',           ksAlt7      },
  { '8',           ksAlt8      },
  { '9',           ksAlt9      },
  { 0,             0           }
};

static chtype chars_0_31 [32] =
{
  ' ', 'O', 'O', '%',   0, '#', '*', 'o', 'o', 'o', 'o', '/',
  '+', 'd', 'A', '*',   0,   0,   0, '!', '#', '$'
};

static chtype *chars_0_31_p [32] =
{
  0, 0, 0, 0,
  &ACS_DIAMOND, 0, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  &ACS_RARROW, &ACS_LARROW, &ACS_VLINE, 0,
  0, 0, &ACS_S9, &ACS_DARROW,
  &ACS_UARROW, &ACS_DARROW, &ACS_RARROW, &ACS_LARROW,
  &ACS_LLCORNER, &ACS_HLINE, &ACS_UARROW, &ACS_DARROW
};

static chtype chars_128_255 [128] =
{
  'C', 'u', 'e', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'i',
  'i', 'i', 'A', 'A', 'E', 'a', 'A', 'o', 'o', 'o', 'u', 'u',
  'y', 'O', 'U', 'C',   0, 'Y', 'P', 'f', 'a', 'i', 'o', 'u',
  'n', 'N',   0,   0, '?',   0,   0, '/', '/', '!',   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  'a', 'B', 'T',   0, 'E', 'o', 'u', 't', 'O', 'O', 'O', 'O',
  'o', 'o', 'E', 'A', '=',   0,   0,   0, '/', '/', '%', '=',
    0, '.', '.', 'V', 'n', '2',   0, ' '
};

static chtype *chars_156_255_p [100] =
{
  &ACS_STERLING, 0, 0, 0,
  0, 0, 0, 0,
  0, 0, &ACS_DEGREE, &ACS_DEGREE,
  0, &ACS_ULCORNER, &ACS_URCORNER, 0,
  0, 0, &ACS_LARROW, &ACS_RARROW,
  &ACS_CKBOARD, &ACS_CKBOARD, &ACS_CKBOARD, &ACS_VLINE,
  &ACS_RTEE, &ACS_RTEE, &ACS_RTEE, &ACS_URCORNER,
  &ACS_URCORNER, &ACS_RTEE, &ACS_VLINE, &ACS_URCORNER,
  &ACS_LRCORNER, &ACS_LRCORNER, &ACS_LRCORNER, &ACS_URCORNER,
  &ACS_LLCORNER, &ACS_BTEE, &ACS_TTEE, &ACS_LTEE,
  &ACS_HLINE, &ACS_PLUS, &ACS_LTEE, &ACS_LTEE,
  &ACS_LLCORNER, &ACS_ULCORNER, &ACS_BTEE, &ACS_TTEE,
  &ACS_LTEE, &ACS_HLINE, &ACS_PLUS, &ACS_BTEE,
  &ACS_BTEE, &ACS_TTEE, &ACS_TTEE, &ACS_LLCORNER,
  &ACS_LLCORNER, &ACS_ULCORNER, &ACS_ULCORNER, &ACS_PLUS,
  &ACS_PLUS, &ACS_LRCORNER, &ACS_ULCORNER, &ACS_BLOCK,
  &ACS_BLOCK, &ACS_BLOCK, &ACS_BLOCK, &ACS_BLOCK,
  0, 0, 0, &ACS_PI,
  0, 0, 0, 0,
  0, 0, 0, 0,
  0, 0, 0, 0,
  0, &ACS_PLMINUS, &ACS_GEQUAL, &ACS_LEQUAL,
  0, 0, 0, 0,
  &ACS_DEGREE, 0, 0, 0,
  0, 0, &ACS_BULLET, 0
};
#endif

static inline void crt_setcbreak ()
{
  if (Checkbreak)
    {
      noraw ();
      cbreak ();
    }
  else
    raw ();
}

static inline void crt_nodelay (bool bf)
{
  crt_setcbreak ();
  nodelay (crt_w, bf);
}

static void crt_checkcheckbreak ()
{
  if (Checkbreak != crt_LastCheckBreak)
    {
      crt_LastCheckBreak = Checkbreak;
      crt_setcbreak ();
    }
}

/* Must be re-entrant! */
static inline void crt_update ()
{
  static int semaphore = 0;
  if (semaphore == 0)
    {
      semaphore++;
      crt_pending_update = FALSE;
      wnoutrefresh (crt_w);
      doupdate ();
      if (idlok (crt_w, FALSE));
      semaphore--;
    }
}

void crt_delete_w (void);
void crt_delete_w ()
{
  if (crt_w)
    {
      wnoutrefresh (crt_w);
      if (crt_update_level >= 3) doupdate ();
      delwin (crt_w);
      crt_w = 0;
    }
}

/* the value of the sig argument is ignored -- it just makes it easier
   to use this function as a signal handler */
static void screen_size_changed (int sig)
{
  #ifndef USE_PDCURSES
  endwin ();
  #endif
  refresh ();
  getmaxyx (stdscr, Screensize.y, Screensize.x);
}

void crt_ungetch (TKey ch);
void crt_ungetch (TKey ch)
{
  crt_unget_key_buf = ch;
}

void crt_settextmode (Boolean Columns40, Boolean Lines50);
int crt_getshiftstate (void);
void crt_sound (unsigned Hz);
void crt_nosound (void);

#if defined(__linux__) && defined(__i386__) && !defined(XCURSES)
#include "crtlinux386.h"
#elif defined(MSDOS) && defined(USE_PDCURSES)
#include "crtdospc.h"
#elif defined(__unix__)
#include "crtunix.h"
#else
#include "crtdummy.h"
#endif

void crt_scroll (Boolean state);
void crt_scroll (Boolean state)
{
  scrollok (crt_w, state);
}

static inline void crt_set_update ()
{
  crt_checkcheckbreak ();
  if (crt_update_level >= 3)
    crt_update ();
  else if (crt_update_level >= 2 && !crt_pending_update)
    {
      crt_pending_update = TRUE;
      crt_doupdate ();
    }
}

static void win_changed (Boolean first)
{
  int x1, y1, x2, y2, x, y, yc,
      LastXMin = crt_LastWindMin & 0xff,
      LastYMin = crt_LastWindMin >> 8,
      LastXSize = (crt_LastWindMax & 0xff) - LastXMin + 1,
      LastYSize = (crt_LastWindMax >> 8) - LastYMin + 1;
  chtype Buffer [LastYSize] [LastXSize + 1];
  x2 = WindXMax; if (x2 >= Screensize.x) x2 = Screensize.x - 1;
  y2 = WindYMax; if (y2 >= Screensize.y) y2 = Screensize.y - 1;
  x1 = WindXMin; if (x1 > x2)            x1 = x2;
  y1 = WindYMin; if (y1 > y2)            y1 = y2;
  if (crt_w)
    {
      getyx (crt_w, y, x);
      x += crt_LastWindMin & 0xff;
      y += crt_LastWindMin >> 8;
    }
  else
    {
      x = x1;
      y = y1;
    }
  /* Save window contents because not all curses packages keep them properly,
     e.g. PDCurses does not after scrolling in the last subwindow */
  if (!first)
    for (yc = 0; yc < LastYSize; yc++)
      mvwinchnstr (crt_w, yc, 0, Buffer [yc], LastXSize);
  crt_delete_w ();
  if (!first)
    for (yc = 0; yc < LastYSize; yc++)
      mvwaddchnstr (stdscr, yc + LastYMin, LastXMin, Buffer [yc], LastXSize);
  crt_w = subwin (stdscr, y2 - y1 + 1, x2 - x1 + 1, y1, x1);
  crt_scroll (TRUE);
  keypad (crt_w, TRUE);
  if (x >= x1 && y >= y1 && x <= x2 && y <= y2) wmove (crt_w, y - y1, x - x1);
  crt_LastWindMin = Windmin = x1 + (y1 << 8);
  crt_LastWindMax = Windmax = x2 + (y2 << 8);
  crt_set_update ();
}

void crt_init_textmode (void);
void crt_init_textmode ()
{
  Textattr = crt_NormAttr;
  getmaxyx (stdscr, Screensize.y, Screensize.x);
  crt_LastWindMin = 0;
  Windmin = 0;
  Windmax = (Screensize.x - 1) + 0x100 * (Screensize.y - 1);
  win_changed (1);
  if (crt_HasColors)
    {
      Lastmode = 0;
      if (crt_ColorFlag)      Lastmode += 1;
      if (Screensize.x >= 80) Lastmode += 2;
    }
  else
    Lastmode = 7;
  if (Screensize.y >= 43) Lastmode += 0x100;
}

void crt_done (void);
void crt_done ()
{
  if (!crt_inited) return;
  crt_special_done ();
  crt_delete_w ();
  doupdate ();
  endwin ();
  #ifdef XCURSES
  XCursesExit ();
  #endif
}

static inline void rawout (Boolean flag)
{
  #ifdef HAVE_RAW_OUTPUT
  raw_output (flag);
  #endif
}

/* Assign the Dos unit's variables to the following routines to be called
   before and after Exec. If Dos is not used, create dummies here. */
void (*Initexec)() __attribute__((weak));
void (*Doneexec)() __attribute__((weak));

void crt_init_exec (void);
void crt_init_exec ()
{
  if (crt_update_level == 2) crt_stop_update ();
  crt_update ();
  reset_shell_mode ();
}

void crt_done_exec (void);
void crt_done_exec ()
{
  reset_prog_mode ();
  clearok (curscr, TRUE);
  crt_set_update ();
}

void crt_setupdatelevel (int level);
void crt_setupdatelevel (int level)
{
  if (crt_update_level == 2)
    {
      crt_stop_update ();
      crt_update ();
    }
  crt_update_level = level;
  typeahead ((level >= 1) ? -1 : 0);
}

void crt_init (void);
void crt_init ()
{
  int fg, bg, c;
  #ifdef XCURSES
  extern char **_p_argv;
  XCursesProgramName = _p_argv [0];
  #endif
  if (!initscr ()) exit (1);
  crt_inited = 1;
  crt_linux_console = strcmp (termname (), "linux") == 0;
  #ifndef USE_PDCURSES
  {
    int i;
    for (i = 0; i < 32; i++)
      if (chars_0_31_p [i])
        chars_0_31 [i] = *chars_0_31_p [i];
    for (i = 0; i < 100; i++)
      if (chars_156_255_p [i])
        chars_128_255 [i + 28] = *chars_156_255_p [i];
  }
  #endif
  cbreak ();
  noecho ();
  scrollok (stdscr, TRUE);
  rawout (TRUE);
  crt_CursorShape = CursorNormal;
  curs_set (1);
  crt_ColorFlag = crt_HasColors = has_colors();
  if (crt_ColorFlag)
    {
      start_color ();
      c = 0;
      for (bg = 0; bg < 8; bg++)
        for (fg = 0; fg < 8; fg++)
          if (INVIS_WORKS && bg == fg && bg > 0)
            crt_attrs [fg] [bg] = crt_attrs [0] [bg] | A_INVIS;
          else if (REVERSE_WORKS && fg < bg)
            crt_attrs [fg] [bg] = crt_attrs [bg] [fg] | A_REVERSE;
          else
            {
              if (init_pair (++c, crt_colors [fg], crt_colors [bg]) == ERR)
                {
                  crt_done ();
                  fprintf (stderr, "Could not create color pair (%i,%i).", fg, bg);
                  exit (1);
                }
              crt_attrs [fg] [bg] = COLOR_PAIR (c);
            }
      for (bg = 0; bg < 8; bg++)
        for (fg = 0; fg < 8; fg++)
          crt_monoattrs [fg] [bg] = crt_attrs [7] [0];
      crt_monoattrs [0] [0] = crt_attrs [0] [0];
      crt_monoattrs [1] [0] = crt_attrs [1] [0];
      crt_monoattrs [0] [7] = crt_attrs [0] [7];
    }
  else
    {
      for (bg = 0; bg < 8; bg++)
        for (fg = 0; fg < 8; fg++)
          crt_monoattrs [fg] [bg] = A_NORMAL;
      crt_monoattrs [0] [0] = A_INVIS;
      crt_monoattrs [1] [0] = A_UNDERLINE;
      crt_monoattrs [0] [7] = A_REVERSE;
    }
  crt_init_textmode ();
  crt_special_init ();
  crt_setupdatelevel (2);
  Initexec = &crt_init_exec;
  Doneexec = &crt_done_exec;
}

static int attr2cursattr (TTextAttr attr)
{
  return (crt_ColorFlag ? crt_attrs : crt_monoattrs) [attr & 7] [(attr >> 4) & 7] |
         (((attr & 8) && (crt_ColorFlag || (attr & 0x77))) ? A_BOLD : 0) |
         ((attr & 0x80) ? A_BLINK : 0);
}

static TTextAttr cursattr2attr (int cursattr)
{
  int attr;
  attr = 0;
  cursattr &= (A_ATTRIBUTES & ~A_ALTCHARSET);
  if (attr2cursattr (7) == cursattr) return 7;
  while (attr < 0x100 && attr2cursattr (attr) != cursattr) attr++;
  if (attr == 0x100)
    return 7;
  else
    return attr;
}

static void setattr ()
{
  int cursattr;
  if (Windmin != crt_LastWindMin || Windmax != crt_LastWindMax) win_changed (0);
  cursattr = attr2cursattr (Textattr);
  wattrset (crt_w, cursattr);
  wbkgdset (crt_w, cursattr);
}

Boolean crt_keypressed (void);
Boolean crt_keypressed ()
{
  int ch;
  if (crt_unget_key_buf || crt_key_buf || crt_fkey_buf) return 1;
  crt_nodelay (TRUE);
  ch = wgetch (crt_w);
  if (ch == ERR) return 0;
  crt_key_buf = ch;
  return 1;
}

static Char find_key (tkeytable *table, int ch)
{
  while (table -> curses_key != 0 && table -> curses_key != ch) table++;
  return table -> crt_key;
}

TKey crt_readkeyword (void);
TKey crt_readkeyword ()
{
  int ch;
  Char c, cs;
  setattr ();
  crt_update ();
  if (crt_unget_key_buf)
    {
      TKey k = crt_unget_key_buf;
      crt_unget_key_buf = 0;
      return k;
    }
  else if (crt_key_buf)
    {
      ch = crt_key_buf;
      crt_key_buf = 0;
    }
  else
    {
      /* halfdelay (2); */ crt_nodelay (FALSE);
      do
        {
          ch = wgetch (crt_w);
          if (crt_unget_key_buf)
            {
              TKey k = crt_unget_key_buf;
              crt_unget_key_buf = 0;
              if (ch != EOF && ch != k) crt_key_buf = ch;
              return k;
            }
        }
      while (ch == EOF);
    }
  #ifndef USE_PDCURSES
  if (ch == chEsc)
    {
      crt_nodelay (TRUE);
      ch = wgetch (crt_w);
      if (ch == ERR) return chEsc;
      c = find_key (esckeytable, ch);
      if (c != 0)
        return 0x100 * c;
      if (ch >= KEY_F (1) && ch <= KEY_F (10))
        return 0x100 * (ksAltF1 + (ch - KEY_F (1)));
      if (ch >= KEY_F (11) && ch <= KEY_F (12))
        return 0x100 * (ksAltF11 + (ch - KEY_F (11)));
      crt_key_buf = ch;
      return chEsc;
    }
  #endif
  c = find_key (keytable, ch);
  if (c != 0) return c;
  if (ch == chTab && (crt_getshiftstate () & shShift)) return 0x100 * ksShTab;
  if (1 <= ch && ch <= 0xff) return ch;
  if (ch >= KEY_F (1) && ch <= KEY_F (10))
    return 0x100 * (ksF1 + (ch - KEY_F (1)));
  if (ch >= KEY_F (FKEYSH) && ch <= KEY_F (FKEYSH + 9))
    return 0x100 * (ksShF1 + (ch - KEY_F (FKEYSH)));
  if (ch >= KEY_F (FKEYCTRL) && ch <= KEY_F (FKEYCTRL + 9))
    return 0x100 * (ksCtrlF1 + (ch - KEY_F (FKEYCTRL)));
  if (ch >= KEY_F (FKEYALT) && ch <= KEY_F (FKEYALT + 9))
    return 0x100 * (ksAltF1 + (ch - KEY_F (FKEYALT)));
  c = find_key (fkeytable, ch);
  cs = find_key (shiftkeytable, c);
  if (cs && (crt_getshiftstate () & shShift))
    c = cs;
  else
    {
      cs = find_key (ctrlkeytable, c);
      if (cs && (crt_getshiftstate () & shCtrl))
        c = cs;
      else
        {
          cs = find_key (altkeytable, c);
          if (cs && (crt_getshiftstate () & shAlt))
            c = cs;
        }
    }
  return 0x100 * c;
}

Char crt_readkey (void);
Char crt_readkey ()
{
  Char tmp;
  int ch;
  if (crt_fkey_buf)
    {
      tmp = crt_fkey_buf;
      crt_fkey_buf = 0;
      return tmp;
    }
  ch = crt_readkeyword ();
  if (ch & 0xff) return ch;
  crt_fkey_buf= ch / 0x100;
  return 0;
}

void crt_gotoxy (int x, int y);
void crt_gotoxy (int x, int y)
{
  setattr ();
  wmove (crt_w, y - 1, x - 1);
  crt_set_update ();
}

int crt_wherex (void);
int crt_wherex ()
{
  int x, y;
  setattr ();
  getyx (crt_w, y, x);
  return x + 1;
}

int crt_wherey (void);
int crt_wherey ()
{
  int x, y;
  setattr ();
  getyx (crt_w, y, x);
  return y + 1;
}

size_t crt_winsize (void);
size_t crt_winsize ()
{
  return (WindXMax - WindXMin + 1) * (WindYMax - WindYMin + 1) * sizeof (chtype);
}

void crt_readwin (chtype *buf);
void crt_readwin (chtype *buf)
{
  int xsize = WindXMax - WindXMin + 1, ysize = WindYMax - WindYMin + 1, yc, sx, sy;
  chtype temp [xsize + 1];
  setattr ();
  getyx (crt_w, sy, sx);
  for (yc = 0; yc < ysize; yc++)
    {
      mvwinchnstr (crt_w, yc, 0, temp, xsize);
      memcpy (buf + xsize * yc, temp, xsize * sizeof (chtype)); /* don't copy the 0 terminator! */
    }
  wmove (crt_w, sy, sx);
}

void crt_writewin (chtype *buf);
void crt_writewin (chtype *buf)
{
  int xsize = WindXMax - WindXMin + 1, ysize = WindYMax - WindYMin + 1, yc, sx, sy;
  setattr ();
  getyx (crt_w, sy, sx);
  for (yc = 0; yc < ysize; yc++)
    mvwaddchnstr (crt_w, yc, 0, buf + xsize * yc, xsize);
  wmove (crt_w, sy, sx);
  crt_set_update ();
}

void crt_clrscr (void);
void crt_clrscr ()
{
  setattr ();
  werase (crt_w);
  crt_set_update ();
}

void crt_clreol (void);
void crt_clreol ()
{
  setattr ();
  wclrtoeol (crt_w);
  crt_set_update ();
}

void crt_insline (void);
void crt_insline ()
{
  setattr ();
  winsertln (crt_w);
  if (idlok (crt_w, TRUE));
  crt_set_update ();
}

void crt_delline (void);
void crt_delline ()
{
  setattr ();
  wdeleteln (crt_w);
  if (idlok (crt_w, TRUE));
  crt_set_update ();
}

void crt_setcursorshape (TCursorShape shape);
void crt_setcursorshape (TCursorShape shape)
{
  crt_CursorShape = shape;
  curs_set ((shape == CursorHidden) ? 0 :
            (shape == CursorNormal) ? 1 : 2);
  crt_set_update ();
}

TCursorShape crt_getcursorshape (void);
TCursorShape crt_getcursorshape ()
{
  return crt_CursorShape;
}

void crt_readchar (int x, int y, Char *ch, TTextAttr *attr);
void crt_readchar (int x, int y, Char *ch, TTextAttr *attr)
{
  int sx, sy;
  chtype c;
  setattr ();
  getyx (crt_w, sy, sx);
  c = mvwinch (crt_w, y - 1, x - 1);
  *ch = c & A_CHARTEXT;
  *attr = cursattr2attr (c);
  wmove (crt_w, sy, sx);
}

void crt_delay (unsigned ms);
void crt_delay (unsigned ms)
{
  if (crt_update_level == 2) crt_stop_update ();
  crt_update ();
  napms (ms);
}

size_t crt_read (void *PrivateData, Char *buffer, size_t size);
size_t crt_read (void *PrivateData, Char *buffer, size_t size)
{
  size_t n;
  Char *p;
  setattr ();
  crt_update ();
  if (!line_buf_count)
    {
      crt_nodelay (FALSE);
      rawout (FALSE);
      echo ();
      wgetnstr (crt_w, line_buf, MAXLENGTH - 1);
      noecho ();
      rawout (TRUE);
      if (Checkeof)
        {
          do
            {
              p = strchr (line_buf, 26);
              if (p) *p = 0;
            }
          while (p);
        }
      line_buf_pos = line_buf;
      line_buf_count = strlen (line_buf_pos);
      line_buf_pos [line_buf_count++] = '\n';
    }
  n = (line_buf_count < size) ? line_buf_count : size;
  memcpy (buffer, line_buf_pos, n);
  line_buf_pos += n;
  line_buf_count -= n;
  return n;
}

void crt_flash (void);
void crt_flash ()
{
  flash ();
}

void crt_beep (void);
void crt_beep ()
{
  if (Visualbell)
    crt_flash ();
  else
    {
      beep ();
      crt_delay (100);
    }
}

static chtype
chtransform (Char ch, TTextAttr attr)
{
  Boolean pccs = Pccharset || (ch < ' ');
  if (ch == 0 || (attr & ~0x88) == 0) return ' ';
  #ifndef USE_PDCURSES
  if ((!crt_linux_console && pccs)
      || ch == chBell || ch == chBkSp || ch == chTab || ch == chLF
      || ch == chFF || ch == chCR || ch == chEsc || ch == 14 || ch == 15 || ch == 155)
    {
      if (ch <  32)  return chars_0_31 [ch];
      if (ch >= 128) return chars_128_255 [ch - 128];
    }
  #endif
  if (!pccs && iscntrl (ch)) return ' ';
  return (crt_linux_console && pccs) ? ch | A_ALTCHARSET : ch;
}

void crt_fillwin (Char ch, TTextAttr attr);
void crt_fillwin (Char ch, TTextAttr attr)
{
  if (Windmin != crt_LastWindMin || Windmax != crt_LastWindMax) win_changed (0);
  wbkgdset (crt_w, attr2cursattr (attr) | chtransform (ch, attr));
  werase (crt_w);
  setattr ();
  crt_set_update ();
}

size_t crt_write (void *PrivateData, const Char *buffer, size_t size);
size_t crt_write (void *PrivateData, const Char *buffer, size_t size)
{
  size_t i;
  Char ch;
  setattr ();
  for (i = 0; i < size; i++)
    {
      ch = buffer [i];
      if (Usecontrolchars)
        {
          if (ch == chBell)
            {
              crt_beep ();
              continue;
            }
          else if (ch == chBkSp)
            {
              int x, y;
              getyx (crt_w, y, x);
              if (x > 0)
                wmove (crt_w, y, x - 1);
              continue;
            }
          else if (ch == chLF)
            {
              int x, y;
              getyx (crt_w, y, x);
              if (y + WindYMin >= WindYMax)
                {
                  wscrl (crt_w, 1);
                  wmove (crt_w, y, 0);
                }
              else
                wmove (crt_w, y + 1, 0);
              continue;
            }
          else if (ch == chCR)
            {
              int x, y;
              getyx (crt_w, y, x);
              wmove (crt_w, y, 0);
              continue;
            }
        }
      waddch (crt_w, chtransform (ch, Textattr));
    }
  crt_set_update ();
  return size;
}

void crt_writecharattrat (int x, int y, int Count, TCharAttr *CharAttr);
void crt_writecharattrat (int x, int y, int Count, TCharAttr *CharAttr)
{
  int m = WindXMax - WindXMin + 1 - x + 1;
  if (Count > m) Count = m;
  if (Count > 0)
    {
      chtype buf [Count];
      int cattr = 0, LastAttr = - 1, sx, sy, i;
      TTextAttr Attr;
      for (i = 0; i < Count; i++)
        {
          Attr = CharAttr [i].Attr;
          if (Attr != LastAttr)
            {
              cattr = attr2cursattr (Attr);
              LastAttr = Attr;
            }
          buf [i] = chtransform (CharAttr [i].Ch, Attr) | cattr;
        }
      setattr ();
      getyx (crt_w, sy, sx);
      wmove (crt_w, y - 1, x - 1);
      crt_scroll (FALSE);
      waddchnstr (crt_w, buf, Count);
      crt_scroll (TRUE);
      wmove (crt_w, sy, sx);
      crt_set_update ();
    }
}
