/* NetHack 3.7	consoletty.c	$NHDT-Date: 1596498316 2020/08/03 23:45:16 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.117 $ */
/* Copyright (c) NetHack PC Development Team 1993    */
/* NetHack may be freely redistributed.  See license for details. */

/* tty.c - (Windows console) version */

/*
 * Initial Creation                             M. Allison      1993/01/31
 * Switch to low level console output routines  M. Allison      2003/10/01
 * Restrict cursor movement until input pending M. Lehotay      2003/10/02
 * Call Unicode version of output API on NT     R. Chason       2005/10/28
 * Use of back buffer to improve performance    B. House        2018/05/06
 *
 */

#ifndef NO_VT
#define VIRTUAL_TERMINAL_SEQUENCES
#define UTF8_FROM_CORE
#endif

#ifdef WIN32
#define NEED_VARARGS /* Uses ... */
#include "win32api.h"
#include "winos.h"
#include "hack.h"
#include "wintty.h"
#include <sys\types.h>
#include <sys\stat.h>
#ifdef VIRTUAL_TERMINAL_SEQUENCES
#include <locale.h>
#ifndef INTEGER_H
#include "integer.h"
#endif
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

#ifdef __MINGW32__
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING  0x0004
#endif
#endif

extern boolean getreturn_enabled; /* from windmain.c */
extern int redirect_stdout;

#ifdef TTY_GRAPHICS
/*
 * Console Buffer Flipping Support
 *
 * To minimize the number of calls into the WriteConsoleOutputXXX methods,
 * we implement a notion of a console back buffer which keeps the next frame
 * of console output as it is being composed.  When ready to show the new
 * frame, we compare this next frame to what is currently being output and
 * only call WriteConsoleOutputXXX for those console values that need to
 * change.
 *
 */
#ifndef VIRTUAL_TERMINAL_SEQUENCES
#define CONSOLE_CLEAR_ATTRIBUTE \
    (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
#define CONSOLE_UNDEFINED_ATTRIBUTE (0)
#else /* VIRTUAL_TERMINAL_SEQUENCES */
#define CONSOLE_CLEAR_ATTRIBUTE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

#define CONSOLE_CLEAR_CHARACTER (' ')
#define CONSOLE_UNDEFINED_CHARACTER ('\0')

#ifdef VIRTUAL_TERMINAL_SEQUENCES
enum console_attributes {
    atr_bold = 1,
    atr_dim = 2,
    atr_uline = 4,
    atr_blink = 8,
    atr_inverse = 16
};
#define MAX_UTF8_SEQUENCE 7
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

typedef struct {
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    WCHAR   character;
    WORD    attribute;
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    uint8 utf8str[MAX_UTF8_SEQUENCE];
    WCHAR wcharacter;
    WORD attr;
    long color24;
    int color256idx;
    const char *bkcolorseq;
    const char *colorseq;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
} cell_t;

#ifndef VIRTUAL_TERMINAL_SEQUENCES
cell_t clear_cell = { CONSOLE_CLEAR_CHARACTER, CONSOLE_CLEAR_ATTRIBUTE };
cell_t undefined_cell = { CONSOLE_UNDEFINED_CHARACTER,
                          CONSOLE_UNDEFINED_ATTRIBUTE };
#else /* VIRTUAL_TERMINAL_SEQUENCES */
cell_t clear_cell = { { CONSOLE_CLEAR_CHARACTER, 0, 0, 0, 0, 0, 0 },
                        CONSOLE_CLEAR_CHARACTER, 0, 0L, 0, "\x1b[0m" };
cell_t undefined_cell = { { CONSOLE_UNDEFINED_CHARACTER, 0, 0, 0, 0, 0, 0 },
                            CONSOLE_UNDEFINED_CHARACTER, 0, 0L, 0, (const char *) 0 };
static const uint8 empty_utf8str[MAX_UTF8_SEQUENCE] = { 0 };
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

/*
 * The following WIN32 Console API routines are used in this file.
 *
 * CreateFile
 * GetConsoleScreenBufferInfo
 * GetStdHandle
 * SetConsoleCursorPosition
 * SetConsoleTextAttribute
 * SetConsoleCtrlHandler
 * PeekConsoleInput
 * ReadConsoleInput
 * GetConsoleOutputCP
#ifndef VIRTUAL_TERMINAL_SEQUENCES
 * WriteConsoleOutputCharacter
 * FillConsoleOutputAttribute
#endif
 */

static BOOL CtrlHandler(DWORD);
#ifndef VIRTUAL_TERMINAL_SEQUENCES
static void xputc_core(char);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
static void xputc_core(int);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
void cmov(int, int);
void nocmov(int, int);
int process_keystroke(INPUT_RECORD *, boolean *, boolean numberpad,
                      int portdebug);
static void init_ttycolor(void);
static void really_move_cursor(void);
static void check_and_set_font(void);
static boolean check_font_widths(void);
static void set_known_good_console_font(void);
static void restore_original_console_font(void);
extern void safe_routines(void);
void tty_ibmgraphics_fixup(void);
#ifdef VIRTUAL_TERMINAL_SEQUENCES
extern void (*ibmgraphics_mode_callback)(void);  /* symbols.c */
extern void (*utf8graphics_mode_callback)(void); /* symbols.c */
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

/* Win32 Screen buffer,coordinate,console I/O information */
COORD ntcoord;
INPUT_RECORD ir;
static boolean orig_QuickEdit;

/* Support for changing console font if existing glyph widths are too wide */

/* Flag for whether NetHack was launched via the GUI, not the command line.
 * The reason we care at all, is so that we can get
 * a final RETURN at the end of the game when launched from the GUI
 * to prevent the scoreboard (or panic message :-|) from vanishing
 * immediately after it is displayed, yet not bother when started
 * from the command line.
 */
int GUILaunched = FALSE;
/* Flag for whether unicode is supported */
static boolean init_ttycolor_completed;
#ifdef PORT_DEBUG
static boolean display_cursor_info = FALSE;
#endif
struct console_t {
    boolean is_ready;
    HWND hWnd;
    WORD background;
    WORD foreground;
    WORD attr;
    int current_nhcolor;
    int current_nhbkcolor;
    int current_nhattr[ATR_INVERSE+1];
    COORD cursor;
    HANDLE hConOut;
    HANDLE hConIn;
    CONSOLE_SCREEN_BUFFER_INFO orig_csbi;
    int width;
    int height;
    boolean has_unicode;
    int buffer_size;
    cell_t * front_buffer;
    cell_t * back_buffer;
    WCHAR cpMap[256];
    boolean font_changed;
    CONSOLE_FONT_INFOEX orig_font_info;
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    UINT original_code_page;
} console = {
    0,
    (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED),
    (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED),
    NO_COLOR,
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    UINT orig_code_page;
    char *orig_localestr;
    DWORD orig_in_cmode;
    DWORD orig_out_cmode;
    CONSOLE_FONT_INFOEX font_info;
    UINT code_page;
    char *localestr;
    DWORD in_cmode;
    DWORD out_cmode;
    long color24;
    int color256idx;
} console = {
    FALSE,
    0,
    (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), /* background */
    (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), /* foreground */
    0,                                                     /* attr */
    0,                                                     /* current_nhcolor */
    0,                                                     /* current_nhbkcolor */
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE},
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    {0, 0},
    NULL,
    NULL,
    { 0 },
    0,
    0,
    FALSE,
    0,
    NULL,
    NULL,
    { 0 },
    FALSE,
    { 0 },
    0
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    { 0, 0 },                                              /* cursor */
    NULL,     /* hConOut*/
    NULL,     /* hConIn */
    { 0 },    /* cbsi */
    0,        /* width */
    0,        /* height */
    FALSE,    /* has_unicode */
    0,        /* buffer_size */
    NULL,     /* front_buffer */
    NULL,     /* back_buffer */
    { 0 },    /* cpMap */
    FALSE,    /* font_changed */
    { 0 },    /* orig_font_info */
    0U,       /* orig_code_page */
    NULL,     /* orig_localestr */
    0,        /* orig_in_cmode */
    0,        /* orig_out_cmode */
    { 0 },    /* font_info */
    0U,       /* code_page */
    NULL,     /* localestr */
    0,        /* in_cmode */
    0,        /* out_cmode */
    0L,       /* color24 */
    0         /* color256idx */
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
};

static const char default_name[] = "default";
const char *const legal_key_handling[] = {
    "none",
    "default",
    "ray",
    "340",
};

enum windows_key_handling keyh[] = { no_keyhandling, default_keyhandling, ray_keyhandling,
                                     nh340_keyhandling };

int default_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, boolean, int);
int default_kbhit(HANDLE, INPUT_RECORD *);
int default_checkinput(HANDLE, INPUT_RECORD *, DWORD *, boolean,
                       int, int *, coord *);

int ray_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, boolean, int);
int ray_kbhit(HANDLE, INPUT_RECORD *);
int ray_checkinput(HANDLE, INPUT_RECORD *, DWORD *, boolean,
                       int, int *, coord *);

int nh340_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, boolean, int);
int nh340_kbhit(HANDLE, INPUT_RECORD *);
int nh340_checkinput(HANDLE, INPUT_RECORD *, DWORD *, boolean,
                       int, int *, coord *);

struct keyboard_handling_t {
    enum windows_key_handling khid;
    int (*pProcessKeystroke)(HANDLE, INPUT_RECORD *, boolean *,
                                          boolean, int);
    int (*pNHkbhit)(HANDLE, INPUT_RECORD *);
    int (*pCheckInput)(HANDLE, INPUT_RECORD *, DWORD *, boolean,
                                   int, int *, coord *);
} keyboard_handling = {
    no_keyhandling,
    default_processkeystroke,
    default_kbhit,
    default_checkinput
};

static DWORD ccount, acount;
#ifndef CLR_MAX
#define CLR_MAX 16
#endif

int ttycolors[CLR_MAX];
int ttycolors_inv[CLR_MAX];

#define MAX_OVERRIDES 256
unsigned char key_overrides[MAX_OVERRIDES];
static char nullstr[] = "";
char erase_char, kill_char;
#define DEFTEXTCOLOR ttycolors[7]
static INPUT_RECORD bogus_key;

#ifdef VIRTUAL_TERMINAL_SEQUENCES
long customcolors[CLR_MAX];
const char *esc_seq_colors[CLR_MAX];
const char *esc_seq_bkcolors[CLR_MAX];

struct rgbvalues {
    int idx;
    const char *name;
    const char *hexval;
    long r, gn, b;
} rgbtable[] = {
    { 0, "maroon", "#800000", 128, 0, 0 },
    { 1, "dark red", "#8B0000", 139, 0, 0 },
    { 2, "brown", "#A52A2A", 165, 42, 42 },
    { 3, "firebrick", "#B22222", 178, 34, 34 },
    { 4, "crimson", "#DC143C", 220, 20, 60 },
    { 5, "red", "#FF0000", 255, 0, 0 },
    { 6, "tomato", "#FF6347", 255, 99, 71 },
    { 7, "coral", "#FF7F50", 255, 127, 80 },
    { 8, "indian red", "#CD5C5C", 205, 92, 92 },
    { 9, "light coral", "#F08080", 240, 128, 128 },
    { 10, "dark salmon", "#E9967A", 233, 150, 122 },
    { 11, "salmon", "#FA8072", 250, 128, 114 },
    { 12, "light salmon", "#FFA07A", 255, 160, 122 },
    { 13, "orange red", "#FF4500", 255, 69, 0 },
    { 14, "dark orange", "#FF8C00", 255, 140, 0 },
    { 15, "orange", "#FFA500", 255, 165, 0 },
    { 16, "gold", "#FFD700", 255, 215, 0 },
    { 17, "dark golden rod", "#B8860B", 184, 134, 11 },
    { 18, "golden rod", "#DAA520", 218, 165, 32 },
    { 19, "pale golden rod", "#EEE8AA", 238, 232, 170 },
    { 20, "dark khaki", "#BDB76B", 189, 183, 107 },
    { 21, "khaki", "#F0E68C", 240, 230, 140 },
    { 22, "olive", "#808000", 128, 128, 0 },
    { 23, "yellow", "#FFFF00", 255, 255, 0 },
    { 24, "yellow green", "#9ACD32", 154, 205, 50 },
    { 25, "dark olive green", "#556B2F", 85, 107, 47 },
    { 26, "olive drab", "#6B8E23", 107, 142, 35 },
    { 27, "lawn green", "#7CFC00", 124, 252, 0 },
    { 28, "chart reuse", "#7FFF00", 127, 255, 0 },
    { 29, "green yellow", "#ADFF2F", 173, 255, 47 },
    { 30, "dark green", "#006400", 0, 100, 0 },
    { 31, "green", "#008000", 0, 128, 0 },
    { 32, "forest green", "#228B22", 34, 139, 34 },
    { 33, "lime", "#00FF00", 0, 255, 0 },
    { 34, "lime green", "#32CD32", 50, 205, 50 },
    { 35, "light green", "#90EE90", 144, 238, 144 },
    { 36, "pale green", "#98FB98", 152, 251, 152 },
    { 37, "dark sea green", "#8FBC8F", 143, 188, 143 },
    { 38, "medium spring green", "#00FA9A", 0, 250, 154 },
    { 39, "spring green", "#00FF7F", 0, 255, 127 },
    { 40, "sea green", "#2E8B57", 46, 139, 87 },
    { 41, "medium aqua marine", "#66CDAA", 102, 205, 170 },
    { 42, "medium sea green", "#3CB371", 60, 179, 113 },
    { 43, "light sea green", "#20B2AA", 32, 178, 170 },
    { 44, "dark slate gray", "#2F4F4F", 47, 79, 79 },
    { 45, "teal", "#008080", 0, 128, 128 },
    { 46, "dark cyan", "#008B8B", 0, 139, 139 },
    { 47, "aqua", "#00FFFF", 0, 255, 255 },
    { 48, "cyan", "#00FFFF", 0, 255, 255 },
    { 49, "light cyan", "#E0FFFF", 224, 255, 255 },
    { 50, "dark turquoise", "#00CED1", 0, 206, 209 },
    { 51, "turquoise", "#40E0D0", 64, 224, 208 },
    { 52, "medium turquoise", "#48D1CC", 72, 209, 204 },
    { 53, "pale turquoise", "#AFEEEE", 175, 238, 238 },
    { 54, "aqua marine", "#7FFFD4", 127, 255, 212 },
    { 55, "powder blue", "#B0E0E6", 176, 224, 230 },
    { 56, "cadet blue", "#5F9EA0", 95, 158, 160 },
    { 57, "steel blue", "#4682B4", 70, 130, 180 },
    { 58, "corn flower blue", "#6495ED", 100, 149, 237 },
    { 59, "deep sky blue", "#00BFFF", 0, 191, 255 },
    { 60, "dodger blue", "#1E90FF", 30, 144, 255 },
    { 61, "light blue", "#ADD8E6", 173, 216, 230 },
    { 62, "sky blue", "#87CEEB", 135, 206, 235 },
    { 63, "light sky blue", "#87CEFA", 135, 206, 250 },
    { 64, "midnight blue", "#191970", 25, 25, 112 },
    { 65, "navy", "#000080", 0, 0, 128 },
    { 66, "dark blue", "#00008B", 0, 0, 139 },
    { 67, "medium blue", "#0000CD", 0, 0, 205 },
    { 68, "blue", "#0000FF", 0, 0, 255 },
    { 69, "royal blue", "#4169E1", 65, 105, 225 },
    { 70, "blue violet", "#8A2BE2", 138, 43, 226 },
    { 71, "indigo", "#4B0082", 75, 0, 130 },
    { 72, "dark slate blue", "#483D8B", 72, 61, 139 },
    { 73, "slate blue", "#6A5ACD", 106, 90, 205 },
    { 74, "medium slate blue", "#7B68EE", 123, 104, 238 },
    { 75, "medium purple", "#9370DB", 147, 112, 219 },
    { 76, "dark magenta", "#8B008B", 139, 0, 139 },
    { 77, "dark violet", "#9400D3", 148, 0, 211 },
    { 78, "dark orchid", "#9932CC", 153, 50, 204 },
    { 79, "medium orchid", "#BA55D3", 186, 85, 211 },
    { 80, "purple", "#800080", 128, 0, 128 },
    { 81, "thistle", "#D8BFD8", 216, 191, 216 },
    { 82, "plum", "#DDA0DD", 221, 160, 221 },
    { 83, "violet", "#EE82EE", 238, 130, 238 },
    { 84, "magenta / fuchsia", "#FF00FF", 255, 0, 255 },
    { 85, "orchid", "#DA70D6", 218, 112, 214 },
    { 86, "medium violet red", "#C71585", 199, 21, 133 },
    { 87, "pale violet red", "#DB7093", 219, 112, 147 },
    { 88, "deep pink", "#FF1493", 255, 20, 147 },
    { 89, "hot pink", "#FF69B4", 255, 105, 180 },
    { 90, "light pink", "#FFB6C1", 255, 182, 193 },
    { 91, "pink", "#FFC0CB", 255, 192, 203 },
    { 92, "antique white", "#FAEBD7", 250, 235, 215 },
    { 93, "beige", "#F5F5DC", 245, 245, 220 },
    { 94, "bisque", "#FFE4C4", 255, 228, 196 },
    { 95, "blanched almond", "#FFEBCD", 255, 235, 205 },
    { 96, "wheat", "#F5DEB3", 245, 222, 179 },
    { 97, "corn silk", "#FFF8DC", 255, 248, 220 },
    { 98, "lemon chiffon", "#FFFACD", 255, 250, 205 },
    { 99, "light golden rod yellow", "#FAFAD2", 250, 250, 210 },
    { 100, "light yellow", "#FFFFE0", 255, 255, 224 },
    { 101, "saddle brown", "#8B4513", 139, 69, 19 },
    { 102, "sienna", "#A0522D", 160, 82, 45 },
    { 103, "chocolate", "#D2691E", 210, 105, 30 },
    { 104, "peru", "#CD853F", 205, 133, 63 },
    { 105, "sandy brown", "#F4A460", 244, 164, 96 },
    { 106, "burly wood", "#DEB887", 222, 184, 135 },
    { 107, "tan", "#D2B48C", 210, 180, 140 },
    { 108, "rosy brown", "#BC8F8F", 188, 143, 143 },
    { 109, "moccasin", "#FFE4B5", 255, 228, 181 },
    { 110, "navajo white", "#FFDEAD", 255, 222, 173 },
    { 111, "peach puff", "#FFDAB9", 255, 218, 185 },
    { 112, "misty rose", "#FFE4E1", 255, 228, 225 },
    { 113, "lavender blush", "#FFF0F5", 255, 240, 245 },
    { 114, "linen", "#FAF0E6", 250, 240, 230 },
    { 115, "old lace", "#FDF5E6", 253, 245, 230 },
    { 116, "papaya whip", "#FFEFD5", 255, 239, 213 },
    { 117, "sea shell", "#FFF5EE", 255, 245, 238 },
    { 118, "mint cream", "#F5FFFA", 245, 255, 250 },
    { 119, "slate gray", "#708090", 112, 128, 144 },
    { 120, "light slate gray", "#778899", 119, 136, 153 },
    { 121, "light steel blue", "#B0C4DE", 176, 196, 222 },
    { 122, "lavender", "#E6E6FA", 230, 230, 250 },
    { 123, "floral white", "#FFFAF0", 255, 250, 240 },
    { 124, "alice blue", "#F0F8FF", 240, 248, 255 },
    { 125, "ghost white", "#F8F8FF", 248, 248, 255 },
    { 126, "honeydew", "#F0FFF0", 240, 255, 240 },
    { 127, "ivory", "#FFFFF0", 255, 255, 240 },
    { 128, "azure", "#F0FFFF", 240, 255, 255 },
    { 129, "snow", "#FFFAFA", 255, 250, 250 },
    { 130, "black", "#000000", 0, 0, 0 },
    { 131, "dim gray / dim grey", "#696969", 105, 105, 105 },
    { 132, "gray / grey", "#808080", 128, 128, 128 },
    { 133, "dark gray / dark grey", "#A9A9A9", 169, 169, 169 },
    { 134, "silver", "#C0C0C0", 192, 192, 192 },
    { 135, "light gray / light grey", "#D3D3D3", 211, 211, 211 },
    { 136, "gainsboro", "#DCDCDC", 220, 220, 220 },
    { 137, "white smoke", "#F5F5F5", 245, 245, 245 },
    { 138, "white", "#FFFFFF", 255, 255, 255 },
};

