1901 lines
53 KiB
C
1901 lines
53 KiB
C
/* NetHack 3.6 nttty.c $NHDT-Date: 1525643540 2018/05/06 21:52:20 $ $NHDT-Branch: tty-status $:$NHDT-Revision: 1.77 $ */
|
|
/* Copyright (c) NetHack PC Development Team 1993 */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/* tty.c - (Windows NT) 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
|
|
*
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
#define NEED_VARARGS /* Uses ... */
|
|
#include "hack.h"
|
|
#include "wintty.h"
|
|
#include <sys\types.h>
|
|
#include <sys\stat.h>
|
|
#include "win32api.h"
|
|
|
|
/*
|
|
* The following WIN32 Console API routines are used in this file.
|
|
*
|
|
* CreateFile
|
|
* GetConsoleScreenBufferInfo
|
|
* GetStdHandle
|
|
* SetConsoleCursorPosition
|
|
* SetConsoleTextAttribute
|
|
* SetConsoleCtrlHandler
|
|
* PeekConsoleInput
|
|
* ReadConsoleInput
|
|
* WriteConsoleOutputCharacter
|
|
* FillConsoleOutputAttribute
|
|
* GetConsoleOutputCP
|
|
*/
|
|
|
|
static BOOL FDECL(CtrlHandler, (DWORD));
|
|
static void FDECL(xputc_core, (char));
|
|
void FDECL(cmov, (int, int));
|
|
void FDECL(nocmov, (int, int));
|
|
int FDECL(process_keystroke,
|
|
(INPUT_RECORD *, boolean *, BOOLEAN_P numberpad, int portdebug));
|
|
static void NDECL(init_ttycolor);
|
|
static void NDECL(really_move_cursor);
|
|
static void NDECL(check_and_set_font);
|
|
static boolean NDECL(check_font_widths);
|
|
static void NDECL(set_known_good_console_font);
|
|
static void NDECL(restore_original_console_font);
|
|
|
|
/* Win32 Console handles for input and output */
|
|
HANDLE hConIn;
|
|
HANDLE hConOut;
|
|
|
|
/* Win32 Screen buffer,coordinate,console I/O information */
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi, origcsbi;
|
|
COORD ntcoord;
|
|
INPUT_RECORD ir;
|
|
|
|
/* Support for changing console font if existing glyph widths are too wide */
|
|
boolean console_font_changed;
|
|
CONSOLE_FONT_INFOEX original_console_font_info;
|
|
UINT original_console_code_page;
|
|
|
|
extern boolean getreturn_enabled; /* from sys/share/pcsys.c */
|
|
extern int redirect_stdout;
|
|
|
|
/* 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;
|
|
/* Flag for whether unicode is supported */
|
|
static boolean has_unicode;
|
|
static boolean init_ttycolor_completed;
|
|
#ifdef PORT_DEBUG
|
|
static boolean display_cursor_info = FALSE;
|
|
#endif
|
|
#ifdef CHANGE_COLOR
|
|
static void NDECL(adjust_palette);
|
|
static int FDECL(match_color_name, (const char *));
|
|
typedef HWND(WINAPI *GETCONSOLEWINDOW)();
|
|
static HWND GetConsoleHandle(void);
|
|
static HWND GetConsoleHwnd(void);
|
|
static boolean altered_palette;
|
|
static COLORREF UserDefinedColors[CLR_MAX];
|
|
static COLORREF NetHackColors[CLR_MAX] = {
|
|
0x00000000, 0x00c80000, 0x0000c850, 0x00b4b432, 0x000000d2, 0x00800080,
|
|
0x000064b4, 0x00c0c0c0, 0x00646464, 0x00f06464, 0x0000ff00, 0x00ffff00,
|
|
0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
|
|
};
|
|
static COLORREF DefaultColors[CLR_MAX] = {
|
|
0x00000000, 0x00800000, 0x00008000, 0x00808000, 0x00000080, 0x00800080,
|
|
0x00008080, 0x00c0c0c0, 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00,
|
|
0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
|
|
};
|
|
#endif
|
|
struct console_t {
|
|
WORD background;
|
|
WORD foreground;
|
|
WORD attr;
|
|
int current_nhcolor;
|
|
int current_nhattr[ATR_INVERSE+1];
|
|
COORD cursor;
|
|
} console = {
|
|
0,
|
|
(FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED),
|
|
(FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED),
|
|
NO_COLOR,
|
|
{FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE},
|
|
{0, 0}
|
|
};
|
|
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]
|
|
|
|
/* dynamic keystroke handling .DLL support */
|
|
typedef int(__stdcall *PROCESS_KEYSTROKE)(HANDLE, INPUT_RECORD *, boolean *,
|
|
BOOLEAN_P, int);
|
|
|
|
typedef int(__stdcall *NHKBHIT)(HANDLE, INPUT_RECORD *);
|
|
|
|
typedef int(__stdcall *CHECKINPUT)(HANDLE, INPUT_RECORD *, DWORD *, BOOLEAN_P,
|
|
int, int *, coord *);
|
|
|
|
typedef int(__stdcall *SOURCEWHERE)(char **);
|
|
|
|
typedef int(__stdcall *SOURCEAUTHOR)(char **);
|
|
|
|
typedef int(__stdcall *KEYHANDLERNAME)(char **, int);
|
|
|
|
HANDLE hLibrary;
|
|
PROCESS_KEYSTROKE pProcessKeystroke;
|
|
NHKBHIT pNHkbhit;
|
|
CHECKINPUT pCheckInput;
|
|
SOURCEWHERE pSourceWhere;
|
|
SOURCEAUTHOR pSourceAuthor;
|
|
KEYHANDLERNAME pKeyHandlerName;
|
|
|
|
/* CP437 to Unicode mapping according to the Unicode Consortium */
|
|
static const WCHAR cp437[] = {
|
|
0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
|
|
0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
|
|
0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
|
|
0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
|
|
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
|
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
|
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
|
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
|
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
|
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
|
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
|
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
|
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
|
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
|
|
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
|
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
|
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
|
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
|
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
|
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
|
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
|
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
|
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
|
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
|
|
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
|
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
|
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
|
|
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
|
|
};
|
|
|
|
/*
|
|
* cpConsole provides the mapping of characters in the console code page to
|
|
* UNICODE. It maps a character to at most two WCHARs storing the number of
|
|
* WCHARs in count.
|
|
*
|
|
* NOTE: cpConsole is only valid if has_unicode is TRUE.
|
|
*/
|
|
|
|
typedef struct {
|
|
WCHAR characters[2];
|
|
int count;
|
|
} CodePageMapping;
|
|
|
|
static CodePageMapping cpConsole[256];
|
|
|
|
static void initialize_cp_console()
|
|
{
|
|
if (has_unicode) {
|
|
UINT codePage = GetConsoleOutputCP();
|
|
|
|
for (int i = 0; i < 256; i++) {
|
|
char c = (char)i;
|
|
cpConsole[i].count = MultiByteToWideChar(codePage, 0, &c, 1,
|
|
&cpConsole[i].characters[0], 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#define CONSOLE_CLEAR_ATTRIBUTE (FOREGROUND_RED | FOREGROUND_GREEN \
|
|
| FOREGROUND_BLUE)
|
|
#define CONSOLE_CLEAR_CHARACTER (' ')
|
|
|
|
typedef struct {
|
|
WCHAR characters[2];
|
|
int count;
|
|
WORD attribute;
|
|
} cell_t;
|
|
|
|
typedef struct {
|
|
cell_t * cells;
|
|
} console_buffer_t;
|
|
|
|
static int buffer_width;
|
|
static int buffer_height;
|
|
|
|
console_buffer_t back_buffer;
|
|
console_buffer_t front_buffer;
|
|
cell_t clear_cell;
|
|
cell_t undefined_cell;
|
|
|
|
static boolean buffer_flipping_initialized = FALSE;
|
|
|
|
static void check_buffer_size(int width, int height);
|
|
static cell_t * buffer_get_cell(console_buffer_t * buffer, int x, int y);
|
|
|
|
static void back_buffer_flip();
|
|
|
|
static void buffer_fill_to_end(console_buffer_t * buffer, cell_t * cell,
|
|
int x, int y);
|
|
static void back_buffer_clear_to_end_of_line(int x, int y);
|
|
static void back_buffer_write(cell_t * cell, int x, int y);
|
|
|
|
static void initialize_buffer_flipping(int width, int height)
|
|
{
|
|
if (buffer_flipping_initialized) {
|
|
check_buffer_size(width, height);
|
|
return;
|
|
}
|
|
|
|
buffer_width = 0;
|
|
buffer_height = 0;
|
|
|
|
back_buffer.cells = NULL;
|
|
front_buffer.cells = NULL;
|
|
|
|
clear_cell.attribute = CONSOLE_CLEAR_ATTRIBUTE;
|
|
clear_cell.characters[0] = CONSOLE_CLEAR_CHARACTER;
|
|
clear_cell.characters[1] = 0;
|
|
clear_cell.count = 1;
|
|
|
|
undefined_cell = clear_cell;
|
|
undefined_cell.count = 0;
|
|
|
|
check_buffer_size(width, height);
|
|
|
|
buffer_flipping_initialized = TRUE;
|
|
}
|
|
|
|
static void resize_buffer(console_buffer_t * buffer, cell_t * fill,
|
|
int width, int height)
|
|
{
|
|
cell_t * cells = (cell_t *)malloc(sizeof(cell_t) * width * height);
|
|
cell_t * dst = cells;
|
|
cell_t * sentinel = dst + (width * height);
|
|
|
|
while (dst != sentinel)
|
|
*dst++ = *fill;
|
|
|
|
int height_to_copy = (buffer_height > height ? height : buffer_height);
|
|
int bytes_to_copy = (buffer_width > width ? width : buffer_width)
|
|
* sizeof(cell_t);
|
|
|
|
for (int y = 0; y < height_to_copy; y++)
|
|
memcpy(cells + (width * y), buffer->cells + (buffer_width * y),
|
|
bytes_to_copy);
|
|
|
|
free(buffer->cells);
|
|
buffer->cells = cells;
|
|
}
|
|
|
|
static void check_buffer_size(int width, int height)
|
|
{
|
|
if (width != buffer_width || height != buffer_height) {
|
|
resize_buffer(&back_buffer, &clear_cell, width, height);
|
|
resize_buffer(&front_buffer, &undefined_cell, width, height);
|
|
buffer_width = width;
|
|
buffer_height = height;
|
|
}
|
|
}
|
|
|
|
static cell_t * buffer_get_cell(console_buffer_t * buffer, int x, int y)
|
|
{
|
|
return buffer->cells + (buffer_width * y) + x;
|
|
}
|
|
|
|
static void back_buffer_flip()
|
|
{
|
|
if (!buffer_flipping_initialized)
|
|
return;
|
|
|
|
cell_t * back = back_buffer.cells;
|
|
cell_t * front = front_buffer.cells;
|
|
COORD pos;
|
|
|
|
for (pos.Y = 0; pos.Y < buffer_height; pos.Y++) {
|
|
for (pos.X = 0; pos.X < buffer_width; pos.X++) {
|
|
if (back->attribute != front->attribute) {
|
|
WriteConsoleOutputAttribute(hConOut, &back->attribute,
|
|
1, pos, &acount);
|
|
front->attribute = back->attribute;
|
|
}
|
|
if (back->count != front->count ||
|
|
back->characters[0] != front->characters[0] ||
|
|
back->characters[1] != front->characters[1]) {
|
|
if (has_unicode) {
|
|
WriteConsoleOutputCharacterW(hConOut, back->characters,
|
|
back->count, pos, &ccount);
|
|
} else {
|
|
char ch = (char)back->characters[0];
|
|
WriteConsoleOutputCharacterA(hConOut, &ch, 1, pos,
|
|
&ccount);
|
|
}
|
|
*front = *back;
|
|
}
|
|
back++;
|
|
front++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void buffer_fill_to_end(console_buffer_t * buffer, cell_t * src,
|
|
int x, int y)
|
|
{
|
|
cell_t * dst = buffer_get_cell(buffer, x, y);
|
|
cell_t * sentinel = buffer_get_cell(buffer, 0, buffer_height);
|
|
while (dst != sentinel)
|
|
*dst++ = clear_cell;
|
|
}
|
|
|
|
static void back_buffer_write(cell_t * cell, int x, int y)
|
|
{
|
|
cell_t * dst = buffer_get_cell(&back_buffer, x, y);
|
|
*dst = *cell;
|
|
}
|
|
|
|
static void back_buffer_clear_to_end_of_line(int x, int y)
|
|
{
|
|
cell_t * cell;
|
|
cell_t *sentinel;
|
|
|
|
cell = buffer_get_cell(&back_buffer, x, y);
|
|
sentinel = buffer_get_cell(&back_buffer, 0, y+1);
|
|
while (cell != sentinel)
|
|
*cell++ = clear_cell;
|
|
}
|
|
|
|
/*
|
|
* Called after returning from ! or ^Z
|
|
*/
|
|
void
|
|
gettty()
|
|
{
|
|
console_font_changed = FALSE;
|
|
|
|
check_and_set_font();
|
|
|
|
#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(s)
|
|
const char *s;
|
|
{
|
|
cmov(ttyDisplay->curx, ttyDisplay->cury);
|
|
end_screen();
|
|
if (s)
|
|
raw_print(s);
|
|
|
|
restore_original_console_font();
|
|
}
|
|
|
|
/* called by init_nhwindows() and resume_nhwindows() */
|
|
void
|
|
setftty()
|
|
{
|
|
#ifdef CHANGE_COLOR
|
|
if (altered_palette)
|
|
adjust_palette();
|
|
#endif
|
|
start_screen();
|
|
}
|
|
|
|
void
|
|
tty_startup(wid, hgt)
|
|
int *wid, *hgt;
|
|
{
|
|
int twid = origcsbi.srWindow.Right - origcsbi.srWindow.Left + 1;
|
|
|
|
if (twid > 80)
|
|
twid = 80;
|
|
*wid = twid;
|
|
*hgt = origcsbi.srWindow.Bottom - origcsbi.srWindow.Top + 1;
|
|
set_option_mod_status("mouse_support", SET_IN_GAME);
|
|
}
|
|
|
|
void
|
|
tty_number_pad(state)
|
|
int state;
|
|
{
|
|
}
|
|
|
|
void
|
|
tty_start_screen()
|
|
{
|
|
if (iflags.num_pad)
|
|
tty_number_pad(1); /* make keypad send digits */
|
|
}
|
|
|
|
void
|
|
tty_end_screen()
|
|
{
|
|
clear_screen();
|
|
really_move_cursor();
|
|
if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
|
|
|
|
buffer_fill_to_end(&back_buffer, &clear_cell, 0, 0);
|
|
buffer_fill_to_end(&front_buffer, &clear_cell, 0, 0);
|
|
|
|
DWORD ccnt;
|
|
COORD newcoord;
|
|
|
|
newcoord.X = 0;
|
|
newcoord.Y = 0;
|
|
FillConsoleOutputAttribute(
|
|
hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
|
|
csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
|
|
FillConsoleOutputCharacter(
|
|
hConOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
|
|
}
|
|
FlushConsoleInputBuffer(hConIn);
|
|
}
|
|
|
|
static BOOL
|
|
CtrlHandler(ctrltype)
|
|
DWORD ctrltype;
|
|
{
|
|
switch (ctrltype) {
|
|
/* case CTRL_C_EVENT: */
|
|
case CTRL_BREAK_EVENT:
|
|
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(hConIn); /* trigger WAIT_FAILED */
|
|
return TRUE;
|
|
#endif
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ntty_open() is called in several places. It is called by win_tty_init
|
|
* passing in a mode of zero. It is then later called again by pcmain passing
|
|
* in a mode of one. Finally, it can also be called by process_options also
|
|
* with a mode of one.
|
|
*
|
|
* barthouse - The fact this is getting called multiple times needs to be
|
|
* reviewed and perhaps cleaned up.
|
|
*
|
|
*/
|
|
void
|
|
nttty_open(mode)
|
|
int mode;
|
|
{
|
|
HANDLE hStdOut;
|
|
DWORD cmode;
|
|
long mask;
|
|
|
|
has_unicode = ((GetVersion() & 0x80000000) == 0);
|
|
|
|
initialize_cp_console();
|
|
|
|
GUILaunched = 0;
|
|
|
|
try :
|
|
/* The following lines of code were suggested by
|
|
* Bob Landau of Microsoft WIN32 Developer support,
|
|
* as the only current means of determining whether
|
|
* we were launched from the command prompt, or from
|
|
* the NT program manager. M. Allison
|
|
*/
|
|
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (hStdOut) {
|
|
GetConsoleScreenBufferInfo(hStdOut, &origcsbi);
|
|
} else if (mode) {
|
|
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
if (!hStdOut && !hStdIn) {
|
|
/* Bool rval; */
|
|
AllocConsole();
|
|
AttachConsole(GetCurrentProcessId());
|
|
/* rval = SetStdHandle(STD_OUTPUT_HANDLE, hWrite); */
|
|
freopen("CON", "w", stdout);
|
|
freopen("CON", "r", stdin);
|
|
}
|
|
mode = 0;
|
|
goto try;
|
|
} else {
|
|
/* barthouse - Need to understand how this can happen and
|
|
* whether we should bail instead of returning.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/* Obtain handles for the standard Console I/O devices */
|
|
hConIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
GetConsoleScreenBufferInfo(hConOut, &csbi);
|
|
|
|
int height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
|
int width = min(csbi.srWindow.Right - csbi.srWindow.Left + 1, 80);
|
|
|
|
/* If the window is not big enough to meet our minimum requirements,
|
|
* grow the console's buffer to be large enough. The user will have
|
|
* to manually extend the size of the window.
|
|
*/
|
|
|
|
width = max(80, width);
|
|
height = max(25, height);
|
|
|
|
COORD size = { max(80, width), max(25, height) };
|
|
SetConsoleScreenBufferSize(hConOut, size);
|
|
|
|
initialize_buffer_flipping(width, height);
|
|
|
|
load_keyboard_handler();
|
|
/* Initialize the function pointer that points to
|
|
* the kbhit() equivalent, in this TTY case nttty_kbhit()
|
|
*/
|
|
nt_kbhit = nttty_kbhit;
|
|
|
|
GetConsoleMode(hConIn, &cmode);
|
|
#ifdef NO_MOUSE_ALLOWED
|
|
mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT
|
|
| ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT;
|
|
#else
|
|
mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
|
|
| ENABLE_WINDOW_INPUT;
|
|
#endif
|
|
/* Turn OFF the settings specified in the mask */
|
|
cmode &= ~mask;
|
|
#ifndef NO_MOUSE_ALLOWED
|
|
cmode |= ENABLE_MOUSE_INPUT;
|
|
#endif
|
|
SetConsoleMode(hConIn, cmode);
|
|
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) {
|
|
/* Unable to set control handler */
|
|
cmode = 0; /* just to have a statement to break on for debugger */
|
|
}
|
|
|
|
LI = height;
|
|
CO = width;
|
|
|
|
console.cursor.X = console.cursor.Y = 0;
|
|
really_move_cursor();
|
|
}
|
|
|
|
int
|
|
process_keystroke(ir, valid, numberpad, portdebug)
|
|
INPUT_RECORD *ir;
|
|
boolean *valid;
|
|
boolean numberpad;
|
|
int portdebug;
|
|
{
|
|
int ch = pProcessKeystroke(hConIn, ir, valid, numberpad, portdebug);
|
|
/* check for override */
|
|
if (ch && ch < MAX_OVERRIDES && key_overrides[ch])
|
|
ch = key_overrides[ch];
|
|
return ch;
|
|
}
|
|
|
|
int
|
|
nttty_kbhit()
|
|
{
|
|
return pNHkbhit(hConIn, &ir);
|
|
}
|
|
|
|
int
|
|
tgetch()
|
|
{
|
|
int mod;
|
|
coord cc;
|
|
DWORD count;
|
|
really_move_cursor();
|
|
return (program_state.done_hup)
|
|
? '\033'
|
|
: pCheckInput(hConIn, &ir, &count, iflags.num_pad, 0, &mod,
|
|
&cc);
|
|
}
|
|
|
|
int
|
|
ntposkey(x, y, mod)
|
|
int *x, *y, *mod;
|
|
{
|
|
int ch;
|
|
coord cc;
|
|
DWORD count;
|
|
really_move_cursor();
|
|
ch = (program_state.done_hup)
|
|
? '\033'
|
|
: pCheckInput(hConIn, &ir, &count, iflags.num_pad, 1, mod, &cc);
|
|
if (!ch) {
|
|
*x = cc.x;
|
|
*y = cc.y;
|
|
}
|
|
return ch;
|
|
}
|
|
|
|
static void
|
|
really_move_cursor()
|
|
{
|
|
#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) nttty=(%02d,%02d)", oldtitle,
|
|
ttyDisplay->curx, ttyDisplay->cury, console.cursor.X, console.cursor.Y);
|
|
(void) SetConsoleTitle(newtitle);
|
|
}
|
|
#endif
|
|
if (ttyDisplay) {
|
|
console.cursor.X = ttyDisplay->curx;
|
|
console.cursor.Y = ttyDisplay->cury;
|
|
}
|
|
SetConsoleCursorPosition(hConOut, console.cursor);
|
|
|
|
back_buffer_flip();
|
|
}
|
|
|
|
void
|
|
cmov(x, y)
|
|
register int x, y;
|
|
{
|
|
ttyDisplay->cury = y;
|
|
ttyDisplay->curx = x;
|
|
console.cursor.X = x;
|
|
console.cursor.Y = y;
|
|
}
|
|
|
|
void
|
|
nocmov(x, y)
|
|
int x, y;
|
|
{
|
|
console.cursor.X = x;
|
|
console.cursor.Y = y;
|
|
ttyDisplay->curx = x;
|
|
ttyDisplay->cury = y;
|
|
}
|
|
|
|
void
|
|
xputc(ch)
|
|
char ch;
|
|
{
|
|
console.cursor.X = ttyDisplay->curx;
|
|
console.cursor.Y = ttyDisplay->cury;
|
|
xputc_core(ch);
|
|
}
|
|
|
|
void
|
|
xputs(s)
|
|
const char *s;
|
|
{
|
|
int k;
|
|
int slen = strlen(s);
|
|
|
|
if (ttyDisplay) {
|
|
console.cursor.X = ttyDisplay->curx;
|
|
console.cursor.Y = ttyDisplay->cury;
|
|
}
|
|
|
|
if (s) {
|
|
for (k = 0; k < slen && s[k]; ++k)
|
|
xputc_core(s[k]);
|
|
}
|
|
}
|
|
|
|
/* xputc_core() and g_putch() are the only
|
|
* two routines that actually place output
|
|
* on the display.
|
|
*/
|
|
void
|
|
xputc_core(ch)
|
|
char ch;
|
|
{
|
|
boolean inverse = FALSE;
|
|
cell_t cell;
|
|
switch (ch) {
|
|
case '\n':
|
|
console.cursor.Y++;
|
|
/* fall through */
|
|
case '\r':
|
|
console.cursor.X = 1;
|
|
break;
|
|
case '\b':
|
|
console.cursor.X--;
|
|
break;
|
|
default:
|
|
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;
|
|
if (has_unicode) {
|
|
cell.characters[0] = cpConsole[ch].characters[0];
|
|
cell.characters[1] = cpConsole[ch].characters[1];
|
|
cell.count = cpConsole[ch].count;
|
|
} else {
|
|
cell.characters[0] = ch;
|
|
cell.characters[1] = 0;
|
|
cell.count = 1;
|
|
}
|
|
back_buffer_write(&cell, console.cursor.X, console.cursor.Y);
|
|
console.cursor.X++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Overrides wintty.c function of the same name
|
|
* for win32. It is used for glyphs only, not text.
|
|
*/
|
|
|
|
void
|
|
g_putch(in_ch)
|
|
int in_ch;
|
|
{
|
|
cell_t cell;
|
|
boolean inverse = FALSE;
|
|
unsigned char ch = (unsigned char) in_ch;
|
|
|
|
console.cursor.X = ttyDisplay->curx;
|
|
console.cursor.Y = ttyDisplay->cury;
|
|
|
|
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;
|
|
cell.attribute = console.attr;
|
|
cell.characters[1] = 0;
|
|
cell.count = 1;
|
|
if (has_unicode)
|
|
cell.characters[0] = cp437[ch];
|
|
else
|
|
cell.characters[0] = ch;
|
|
back_buffer_write(&cell, console.cursor.X, console.cursor.Y);
|
|
}
|
|
|
|
void
|
|
cl_end()
|
|
{
|
|
console.cursor.X = ttyDisplay->curx;
|
|
console.cursor.Y = ttyDisplay->cury;
|
|
|
|
back_buffer_clear_to_end_of_line(console.cursor.X, console.cursor.Y);
|
|
|
|
tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury);
|
|
}
|
|
|
|
void
|
|
raw_clear_screen()
|
|
{
|
|
if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
|
|
|
|
buffer_fill_to_end(&front_buffer, &clear_cell, 0, 0);
|
|
buffer_fill_to_end(&back_buffer, &clear_cell, 0, 0);
|
|
|
|
DWORD ccnt;
|
|
COORD newcoord;
|
|
|
|
newcoord.X = 0;
|
|
newcoord.Y = 0;
|
|
FillConsoleOutputAttribute(
|
|
hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
|
|
csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
|
|
FillConsoleOutputCharacter(
|
|
hConOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
clear_screen()
|
|
{
|
|
raw_clear_screen();
|
|
home();
|
|
}
|
|
|
|
void
|
|
home()
|
|
{
|
|
console.cursor.X = console.cursor.Y = 0;
|
|
ttyDisplay->curx = ttyDisplay->cury = 0;
|
|
}
|
|
|
|
void
|
|
backsp()
|
|
{
|
|
console.cursor.X = ttyDisplay->curx;
|
|
console.cursor.Y = ttyDisplay->cury;
|
|
xputc_core('\b');
|
|
}
|
|
|
|
void
|
|
cl_eos()
|
|
{
|
|
int cy = ttyDisplay->cury + 1;
|
|
if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
|
|
|
|
buffer_fill_to_end(&front_buffer, &clear_cell, ttyDisplay->curx,
|
|
ttyDisplay->cury);
|
|
buffer_fill_to_end(&back_buffer, &clear_cell, ttyDisplay->curx,
|
|
ttyDisplay->cury);
|
|
|
|
DWORD ccnt;
|
|
COORD newcoord;
|
|
|
|
newcoord.X = ttyDisplay->curx;
|
|
newcoord.Y = ttyDisplay->cury;
|
|
|
|
FillConsoleOutputAttribute(
|
|
hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
|
|
csbi.dwSize.X * csbi.dwSize.Y - cy, newcoord, &ccnt);
|
|
FillConsoleOutputCharacter(hConOut, ' ',
|
|
csbi.dwSize.X * csbi.dwSize.Y - cy,
|
|
newcoord, &ccnt);
|
|
}
|
|
tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury);
|
|
}
|
|
|
|
void
|
|
tty_nhbell()
|
|
{
|
|
if (flags.silent)
|
|
return;
|
|
Beep(8000, 500);
|
|
}
|
|
|
|
volatile int junk; /* prevent optimizer from eliminating loop below */
|
|
|
|
void
|
|
tty_delay_output()
|
|
{
|
|
/* delay 50 ms - uses ANSI C clock() function now */
|
|
clock_t goal;
|
|
int k;
|
|
|
|
goal = 50 + clock();
|
|
while (goal > clock()) {
|
|
k = junk; /* Do nothing */
|
|
}
|
|
}
|
|
|
|
#ifdef TEXTCOLOR
|
|
/*
|
|
* 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()
|
|
{
|
|
#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;
|
|
}
|
|
#endif /* TEXTCOLOR */
|
|
|
|
int
|
|
has_color(int color)
|
|
{
|
|
#ifdef TEXTCOLOR
|
|
if ((color >= 0) && (color < CLR_MAX))
|
|
return 1;
|
|
#else
|
|
if ((color == CLR_BLACK) || (color == CLR_WHITE) || (color == NO_COLOR))
|
|
return 1;
|
|
#endif
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
term_start_attr(int attrib)
|
|
{
|
|
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:
|
|
case ATR_ULINE:
|
|
case ATR_BLINK:
|
|
case ATR_BOLD:
|
|
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_end_color(void)
|
|
{
|
|
#ifdef TEXTCOLOR
|
|
console.foreground = DEFTEXTCOLOR;
|
|
#endif
|
|
console.attr = (console.foreground | console.background);
|
|
console.current_nhcolor = NO_COLOR;
|
|
}
|
|
|
|
void
|
|
standoutbeg()
|
|
{
|
|
term_start_attr(ATR_BOLD);
|
|
}
|
|
|
|
void
|
|
standoutend()
|
|
{
|
|
term_end_attr(ATR_BOLD);
|
|
}
|
|
|
|
#ifndef NO_MOUSE_ALLOWED
|
|
void
|
|
toggle_mouse_support()
|
|
{
|
|
DWORD cmode;
|
|
GetConsoleMode(hConIn, &cmode);
|
|
if (iflags.wc_mouse_support)
|
|
cmode |= ENABLE_MOUSE_INPUT;
|
|
else
|
|
cmode &= ~ENABLE_MOUSE_INPUT;
|
|
SetConsoleMode(hConIn, cmode);
|
|
}
|
|
#endif
|
|
|
|
/* handle tty options updates here */
|
|
void
|
|
nttty_preference_update(pref)
|
|
const char *pref;
|
|
{
|
|
if (stricmp(pref, "mouse_support") == 0) {
|
|
#ifndef NO_MOUSE_ALLOWED
|
|
toggle_mouse_support();
|
|
#endif
|
|
}
|
|
if (stricmp(pref, "symset") == 0)
|
|
check_and_set_font();
|
|
return;
|
|
}
|
|
|
|
#ifdef PORT_DEBUG
|
|
void
|
|
win32con_debug_keystrokes()
|
|
{
|
|
DWORD count;
|
|
boolean valid = 0;
|
|
int ch;
|
|
xputs("\n");
|
|
while (!valid || ch != 27) {
|
|
nocmov(ttyDisplay->curx, ttyDisplay->cury);
|
|
ReadConsoleInput(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_handler_info()
|
|
{
|
|
char *buf;
|
|
int ci;
|
|
if (!pSourceAuthor && !pSourceWhere)
|
|
pline("Keyboard handler source info and author unavailable.");
|
|
else {
|
|
if (pKeyHandlerName && pKeyHandlerName(&buf, 1)) {
|
|
xputs("\n");
|
|
xputs("Keystroke handler loaded: \n ");
|
|
xputs(buf);
|
|
}
|
|
if (pSourceAuthor && pSourceAuthor(&buf)) {
|
|
xputs("\n");
|
|
xputs("Keystroke handler Author: \n ");
|
|
xputs(buf);
|
|
}
|
|
if (pSourceWhere && pSourceWhere(&buf)) {
|
|
xputs("\n");
|
|
xputs("Keystroke handler source code available at:\n ");
|
|
xputs(buf);
|
|
}
|
|
xputs("\nPress any key to resume.");
|
|
ci = nhgetch();
|
|
(void) doredraw();
|
|
}
|
|
}
|
|
|
|
void
|
|
win32con_toggle_cursor_info()
|
|
{
|
|
display_cursor_info = !display_cursor_info;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
map_subkeyvalue(op)
|
|
register char *op;
|
|
{
|
|
char digits[] = "0123456789";
|
|
int length, i, idx, val;
|
|
char *kp;
|
|
|
|
idx = -1;
|
|
val = -1;
|
|
kp = index(op, '/');
|
|
if (kp) {
|
|
*kp = '\0';
|
|
kp++;
|
|
length = strlen(kp);
|
|
if (length < 1 || length > 3)
|
|
return;
|
|
for (i = 0; i < length; i++)
|
|
if (!index(digits, kp[i]))
|
|
return;
|
|
val = atoi(kp);
|
|
length = strlen(op);
|
|
if (length < 1 || length > 3)
|
|
return;
|
|
for (i = 0; i < length; i++)
|
|
if (!index(digits, op[i]))
|
|
return;
|
|
idx = atoi(op);
|
|
}
|
|
if (idx >= MAX_OVERRIDES || idx < 0 || val >= MAX_OVERRIDES || val < 1)
|
|
return;
|
|
key_overrides[idx] = val;
|
|
}
|
|
|
|
void
|
|
load_keyboard_handler()
|
|
{
|
|
char suffx[] = ".dll";
|
|
char *truncspot;
|
|
#define MAX_DLLNAME 25
|
|
char kh[MAX_ALTKEYHANDLER];
|
|
if (iflags.altkeyhandler[0]) {
|
|
if (hLibrary) { /* already one loaded apparently */
|
|
FreeLibrary(hLibrary);
|
|
hLibrary = (HANDLE) 0;
|
|
pNHkbhit = (NHKBHIT) 0;
|
|
pCheckInput = (CHECKINPUT) 0;
|
|
pSourceWhere = (SOURCEWHERE) 0;
|
|
pSourceAuthor = (SOURCEAUTHOR) 0;
|
|
pKeyHandlerName = (KEYHANDLERNAME) 0;
|
|
pProcessKeystroke = (PROCESS_KEYSTROKE) 0;
|
|
}
|
|
if ((truncspot = strstri(iflags.altkeyhandler, suffx)) != 0)
|
|
*truncspot = '\0';
|
|
(void) strncpy(kh, iflags.altkeyhandler,
|
|
(MAX_ALTKEYHANDLER - sizeof suffx) - 1);
|
|
kh[(MAX_ALTKEYHANDLER - sizeof suffx) - 1] = '\0';
|
|
Strcat(kh, suffx);
|
|
Strcpy(iflags.altkeyhandler, kh);
|
|
hLibrary = LoadLibrary(kh);
|
|
if (hLibrary) {
|
|
pProcessKeystroke = (PROCESS_KEYSTROKE) GetProcAddress(
|
|
hLibrary, TEXT("ProcessKeystroke"));
|
|
pNHkbhit = (NHKBHIT) GetProcAddress(hLibrary, TEXT("NHkbhit"));
|
|
pCheckInput =
|
|
(CHECKINPUT) GetProcAddress(hLibrary, TEXT("CheckInput"));
|
|
pSourceWhere =
|
|
(SOURCEWHERE) GetProcAddress(hLibrary, TEXT("SourceWhere"));
|
|
pSourceAuthor =
|
|
(SOURCEAUTHOR) GetProcAddress(hLibrary, TEXT("SourceAuthor"));
|
|
pKeyHandlerName = (KEYHANDLERNAME) GetProcAddress(
|
|
hLibrary, TEXT("KeyHandlerName"));
|
|
}
|
|
}
|
|
if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) {
|
|
if (hLibrary) {
|
|
FreeLibrary(hLibrary);
|
|
hLibrary = (HANDLE) 0;
|
|
pNHkbhit = (NHKBHIT) 0;
|
|
pCheckInput = (CHECKINPUT) 0;
|
|
pSourceWhere = (SOURCEWHERE) 0;
|
|
pSourceAuthor = (SOURCEAUTHOR) 0;
|
|
pKeyHandlerName = (KEYHANDLERNAME) 0;
|
|
pProcessKeystroke = (PROCESS_KEYSTROKE) 0;
|
|
}
|
|
(void) strncpy(kh, "nhdefkey.dll",
|
|
(MAX_ALTKEYHANDLER - sizeof suffx) - 1);
|
|
kh[(MAX_ALTKEYHANDLER - sizeof suffx) - 1] = '\0';
|
|
Strcpy(iflags.altkeyhandler, kh);
|
|
hLibrary = LoadLibrary(kh);
|
|
if (hLibrary) {
|
|
pProcessKeystroke = (PROCESS_KEYSTROKE) GetProcAddress(
|
|
hLibrary, TEXT("ProcessKeystroke"));
|
|
pCheckInput =
|
|
(CHECKINPUT) GetProcAddress(hLibrary, TEXT("CheckInput"));
|
|
pNHkbhit = (NHKBHIT) GetProcAddress(hLibrary, TEXT("NHkbhit"));
|
|
pSourceWhere =
|
|
(SOURCEWHERE) GetProcAddress(hLibrary, TEXT("SourceWhere"));
|
|
pSourceAuthor =
|
|
(SOURCEAUTHOR) GetProcAddress(hLibrary, TEXT("SourceAuthor"));
|
|
pKeyHandlerName = (KEYHANDLERNAME) GetProcAddress(
|
|
hLibrary, TEXT("KeyHandlerName"));
|
|
}
|
|
}
|
|
if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) {
|
|
if (!hLibrary)
|
|
raw_printf("\nNetHack was unable to load keystroke handler.\n");
|
|
else {
|
|
FreeLibrary(hLibrary);
|
|
hLibrary = (HANDLE) 0;
|
|
raw_printf("\nNetHack keystroke handler is invalid.\n");
|
|
}
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* 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 *);
|
|
Vsprintf(buf, fmt, VA_ARGS);
|
|
if (redirect_stdout)
|
|
fprintf(stdout, "%s", buf);
|
|
else {
|
|
if(!init_ttycolor_completed)
|
|
init_ttycolor();
|
|
xputs(buf);
|
|
if (ttyDisplay)
|
|
curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y);
|
|
really_move_cursor();
|
|
}
|
|
VA_END();
|
|
return;
|
|
}
|
|
|
|
/* fatal error */
|
|
/*VARARGS1*/
|
|
void nttty_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) vsprintf(&buf[1], s, VA_ARGS);
|
|
msmsg(buf);
|
|
really_move_cursor();
|
|
VA_END();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void
|
|
synch_cursor()
|
|
{
|
|
really_move_cursor();
|
|
}
|
|
|
|
#ifdef CHANGE_COLOR
|
|
void
|
|
tty_change_color(color_number, rgb, reverse)
|
|
int color_number, reverse;
|
|
long rgb;
|
|
{
|
|
/* Map NetHack color index to NT Console palette index */
|
|
int idx, win32_color_number[] = {
|
|
0, /* CLR_BLACK 0 */
|
|
4, /* CLR_RED 1 */
|
|
2, /* CLR_GREEN 2 */
|
|
6, /* CLR_BROWN 3 */
|
|
1, /* CLR_BLUE 4 */
|
|
5, /* CLR_MAGENTA 5 */
|
|
3, /* CLR_CYAN 6 */
|
|
7, /* CLR_GRAY 7 */
|
|
8, /* NO_COLOR 8 */
|
|
12, /* CLR_ORANGE 9 */
|
|
10, /* CLR_BRIGHT_GREEN 10 */
|
|
14, /* CLR_YELLOW 11 */
|
|
9, /* CLR_BRIGHT_BLUE 12 */
|
|
13, /* CLR_BRIGHT_MAGENTA 13 */
|
|
11, /* CLR_BRIGHT_CYAN 14 */
|
|
15 /* CLR_WHITE 15 */
|
|
};
|
|
int k;
|
|
if (color_number < 0) { /* indicates OPTIONS=palette with no value */
|
|
/* copy the NetHack palette into UserDefinedColors */
|
|
for (k = 0; k < CLR_MAX; k++)
|
|
UserDefinedColors[k] = NetHackColors[k];
|
|
} else if (color_number >= 0 && color_number < CLR_MAX) {
|
|
if (!altered_palette) {
|
|
/* make sure a full suite is available */
|
|
for (k = 0; k < CLR_MAX; k++)
|
|
UserDefinedColors[k] = DefaultColors[k];
|
|
}
|
|
idx = win32_color_number[color_number];
|
|
UserDefinedColors[idx] = rgb;
|
|
}
|
|
altered_palette = TRUE;
|
|
}
|
|
|
|
char *
|
|
tty_get_color_string()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
int
|
|
match_color_name(c)
|
|
const char *c;
|
|
{
|
|
const struct others {
|
|
int idx;
|
|
const char *colorname;
|
|
} othernames[] = {
|
|
{ CLR_MAGENTA, "purple" },
|
|
{ CLR_BRIGHT_MAGENTA, "bright purple" },
|
|
{ NO_COLOR, "dark gray" },
|
|
{ NO_COLOR, "dark grey" },
|
|
{ CLR_GRAY, "grey" },
|
|
};
|
|
|
|
int cnt;
|
|
for (cnt = 0; cnt < CLR_MAX; ++cnt) {
|
|
if (!strcmpi(c, c_obj_colors[cnt]))
|
|
return cnt;
|
|
}
|
|
for (cnt = 0; cnt < SIZE(othernames); ++cnt) {
|
|
if (!strcmpi(c, othernames[cnt].colorname))
|
|
return othernames[cnt].idx;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Returns 0 if badoption syntax
|
|
*/
|
|
int
|
|
alternative_palette(op)
|
|
char *op;
|
|
{
|
|
/*
|
|
* palette:color-R-G-B
|
|
* OPTIONS=palette:green-4-3-1, palette:0-0-0-0
|
|
*/
|
|
int fieldcnt, color_number, rgb, red, green, blue;
|
|
char *fields[4], *cp;
|
|
|
|
if (!op) {
|
|
change_color(-1, 0, 0); /* indicates palette option with
|
|
no value meaning "load an entire
|
|
hard-coded NetHack palette." */
|
|
return 1;
|
|
}
|
|
|
|
cp = fields[0] = op;
|
|
for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) {
|
|
cp = index(cp, '-');
|
|
if (!cp)
|
|
return 0;
|
|
fields[fieldcnt] = cp;
|
|
cp++;
|
|
}
|
|
for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) {
|
|
*(fields[fieldcnt]) = '\0';
|
|
++fields[fieldcnt];
|
|
}
|
|
rgb = 0;
|
|
for (fieldcnt = 0; fieldcnt < 4; ++fieldcnt) {
|
|
if (fieldcnt == 0 && isalpha(*(fields[0]))) {
|
|
color_number = match_color_name(fields[0]);
|
|
if (color_number == -1)
|
|
return 0;
|
|
} else {
|
|
int dcount = 0, cval = 0;
|
|
cp = fields[fieldcnt];
|
|
if (*cp == '\\' && index("0123456789xXoO", cp[1])) {
|
|
const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
|
|
|
|
cp++;
|
|
if (*cp == 'x' || *cp == 'X')
|
|
for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
|
|
cval = (int) ((cval * 16) + (dp - hex) / 2);
|
|
else if (*cp == 'o' || *cp == 'O')
|
|
for (++cp; (index("01234567", *cp)) && (dcount++ < 3);
|
|
cp++)
|
|
cval = (cval * 8) + (*cp - '0');
|
|
else
|
|
return 0;
|
|
} else {
|
|
for (; *cp && (index("0123456789", *cp)) && (dcount++ < 3);
|
|
cp++)
|
|
cval = (cval * 10) + (*cp - '0');
|
|
}
|
|
switch (fieldcnt) {
|
|
case 0:
|
|
color_number = cval;
|
|
break;
|
|
case 1:
|
|
red = cval;
|
|
break;
|
|
case 2:
|
|
green = cval;
|
|
break;
|
|
case 3:
|
|
blue = cval;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
rgb = RGB(red, green, blue);
|
|
if (color_number >= 0 && color_number < CLR_MAX)
|
|
change_color(color_number, rgb, 0);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* This uses an undocumented method to set console attributes
|
|
* at runtime including console palette
|
|
*
|
|
* VOID WINAPI SetConsolePalette(COLORREF palette[16])
|
|
*
|
|
* Author: James Brown at www.catch22.net
|
|
*
|
|
* Set palette of current console.
|
|
* Palette should be of the form:
|
|
*
|
|
* COLORREF DefaultColors[CLR_MAX] =
|
|
* {
|
|
* 0x00000000, 0x00800000, 0x00008000, 0x00808000,
|
|
* 0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0,
|
|
* 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00,
|
|
* 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
|
|
* };
|
|
*/
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
/*
|
|
* Structure to send console via WM_SETCONSOLEINFO
|
|
*/
|
|
typedef struct _CONSOLE_INFO {
|
|
ULONG Length;
|
|
COORD ScreenBufferSize;
|
|
COORD WindowSize;
|
|
ULONG WindowPosX;
|
|
ULONG WindowPosY;
|
|
|
|
COORD FontSize;
|
|
ULONG FontFamily;
|
|
ULONG FontWeight;
|
|
WCHAR FaceName[32];
|
|
|
|
ULONG CursorSize;
|
|
ULONG FullScreen;
|
|
ULONG QuickEdit;
|
|
ULONG AutoPosition;
|
|
ULONG InsertMode;
|
|
|
|
USHORT ScreenColors;
|
|
USHORT PopupColors;
|
|
ULONG HistoryNoDup;
|
|
ULONG HistoryBufferSize;
|
|
ULONG NumberOfHistoryBuffers;
|
|
|
|
COLORREF ColorTable[16];
|
|
|
|
ULONG CodePage;
|
|
HWND Hwnd;
|
|
|
|
WCHAR ConsoleTitle[0x100];
|
|
} CONSOLE_INFO;
|
|
|
|
#pragma pack(pop)
|
|
|
|
BOOL SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci);
|
|
static void GetConsoleSizeInfo(CONSOLE_INFO *pci);
|
|
VOID WINAPI SetConsolePalette(COLORREF crPalette[16]);
|
|
|
|
void
|
|
adjust_palette(VOID_ARGS)
|
|
{
|
|
SetConsolePalette(UserDefinedColors);
|
|
altered_palette = 0;
|
|
}
|
|
|
|
/*
|
|
/* only in Win2k+ (use FindWindow for NT4) */
|
|
/* HWND WINAPI GetConsoleWindow(); */
|
|
|
|
/* Undocumented console message */
|
|
#define WM_SETCONSOLEINFO (WM_USER + 201)
|
|
|
|
VOID WINAPI
|
|
SetConsolePalette(COLORREF palette[16])
|
|
{
|
|
CONSOLE_INFO ci = { sizeof(ci) };
|
|
int i;
|
|
HWND hwndConsole = GetConsoleHandle();
|
|
|
|
/* get current size/position settings rather than using defaults.. */
|
|
GetConsoleSizeInfo(&ci);
|
|
|
|
/* set these to zero to keep current settings */
|
|
ci.FontSize.X = 0; /* def = 8 */
|
|
ci.FontSize.Y = 0; /* def = 12 */
|
|
ci.FontFamily = 0; /* def = 0x30 = FF_MODERN|FIXED_PITCH */
|
|
ci.FontWeight = 0; /* 0x400; */
|
|
/* lstrcpyW(ci.FaceName, L"Terminal"); */
|
|
ci.FaceName[0] = L'\0';
|
|
|
|
ci.CursorSize = 25;
|
|
ci.FullScreen = FALSE;
|
|
ci.QuickEdit = TRUE;
|
|
ci.AutoPosition = 0x10000;
|
|
ci.InsertMode = TRUE;
|
|
ci.ScreenColors = MAKEWORD(0x7, 0x0);
|
|
ci.PopupColors = MAKEWORD(0x5, 0xf);
|
|
|
|
ci.HistoryNoDup = FALSE;
|
|
ci.HistoryBufferSize = 50;
|
|
ci.NumberOfHistoryBuffers = 4;
|
|
|
|
// colour table
|
|
for (i = 0; i < 16; i++)
|
|
ci.ColorTable[i] = palette[i];
|
|
|
|
ci.CodePage = GetConsoleOutputCP();
|
|
ci.Hwnd = hwndConsole;
|
|
|
|
lstrcpyW(ci.ConsoleTitle, L"");
|
|
|
|
SetConsoleInfo(hwndConsole, &ci);
|
|
}
|
|
|
|
/*
|
|
* Wrapper around WM_SETCONSOLEINFO. We need to create the
|
|
* necessary section (file-mapping) object in the context of the
|
|
* process which owns the console, before posting the message
|
|
*/
|
|
BOOL
|
|
SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci)
|
|
{
|
|
DWORD dwConsoleOwnerPid;
|
|
HANDLE hProcess;
|
|
HANDLE hSection, hDupSection;
|
|
PVOID ptrView = 0;
|
|
HANDLE hThread;
|
|
|
|
/*
|
|
* Open the process which "owns" the console
|
|
*/
|
|
GetWindowThreadProcessId(hwndConsole, &dwConsoleOwnerPid);
|
|
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwConsoleOwnerPid);
|
|
|
|
/*
|
|
* Create a SECTION object backed by page-file, then map a view of
|
|
* this section into the owner process so we can write the contents
|
|
* of the CONSOLE_INFO buffer into it
|
|
*/
|
|
hSection = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0,
|
|
pci->Length, 0);
|
|
|
|
/*
|
|
* Copy our console structure into the section-object
|
|
*/
|
|
ptrView = MapViewOfFile(hSection, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0,
|
|
pci->Length);
|
|
memcpy(ptrView, pci, pci->Length);
|
|
UnmapViewOfFile(ptrView);
|
|
|
|
/*
|
|
* Map the memory into owner process
|
|
*/
|
|
DuplicateHandle(GetCurrentProcess(), hSection, hProcess, &hDupSection, 0,
|
|
FALSE, DUPLICATE_SAME_ACCESS);
|
|
|
|
/* Send console window the "update" message */
|
|
SendMessage(hwndConsole, WM_SETCONSOLEINFO, (WPARAM) hDupSection, 0);
|
|
|
|
/*
|
|
* clean up
|
|
*/
|
|
hThread = CreateRemoteThread(hProcess, 0, 0,
|
|
(LPTHREAD_START_ROUTINE) CloseHandle,
|
|
hDupSection, 0, 0);
|
|
|
|
CloseHandle(hThread);
|
|
CloseHandle(hSection);
|
|
CloseHandle(hProcess);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Fill the CONSOLE_INFO structure with information
|
|
* about the current console window
|
|
*/
|
|
static void
|
|
GetConsoleSizeInfo(CONSOLE_INFO *pci)
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
|
|
HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
GetConsoleScreenBufferInfo(hConsoleOut, &csbi);
|
|
|
|
pci->ScreenBufferSize = csbi.dwSize;
|
|
pci->WindowSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
|
pci->WindowSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
|
pci->WindowPosX = csbi.srWindow.Left;
|
|
pci->WindowPosY = csbi.srWindow.Top;
|
|
}
|
|
|
|
static HWND
|
|
GetConsoleHandle(void)
|
|
{
|
|
HMODULE hMod = GetModuleHandle("kernel32.dll");
|
|
GETCONSOLEWINDOW pfnGetConsoleWindow =
|
|
(GETCONSOLEWINDOW) GetProcAddress(hMod, "GetConsoleWindow");
|
|
if (pfnGetConsoleWindow)
|
|
return pfnGetConsoleWindow();
|
|
else
|
|
return GetConsoleHwnd();
|
|
}
|
|
|
|
static HWND
|
|
GetConsoleHwnd(void)
|
|
{
|
|
int iterations = 0;
|
|
HWND hwndFound = 0;
|
|
char OldTitle[1024], NewTitle[1024], TestTitle[1024];
|
|
|
|
/* Get current window title */
|
|
GetConsoleTitle(OldTitle, sizeof OldTitle);
|
|
|
|
(void) sprintf(NewTitle, "NETHACK%d/%d", GetTickCount(),
|
|
GetCurrentProcessId());
|
|
SetConsoleTitle(NewTitle);
|
|
|
|
GetConsoleTitle(TestTitle, sizeof TestTitle);
|
|
while (strcmp(TestTitle, NewTitle) != 0) {
|
|
iterations++;
|
|
/* sleep(0); */
|
|
GetConsoleTitle(TestTitle, sizeof TestTitle);
|
|
}
|
|
hwndFound = FindWindow(NULL, NewTitle);
|
|
SetConsoleTitle(OldTitle);
|
|
/* printf("%d iterations\n", iterations); */
|
|
return hwndFound;
|
|
}
|
|
#endif /*CHANGE_COLOR*/
|
|
|
|
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()
|
|
{
|
|
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
|
|
check_font_widths()
|
|
{
|
|
CONSOLE_FONT_INFOEX console_font_info;
|
|
console_font_info.cbSize = sizeof(console_font_info);
|
|
BOOL success = GetCurrentConsoleFontEx(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.
|
|
*/
|
|
HWND hWnd = GetConsoleWindow();
|
|
HDC hDC = GetDC(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);
|
|
|
|
/* 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[l_syms[i]] = TRUE;
|
|
used[r_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];
|
|
|
|
/* measure the set of used glyphs to ensure they fit */
|
|
boolean 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()
|
|
{
|
|
CONSOLE_FONT_INFOEX console_font_info;
|
|
console_font_info.cbSize = sizeof(console_font_info);
|
|
BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE,
|
|
&console_font_info);
|
|
|
|
console_font_changed = TRUE;
|
|
original_console_font_info = console_font_info;
|
|
original_console_code_page = GetConsoleOutputCP();
|
|
|
|
wcscpy_s(console_font_info.FaceName,
|
|
sizeof(console_font_info.FaceName)
|
|
/ sizeof(console_font_info.FaceName[0]),
|
|
L"Consolas");
|
|
|
|
success = SetConsoleOutputCP(437);
|
|
if (!success)
|
|
raw_print("Unable to set console code page to 437\n");
|
|
|
|
success = SetCurrentConsoleFontEx(hConOut, FALSE, &console_font_info);
|
|
if (!success)
|
|
raw_print("Unable to set console font to Consolas\n");
|
|
}
|
|
|
|
/* 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()
|
|
{
|
|
if (console_font_changed) {
|
|
BOOL success;
|
|
raw_print("Restoring original font and code page\n");
|
|
success = SetConsoleOutputCP(original_console_code_page);
|
|
if (!success)
|
|
raw_print("Unable to restore original code page\n");
|
|
|
|
success = SetCurrentConsoleFontEx(hConOut, FALSE,
|
|
&original_console_font_info);
|
|
if (!success)
|
|
raw_print("Unable to restore original font\n");
|
|
|
|
console_font_changed = FALSE;
|
|
}
|
|
}
|
|
|
|
#endif /* WIN32 */
|