long
rgbtable_to_long(struct rgbvalues *tbl)
{
    long rgblong = (tbl->r << 0) | (tbl->gn << 8) | (tbl->b << 16);
    return rgblong;
}

static void
init_custom_colors(void)
{
    char bkcolorbuf[32];

    customcolors[CLR_BLACK] = rgbtable_to_long(&rgbtable[131]);
    customcolors[CLR_RED] = rgbtable_to_long(&rgbtable[5]);
    customcolors[CLR_GREEN] = rgbtable_to_long(&rgbtable[31]);
    customcolors[CLR_BROWN] = rgbtable_to_long(&rgbtable[104]);
    customcolors[CLR_BLUE] = rgbtable_to_long(&rgbtable[58]);
    customcolors[CLR_MAGENTA] = rgbtable_to_long(&rgbtable[76]);
    customcolors[CLR_CYAN] = rgbtable_to_long(&rgbtable[48]);
    customcolors[CLR_GRAY] = rgbtable_to_long(&rgbtable[73]);
    customcolors[NO_COLOR] = rgbtable_to_long(&rgbtable[137]);
    customcolors[CLR_ORANGE] = rgbtable_to_long(&rgbtable[15]);
    customcolors[CLR_BRIGHT_GREEN] = rgbtable_to_long(&rgbtable[34]);
    customcolors[CLR_YELLOW] = rgbtable_to_long(&rgbtable[18]);
    customcolors[CLR_BRIGHT_BLUE] = rgbtable_to_long(&rgbtable[69]);
    customcolors[CLR_BRIGHT_MAGENTA] = rgbtable_to_long(&rgbtable[84]);
    customcolors[CLR_BRIGHT_CYAN] = rgbtable_to_long(&rgbtable[49]);
    customcolors[CLR_WHITE] = rgbtable_to_long(&rgbtable[138]);

/*    esc_seq_colors[CLR_BLACK] = "\x1b[30m"; */
    esc_seq_colors[CLR_BLACK] = "\x1b[38;2;47;79;79m";
    esc_seq_colors[CLR_RED] = "\x1b[31m";
    esc_seq_colors[CLR_GREEN] = "\x1b[32m";
    esc_seq_colors[CLR_YELLOW] = "\x1b[38;2;255;255;0m";
    esc_seq_colors[CLR_BLUE] = "\x1b[38;2;100;149;237m";
    esc_seq_colors[CLR_MAGENTA] = "\x1b[35m";
    esc_seq_colors[CLR_CYAN] = "\x1b[36m";
    esc_seq_colors[CLR_WHITE] = "\x1b[37m";

    esc_seq_colors[CLR_BROWN] = "\x1b[38;2;205;133;63m";
//    esc_seq_colors[CLR_GRAY] = "\x1b[31m\x1b[32m\x1b[34m";
    esc_seq_colors[CLR_GRAY] = "\x1b[90m";
    esc_seq_colors[NO_COLOR] = "\x1b[39m";
    esc_seq_colors[CLR_ORANGE] = "\x1b[38;2;255;140;0m";
    esc_seq_colors[CLR_BRIGHT_GREEN] = "\x1b[39m";
    esc_seq_colors[CLR_BRIGHT_BLUE] = "\x1b[34m\x1b[94m";
    esc_seq_colors[CLR_BRIGHT_MAGENTA] = "\x1b[35m\x1b[95m";
    esc_seq_colors[CLR_BRIGHT_CYAN] = "\x1b[36m\x1b[96m";

     /* Sprintf(tmp, "\033[%dm", ((color % 8) + 40)); */
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_BLACK % 8) + 40));
    esc_seq_bkcolors[CLR_BLACK] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_RED % 8) + 40));
    esc_seq_bkcolors[CLR_RED] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_GREEN % 8) + 40));
    esc_seq_bkcolors[CLR_GREEN] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_YELLOW % 8) + 40));
    esc_seq_bkcolors[CLR_YELLOW] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_BLUE % 8) + 40));
    esc_seq_bkcolors[CLR_BLUE] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_MAGENTA % 8) + 40));
    esc_seq_bkcolors[CLR_MAGENTA] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_CYAN % 8) + 40));
    esc_seq_bkcolors[CLR_CYAN] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_WHITE % 8) + 40));
    esc_seq_bkcolors[CLR_WHITE] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_BROWN % 8) + 40));
    esc_seq_bkcolors[CLR_BROWN] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_GRAY % 8) + 40));
    esc_seq_bkcolors[CLR_GRAY] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((NO_COLOR % 8) + 40));
    esc_seq_bkcolors[NO_COLOR] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_ORANGE % 8) + 40));
    esc_seq_bkcolors[CLR_ORANGE] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_BRIGHT_BLUE % 8) + 40));
    esc_seq_bkcolors[CLR_BRIGHT_BLUE] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_BRIGHT_GREEN % 8) + 40));
    esc_seq_bkcolors[CLR_BRIGHT_GREEN] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_BRIGHT_MAGENTA % 8) + 40));
    esc_seq_bkcolors[CLR_BRIGHT_MAGENTA] = dupstr(bkcolorbuf);
    Snprintf(bkcolorbuf, sizeof bkcolorbuf, "\x1b[%dm", ((CLR_BRIGHT_CYAN % 8) + 40));
    esc_seq_bkcolors[CLR_BRIGHT_CYAN] = dupstr(bkcolorbuf);
}

void emit_start_bold(void);
void emit_stop_bold(void);
void emit_start_dim(void);
void emit_stop_dim(void);
void emit_start_blink(void);
void emit_stop_blink(void);
void emit_start_underline(void);
void emit_stop_underline(void);
void emit_start_inverse(void);
void emit_stop_inverse(void);
void emit_start_24bitcolor(long color24bit);
void emit_start_256color(int u256coloridx);
void emit_default_color(void);
void emit_return_to_default(void);
void emit_hide_cursor(void);
void emit_show_curor(void);

void
emit_hide_cursor(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[?25l";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

void
emit_show_cursor(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[?25h";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

void
emit_start_bold(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[4m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

void
emit_stop_bold(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[24m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}
#if 0
emit_start_dim(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[4m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

void
emit_stop_dim(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[24m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}
#endif

void
emit_start_blink(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[5m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

void
emit_stop_blink(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[25m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

void
emit_start_underline(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[4m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

void
emit_stop_underline(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[24m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}
void
emit_start_inverse(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[7m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

void
emit_stop_inverse(void)
{
    DWORD unused, reserved;
    static const char escseq[] = "\x1b[27m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}

#if 0
#define tcfmtstr "\x1b[38;2;%d;%d;%dm"
#if 0
#define tcfmtstr "\x1b[38:2:%d:%d:%dm"
#endif
#endif

#ifndef SEP2
#define tcfmtstr24bit "\x1b[38;2;%ld;%ld;%ldm"
#define tcfmtstr256 "\x1b[38;5;%ldm"
#else
#define tcfmtstr24bit "\x1b[38:2:%ld:%ld:%ldm"
#define tcfmtstr256 "\x1b[38:5:%dm"
#endif
 
void
emit_start_256color(int u256coloridx)
{
    DWORD unused, reserved;
    static char tcolorbuf[QBUFSZ];
    Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, u256coloridx);
    WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf,
                  (int) strlen(tcolorbuf), &unused, &reserved);
}

void
emit_start_24bitcolor(long color24bit)
{
    DWORD unused, reserved;
    static char tcolorbuf[QBUFSZ];
    long mcolor =
        (color24bit & 0xFFFFFF); /* color 0 has bit 0x1000000 set */
    Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr24bit,
             ((mcolor >> 16) & 0xFF),   /* red */
             ((mcolor >>  8) & 0xFF),   /* green */
             ((mcolor >>  0) & 0xFF));  /* blue */
    WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf,
                  (int) strlen(tcolorbuf), &unused, &reserved);
}

void
emit_default_color(void)
{
    DWORD unused, reserved;
    static char escseq[] = "\x1b[39m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}
void
emit_return_to_default(void)
{
    DWORD unused, reserved;
    static char escseq[] = "\x1b[0m";

    WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq),
                  &unused, &reserved);
}
static boolean newattr_on = TRUE;
static boolean color24_on = TRUE;

/* for debugging */
WORD what_is_there_now;
BOOL success;
DWORD error_result;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

/* Console buffer flipping support */
#ifdef VIRTUAL_TERMINAL_SEQUENCES
enum do_flags {
    do_utf8_content = 0x01,
    do_wide_content = 0x02,
    do_colorseq     = 0x04,
    do_color24      = 0x08,
    do_newattr      = 0x10,
    do_bkcolorseq   = 0x20,
};
enum did_flags {
    did_utf8_content = 0x01,
    did_wide_content = 0x02,
    did_colorseq     = 0x04,
    did_color24      = 0x08,
    did_newattr      = 0x10,
    did_bkcolorseq   = 0x20,
};

static void
back_buffer_flip(void)
{
    cell_t *back = console.back_buffer;
    cell_t *front = console.front_buffer;
    COORD pos;
    DWORD unused;
    DWORD reserved;
    unsigned do_anything, did_anything;

    if (!console.is_ready)
        return;

    emit_hide_cursor();
    for (pos.Y = 0; pos.Y < console.height; pos.Y++) {
        for (pos.X = 0; pos.X < console.width; pos.X++) {
            boolean pos_set = FALSE;
            do_anything = did_anything = 0U;
            if (back->color24 != front->color24)
                do_anything |= do_color24;
            if (back->colorseq != front->colorseq)
                do_anything |= do_colorseq;
            if (back->attr != front->attr)
                do_anything |= do_newattr;
            if (back->bkcolorseq != front->bkcolorseq)
                do_anything |= do_bkcolorseq;
#ifdef UTF8_FROM_CORE
            if (!SYMHANDLING(H_UTF8)) {
                if (console.has_unicode
                    && (back->wcharacter != front->wcharacter))
                    do_anything |= do_wide_content;
            } else {
#endif
                if (strcmp((const char *) back->utf8str,
                           (const char *) front->utf8str))
                    do_anything |= do_utf8_content;
#ifdef UTF8_FROM_CORE
            }
#endif
            if (do_anything) {
                SetConsoleCursorPosition(console.hConOut, pos);
                pos_set = TRUE;
                did_anything |= did_newattr;
                if (back->attr) {
                    if (back->attr & atr_bold)
                        emit_start_bold();
                    //  if (back->attr & atr_dim)
                    //      emit_start_dim();
                    if (back->attr & atr_uline)
                        emit_start_underline();
                    // if (back->attr & atr_blink)
                    //    emit_start_blink();
                    if (back->attr & atr_inverse)
                        emit_start_inverse();
                    // front->attr = back->attr; /* will happen below due
                    // to did_newattr */
                } else {
                    emit_return_to_default();
                }

                if (color24_on && back->color24) {
                    did_anything |= did_color24;
                    if (back->color24) {
                        if (!iflags.use_truecolor && iflags.colorcount == 256)
                            emit_start_256color(back->color256idx);
                        else
                            emit_start_24bitcolor(back->color24);
                    }
                } else if (back->colorseq) {
                    did_anything |= did_colorseq;
                    WriteConsoleA(console.hConOut, back->colorseq,
                                  (int) strlen(back->colorseq), &unused,
                                  &reserved);
                }
                if (back->bkcolorseq) {
                    did_anything |= did_bkcolorseq;
                    WriteConsoleA(console.hConOut, back->bkcolorseq,
                                  (int) strlen(back->bkcolorseq), &unused,
                                  &reserved);
                }
                if ((did_anything | (did_colorseq | did_bkcolorseq | did_color24)) == 0) {
                    did_anything &= ~(did_bkcolorseq | did_color24);
                    did_anything |= did_colorseq;
                    emit_default_color();
                }
                if (did_anything
                    || (do_anything & (do_wide_content | do_utf8_content))) {
#ifdef UTF8_FROM_CORE
                    if (SYMHANDLING(H_UTF8) || !console.has_unicode) {
                        WriteConsoleA(console.hConOut, (LPCSTR) back->utf8str,
                                      (int) strlen((char *) back->utf8str),
                                      &unused, &reserved);
                        did_anything |= did_utf8_content;
                    } else {
#endif
                    WriteConsoleW(console.hConOut, &back->wcharacter, 1,
                                  &unused, &reserved);
                    did_anything |= did_wide_content;
#ifdef UTF8_FROM_CORE
                    }
#endif
                }
            }
            if (did_anything) {
                if (!pos_set) {
                    SetConsoleCursorPosition(console.hConOut, pos);
                    pos_set = TRUE;
                }
                emit_return_to_default();
                *front = *back;
            }
            back++;
            front++;
        }
    }
    emit_show_cursor();
}
#else
static void
back_buffer_flip(void)
{
    cell_t *back = console.back_buffer;
    cell_t *front = console.front_buffer;
    COORD pos;
    DWORD unused;

    if (!console.is_ready)
        return;

    for (pos.Y = 0; pos.Y < console.height; pos.Y++) {
        for (pos.X = 0; pos.X < console.width; pos.X++) {
            if (back->attribute != front->attribute) {
                WriteConsoleOutputAttribute(console.hConOut, &back->attribute,
                                            1, pos, &unused);
                front->attribute = back->attribute;
            }
            if (back->character != front->character) {
                if (console.has_unicode) {
                    WriteConsoleOutputCharacterW(
                        console.hConOut, &back->character, 1, pos, &unused);
                } else {
                    char ch = (char) back->character;
                    WriteConsoleOutputCharacterA(console.hConOut, &ch, 1, pos,
                                                 &unused);
                }
                *front = *back;
            }
            back++;
            front++;
        }
    }
}
#endif

void buffer_fill_to_end(cell_t * buffer, cell_t * fill, int x, int y)
{
    nhassert(x >= 0 && x < console.width);
    nhassert(y >= 0 && ((y < console.height) || (y == console.height &&
                                                 x == 0)));

    cell_t * dst = buffer + console.width * y + x;
    cell_t * sentinel = buffer + console.buffer_size;
    while (dst != sentinel)
        *dst++ = *fill;

    if ((iflags.debug.immediateflips || !gp.program_state.in_moveloop)
        && buffer == console.back_buffer)
        back_buffer_flip();
}

static void buffer_clear_to_end_of_line(cell_t * buffer, int x, int y)
{
    nhassert(x >= 0 && x < console.width);
    nhassert(y >= 0 && ((y < console.height) || (y == console.height &&
                                                 x == 0)));
    cell_t * dst = buffer + console.width * y + x;
    cell_t *sentinel = buffer + console.width * (y + 1);

    while (dst != sentinel)
        *dst++ = clear_cell;

    if (iflags.debug.immediateflips || !gp.program_state.in_moveloop)
        back_buffer_flip();
}

void buffer_write(cell_t * buffer, cell_t * cell, COORD pos)
{
    nhassert(pos.X >= 0 && pos.X < console.width);
    nhassert(pos.Y >= 0 && pos.Y < console.height);

    cell_t * dst = buffer + (console.width * pos.Y) + pos.X;
    *dst = *cell;

    if ((iflags.debug.immediateflips || !gp.program_state.in_moveloop)
        && buffer == console.back_buffer)
        back_buffer_flip();
}

/*
 * Called after returning from ! or ^Z
 */
void
gettty(void)
{
#ifndef TEXTCOLOR
    int k;
#endif
    erase_char = '\b';
    kill_char = 21; /* cntl-U */
    iflags.cbreak = TRUE;
#ifdef TEXTCOLOR
    init_ttycolor();
#else
    for (k = 0; k < CLR_MAX; ++k)
        ttycolors[k] = NO_COLOR;
#endif
}

/* reset terminal to original state */
void
settty(const char* s)
{
    cmov(ttyDisplay->curx, ttyDisplay->cury);
    end_screen();
    if (s)
        raw_print(s);
    restore_original_console_font();
    if (orig_QuickEdit) {
#ifndef VIRTUAL_TERMINAL_SEQUENCES
        DWORD cmode;

        GetConsoleMode(console.hConIn, &cmode);
        cmode |= (ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
        SetConsoleMode(console.hConIn, cmode);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
        GetConsoleMode(console.hConIn, &console.in_cmode);
        console.in_cmode |= (ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
        SetConsoleMode(console.hConIn, console.in_cmode);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    }
}

/* called by init_nhwindows() and resume_nhwindows() */
void
setftty(void)
{
    start_screen();
}

void
tty_startup(int *wid, int *hgt)
{
    *wid = console.width;
    *hgt = console.height;
    set_option_mod_status("mouse_support", set_in_game);
    iflags.colorcount = 16777216;
//    iflags.colorcount = 256;
}

void
tty_number_pad(int state)
{
    // do nothing
}

void
tty_start_screen(void)
{
    if (iflags.num_pad)
        tty_number_pad(1); /* make keypad send digits */
#ifdef VIRTUAL_TERMINAL_SEQUENCES
    ibmgraphics_mode_callback = tty_ibmgraphics_fixup;
#ifdef ENHANCED_SYMBOLS
#ifdef UTF8_FROM_CORE
    utf8graphics_mode_callback = tty_utf8graphics_fixup;
#endif
#endif
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
}

void
tty_end_screen(void)
{
    term_clear_screen();
    really_move_cursor();
    buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0);
    back_buffer_flip();
    FlushConsoleInputBuffer(console.hConIn);
}

static BOOL
CtrlHandler(DWORD ctrltype)
{
    switch (ctrltype) {
    /* case CTRL_C_EVENT: */
    case CTRL_BREAK_EVENT:
        term_clear_screen();
    case CTRL_CLOSE_EVENT:
    case CTRL_LOGOFF_EVENT:
    case CTRL_SHUTDOWN_EVENT:
        getreturn_enabled = FALSE;
#ifndef NOSAVEONHANGUP
        hangup(0);
#endif
#if defined(SAFERHANGUP)
        CloseHandle(console.hConIn); /* trigger WAIT_FAILED */
        return TRUE;
#endif
    default:
        return FALSE;
    }
}

/* called by pcmain() and process_options() */
void
consoletty_open(int mode)
{
    int debugvar;

    /* Initialize the function pointer that points to
     * the kbhit() equivalent, in this TTY case consoletty_kbhit()
     */
    nt_kbhit = consoletty_kbhit;

    if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) {
        /* Unable to set control handler */
        debugvar = 0; /* just to have a statement to break on for debugger */
    }

    LI = console.height;
    CO = console.width;

    really_move_cursor();
}

void
consoletty_exit(void)
{
    /* go back to using the safe routines */
    safe_routines();
}

int
process_keystroke(
    INPUT_RECORD *ir,
    boolean *valid,
    boolean numberpad,
    int portdebug)
{
    int ch;

#ifdef QWERTZ_SUPPORT
    if (gc.Cmd.swap_yz)
        numberpad |= 0x10;
#endif
    ch = keyboard_handling.pProcessKeystroke(
                    console.hConIn, ir, valid, numberpad, portdebug);
#ifdef QWERTZ_SUPPORT
    numberpad &= ~0x10;
#endif
    /* check for override */
    if (ch && ch < MAX_OVERRIDES && key_overrides[ch])
        ch = key_overrides[ch];
    return ch;
}

int
consoletty_kbhit(void)
{
    return keyboard_handling.pNHkbhit(console.hConIn, &ir);
}

int
tgetch(void)
{
    int mod;
    coord cc;
    DWORD count;
    boolean numpad = iflags.num_pad;

    really_move_cursor();
    if (iflags.debug_fuzzer)
        return randomkey();
#ifdef QWERTZ_SUPPORT
    if (gc.Cmd.swap_yz)
        numpad |= 0x10;
#endif

    return (gp.program_state.done_hup)
               ? '\033'
               : keyboard_handling.pCheckInput(
                   console.hConIn, &ir, &count, numpad, 0, &mod, &cc);
}

int
console_poskey(coordxy *x, coordxy *y, int *mod)
{
    int ch;
    coord cc = { 0, 0 };
    DWORD count;
    boolean numpad = iflags.num_pad;

    really_move_cursor();
    if (iflags.debug_fuzzer) {
        int poskey = randomkey();

        if (poskey == 0) {
            *x = rn2(console.width);
            *y = rn2(console.height);
        }
        return poskey;
    }
#ifdef QWERTZ_SUPPORT
    if (gc.Cmd.swap_yz)
        numpad |= 0x10;
#endif
    ch = (gp.program_state.done_hup)
             ? '\033'
             : keyboard_handling.pCheckInput(
                   console.hConIn, &ir, &count, numpad, 1, mod, &cc);
#ifdef QWERTZ_SUPPORT
    numpad &= ~0x10;
#endif
    if (!ch) {
        *x = cc.x;
        *y = cc.y;
    }
    return ch;
}

static void set_console_cursor(int x, int y)
{
    nhassert(x >= 0 && x < console.width);
    nhassert(y >= 0 && y < console.height);

    console.cursor.X = max(0, min(console.width - 1, x));
    console.cursor.Y = max(0, min(console.height - 1, y));
}

static void
really_move_cursor(void)
{
#ifdef PORT_DEBUG
    char oldtitle[BUFSZ], newtitle[BUFSZ];
    if (display_cursor_info && wizard) {
        oldtitle[0] = '\0';
        if (GetConsoleTitle(oldtitle, BUFSZ)) {
            oldtitle[39] = '\0';
        }
        Sprintf(newtitle, "%-55s tty=(%02d,%02d) consoletty=(%02d,%02d)", oldtitle,
                ttyDisplay->curx, ttyDisplay->cury,
                console.cursor.X, console.cursor.Y);
        (void) SetConsoleTitle(newtitle);
    }
#endif
    if (ttyDisplay)
        set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);

    back_buffer_flip();
    SetConsoleCursorPosition(console.hConOut, console.cursor);
}

void
cmov(int x, int y)
{
    if (x >= console.width)
        x = console.width - 1;
    if (y >= console.height)
        y = console.height - 1;

    ttyDisplay->cury = y;
    ttyDisplay->curx = x;

    set_console_cursor(x, y);
}

void
nocmov(int x, int y)
{
    ttyDisplay->curx = x;
    ttyDisplay->cury = y;

    set_console_cursor(x, y);
}

void
xputs(const char* s)
{
    int k;
    int slen = (int) strlen(s);

    if (ttyDisplay)
        set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);

    if (s) {
        for (k = 0; k < slen && s[k]; ++k)
#ifndef VIRTUAL_TERMINAL_SEQUENCES
            xputc_core(s[k]);
#else
            xputc_core((int) s[k]);
#endif
    }
}

/* xputc_core() and g_putch() are the only routines that actually place output.
   same signature as 'putchar()' with potential failure result ignored */
int
xputc(int ch)
{
    int x = ttyDisplay->curx, y = ttyDisplay->cury;
    if (x < console.width && y < console.height) {
        set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
        xputc_core(ch);
    }
    return 0;
}

#ifndef VIRTUAL_TERMINAL_SEQUENCES
void
xputc_core(char ch) 
#else
void
xputc_core(int ch)
#endif
{
    ccount = 1; /* default to non-zero */
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    boolean inverse = FALSE;
#else
    WCHAR wch[2];
#endif
    nhassert(console.cursor.X >= 0 && console.cursor.X < console.width);
    nhassert(console.cursor.Y >= 0 && console.cursor.Y < console.height);

    cell_t cell;

    switch (ch) {
    case '\n':
        if (console.cursor.Y < console.height - 1)
            console.cursor.Y++;
    /* fall through */
    case '\r':
        console.cursor.X = 1;
        break;
    case '\b':
        if (console.cursor.X > 1) {
            console.cursor.X--;
        } else if (console.cursor.Y > 0) {
            console.cursor.X = console.width - 1;
            console.cursor.Y--;
        }
        break;
    default:
#ifdef VIRTUAL_TERMINAL_SEQUENCES
        /* this causes way too much performance degradation */
        /* cell.color24 = customcolors[console.current_nhcolor]; */
        cell.colorseq = esc_seq_colors[console.current_nhcolor];
        cell.bkcolorseq = esc_seq_bkcolors[console.current_nhbkcolor];
        cell.attr = console.attr;
        // if (console.color24)
        //    __debugbreak();
        cell.color24 = 0L;
        cell.color256idx = 0;
        wch[1] = 0;
        if (console.has_unicode) {
            wch[0] = (ch >= 0 && ch < SIZE(console.cpMap)) ? console.cpMap[ch]
                                                           : ch;
#ifdef UTF8_FROM_CORE
            if (SYMHANDLING(H_UTF8)) {
                /* we have to convert it to UTF-8 for cell.utf8str */
                ccount = WideCharToMultiByte(
                    CP_UTF8, 0, wch, -1, (char *) cell.utf8str,
                    (int) sizeof cell.utf8str, NULL, NULL);
            } else {
#endif
            /* store the wide version here also, so we don't slow
               down back_buffer_flip() with conversions */
            cell.wcharacter = wch[0];
#ifdef UTF8_FROM_CORE
            }
#endif
        } else {
            /* we can just use the UTF-8 utf8str field, since ascii is a
               single-byte representation of a small subset of unicode */
            cell.utf8str[0] = ch;
            cell.utf8str[1] = 0;
        }
#else /* VIRTUAL_TERMINAL_SEQUENCES */
        inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse);
        console.attr = (inverse) ? ttycolors_inv[console.current_nhcolor]
                                 : ttycolors[console.current_nhcolor];
        if (console.current_nhattr[ATR_BOLD])
            console.attr |=
                (inverse) ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;

        cell.attribute = console.attr;
        cell.character = (console.has_unicode ? console.cpMap[ch] : ch);
#endif
        if (ccount) {
            buffer_write(console.back_buffer, &cell, console.cursor);
            if (console.cursor.X == console.width - 1) {
                if (console.cursor.Y < console.height - 1) {
                    console.cursor.X = 1;
                    console.cursor.Y++;
                }
            } else {
                console.cursor.X++;
            }
        }
    }
    nhassert(console.cursor.X >= 0 && console.cursor.X < console.width);
    nhassert(console.cursor.Y >= 0 && console.cursor.Y < console.height);
}

/*
 * Overrides wintty.c function of the same name
 * for win32. It is used for glyphs only, not text.
 */
#ifdef UTF8_FROM_CORE
 /* See g_pututf8() for a corresponding UTF-8 sequence
  * version, rather than a single character.
  */
#endif
void
g_putch(int in_ch)
{
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    boolean inverse = FALSE;
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    int ccount = 0;
    WCHAR wch[2];
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    unsigned char ch = (unsigned char) in_ch;

    set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
#ifndef VIRTUAL_TERMINAL_SEQUENCES

    inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse);
    console.attr = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse) ?
                    ttycolors_inv[console.current_nhcolor] :
                    ttycolors[console.current_nhcolor];
    if (console.current_nhattr[ATR_BOLD])
        console.attr |= (inverse) ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;

#endif /* ! VIRTUAL_TERMINAL_SEQUENCES */
    cell_t cell;
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    cell.attribute = console.attr;
    cell.character = (console.has_unicode ? cp437[ch] : ch);
#else
    cell.attr = console.attr;
    cell.colorseq = esc_seq_colors[console.current_nhcolor];
    cell.bkcolorseq = esc_seq_bkcolors[console.current_nhbkcolor];
    cell.color24 = console.color24 ? console.color24 : 0L;
    cell.color256idx = 0;
    wch[1] = 0;
    if (console.has_unicode) {
        wch[0] = (ch >= 0 && ch < SIZE(console.cpMap)) ? console.cpMap[ch] : ch;
#ifdef UTF8_FROM_CORE
        if (SYMHANDLING(H_UTF8)) {
            /* we have to convert it to UTF-8 for cell.utf8str */
            ccount = WideCharToMultiByte(
                CP_UTF8, 0, wch, -1, (char *) cell.utf8str,
                (int) sizeof cell.utf8str, NULL, NULL);
        } else {
#endif
            /* store the wide version here also, so we don't slow
               down back_buffer_flip() with conversions */
            cell.wcharacter = wch[0];
#ifdef UTF8_FROM_CORE
        }
#endif
    } else {
        /* we can just use the UTF-8 utf8str field, since ascii is a
           single-byte representation of a small subset of unicode */
        cell.utf8str[0] = ch;
        cell.utf8str[1] = 0;
        ccount = 2;
    }
#endif
    buffer_write(console.back_buffer, &cell, console.cursor);
}

#ifdef VIRTUAL_TERMINAL_SEQUENCES
#ifdef UTF8_FROM_CORE
/*
 * Overrides wintty.c function of the same name
 * for win32. It is used for glyphs only, not text and
 * only when a UTF-8 sequence is involved for the
 * representation. Single character representations
 * use g_putch() instead.
 */
void
g_pututf8(uint8 *sequence)
{
    set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
    cell_t cell;
    cell.attr = console.attr;
    cell.colorseq = esc_seq_colors[console.current_nhcolor];
    cell.bkcolorseq = esc_seq_bkcolors[console.current_nhbkcolor];
    cell.color24 = console.color24 ? console.color24 : 0L;
    cell.color256idx =console.color256idx ? console.color256idx : 0;
    Snprintf((char *) cell.utf8str, sizeof cell.utf8str, "%s",
             (char *) sequence);
    buffer_write(console.back_buffer, &cell, console.cursor);
}
#endif /* UTF8_FROM_CORE */

void
term_start_24bitcolor(struct unicode_representation *uval)
{
    console.color24 = uval->ucolor; /* color 0 has bit 0x1000000 set */
    console.color256idx = uval->u256coloridx;
}

void
term_end_24bitcolor(void)
{
    console.color24 = 0L;
    console.color256idx = 0;
}
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

void
cl_end(void)
{
    if (ttyDisplay->curx < console.width 
            && ttyDisplay->cury < console.height) {
        set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
        buffer_clear_to_end_of_line(console.back_buffer, console.cursor.X,
                                    console.cursor.Y);
        tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1,
                 (int) ttyDisplay->cury);
    }
}

void
raw_clear_screen(void)
{
    if (WINDOWPORT(tty)) {
        cell_t * back = console.back_buffer;
        cell_t * front = console.front_buffer;
        COORD pos;
        DWORD unused;
#ifdef VIRTUAL_TERMINAL_SEQUENCES
        DWORD reserved;
#endif

#ifdef VIRTUAL_TERMINAL_SEQUENCES
        pos.Y = 0;
        pos.X = 0;
        SetConsoleCursorPosition(console.hConOut, pos);
        emit_return_to_default();
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
        for (pos.Y = 0; pos.Y < console.height; pos.Y++) {
            for (pos.X = 0; pos.X < console.width; pos.X++) {
#ifndef VIRTUAL_TERMINAL_SEQUENCES
                 WriteConsoleOutputAttribute(console.hConOut, &back->attribute,
                                             1, pos, &unused);
                 front->attribute = back->attribute;
                 if (console.has_unicode) {
                     WriteConsoleOutputCharacterW(console.hConOut,
                             &back->character, 1, pos, &unused);
                 } else {
                     char ch = (char)back->character;
                     WriteConsoleOutputCharacterA(console.hConOut, &ch, 1, pos,
                                                         &unused);
                 }
#else /* VIRTUAL_TERMINAL_SEQUENCES */
                *back = clear_cell;
                if (console.has_unicode)
                    WriteConsoleW(console.hConOut, &back->wcharacter, 1,
                                  &unused, &reserved);
                else
                    WriteConsoleA(console.hConOut, (LPCSTR) back->utf8str,
                                    (int) strlen((char *) back->utf8str), &unused, &reserved);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
                *front = *back;
                back++;
                front++;
            }
        }
    }
}

void
term_clear_screen(void)
{
    buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0);
    home();
}

void
home(void)
{
    ttyDisplay->curx = ttyDisplay->cury = 0;
    set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
}

void
backsp(void)
{
    set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
    xputc_core('\b');
}

void
cl_eos(void)
{
    buffer_fill_to_end(console.back_buffer, &clear_cell, ttyDisplay->curx,
                        ttyDisplay->cury);
    tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury);
}

void
tty_nhbell(void)
{
    if (flags.silent || iflags.debug_fuzzer)
        return;
    Beep(8000, 500);
}

volatile int junk; /* prevent optimizer from eliminating loop below */

void
tty_delay_output(void)
{
    /* delay 50 ms - uses ANSI C clock() function now */
    clock_t goal;
    int k;

    goal = 50 + clock();
    back_buffer_flip();
    if (iflags.debug_fuzzer)
        return;

    while (goal > clock()) {
        k = junk; /* Do nothing */
    }
}

/*
 * CLR_BLACK             0
 * CLR_RED               1
 * CLR_GREEN             2
 * CLR_BROWN             3  low-intensity yellow
 * CLR_BLUE              4
 * CLR_MAGENTA           5
 * CLR_CYAN              6
 * CLR_GRAY              7  low-intensity white
 * NO_COLOR              8
 * CLR_ORANGE            9
 * CLR_BRIGHT_GREEN     10
 * CLR_YELLOW           11
 * CLR_BRIGHT_BLUE      12
 * CLR_BRIGHT_MAGENTA   13
 * CLR_BRIGHT_CYAN      14
 * CLR_WHITE            15
 * CLR_MAX              16
 * BRIGHT                8
 */

static void
init_ttycolor(void)
{
#ifdef TEXTCOLOR
    ttycolors[CLR_BLACK]        = FOREGROUND_INTENSITY; /* fix by Quietust */
    ttycolors[CLR_RED]          = FOREGROUND_RED;
    ttycolors[CLR_GREEN]        = FOREGROUND_GREEN;
    ttycolors[CLR_BROWN]        = FOREGROUND_GREEN | FOREGROUND_RED;
    ttycolors[CLR_BLUE]         = FOREGROUND_BLUE;
    ttycolors[CLR_MAGENTA]      = FOREGROUND_BLUE | FOREGROUND_RED;
    ttycolors[CLR_CYAN]         = FOREGROUND_GREEN | FOREGROUND_BLUE;
    ttycolors[CLR_GRAY]         = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE;
    ttycolors[NO_COLOR]         = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
    ttycolors[CLR_ORANGE]       = FOREGROUND_RED | FOREGROUND_INTENSITY;
    ttycolors[CLR_BRIGHT_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    ttycolors[CLR_YELLOW]       = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
    ttycolors[CLR_BRIGHT_BLUE]  = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
    ttycolors[CLR_BRIGHT_MAGENTA]=FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
    ttycolors[CLR_BRIGHT_CYAN]  = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
    ttycolors[CLR_WHITE]        = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED
                                    | FOREGROUND_INTENSITY;

    ttycolors_inv[CLR_BLACK]       = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
                                        | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_RED]         = BACKGROUND_RED | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_GREEN]       = BACKGROUND_GREEN;
    ttycolors_inv[CLR_BROWN]       = BACKGROUND_GREEN | BACKGROUND_RED;
    ttycolors_inv[CLR_BLUE]        = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_MAGENTA]     = BACKGROUND_BLUE | BACKGROUND_RED;
    ttycolors_inv[CLR_CYAN]        = BACKGROUND_GREEN | BACKGROUND_BLUE;
    ttycolors_inv[CLR_GRAY]        = BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE;
    ttycolors_inv[NO_COLOR]        = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
    ttycolors_inv[CLR_ORANGE]      = BACKGROUND_RED | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_BRIGHT_GREEN]= BACKGROUND_GREEN | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_YELLOW]      = BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_BRIGHT_BLUE] = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_BRIGHT_MAGENTA] =BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_BRIGHT_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
    ttycolors_inv[CLR_WHITE]       = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
                                       | BACKGROUND_INTENSITY;
#else
    int k;
    ttycolors[0] = FOREGROUND_INTENSITY;
    ttycolors_inv[0] = BACKGROUND_INTENSITY;
    for (k = 1; k < SIZE(ttycolors); ++k) {
        ttycolors[k] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
        ttycolors_inv[k] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
    }
#endif
    init_ttycolor_completed = TRUE;
}

int
term_attr_fixup(int attrmask)
{
    return attrmask;
}

void
term_start_attr(int attrib)
{
#ifdef VIRTUAL_TERMINAL_SEQUENCES
    switch (attrib) {
    case ATR_INVERSE:
        console.attr |= atr_inverse;
        break;
    case ATR_ULINE:
        console.attr |= atr_uline;
        break;
    case ATR_BLINK:
        console.attr |= atr_blink;
        break;
    case ATR_BOLD:
        console.attr |= atr_bold;
        break;
    }
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    console.current_nhattr[attrib] = TRUE;
    if (attrib) console.current_nhattr[ATR_NONE] = FALSE;
}

void
term_end_attr(int attrib)
{
    int k;

    switch (attrib) {
    case ATR_INVERSE:
#ifdef VIRTUAL_TERMINAL_SEQUENCES
        console.attr &= ~atr_inverse;
        break;
#endif
    case ATR_ULINE:
#ifdef VIRTUAL_TERMINAL_SEQUENCES
        console.attr &= ~atr_uline;
        break;
#endif
    case ATR_BLINK:
#ifdef VIRTUAL_TERMINAL_SEQUENCES
        console.attr &= ~atr_blink;
        break;
#endif
    case ATR_BOLD:
#ifdef VIRTUAL_TERMINAL_SEQUENCES
        console.attr &= ~atr_bold;
#endif
        break;
    }
    console.current_nhattr[attrib] = FALSE;
    console.current_nhattr[ATR_NONE] = TRUE;
    /* re-evaluate all attr now for performance at output time */
    for (k=ATR_NONE; k <= ATR_INVERSE; ++k) {
        if (console.current_nhattr[k])
            console.current_nhattr[ATR_NONE] = FALSE;
    }
}

void
term_end_raw_bold(void)
{
    term_end_attr(ATR_BOLD);
}

void
term_start_raw_bold(void)
{
    term_start_attr(ATR_BOLD);
}

void
term_start_color(int color)
{
#ifdef TEXTCOLOR
    if (color >= 0 && color < CLR_MAX) {
        console.current_nhcolor = color;
    } else
#endif
    console.current_nhcolor = NO_COLOR;
}

void
term_start_bgcolor(int color)
{
#ifdef TEXTCOLOR
    if (color >= 0 && color < CLR_MAX) {
        console.current_nhbkcolor = color;
    } else
#endif
    console.current_nhbkcolor = NO_COLOR;
}

void
term_end_color(void)
{
#ifdef TEXTCOLOR
    console.foreground = DEFTEXTCOLOR;
#endif
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    console.attr = (console.foreground | console.background);
#endif /* ! VIRTUAL_TERMINAL_SEQUENCES */
    console.current_nhcolor = NO_COLOR;
    console.current_nhbkcolor = NO_COLOR;
}

void
standoutbeg(void)
{
    term_start_attr(ATR_BOLD);
}

void
standoutend(void)
{
    term_end_attr(ATR_BOLD);
}

#ifndef NO_MOUSE_ALLOWED
void
toggle_mouse_support(void)
{
    static int qeinit = 0;
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    DWORD cmode;
#endif /* ! VIRTUAL_TERMINAL_SEQUENCES */

#ifndef VIRTUAL_TERMINAL_SEQUENCES
    GetConsoleMode(console.hConIn, &cmode);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    GetConsoleMode(console.hConIn, &console.in_cmode);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    if (!qeinit) {
        qeinit = 1;
#ifndef VIRTUAL_TERMINAL_SEQUENCES
        orig_QuickEdit = ((cmode & ENABLE_QUICK_EDIT_MODE) != 0);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
        orig_QuickEdit = ((console.in_cmode & ENABLE_QUICK_EDIT_MODE) != 0);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    }
    switch(iflags.wc_mouse_support) {
        case 2:
#ifndef VIRTUAL_TERMINAL_SEQUENCES
                cmode |= ENABLE_MOUSE_INPUT;
#else /* VIRTUAL_TERMINAL_SEQUENCES */
                console.in_cmode |= ENABLE_MOUSE_INPUT;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
                break;
        case 1:
#ifndef VIRTUAL_TERMINAL_SEQUENCES
                cmode |= ENABLE_MOUSE_INPUT;
                cmode &= ~ENABLE_QUICK_EDIT_MODE;
                cmode |= ENABLE_EXTENDED_FLAGS;
#else /* VIRTUAL_TERMINAL_SEQUENCES */
                console.in_cmode |= ENABLE_MOUSE_INPUT;
                console.in_cmode &= ~ENABLE_QUICK_EDIT_MODE;
                console.in_cmode |= ENABLE_EXTENDED_FLAGS;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
                break;
        case 0:
                /*FALLTHRU*/
        default:
#ifndef VIRTUAL_TERMINAL_SEQUENCES
                cmode &= ~ENABLE_MOUSE_INPUT;
#else /* VIRTUAL_TERMINAL_SEQUENCES */
                console.in_cmode &= ~ENABLE_MOUSE_INPUT;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
                if (orig_QuickEdit)
#ifndef VIRTUAL_TERMINAL_SEQUENCES
                    cmode |= (ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
                    console.in_cmode |= (ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    }
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    SetConsoleMode(console.hConIn, cmode);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    SetConsoleMode(console.hConIn, console.in_cmode);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
}
#endif

/* handle tty options updates here */
void
consoletty_preference_update(const char* pref)
{
    if (stricmp(pref, "mouse_support") == 0) {
#ifndef NO_MOUSE_ALLOWED
        toggle_mouse_support();
#endif
    }
    if (stricmp(pref, "symset") == 0) {
#ifdef UTF8_FROM_CORE
        if ((tty_procs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) {
#ifdef ENHANCED_SYMBOLS
            tty_utf8graphics_fixup();
#endif
        } else if ((tty_procs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_IBM)) {
#else
        if (SYMHANDLING(H_IBM)) {
#endif
            tty_ibmgraphics_fixup();
        }
        check_and_set_font();
    }
    return;
}

#ifdef UTF8_FROM_CORE
#ifdef VIRTUAL_TERMINAL_SEQUENCES
/*
 * This is called when making the switch to a symset
 * with a UTF8 handler to allow any operating system
 * specific changes to be carried out.
 */
void
tty_utf8graphics_fixup(void)
{
    CONSOLE_FONT_INFOEX console_font_info;
    if ((tty_procs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) {
        char *localestr = 0;

        if (!console.hConOut)
            console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
        /* the locale */
        if (console.localestr) {
            free(console.localestr);
            console.localestr = NULL;
        }
        localestr = setlocale(LC_ALL, ".UTF8");
        if (localestr)
            console.localestr = dupstr(localestr);
        /* the code page */
        SetConsoleOutputCP(65001);
        console.code_page = GetConsoleOutputCP();
        /* the font */
        console_font_info.cbSize = sizeof(console_font_info);
        BOOL success = GetCurrentConsoleFontEx(console.hConOut, FALSE,
                                               &console_font_info);
        /* Try DejaVu Sans Mono for Powerline */
        wcscpy_s(console_font_info.FaceName,
                 sizeof(console_font_info.FaceName)
                     / sizeof(console_font_info.FaceName[0]),
                 L"DejaVu Sans Mono for Powerline");
        console_font_info.cbSize = sizeof(console_font_info);
        success = SetCurrentConsoleFontEx(console.hConOut, FALSE,
                                          &console_font_info);
        if (!success) {
            /* Next, try Lucida Console */
            wcscpy_s(console_font_info.FaceName,
                     sizeof(console_font_info.FaceName)
                         / sizeof(console_font_info.FaceName[0]),
                     L"Lucida Console");
            console_font_info.cbSize = sizeof(console_font_info);
            success = SetCurrentConsoleFontEx(console.hConOut, FALSE,
                                              &console_font_info);
        }
        nhassert(success);
        if (success) {
            console.font_info = console_font_info;
            console.font_changed = TRUE;
        }
        /* the console mode */
        GetConsoleMode(console.hConOut, &console.out_cmode);
#if 1
        if ((console.out_cmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) {
            /* recognize escape sequences */
            console.out_cmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
            SetConsoleMode(console.hConOut, console.out_cmode);
        }
#else
        console.out_cmode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING;
#endif
    }
}
#endif
#endif /* UTF8_FROM_CORE */

/*
 * This is called when making the switch to a symset
 * with an IBM handler to allow any operating system
 * specific changes to be carried out.
 */
void
tty_ibmgraphics_fixup(void)
{
#ifdef VIRTUAL_TERMINAL_SEQUENCES
    char buf[BUFSZ], *bp, *localestr;

    if (!console.hConOut)
        console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    /* the locale */
    Snprintf(buf, sizeof buf, "%s", console.orig_localestr);
    if ((bp = strstri(buf, ".utf8")) != 0)
        *bp = '\0';
    localestr = setlocale(LC_ALL, buf);
    if (localestr) {
        if (console.localestr)
            free(console.localestr);
        console.localestr = dupstr(localestr);
    }
    set_known_good_console_font();
    /* the console mode */
    GetConsoleMode(console.hConOut, &console.out_cmode);
    if ((console.out_cmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) {
        console.out_cmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(console.hConOut, console.out_cmode);
        GetConsoleMode(console.hConOut, &console.out_cmode);
    }
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
}

#ifdef PORT_DEBUG
void
win32con_debug_keystrokes(void)
{
    DWORD count;
    boolean valid = 0;
    int ch = 0;
    xputs("\n");
    while (!valid || ch != 27) {
        nocmov(ttyDisplay->curx, ttyDisplay->cury);
        ReadConsoleInput(console.hConIn, &ir, 1, &count);
        if ((ir.EventType == KEY_EVENT) && ir.Event.KeyEvent.bKeyDown)
            ch = process_keystroke(&ir, &valid, iflags.num_pad, 1);
    }
    (void) doredraw();
}
void
win32con_toggle_cursor_info(void)
{
    display_cursor_info = !display_cursor_info;
}
#endif

void
map_subkeyvalue(char* op)
{
    char digits[] = "0123456789";
    int length, i, idx, val;
    char *kp;

    idx = -1;
    val = -1;
    kp = strchr(op, '/');
    if (kp) {
        *kp = '\0';
        kp++;
        length = strlen(kp);
        if (length < 1 || length > 3)
            return;
        for (i = 0; i < length; i++)
            if (!strchr(digits, kp[i]))
                return;
        val = atoi(kp);
        length = strlen(op);
        if (length < 1 || length > 3)
            return;
        for (i = 0; i < length; i++)
            if (!strchr(digits, op[i]))
                return;
        idx = atoi(op);
    }
    if (idx >= MAX_OVERRIDES || idx < 0 || val >= MAX_OVERRIDES || val < 1)
        return;
    key_overrides[idx] = val;
}


/* fatal error */
/*VARARGS1*/
void consoletty_error
VA_DECL(const char *, s)
{
    char buf[BUFSZ];
    VA_START(s);
    VA_INIT(s, const char *);
    /* error() may get called before tty is initialized */
    if (iflags.window_inited)
        end_screen();
    buf[0] = '\n';
    (void) vsnprintf(&buf[1], sizeof buf - 1, s, VA_ARGS);
    msmsg(buf);
    really_move_cursor();
    VA_END();
    exit(EXIT_FAILURE);
}

void
synch_cursor(void)
{
    really_move_cursor();
}

static int CALLBACK EnumFontCallback(
    const LOGFONTW * lf, const TEXTMETRICW * tm, DWORD fontType, LPARAM lParam)
{
    LOGFONTW * lf_ptr = (LOGFONTW *) lParam;
    *lf_ptr = *lf;
    return 0;
}

/* check_and_set_font ensures that the current font will render the symbols
 * that are currently being used correctly.  If they will not be rendered
 * correctly, then it will change the font to a known good font.
 */
void
check_and_set_font(void)
{
    if (!check_font_widths()) {
        raw_print("WARNING: glyphs too wide in console font."
                  "  Changing code page to 437 and font to Consolas\n");
        set_known_good_console_font();
    }
}

/* check_font_widths returns TRUE if all glyphs in current console font
 * fit within the width of a single console cell.
 */
boolean
#ifndef VIRTUAL_TERMINAL_SEQUENCES
check_font_widths(void)
#else /* VIRTUAL_TERMINAL_SEQUENCES */
check_font_widths(void)
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
{
    CONSOLE_FONT_INFOEX console_font_info;
    console_font_info.cbSize = sizeof(console_font_info);
    BOOL success = GetCurrentConsoleFontEx(console.hConOut, FALSE,
                                            &console_font_info);

    /* get console window and DC
     * NOTE: the DC from the console window does not have the correct
     *       font selected at this point.
     */
    console.hWnd = GetConsoleWindow();
    HDC hDC = GetDC(console.hWnd);

    LOGFONTW logical_font;
    logical_font.lfCharSet = DEFAULT_CHARSET;
    wcscpy(logical_font.lfFaceName, console_font_info.FaceName);
    logical_font.lfPitchAndFamily = 0;

    /* getting matching console font */
    LOGFONTW matching_log_font = { 0 };
    EnumFontFamiliesExW(hDC, &logical_font, EnumFontCallback,
                                        (LPARAM) &matching_log_font, 0);

    if (matching_log_font.lfHeight == 0) {
        raw_print("Unable to enumerate system fonts\n");
        return FALSE;
    }

    /* create font matching console font */
    LOGFONTW console_font_log_font = matching_log_font;
    console_font_log_font.lfWeight = console_font_info.FontWeight;
    console_font_log_font.lfHeight = console_font_info.dwFontSize.Y;
    console_font_log_font.lfWidth = console_font_info.dwFontSize.X;
    HFONT console_font = CreateFontIndirectW(&console_font_log_font);

    if (console_font == NULL) {
        raw_print("Unable to create console font\n");
        return FALSE;
    }

    /* select font */
    HGDIOBJ saved_font = SelectObject(hDC, console_font);

    /* measure the set of used glyphs to ensure they fit */
    boolean all_glyphs_fit = FALSE;

    /* determine whether it is a true type font */
    TEXTMETRICA tm;
    success = GetTextMetricsA(hDC, &tm);

    if (!success) {
        raw_print("Unable to get console font text metrics\n");
        goto clean_up;
    }

    boolean isTrueType = (tm.tmPitchAndFamily & TMPF_TRUETYPE) != 0;

    /* determine which glyphs are used */
    boolean used[256];
    memset(used, 0, sizeof(used));
    for (int i = 0; i < SYM_MAX; i++) {
        used[gp.primary_syms[i]] = TRUE;
        used[gr.rogue_syms[i]] = TRUE;
    }

    int wcUsedCount = 0;
    wchar_t wcUsed[256];
    for (int i = 0; i < sizeof(used); i++)
        if (used[i])
            wcUsed[wcUsedCount++] = cp437[i];

    all_glyphs_fit = TRUE;

    for (int i = 0; i < wcUsedCount; i++) {
        int width;
        if (isTrueType) {
            ABC abc;
            success = GetCharABCWidthsW(hDC, wcUsed[i], wcUsed[i], &abc);
            width = abc.abcA + abc.abcB + abc.abcC;
        } else {
            success = GetCharWidthW(hDC, wcUsed[i], wcUsed[i], &width);
        }

        if (success && width > console_font_info.dwFontSize.X) {
            all_glyphs_fit = FALSE;
            break;
        }
    }

clean_up:

    SelectObject(hDC, saved_font);
    DeleteObject(console_font);

    return all_glyphs_fit;
}

/* set_known_good_console_font sets the code page and font used by the console
 * to settings know to work well with NetHack.  It also saves the original
 * settings so that they can be restored prior to NetHack exit.
 */
void
set_known_good_console_font(void)
{
    CONSOLE_FONT_INFOEX console_font_info;
    console_font_info.cbSize = sizeof(console_font_info);
    BOOL success = GetCurrentConsoleFontEx(console.hConOut, FALSE,
                                            &console_font_info);

    success = SetConsoleOutputCP(437);
    nhassert(success);
    console.font_changed = TRUE;
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    console.orig_font_info = console_font_info;
    console.original_code_page = GetConsoleOutputCP();

#else /* VIRTUAL_TERMINAL_SEQUENCES */
    console.code_page = GetConsoleOutputCP();
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    wcscpy_s(console_font_info.FaceName,
        sizeof(console_font_info.FaceName)
            / sizeof(console_font_info.FaceName[0]),
        L"Consolas");
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    success = SetConsoleOutputCP(437);
    nhassert(success);
#endif /* ! VIRTUAL_TERMINAL_SEQUENCES */
    success = SetCurrentConsoleFontEx(console.hConOut, FALSE, &console_font_info);
    nhassert(success);
#ifdef VIRTUAL_TERMINAL_SEQUENCES
    if (success)
        console.font_info = console_font_info;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
}

/* restore_original_console_font will restore the console font and code page
 * settings to what they were when NetHack was launched.
 */
void
restore_original_console_font(void)
{
#ifdef VIRTUAL_TERMINAL_SEQUENCES
    char *tmplocalestr;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

    if (console.font_changed) {
        BOOL success;
#ifndef VIRTUAL_TERMINAL_SEQUENCES
        raw_print("Restoring original font and code page\n");
        success = SetConsoleOutputCP(console.original_code_page);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
        if (wizard)
            raw_print("Restoring original font, code page and locale\n");

        tmplocalestr = setlocale(LC_ALL, console.orig_localestr);
        if (tmplocalestr) {
            if (console.localestr)
                free(console.localestr);
            console.localestr = dupstr(tmplocalestr);
        }
        success = SetConsoleOutputCP(console.orig_code_page);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
        if (!success)
            raw_print("Unable to restore original code page\n");

        success = SetCurrentConsoleFontEx(console.hConOut, FALSE,
                                            &console.orig_font_info);
        if (!success)
            raw_print("Unable to restore original font\n");

        console.font_changed = FALSE;
    }
}

/* set_cp_map() creates a mapping of every possible character of a code
 * page to its corresponding WCHAR.  This is necessary due to the high
 * cost of making calls to MultiByteToWideChar() for every character we
 * wish to print to the console.
 */

void set_cp_map(void)
{
    if (console.has_unicode) {
#ifndef VIRTUAL_TERMINAL_SEQUENCES
        UINT codePage = GetConsoleOutputCP();
#else
        console.code_page = GetConsoleOutputCP();
#endif

#ifndef VIRTUAL_TERMINAL_SEQUENCES
        if (codePage == 437) {
#else
        if (console.code_page == 437) {
#endif
            memcpy(console.cpMap, cp437, sizeof(console.cpMap));
        } else {
            for (int i = 0; i < 256; i++) {
                char c = (char)i;
                int count = MultiByteToWideChar(
#ifndef VIRTUAL_TERMINAL_SEQUENCES
                                                codePage,
#else
                                                console.code_page,
#endif
                                              0, &c, 1, &console.cpMap[i], 1);
                nhassert(count == 1);

                // If a character was mapped to unicode control codes,
                // remap to the appropriate unicode character per our
                // code page 437 mappings.
                if (console.cpMap[i] < 32)
                    console.cpMap[i] = cp437[console.cpMap[i]];
            }
        }

    }
}

#if 0
/* early_raw_print() is used during early game intialization prior to the
 * setting up of the windowing system.  This allows early errors and panics
 * to have there messages displayed.
 *
 * early_raw_print() eventually gets replaced by tty_raw_print().
 *
 */

void early_raw_print(const char *s)
{
    if (console.hConOut == NULL)
        return;

    nhassert(console.cursor.X >= 0 && console.cursor.X < console.width);
    nhassert(console.cursor.Y >= 0 && console.cursor.Y < console.height);

    WORD attribute = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE;
    DWORD unused;

    while (*s != '\0') {
        switch (*s) {
        case '\n':
            if (console.cursor.Y < console.height - 1)
                console.cursor.Y++;
        /* fall through */
        case '\r':
            console.cursor.X = 1;
            break;
        case '\b':
            if (console.cursor.X > 1) {
                console.cursor.X--;
            } else if(console.cursor.Y > 0) {
                console.cursor.X = console.width - 1;
                console.cursor.Y--;
            }
            break;
        default:
            WriteConsoleOutputAttribute(console.hConOut, &attribute,
                                            1, console.cursor, &unused);
            WriteConsoleOutputCharacterA(console.hConOut, s,
                                            1, console.cursor, &unused);
            if (console.cursor.X == console.width - 1) {
                if (console.cursor.Y < console.height - 1) {
                    console.cursor.X = 1;
                    console.cursor.Y++;
                }
            } else {
                console.cursor.X++;
            }
        }
        s++;
    }

    nhassert(console.cursor.X >= 0 && console.cursor.X < console.width);
    nhassert(console.cursor.Y >= 0 && console.cursor.Y < console.height);

    SetConsoleCursorPosition(console.hConOut, console.cursor);

}
#endif

/* nethack_enter_consoletty() is the first thing that is called from main
 * once the tty port is confirmed.
 *
 * We initialize all console state to support rendering to the console
 * through out flipping support at this time.  This allows us to support
 * raw_print prior to our returning.
 *
 * During this early initialization, we also determine the width and
 * height of the console that will be used.  This width and height will
 * not later change.
 *
 * We also check and set the console font to a font that we know will work
 * well with nethack.
 *
 * The intent of this early initialization is to get all state that is
 * not dependent upon game options initialized allowing us to simplify
 * any additional initialization that might be needed when we are actually
 * asked to open.
 *
 * Other then the call below which clears the entire console buffer, no
 * other code outputs directly to the console other then the code that
 * handles flipping the back buffer.
 *
 */

void nethack_enter_consoletty(void)
{
#ifdef VIRTUAL_TERMINAL_SEQUENCES
    char buf[BUFSZ], *bp, *localestr;
    BOOL success;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
#if 0
    /* set up state needed by early_raw_print() */
    windowprocs.win_raw_print = early_raw_print;
#endif
#if 0
    /* prevent re-sizing of the console window */
    if (!console.hWnd)
        console.hWnd = GetConsoleWindow();
    SetWindowLong(console.hWnd, GWL_STYLE,
                  GetWindowLong(console.hWnd, GWL_STYLE)
                     & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX);
#endif
    console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    nhassert(console.hConOut != NULL); // NOTE: this assert will not print
    GetConsoleScreenBufferInfo(console.hConOut, &console.orig_csbi);
    /* Testing of widths != COLNO has not turned up any problems.  Need
     * to do a bit more testing and then we are likely to enable having
     * console width match window width.
     */
#if 0
    console.width = console.orig_csbi.srWindow.Right -
                     console.orig_csbi.srWindow.Left + 1;
    console.Width = max(console.Width, COLNO);
#else
//    console.width = COLNO;
    console.width = console.orig_csbi.srWindow.Right -
                     console.orig_csbi.srWindow.Left + 1;
    if (console.width < COLNO)
        console.width = COLNO;
#endif

    console.height = console.orig_csbi.srWindow.Bottom -
                     console.orig_csbi.srWindow.Top + 1;
//    console.height = max(console.height, ROWNO + 3);
    if (console.height < (ROWNO + 2 + 1))
        console.height = (ROWNO + 2 + 1);
    console.buffer_size = console.width * console.height;


    /* clear the entire console buffer */
    int size = console.orig_csbi.dwSize.X * console.orig_csbi.dwSize.Y;
    DWORD unused;
    set_console_cursor(0, 0);
    FillConsoleOutputAttribute(
        console.hConOut, CONSOLE_CLEAR_ATTRIBUTE,
        size, console.cursor, &unused);

    FillConsoleOutputCharacter(
        console.hConOut, CONSOLE_CLEAR_CHARACTER,
        size, console.cursor, &unused);

    set_console_cursor(1, 0);
    SetConsoleCursorPosition(console.hConOut, console.cursor);

    /* At this point early_raw_print will work */

    console.hConIn = GetStdHandle(STD_INPUT_HANDLE);
    nhassert(console.hConIn  != NULL);

    /* grow the size of the console buffer if it is not wide enough */
    if (console.orig_csbi.dwSize.X < console.width) {
        COORD screen_size = {0};

        screen_size.Y = console.orig_csbi.dwSize.Y,
        screen_size.X = console.width;
        SetConsoleScreenBufferSize(console.hConOut, screen_size);
    }

    /* setup front and back buffers */
    int buffer_size_bytes = sizeof(cell_t) * console.buffer_size;

    console.front_buffer = (cell_t *)malloc(buffer_size_bytes);
    buffer_fill_to_end(console.front_buffer, &undefined_cell, 0, 0);

    console.back_buffer = (cell_t *)malloc(buffer_size_bytes);
    buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0);

    /* determine whether OS version has unicode support */
    console.has_unicode = ((GetVersion() & 0x80000000) == 0);

#ifdef VIRTUAL_TERMINAL_SEQUENCES
    /* store the original code page*/
    console.orig_code_page = GetConsoleOutputCP();

    /* store the original locale */
    console.orig_localestr = NULL;
    localestr = setlocale(LC_ALL, "");
    if (localestr)
        console.orig_localestr = dupstr(localestr);

    /* store the original font */
    console.orig_font_info.cbSize = sizeof(console.orig_font_info);
    success = GetCurrentConsoleFontEx(console.hConOut,
                                      FALSE, &console.orig_font_info);
    console.font_info = console.orig_font_info;

    /* adjust the locale */
    Snprintf(buf, sizeof buf, "%s", console.orig_localestr);
    if ((bp = strstri(buf, ".utf8")) != 0)
        *bp = '\0';
    localestr = setlocale(LC_ALL, buf);
    if (localestr) {
        if (console.localestr)
            free(console.localestr);
        console.localestr = dupstr(localestr);
    }
    console.code_page = console.orig_code_page;
    if (console.has_unicode) {
        if (console.code_page != 437)
            success = SetConsoleOutputCP(437);
    } else if (console.code_page != 1252) {
        success = SetConsoleOutputCP(1252);
    }
    console.code_page = GetConsoleOutputCP();
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

    /* check the font before we capture the code page map */
    check_and_set_font();
    set_cp_map();

#ifndef VIRTUAL_TERMINAL_SEQUENCES
    /* Set console mode */
    DWORD cmode, mask;
    GetConsoleMode(console.hConIn, &cmode);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    /* input console mode */
    DWORD disablemask;
    GetConsoleMode(console.hConIn, &console.orig_in_cmode);
    GetConsoleMode(console.hConOut, &console.orig_out_cmode);

    console.in_cmode = console.orig_in_cmode;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
#ifdef NO_MOUSE_ALLOWED
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT
#else
    disablemask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT
#endif
           | ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT;
#else
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
#else
    disablemask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
#endif
           | ENABLE_WINDOW_INPUT;
#endif
    /* Turn OFF the settings specified in the mask */
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    cmode &= ~mask;
#else
    console.in_cmode &= ~disablemask;
#endif
#ifndef NO_MOUSE_ALLOWED
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    cmode |= ENABLE_MOUSE_INPUT;
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    console.in_cmode |= ENABLE_MOUSE_INPUT;
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
#endif /* NO_MOUSE_ALLOWED */
#ifndef VIRTUAL_TERMINAL_SEQUENCES
    SetConsoleMode(console.hConIn, cmode);
#else /* VIRTUAL_TERMINAL_SEQUENCES */
    SetConsoleMode(console.hConIn, console.in_cmode);

    console.out_cmode = console.orig_out_cmode;
    if ((console.out_cmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) {
        /* recognize escape sequences */
        console.out_cmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(console.hConOut, console.out_cmode);
    }
    GetConsoleMode(console.hConOut, &console.out_cmode);
#endif /* VIRTUAL_TERMINAL_SEQUENCES */

    /* load default keyboard handler */
    HKL keyboard_layout = GetKeyboardLayout(0);
    DWORD primary_language = (UINT_PTR) keyboard_layout & 0x3f;

    /* This was overriding the handler that had already
       been loaded during options parsing. Needs to
       check first */
    if (iflags.key_handling == no_keyhandling) {
        if (primary_language == LANG_ENGLISH) {
            set_altkeyhandling("default");
        } else {
            set_altkeyhandling("ray");
        }
    }
#ifdef VIRTUAL_TERMINAL_SEQUENCES
    init_custom_colors();
#endif /* VIRTUAL_TERMINAL_SEQUENCES */
    console.current_nhcolor = NO_COLOR;
    console.is_ready = TRUE;
}
#endif /* TTY_GRAPHICS */

/* this is used as a printf() replacement when the window
 * system isn't initialized yet
 */
void msmsg
VA_DECL(const char *, fmt)
{
    char buf[ROWNO * COLNO]; /* worst case scenario */
    VA_START(fmt);
    VA_INIT(fmt, const char *);
    (void) vsnprintf(buf, sizeof buf, fmt, VA_ARGS);
    if (redirect_stdout)
        fprintf(stdout, "%s", buf);
    else {
#ifdef TTY_GRAPHICS
        if(!init_ttycolor_completed)
            init_ttycolor();
        /* if we have generated too many messages ... ask the user to
         * confirm and then clear.
         */
        if (console.cursor.Y > console.height - 4) {
            xputs("Hit <Enter> to continue.");
            while (pgetchar() != '\n')
                ;
            raw_clear_screen();
            set_console_cursor(1, 0);
        }
        xputs(buf);
        if (ttyDisplay)
            curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y);
#else
        fprintf(stdout, "%s", buf);
#endif
    }
    VA_END();
    return;
}
#ifndef VIRTUAL_TERMINAL_SEQUENCES

#endif /* ! VIRTUAL_TERMINAL_SEQUENCES */

/*
 *  Keyboard translation tables.
 *  (Adopted from the MSDOS port)
 */

#define KEYPADLO 0x47
#define KEYPADHI 0x53

#define PADKEYS (KEYPADHI - KEYPADLO + 1)
#define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI)
#define isnumkeypad(x) \
    (KEYPADLO <= (x) && (x) <= 0x51 && (x) != 0x4A && (x) != 0x4E)

#ifdef QWERTZ_SUPPORT
/* when 'numberpad' is 0 and Cmd.swap_yz is True
   (signaled by setting 0x10 on boolean numpad argument)
   treat keypress of numpad 7 as 'z' rather than 'y' */
static boolean qwertz = FALSE;
#endif
#define inmap(x, vk) (((x) > 'A' && (x) < 'Z') || (vk) == 0xBF || (x) == '2')

struct pad {
    uchar normal, shift, cntrl;
};

/*
 * default key handling
 *
 * This is the default NetHack keystroke processing.
 * Use the .nethackrc "altkeyhandling" option to set a
 * different handling type.
 *
 */
/*
 * Keypad keys are translated to the normal values below.
 * Shifted keypad keys are translated to the
 *    shift values below.
 */

static const struct pad default_keypad[PADKEYS] = {
      { 'y', 'Y', C('y') },    /* 7 */
      { 'k', 'K', C('k') },    /* 8 */
      { 'u', 'U', C('u') },    /* 9 */
      { 'm', C('p'), C('p') }, /* - */
      { 'h', 'H', C('h') },    /* 4 */
      { 'g', 'G', 'g' },       /* 5 */
      { 'l', 'L', C('l') },    /* 6 */
      { '+', 'P', C('p') },    /* + */
      { 'b', 'B', C('b') },    /* 1 */
      { 'j', 'J', C('j') },    /* 2 */
      { 'n', 'N', C('n') },    /* 3 */
      { 'i', 'I', C('i') },    /* Ins */
      { '.', ':', ':' }        /* Del */
}, default_numpad[PADKEYS] = {
      { '7', M('7'), '7' },    /* 7 */
      { '8', M('8'), '8' },    /* 8 */
      { '9', M('9'), '9' },    /* 9 */
      { 'm', C('p'), C('p') }, /* - */
      { '4', M('4'), '4' },    /* 4 */
      { '5', M('5'), '5' },    /* 5 */
      { '6', M('6'), '6' },    /* 6 */
      { '+', 'P', C('p') },    /* + */
      { '1', M('1'), '1' },    /* 1 */
      { '2', M('2'), '2' },    /* 2 */
      { '3', M('3'), '3' },    /* 3 */
      { '0', M('0'), '0' },    /* Ins */
      { '.', ':', ':' }        /* Del */
};

/*
 * Keypad keys are translated to the normal values below.
 * Shifted keypad keys are translated to the
 *    shift values below.
 */

static const struct pad
ray_keypad[PADKEYS] = {
      { 'y', 'Y', C('y') },    /* 7 */
      { 'k', 'K', C('k') },    /* 8 */
      { 'u', 'U', C('u') },    /* 9 */
      { 'm', C('p'), C('p') }, /* - */
      { 'h', 'H', C('h') },    /* 4 */
      { 'g', 'G', 'g' },       /* 5 */
      { 'l', 'L', C('l') },    /* 6 */
      { '+', 'P', C('p') },    /* + */
      { 'b', 'B', C('b') },    /* 1 */
      { 'j', 'J', C('j') },    /* 2 */
      { 'n', 'N', C('n') },    /* 3 */
      { 'i', 'I', C('i') },    /* Ins */
      { '.', ':', ':' }        /* Del */
},
ray_numpad[PADKEYS] = {
      { '7', M('7'), '7' },    /* 7 */
      { '8', M('8'), '8' },    /* 8 */
      { '9', M('9'), '9' },    /* 9 */
      { 'm', C('p'), C('p') }, /* - */
      { '4', M('4'), '4' },    /* 4 */
      { 'g', 'G', 'g' },       /* 5 */
      { '6', M('6'), '6' },    /* 6 */
      { '+', 'P', C('p') },    /* + */
      { '1', M('1'), '1' },    /* 1 */
      { '2', M('2'), '2' },    /* 2 */
      { '3', M('3'), '3' },    /* 3 */
      { 'i', 'I', C('i') },    /* Ins */
      { '.', ':', ':' }        /* Del */
};

static const struct pad
nh340_keypad[PADKEYS] = {
      { 'y', 'Y', C('y') },    /* 7 */
      { 'k', 'K', C('k') },    /* 8 */
      { 'u', 'U', C('u') },    /* 9 */
      { 'm', C('p'), C('p') }, /* - */
      { 'h', 'H', C('h') },    /* 4 */
      { 'g', 'G', 'g' },       /* 5 */
      { 'l', 'L', C('l') },    /* 6 */
      { '+', 'P', C('p') },    /* + */
      { 'b', 'B', C('b') },    /* 1 */
      { 'j', 'J', C('j') },    /* 2 */
      { 'n', 'N', C('n') },    /* 3 */
      { 'i', 'I', C('i') },    /* Ins */
      { '.', ':', ':' }        /* Del */
},
nh340_numpad[PADKEYS] = {
      { '7', M('7'), '7' },    /* 7 */
      { '8', M('8'), '8' },    /* 8 */
      { '9', M('9'), '9' },    /* 9 */
      { 'm', C('p'), C('p') }, /* - */
      { '4', M('4'), '4' },    /* 4 */
      { 'g', 'G', 'g' },       /* 5 */
      { '6', M('6'), '6' },    /* 6 */
      { '+', 'P', C('p') },    /* + */
      { '1', M('1'), '1' },    /* 1 */
      { '2', M('2'), '2' },    /* 2 */
      { '3', M('3'), '3' },    /* 3 */
      { 'i', 'I', C('i') },    /* Ins */
      { '.', ':', ':' }        /* Del */
};

static struct pad keypad[PADKEYS], numpad[PADKEYS];
static BYTE KeyState[256];

void set_altkeyhandling(const char *inName)
{
    int i, k;
    /*backward compatibility - so people's existing config files
      may work as is */
    if (!strcmpi(inName, "nhraykey.dll"))
         inName = legal_key_handling[ray_keyhandling];
    else if (!strcmpi(inName, "nh340key.dll"))
         inName = legal_key_handling[nh340_keyhandling];
    else if (!strcmpi(inName, "nhdefkey.dll"))
         inName = legal_key_handling[default_keyhandling];

    for (i = default_keyhandling; i < SIZE(legal_key_handling); i++) {
        if (!strcmpi(inName, legal_key_handling[i])) {
            iflags.key_handling = keyh[i];
            switch(iflags.key_handling) {
            case ray_keyhandling:
                keyboard_handling.khid = ray_keyhandling;
                keyboard_handling.pProcessKeystroke = ray_processkeystroke;
                keyboard_handling.pNHkbhit = ray_kbhit;
                keyboard_handling.pCheckInput = ray_checkinput;
                /* A bogus key that will be filtered when received, to keep ReadConsole
                 * from blocking */
                bogus_key.EventType = KEY_EVENT;
                bogus_key.Event.KeyEvent.bKeyDown = 1;
                bogus_key.Event.KeyEvent.wRepeatCount = 1;
                bogus_key.Event.KeyEvent.wVirtualKeyCode = 0;
                bogus_key.Event.KeyEvent.wVirtualScanCode = 0;
                bogus_key.Event.KeyEvent.uChar.AsciiChar = (uchar) 0x80;
                bogus_key.Event.KeyEvent.dwControlKeyState = 0;
                for (k = 0; k < SIZE(keypad); ++k) {
                    keypad[k] = ray_keypad[k];
                    numpad[k] = ray_numpad[k];
                }
                break;
            case nh340_keyhandling:
                keyboard_handling.khid = nh340_keyhandling;
                keyboard_handling.pProcessKeystroke = nh340_processkeystroke;
                keyboard_handling.pNHkbhit = nh340_kbhit;
                keyboard_handling.pCheckInput = nh340_checkinput;
                for (k = 0; k < SIZE(keypad); ++k) {
                    keypad[k] = nh340_keypad[k];
                    numpad[k] = nh340_numpad[k];
                }
                break;
            case default_keyhandling:
            default:
                keyboard_handling.khid = default_keyhandling;
                keyboard_handling.pProcessKeystroke
                                            = default_processkeystroke;
                keyboard_handling.pNHkbhit = default_kbhit;
                keyboard_handling.pCheckInput = default_checkinput;
                for (k = 0; k < SIZE(keypad); ++k) {
                    keypad[k] = default_keypad[k];
                    numpad[k] = default_numpad[k];
                }
                break;
            }
            return;
        }
    }
    config_error_add("invalid altkeyhandling '%s'", inName);
    return;
}

int
set_keyhandling_via_option(void)
{
    winid tmpwin;
    anything any;
    int i, clr = 0;
    menu_item *console_key_handling_pick = (menu_item *) 0;

    tmpwin = create_nhwindow(NHW_MENU);
    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
    any = cg.zeroany;
    for (i = default_keyhandling; i < SIZE(legal_key_handling); i++) {
        any.a_int = i + 1;
        add_menu(tmpwin, &nul_glyphinfo, &any, 'a' + i,
                 0, ATR_NONE, clr,
                 legal_key_handling[i], MENU_ITEMFLAGS_NONE);
    }
    end_menu(tmpwin, "Select windows console key handling:");
    if (select_menu(tmpwin, PICK_ONE, &console_key_handling_pick) > 0) {
        iflags.key_handling = keyh[console_key_handling_pick->item.a_int - 1];
        free((genericptr_t) console_key_handling_pick);
    }
    destroy_nhwindow(tmpwin);
    set_altkeyhandling(legal_key_handling[iflags.key_handling]);
    return 1;       /* optn_ok */
}

int
default_processkeystroke(
    HANDLE hConIn,
    INPUT_RECORD* ir,
    boolean* valid,
    boolean numberpad,
    int portdebug)
{
    int k = 0;
    int keycode, vk;
    unsigned char ch, pre_ch;
    unsigned short int scan;
    unsigned long shiftstate;
    int altseq = 0;
    const struct pad *kpad;

#ifdef QWERTZ_SUPPORT
    if (numberpad & 0x10) {
        numberpad &= ~0x10;
        qwertz = TRUE;
    } else {
        qwertz = FALSE;
    }
#endif
    shiftstate = 0L;
    ch = pre_ch = ir->Event.KeyEvent.uChar.AsciiChar;
    scan = ir->Event.KeyEvent.wVirtualScanCode;
    vk = ir->Event.KeyEvent.wVirtualKeyCode;
    keycode = MapVirtualKey(vk, 2);
    shiftstate = ir->Event.KeyEvent.dwControlKeyState;
    KeyState[VK_SHIFT] = (shiftstate & SHIFT_PRESSED) ? 0x81 : 0;
    KeyState[VK_CONTROL] =
        (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) ? 0x81 : 0;
    KeyState[VK_CAPITAL] = (shiftstate & CAPSLOCK_ON) ? 0x81 : 0;

    if (shiftstate & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {
        if (ch || inmap(keycode, vk))
            altseq = 1;
        else
            altseq = -1; /* invalid altseq */
    }
    if (ch || (iskeypad(scan)) || (altseq > 0))
        *valid = TRUE;
    /* if (!valid) return 0; */
    /*
     * shiftstate can be checked to see if various special
     * keys were pressed at the same time as the key.
     * Currently we are using the ALT & SHIFT & CONTROLS.
     *
     *           RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED,
     *           RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED,
     *           SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON,
     *           CAPSLOCK_ON, ENHANCED_KEY
     *
     * are all valid bit masks to use on shiftstate.
     * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the
     *      left control key was pressed with the keystroke.
     */
    if (iskeypad(scan)) {
        kpad = numberpad ? numpad : keypad;
        if (shiftstate & SHIFT_PRESSED) {
            ch = kpad[scan - KEYPADLO].shift;
        } else if (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
            ch = kpad[scan - KEYPADLO].cntrl;
        } else {
            ch = kpad[scan - KEYPADLO].normal;
        }
#ifdef QWERTZ_SUPPORT
        /* OPTIONS=number_pad:-1 is for qwertz keyboard; for that setting,
           'numberpad' will be 0; core swaps y to zap, z to move northwest;
           we want numpad 7 to move northwest, so when qwertz is set,
           tell core that user who types numpad 7 typed z rather than y */
        if (qwertz && kpad[scan - KEYPADLO].normal == 'y')
            ch += 1; /* changes y to z, Y to Z, ^Y to ^Z */
#endif /*QWERTZ_SUPPORT*/
    } else if (altseq > 0) { /* ALT sequence */
        if (vk == 0xBF)
            ch = M('?');
        else
            ch = M(tolower((uchar) keycode));
    }
    /* Attempt to work better with international keyboards. */
    else {
        WORD chr[2];
        k = ToAscii(vk, scan, KeyState, chr, 0);
        if (k <= 2)
            switch (k) {
            case 2: /* two characters */
                ch = (unsigned char) chr[1];
                *valid = TRUE;
                break;
            case 1: /* one character */
                ch = (unsigned char) chr[0];
                *valid = TRUE;
                break;
            case 0:  /* no translation */
            default: /* negative */
                *valid = FALSE;
            }
    }
    if (ch == '\r')
        ch = '\n';
#ifdef PORT_DEBUG
    if (portdebug) {
        char buf[BUFSZ];
        Sprintf(buf, "PORTDEBUG (%s): ch=%u, sc=%u, vk=%d, pre=%d, sh=0x%lX, "
                     "ta=%d (ESC to end)",
                "default", ch, scan, vk, pre_ch, shiftstate, k);
        fprintf(stdout, "\n%s", buf);
    }
#endif
    return ch;
}

int
default_kbhit(HANDLE hConIn, INPUT_RECORD *ir)
{
    int done = 0; /* true =  "stop searching"        */
    int retval;   /* true =  "we had a match"        */
    DWORD count;
    unsigned short int scan;
    unsigned char ch;
    unsigned long shiftstate;
    int altseq = 0, keycode, vk;
    done = 0;
    retval = 0;
    while (!done) {
        count = 0;
        PeekConsoleInput(hConIn, ir, 1, &count);
        if (count > 0) {
            if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) {
                ch = ir->Event.KeyEvent.uChar.AsciiChar;
                scan = ir->Event.KeyEvent.wVirtualScanCode;
                shiftstate = ir->Event.KeyEvent.dwControlKeyState;
                vk = ir->Event.KeyEvent.wVirtualKeyCode;
                keycode = MapVirtualKey(vk, 2);
                if (shiftstate & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {
                    if (ch || inmap(keycode, vk))
                        altseq = 1;
                    else
                        altseq = -1; /* invalid altseq */
                }
                if (ch || iskeypad(scan) || altseq) {
                    done = 1;   /* Stop looking         */
                    retval = 1; /* Found what we sought */
                } else {
                    /* Strange Key event; let's purge it to avoid trouble */
                    ReadConsoleInput(hConIn, ir, 1, &count);
                }

            } else if ((ir->EventType == MOUSE_EVENT
                        && (ir->Event.MouseEvent.dwButtonState
                            & MOUSEMASK))) {
                done = 1;
                retval = 1;
            }

            else /* Discard it, it's an insignificant event */
                ReadConsoleInput(hConIn, ir, 1, &count);
        } else /* There are no events in console event queue */ {
            done = 1; /* Stop looking               */
            retval = 0;
        }
    }
    return retval;
}

int 
default_checkinput(
    HANDLE hConIn,
    INPUT_RECORD *ir,
    DWORD* count,
    boolean numpad,
    int mode,
    int *mod,
    coord *cc)
{
#if defined(SAFERHANGUP)
    DWORD dwWait;
#endif
    int ch = 0;
    boolean valid = 0, done = 0;

#ifdef QWERTZ_SUPPORT
    if (numpad & 0x10) {
        numpad &= ~0x10;
        qwertz = TRUE;
    } else {
        qwertz = FALSE;
    }
#endif
    while (!done) {
#if defined(SAFERHANGUP)
        dwWait = WaitForSingleObjectEx(hConIn,   // event object to wait for
                                       INFINITE, // waits indefinitely
                                       TRUE);    // alertable wait enabled
        if (dwWait == WAIT_FAILED)
            return '\033';
#endif
        ReadConsoleInput(hConIn, ir, 1, count);
        if (mode == 0) {
            if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) {
                ch = default_processkeystroke(hConIn, ir, &valid, numpad, 0);
                done = valid;
            }
        } else {
            if (count > 0) {
                if (ir->EventType == KEY_EVENT
                    && ir->Event.KeyEvent.bKeyDown) {
#ifdef QWERTZ_SUPPORT
                    if (qwertz)
                        numpad |= 0x10;
#endif
                    ch = default_processkeystroke(hConIn, ir, &valid, numpad, 0);
#ifdef QWERTZ_SUPPORT
                    numpad &= ~0x10;
#endif                    
                    if (valid)
                        return ch;
                } else if (ir->EventType == MOUSE_EVENT) {
                    if ((ir->Event.MouseEvent.dwEventFlags == 0)
                        && (ir->Event.MouseEvent.dwButtonState & MOUSEMASK)) {
                        cc->x = ir->Event.MouseEvent.dwMousePosition.X + 1;
                        cc->y = ir->Event.MouseEvent.dwMousePosition.Y - 1;

                        if (ir->Event.MouseEvent.dwButtonState & LEFTBUTTON)
                            *mod = CLICK_1;
                        else if (ir->Event.MouseEvent.dwButtonState
                                 & RIGHTBUTTON)
                            *mod = CLICK_2;
#if 0 /* middle button */
                                    else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON)
                                        *mod = CLICK_3;
#endif
                        return 0;
                    }
                }
            } else
                done = 1;
        }
    }
    return mode ? 0 : ch;
}

/*
 * Keystroke handling contributed by Ray Chason.
 * The following text was written by Ray Chason.
 *
 * The problem
 * ===========
 *
 * The console-mode Nethack wants both keyboard and mouse input.  The
 * problem is that the Windows API provides no easy way to get mouse input
 * and also keyboard input properly translated according to the user's
 * chosen keyboard layout.
 *
 * The ReadConsoleInput function returns a stream of keyboard and mouse
 * events.  Nethack is interested in those events that represent a key
 * pressed, or a click on a mouse button.  The keyboard events from
 * ReadConsoleInput are not translated according to the keyboard layout,
 * and do not take into account the shift, control, or alt keys.
 *
 * The PeekConsoleInput function works similarly to ReadConsoleInput,
 * except that it does not remove an event from the queue and it returns
 * instead of blocking when the queue is empty.
 *
 * A program can also use ReadConsole to get a properly translated stream
 * of characters.  Unfortunately, ReadConsole does not return mouse events,
 * does not distinguish the keypad from the main keyboard, does not return
 * keys shifted with Alt, and does not even return the ESC key when
 * pressed.
 *
 * We want both the functionality of ReadConsole and the functionality of
 * ReadConsoleInput.  But Microsoft didn't seem to think of that.
 *
 *
 * The solution, in the original code
 * ==================================
 *
 * The original 3.4.1 distribution tries to get proper keyboard translation
 * by passing keyboard events to the ToAscii function.  This works, to some
 * extent -- it takes the shift key into account, and it processes dead
 * keys properly.  But it doesn't take non-US keyboards into account.  It
 * appears that ToAscii is meant for windowed applications, and does not
 * have enough information to do its job properly in a console application.
 *
 *
 * The Finnish keyboard patch
 * ==========================
 *
 * This patch adds the "subkeyvalue" option to the defaults.nh file.  The
 * user can then add OPTIONS=sukeyvalue:171/92, for instance, to replace
 * the 171 character with 92, which is \.  This works, once properly
 * configured, but places too much burden on the user.  It also bars the
 * use of the substituted characters in naming objects or monsters.
 *
 *
 * The solution presented here
 * ===========================
 *
 * The best way I could find to combine the functionality of ReadConsole
 * with that of ReadConsoleInput is simple in concept.  First, call
 * PeekConsoleInput to get the first event.  If it represents a key press,
 * call ReadConsole to retrieve the key.  Otherwise, pop it off the queue
 * with ReadConsoleInput and, if it's a mouse click, return it as such.
 *
 * But the Devil, as they say, is in the details.  The problem is in
 * recognizing an event that ReadConsole will return as a key.  We don't
 * want to call ReadConsole unless we know that it will immediately return:
 * if it blocks, the mouse and the Alt sequences will cease to function
 * until it returns.
 *
 * Separating process_keystroke into two functions, one for commands and a
 * new one, process_keystroke2, for answering prompts, makes the job a lot
 * easier.  process_keystroke2 doesn't have to worry about mouse events or
 * Alt sequences, and so the consequences are minor if ReadConsole blocks.
 * process_keystroke, OTOH, never needs to return a non-ASCII character
 * that was read from ReadConsole; it returns bytes with the high bit set
 * only in response to an Alt sequence.
 *
 * So in process_keystroke, before calling ReadConsole, a bogus key event
 * is pushed on the queue.  This event causes ReadConsole to return, even
 * if there was no other character available.  Because the bogus key has
 * the eighth bit set, it is filtered out.  This is not done in
 * process_keystroke2, because that would render dead keys unusable.
 *
 * A separate process_keystroke2 can also process the numeric keypad in a
 * way that makes sense for prompts:  just return the corresponding symbol,
 * and pay no mind to number_pad or the num lock key.
 *
 * The recognition of Alt sequences is modified, to support the use of
 * characters generated with the AltGr key.  A keystroke is an Alt sequence
 * if an Alt key is seen that can't be an AltGr (since an AltGr sequence
 * could be a character, and in some layouts it could even be an ASCII
 * character).  This recognition is different on NT-based and 95-based
 * Windows:
 *
 *    * On NT-based Windows, AltGr signals as right Alt and left Ctrl
 *      together.  So an Alt sequence is recognized if either Alt key is
 *      pressed and if right Alt and left Ctrl are not both present.  This
 *      is true even if the keyboard in use does not have an AltGr key, and
 *      uses right Alt for AltGr.
 *
 *    * On 95-based Windows, with a keyboard that lacks the AltGr key, the
 *      right Alt key is used instead.  But it still signals as right Alt,
 *      without left Ctrl.  There is no way for the application to know
 *      whether right Alt is Alt or AltGr, and so it is always assumed
 *      to be AltGr.  This means that Alt sequences must be formed with
 *      left Alt.
 *
 * So the patch processes keystrokes as follows:
 *
 *     * If the scan and virtual key codes are both 0, it's the bogus key,
 *       and we ignore it.
 *
 *     * Keys on the numeric keypad are processed for commands as in the
 *       unpatched Nethack, and for prompts by returning the ASCII
 *       character, even if the num lock is off.
 *
 *     * Alt sequences are processed for commands as in the unpatched
 *       Nethack, and ignored for prompts.
 *
 *     * Control codes are returned as received, because ReadConsole will
 *       not return the ESC key.
 *
 *     * Other key-down events are passed to ReadConsole.  The use of
 *       ReadConsole is different for commands than for prompts:
 *
 *       o For commands, the bogus key is pushed onto the queue before
 *         ReadConsole is called.  On return, non-ASCII characters are
 *         filtered, so they are not mistaken for Alt sequences; this also
 *         filters the bogus key.
 *
 *       o For prompts, the bogus key is not used, because that would
 *         interfere with dead keys.  Eight bit characters may be returned,
 *         and are coded in the configured code page.
 *
 *
 * Possible improvements
 * =====================
 *
 * Some possible improvements remain:
 *
 *     * Integrate the existing Finnish keyboard patch, for use with non-
 *       QWERTY layouts such as the German QWERTZ keyboard or Dvorak.
 *
 *     * Fix the keyboard glitches in the graphical version.  Namely, dead
 *       keys don't work, and input comes in as ISO-8859-1 but is displayed
 *       as code page 437 if IBMgraphics is set on startup.
 *
 *     * Transform incoming text to ISO-8859-1, for full compatibility with
 *       the graphical version.
 *
 *     * After pushing the bogus key and calling ReadConsole, check to see
 *       if we got the bogus key; if so, and an Alt is pressed, process the
 *       event as an Alt sequence.
 *
 */

#if 0
static char where_to_get_source[] = "http://www.nethack.org/";
static char author[] = "Ray Chason";
#endif

int process_keystroke2(HANDLE hConIn, INPUT_RECORD *ir, boolean *valid);

/* Use ray_processkeystroke for key commands, process_keystroke2 for prompts */
/* int ray_processkeystroke(INPUT_RECORD *ir, boolean *valid, int
 *                          portdebug);
 */

int process_keystroke2(HANDLE, INPUT_RECORD *ir, boolean *valid);
static int is_altseq(unsigned long shiftstate);

static int
is_altseq(unsigned long shiftstate)
{
    /* We need to distinguish the Alt keys from the AltGr key.
     * On NT-based Windows, AltGr signals as right Alt and left Ctrl together;
     * on 95-based Windows, AltGr signals as right Alt only.
     * So on NT, we signal Alt if either Alt is pressed and left Ctrl is not,
     * and on 95, we signal Alt for left Alt only. */
    switch (shiftstate
            & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) {
    case LEFT_ALT_PRESSED:
    case LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED:
        return 1;

    case RIGHT_ALT_PRESSED:
    case RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED:
        return (GetVersion() & 0x80000000) == 0;

    default:
        return 0;
    }
}

int ray_processkeystroke(
    HANDLE hConIn,
    INPUT_RECORD *ir,
    boolean *valid,
    boolean numberpad,
    int portdebug)
{
    int keycode, vk;
    unsigned char ch, pre_ch;
    unsigned short int scan;
    unsigned long shiftstate;
    int altseq = 0;
    const struct pad *kpad;
    DWORD count;

#ifdef QWERTZ_SUPPORT
    if (numberpad & 0x10) {
        numberpad &= ~0x10;
        qwertz = TRUE;
    } else {
        qwertz = FALSE;
    }
#endif
    shiftstate = 0L;
    ch = pre_ch = ir->Event.KeyEvent.uChar.AsciiChar;
    scan = ir->Event.KeyEvent.wVirtualScanCode;
    vk = ir->Event.KeyEvent.wVirtualKeyCode;
    keycode = MapVirtualKey(vk, 2);
    shiftstate = ir->Event.KeyEvent.dwControlKeyState;
    if (scan == 0 && vk == 0) {
        /* It's the bogus_key */
        ReadConsoleInput(hConIn, ir, 1, &count);
        *valid = FALSE;
        return 0;
    }

    if (is_altseq(shiftstate)) {
        if (ch || inmap(keycode, vk))
            altseq = 1;
        else
            altseq = -1; /* invalid altseq */
    }
    if (ch || (iskeypad(scan)) || (altseq > 0))
        *valid = TRUE;
    /* if (!valid) return 0; */
    /*
     * shiftstate can be checked to see if various special
     * keys were pressed at the same time as the key.
     * Currently we are using the ALT & SHIFT & CONTROLS.
     *
     *           RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED,
     *           RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED,
     *           SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON,
     *           CAPSLOCK_ON, ENHANCED_KEY
     *
     * are all valid bit masks to use on shiftstate.
     * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the
     *      left control key was pressed with the keystroke.
     */
    if (iskeypad(scan)) {
        ReadConsoleInput(hConIn, ir, 1, &count);
        kpad = numberpad ? numpad : keypad;
        if (shiftstate & SHIFT_PRESSED) {
            ch = kpad[scan - KEYPADLO].shift;
        } else if (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
            ch = kpad[scan - KEYPADLO].cntrl;
        } else {
            ch = kpad[scan - KEYPADLO].normal;
        }
#ifdef QWERTZ_SUPPORT
        /* OPTIONS=number_pad:-1 is for qwertz keyboard; for that setting,
           'numberpad' will be 0; core swaps y to zap, z to move northwest;
           we want numpad 7 to move northwest, so when qwertz is set,
           tell core that user who types numpad 7 typed z rather than y */
        if (qwertz && kpad[scan - KEYPADLO].normal == 'y')
            ch += 1; /* changes y to z, Y to Z, ^Y to ^Z */
#endif /*QWERTZ_SUPPORT*/
    } else if (altseq > 0) { /* ALT sequence */
        ReadConsoleInput(hConIn, ir, 1, &count);
        if (vk == 0xBF)
            ch = M('?');
        else
            ch = M(tolower((uchar) keycode));
    } else if (ch < 32 && !isnumkeypad(scan)) {
        /* Control code; ReadConsole seems to filter some of these,
         * including ESC */
        ReadConsoleInput(hConIn, ir, 1, &count);
    }
    /* Attempt to work better with international keyboards. */
    else {
        CHAR ch2;
        DWORD written;
        /* The bogus_key guarantees that ReadConsole will return,
         * and does not itself do anything */
        WriteConsoleInput(hConIn, &bogus_key, 1, &written);
        ReadConsole(hConIn, &ch2, 1, &count, NULL);
        /* Prevent high characters from being interpreted as alt
         * sequences; also filter the bogus_key */
        if (ch2 & 0x80)
            *valid = FALSE;
        else
            ch = ch2;
        if (ch == 0)
            *valid = FALSE;
    }
    if (ch == '\r')
        ch = '\n';
#ifdef PORT_DEBUG
    if (portdebug) {
        char buf[BUFSZ];
        Sprintf(buf, "PORTDEBUG: ch=%u, scan=%u, vk=%d, pre=%d, "
                     "shiftstate=0x%lX (ESC to end)\n",
                ch, scan, vk, pre_ch, shiftstate);
        fprintf(stdout, "\n%s", buf);
    }
#endif
    return ch;
}

int
process_keystroke2(
    HANDLE hConIn,
    INPUT_RECORD *ir,
    boolean *valid)
{
    /* Use these values for the numeric keypad */
    static const char keypad_nums[] = "789-456+1230.";

    unsigned char ch;
    int vk;
    unsigned short int scan;
    unsigned long shiftstate;
    int altseq;
    DWORD count;

    ch = ir->Event.KeyEvent.uChar.AsciiChar;
    vk = ir->Event.KeyEvent.wVirtualKeyCode;
    scan = ir->Event.KeyEvent.wVirtualScanCode;
    shiftstate = ir->Event.KeyEvent.dwControlKeyState;

    if (scan == 0 && vk == 0) {
        /* It's the bogus_key */
        ReadConsoleInput(hConIn, ir, 1, &count);
        *valid = FALSE;
        return 0;
    }

    altseq = is_altseq(shiftstate);
    if (ch || (iskeypad(scan)) || altseq)
        *valid = TRUE;
    /* if (!valid) return 0; */
    /*
     * shiftstate can be checked to see if various special
     * keys were pressed at the same time as the key.
     * Currently we are using the ALT & SHIFT & CONTROLS.
     *
     *           RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED,
     *           RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED,
     *           SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON,
     *           CAPSLOCK_ON, ENHANCED_KEY
     *
     * are all valid bit masks to use on shiftstate.
     * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the
     *      left control key was pressed with the keystroke.
     */
    if (iskeypad(scan) && !altseq) {
        ReadConsoleInput(hConIn, ir, 1, &count);
        ch = keypad_nums[scan - KEYPADLO];
    } else if (ch < 32 && !isnumkeypad(scan)) {
        /* Control code; ReadConsole seems to filter some of these,
         * including ESC */
        ReadConsoleInput(hConIn, ir, 1, &count);
    }
    /* Attempt to work better with international keyboards. */
    else {
        CHAR ch2;
        ReadConsole(hConIn, &ch2, 1, &count, NULL);
        ch = ch2 & 0xFF;
        if (ch == 0)
            *valid = FALSE;
    }
    if (ch == '\r')
        ch = '\n';
    return ch;
}

int
ray_checkinput(
    HANDLE hConIn,
    INPUT_RECORD *ir,
    DWORD *count,
    boolean numpad,
    int mode,
    int *mod,
    coord *cc)
{
#if defined(SAFERHANGUP)
    DWORD dwWait;
#endif
    int ch = 0;
    boolean valid = 0, done = 0;

#ifdef QWERTZ_SUPPORT
    if (numpad & 0x10) {
        numpad &= ~0x10;
        qwertz = TRUE;
    } else {
        qwertz = FALSE;
    }
#endif
    while (!done) {
        *count = 0;
        dwWait = WaitForSingleObject(hConIn, INFINITE);
#if defined(SAFERHANGUP)
        if (dwWait == WAIT_FAILED)
            return '\033';
#endif
        PeekConsoleInput(hConIn, ir, 1, count);
        if (mode == 0) {
            if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) {
                ch = process_keystroke2(hConIn, ir, &valid);
                done = valid;
            } else
                ReadConsoleInput(hConIn, ir, 1, count);
        } else {
            ch = 0;
            if (count > 0) {
                if (ir->EventType == KEY_EVENT
                    && ir->Event.KeyEvent.bKeyDown) {
#ifdef QWERTZ_SUPPORT
                    if (qwertz)
                        numpad |= 0x10;
#endif
                    ch = ray_processkeystroke(hConIn, ir, &valid, numpad,
#ifdef PORTDEBUG
                                          1);
#else
                                          0);
#endif
#ifdef QWERTZ_SUPPORT
                    numpad &= ~0x10;
#endif                    
                    if (valid)
                        return ch;
                } else {
                    ReadConsoleInput(hConIn, ir, 1, count);
                    if (ir->EventType == MOUSE_EVENT) {
                        if ((ir->Event.MouseEvent.dwEventFlags == 0)
                            && (ir->Event.MouseEvent.dwButtonState
                                & MOUSEMASK)) {
                            cc->x =
                                ir->Event.MouseEvent.dwMousePosition.X + 1;
                            cc->y =
                                ir->Event.MouseEvent.dwMousePosition.Y - 1;

                            if (ir->Event.MouseEvent.dwButtonState
                                & LEFTBUTTON)
                                *mod = CLICK_1;
                            else if (ir->Event.MouseEvent.dwButtonState
                                     & RIGHTBUTTON)
                                *mod = CLICK_2;
#if 0 /* middle button */       
                            else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON)
                                *mod = CLICK_3;
#endif
                            return 0;
                        }
                    }
#if 0
                    /* We ignore these types of console events */
                        else if (ir->EventType == FOCUS_EVENT) {
                        }
                        else if (ir->EventType == MENU_EVENT) {
                        }
#endif
                }
            } else
                done = 1;
        }
    }
    *mod = 0;
    return ch;
}

int
ray_kbhit(
    HANDLE hConIn,
    INPUT_RECORD *ir)
{
    int done = 0; /* true =  "stop searching"        */
    int retval;   /* true =  "we had a match"        */
    DWORD count;
    unsigned short int scan;
    unsigned char ch;
    unsigned long shiftstate;
    int altseq = 0, keycode, vk;

    done = 0;
    retval = 0;
    while (!done) {
        count = 0;
        PeekConsoleInput(hConIn, ir, 1, &count);
        if (count > 0) {
            if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) {
                ch = ir->Event.KeyEvent.uChar.AsciiChar;
                scan = ir->Event.KeyEvent.wVirtualScanCode;
                shiftstate = ir->Event.KeyEvent.dwControlKeyState;
                vk = ir->Event.KeyEvent.wVirtualKeyCode;
                if (scan == 0 && vk == 0) {
                    /* It's the bogus_key.  Discard it */
                    ReadConsoleInput(hConIn,ir,1,&count);
                } else {
                    keycode = MapVirtualKey(vk, 2);
                    if (is_altseq(shiftstate)) {
                        if (ch || inmap(keycode, vk))
                            altseq = 1;
                        else
                            altseq = -1; /* invalid altseq */
                    }
                    if (ch || iskeypad(scan) || altseq) {
                        done = 1;   /* Stop looking         */
                        retval = 1; /* Found what we sought */
                    } else {
                        /* Strange Key event; let's purge it to avoid trouble */
                        ReadConsoleInput(hConIn, ir, 1, &count);
                    }
                }

            } else if ((ir->EventType == MOUSE_EVENT
                        && (ir->Event.MouseEvent.dwButtonState
                            & MOUSEMASK))) {
                done = 1;
                retval = 1;
            }

            else /* Discard it, it's an insignificant event */
                ReadConsoleInput(hConIn, ir, 1, &count);
        } else /* There are no events in console event queue */ {
            done = 1; /* Stop looking               */
            retval = 0;
        }
    }
    return retval;
}

/*
 * nh340 key handling
 *
 * This is the NetHack keystroke processing from NetHack 3.4.0.
 * It can be built as a run-time loadable dll (nh340key.dll),
 * placed in the same directory as the nethack.exe executable,
 * and loaded by specifying OPTIONS=altkeyhandler:nh340key
 * in defaults.nh
 *
 * Keypad keys are translated to the normal values below.
 * Shifted keypad keys are translated to the
 *    shift values below.
 */

int 
nh340_processkeystroke(
    HANDLE hConIn,
    INPUT_RECORD *ir,
    boolean *valid,
    boolean numberpad,
    int portdebug)
{
    int keycode, vk;
    unsigned char ch, pre_ch;
    unsigned short int scan;
    unsigned long shiftstate;
    int altseq = 0;
    const struct pad *kpad;

#ifdef QWERTZ_SUPPORT
    if (numberpad & 0x10) {
        numberpad &= ~0x10;
        qwertz = TRUE;
    } else {
        qwertz = FALSE;
    }
#endif

    shiftstate = 0L;
    ch = pre_ch = ir->Event.KeyEvent.uChar.AsciiChar;
    scan = ir->Event.KeyEvent.wVirtualScanCode;
    vk = ir->Event.KeyEvent.wVirtualKeyCode;
    keycode = MapVirtualKey(vk, 2);
    shiftstate = ir->Event.KeyEvent.dwControlKeyState;

    if (shiftstate & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {
        if (ch || inmap(keycode, vk))
            altseq = 1;
        else
            altseq = -1; /* invalid altseq */
    }
    if (ch || (iskeypad(scan)) || (altseq > 0))
        *valid = TRUE;
    /* if (!valid) return 0; */
    /*
     * shiftstate can be checked to see if various special
     * keys were pressed at the same time as the key.
     * Currently we are using the ALT & SHIFT & CONTROLS.
     *
     *           RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED,
     *           RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED,
     *           SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON,
     *           CAPSLOCK_ON, ENHANCED_KEY
     *
     * are all valid bit masks to use on shiftstate.
     * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the
     *      left control key was pressed with the keystroke.
     */
    if (iskeypad(scan)) {
        kpad = numberpad ? numpad : keypad;
        if (shiftstate & SHIFT_PRESSED) {
            ch = kpad[scan - KEYPADLO].shift;
        } else if (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
            ch = kpad[scan - KEYPADLO].cntrl;
        } else {
            ch = kpad[scan - KEYPADLO].normal;
        }
#ifdef QWERTZ_SUPPORT
        /* OPTIONS=number_pad:-1 is for qwertz keyboard; for that setting,
           'numberpad' will be 0; core swaps y to zap, z to move northwest;
           we want numpad 7 to move northwest, so when qwertz is set,
           tell core that user who types numpad 7 typed z rather than y */
        if (qwertz && kpad[scan - KEYPADLO].normal == 'y')
            ch += 1; /* changes y to z, Y to Z, ^Y to ^Z */
#endif /*QWERTZ_SUPPORT*/
    } else if (altseq > 0) { /* ALT sequence */
        if (vk == 0xBF)
            ch = M('?');
        else
            ch = M(tolower((uchar) keycode));
    }
    if (ch == '\r')
        ch = '\n';
#ifdef PORT_DEBUG
    if (portdebug) {
        char buf[BUFSZ];
        Sprintf(buf,
                "PORTDEBUG (%s): ch=%u, sc=%u, vk=%d, sh=0x%lX (ESC to end)",
                "nh340", ch, scan, vk, shiftstate);
        fprintf(stdout, "\n%s", buf);
    }
#endif
    return ch;
}

int
nh340_kbhit(
    HANDLE hConIn,
    INPUT_RECORD *ir)
{
    int done = 0; /* true =  "stop searching"        */
    int retval;   /* true =  "we had a match"        */
    DWORD count;
    unsigned short int scan;
    unsigned char ch;
    unsigned long shiftstate;
    int altseq = 0, keycode, vk;
    done = 0;
    retval = 0;
    while (!done) {
        count = 0;
        PeekConsoleInput(hConIn, ir, 1, &count);
        if (count > 0) {
            if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) {
                ch = ir->Event.KeyEvent.uChar.AsciiChar;
                scan = ir->Event.KeyEvent.wVirtualScanCode;
                shiftstate = ir->Event.KeyEvent.dwControlKeyState;
                vk = ir->Event.KeyEvent.wVirtualKeyCode;
                keycode = MapVirtualKey(vk, 2);
                if (shiftstate & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {
                    if (ch || inmap(keycode, vk))
                        altseq = 1;
                    else
                        altseq = -1; /* invalid altseq */
                }
                if (ch || iskeypad(scan) || altseq) {
                    done = 1;   /* Stop looking         */
                    retval = 1; /* Found what we sought */
                } else {
                    /* Strange Key event; let's purge it to avoid trouble */
                    ReadConsoleInput(hConIn, ir, 1, &count);
                }

            } else if ((ir->EventType == MOUSE_EVENT
                        && (ir->Event.MouseEvent.dwButtonState
                            & MOUSEMASK))) {
                done = 1;
                retval = 1;
            }

            else /* Discard it, it's an insignificant event */
                ReadConsoleInput(hConIn, ir, 1, &count);
        } else /* There are no events in console event queue */ {
            done = 1; /* Stop looking               */
            retval = 0;
        }
    }
    return retval;
}

int
nh340_checkinput(
    HANDLE hConIn,
    INPUT_RECORD *ir,
    DWORD *count,
    boolean numpad,
    int mode,
    int *mod,
    coord *cc)
{
#if defined(SAFERHANGUP)
    DWORD dwWait;
#endif
    int ch = 0;
    boolean valid = 0, done = 0;

#ifdef QWERTZ_SUPPORT
    if (numpad & 0x10) {
        numpad &= ~0x10;
        qwertz = TRUE;
    } else {
        qwertz = FALSE;
    }
#endif
    while (!done) {
#if defined(SAFERHANGUP)
        dwWait = WaitForSingleObjectEx(hConIn,   // event object to wait for
                                       INFINITE, // waits indefinitely
                                       TRUE);    // alertable wait enabled
        if (dwWait == WAIT_FAILED)
            return '\033';
#endif
        ReadConsoleInput(hConIn, ir, 1, count);
        if (mode == 0) {
            if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) {
#ifdef QWERTZ_SUPPORT
                if (qwertz)
                    numpad |= 0x10;
#endif
                ch = nh340_processkeystroke(hConIn, ir, &valid, numpad, 0);
#ifdef QWERTZ_SUPPORT
                numpad &= ~0x10;
#endif                    
                done = valid;
            }
        } else {
            if (count > 0) {
                if (ir->EventType == KEY_EVENT
                    && ir->Event.KeyEvent.bKeyDown) {
                    ch = nh340_processkeystroke(hConIn, ir, &valid, numpad, 0);
                    if (valid)
                        return ch;
                } else if (ir->EventType == MOUSE_EVENT) {
                    if ((ir->Event.MouseEvent.dwEventFlags == 0)
                        && (ir->Event.MouseEvent.dwButtonState & MOUSEMASK)) {
                        cc->x = ir->Event.MouseEvent.dwMousePosition.X + 1;
                        cc->y = ir->Event.MouseEvent.dwMousePosition.Y - 1;

                        if (ir->Event.MouseEvent.dwButtonState & LEFTBUTTON)
                            *mod = CLICK_1;
                        else if (ir->Event.MouseEvent.dwButtonState
                                 & RIGHTBUTTON)
                            *mod = CLICK_2;
#if 0 /* middle button */
                        else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON)
                            *mod = CLICK_3;
#endif
                        return 0;
                    }
                }
            } else
                done = 1;
        }
    }
    return mode ? 0 : ch;
}

#endif /* WIN32 */
