OSDN Git Service

upgrade to 3.6.1
[jnethack/source.git] / sys / winnt / nttty.c
1 /* NetHack 3.6  nttty.c $NHDT-Date: 1520825872 2018/03/12 03:37:52 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.70 $ */
2 /* Copyright (c) NetHack PC Development Team 1993    */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* tty.c - (Windows NT) version */
6
7 /*
8  * Initial Creation                             M. Allison      1993/01/31
9  * Switch to low level console output routines  M. Allison      2003/10/01
10  * Restrict cursor movement until input pending M. Lehotay      2003/10/02
11  * Call Unicode version of output API on NT     R. Chason       2005/10/28
12  *
13  */
14
15 #ifdef WIN32
16 #define NEED_VARARGS /* Uses ... */
17 #include "hack.h"
18 #include "wintty.h"
19 #include <sys\types.h>
20 #include <sys\stat.h>
21 #include "win32api.h"
22
23 /*
24  * The following WIN32 Console API routines are used in this file.
25  *
26  * CreateFile
27  * GetConsoleScreenBufferInfo
28  * GetStdHandle
29  * SetConsoleCursorPosition
30  * SetConsoleTextAttribute
31  * SetConsoleCtrlHandler
32  * PeekConsoleInput
33  * ReadConsoleInput
34  * WriteConsoleOutputCharacter
35  * FillConsoleOutputAttribute
36  * GetConsoleOutputCP
37  */
38
39 static BOOL FDECL(CtrlHandler, (DWORD));
40 static void FDECL(xputc_core, (char));
41 void FDECL(cmov, (int, int));
42 void FDECL(nocmov, (int, int));
43 int FDECL(process_keystroke,
44           (INPUT_RECORD *, boolean *, BOOLEAN_P numberpad, int portdebug));
45 static void NDECL(init_ttycolor);
46 static void NDECL(really_move_cursor);
47 static void NDECL(check_and_set_font);
48 static boolean NDECL(check_font_widths);
49 static void NDECL(set_known_good_console_font);
50 static void NDECL(restore_original_console_font);
51
52 /* Win32 Console handles for input and output */
53 HANDLE hConIn;
54 HANDLE hConOut;
55
56 /* Win32 Screen buffer,coordinate,console I/O information */
57 CONSOLE_SCREEN_BUFFER_INFO csbi, origcsbi;
58 COORD ntcoord;
59 INPUT_RECORD ir;
60
61 /* Support for changing console font if existing glyph widths are too wide */
62 boolean console_font_changed;
63 CONSOLE_FONT_INFOEX original_console_font_info;
64 UINT original_console_code_page;
65
66 extern boolean getreturn_enabled; /* from sys/share/pcsys.c */
67 extern int redirect_stdout;
68
69 /* Flag for whether NetHack was launched via the GUI, not the command line.
70  * The reason we care at all, is so that we can get
71  * a final RETURN at the end of the game when launched from the GUI
72  * to prevent the scoreboard (or panic message :-|) from vanishing
73  * immediately after it is displayed, yet not bother when started
74  * from the command line.
75  */
76 int GUILaunched;
77 /* Flag for whether unicode is supported */
78 static boolean has_unicode;
79 static boolean init_ttycolor_completed;
80 #ifdef PORT_DEBUG
81 static boolean display_cursor_info = FALSE;
82 #endif
83 #ifdef CHANGE_COLOR
84 static void NDECL(adjust_palette);
85 static int FDECL(match_color_name, (const char *));
86 typedef HWND(WINAPI *GETCONSOLEWINDOW)();
87 static HWND GetConsoleHandle(void);
88 static HWND GetConsoleHwnd(void);
89 static boolean altered_palette;
90 static COLORREF UserDefinedColors[CLR_MAX];
91 static COLORREF NetHackColors[CLR_MAX] = {
92     0x00000000, 0x00c80000, 0x0000c850, 0x00b4b432, 0x000000d2, 0x00800080,
93     0x000064b4, 0x00c0c0c0, 0x00646464, 0x00f06464, 0x0000ff00, 0x00ffff00,
94     0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
95 };
96 static COLORREF DefaultColors[CLR_MAX] = {
97     0x00000000, 0x00800000, 0x00008000, 0x00808000, 0x00000080, 0x00800080,
98     0x00008080, 0x00c0c0c0, 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00,
99     0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
100 };
101 #endif
102 struct console_t {
103     WORD background;
104     WORD foreground;
105     WORD attr;
106     int current_nhcolor;
107     int current_nhattr[ATR_INVERSE+1];
108     COORD cursor;
109 } console = {
110     0,
111     (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED),
112     (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED),
113     NO_COLOR,
114     {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE},
115     {0, 0}
116 };
117 static DWORD ccount, acount;
118 #ifndef CLR_MAX
119 #define CLR_MAX 16
120 #endif
121
122 int ttycolors[CLR_MAX];
123 int ttycolors_inv[CLR_MAX];
124
125 #define MAX_OVERRIDES 256
126 unsigned char key_overrides[MAX_OVERRIDES];
127 static char nullstr[] = "";
128 char erase_char, kill_char;
129 #define DEFTEXTCOLOR ttycolors[7]
130
131 /* dynamic keystroke handling .DLL support */
132 typedef int(__stdcall *PROCESS_KEYSTROKE)(HANDLE, INPUT_RECORD *, boolean *,
133                                           BOOLEAN_P, int);
134
135 typedef int(__stdcall *NHKBHIT)(HANDLE, INPUT_RECORD *);
136
137 typedef int(__stdcall *CHECKINPUT)(HANDLE, INPUT_RECORD *, DWORD *, BOOLEAN_P,
138                                    int, int *, coord *);
139
140 typedef int(__stdcall *SOURCEWHERE)(char **);
141
142 typedef int(__stdcall *SOURCEAUTHOR)(char **);
143
144 typedef int(__stdcall *KEYHANDLERNAME)(char **, int);
145
146 HANDLE hLibrary;
147 PROCESS_KEYSTROKE pProcessKeystroke;
148 NHKBHIT pNHkbhit;
149 CHECKINPUT pCheckInput;
150 SOURCEWHERE pSourceWhere;
151 SOURCEAUTHOR pSourceAuthor;
152 KEYHANDLERNAME pKeyHandlerName;
153
154 /*
155  * Called after returning from ! or ^Z
156  */
157 void
158 gettty()
159 {
160     console_font_changed = FALSE;
161
162     check_and_set_font();
163
164 #ifndef TEXTCOLOR
165     int k;
166 #endif
167     erase_char = '\b';
168     kill_char = 21; /* cntl-U */
169     iflags.cbreak = TRUE;
170 #ifdef TEXTCOLOR
171     init_ttycolor();
172 #else
173     for (k = 0; k < CLR_MAX; ++k)
174         ttycolors[k] = NO_COLOR;
175 #endif
176 }
177
178 /* reset terminal to original state */
179 void
180 settty(s)
181 const char *s;
182 {
183     cmov(ttyDisplay->curx, ttyDisplay->cury);
184     end_screen();
185     if (s)
186         raw_print(s);
187
188     restore_original_console_font();
189 }
190
191 /* called by init_nhwindows() and resume_nhwindows() */
192 void
193 setftty()
194 {
195 #ifdef CHANGE_COLOR
196     if (altered_palette)
197         adjust_palette();
198 #endif
199     start_screen();
200     has_unicode = ((GetVersion() & 0x80000000) == 0);
201 }
202
203 void
204 tty_startup(wid, hgt)
205 int *wid, *hgt;
206 {
207     int twid = origcsbi.srWindow.Right - origcsbi.srWindow.Left + 1;
208
209     if (twid > 80)
210         twid = 80;
211     *wid = twid;
212     *hgt = origcsbi.srWindow.Bottom - origcsbi.srWindow.Top + 1;
213     set_option_mod_status("mouse_support", SET_IN_GAME);
214 }
215
216 void
217 tty_number_pad(state)
218 int state;
219 {
220 }
221
222 void
223 tty_start_screen()
224 {
225     if (iflags.num_pad)
226         tty_number_pad(1); /* make keypad send digits */
227 }
228
229 void
230 tty_end_screen()
231 {
232     clear_screen();
233     really_move_cursor();
234     if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
235         DWORD ccnt;
236         COORD newcoord;
237
238         newcoord.X = 0;
239         newcoord.Y = 0;
240         FillConsoleOutputAttribute(
241             hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
242             csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
243         FillConsoleOutputCharacter(
244             hConOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
245     }
246     FlushConsoleInputBuffer(hConIn);
247 }
248
249 static BOOL
250 CtrlHandler(ctrltype)
251 DWORD ctrltype;
252 {
253     switch (ctrltype) {
254     /*  case CTRL_C_EVENT: */
255     case CTRL_BREAK_EVENT:
256         clear_screen();
257     case CTRL_CLOSE_EVENT:
258     case CTRL_LOGOFF_EVENT:
259     case CTRL_SHUTDOWN_EVENT:
260         getreturn_enabled = FALSE;
261 #ifndef NOSAVEONHANGUP
262         hangup(0);
263 #endif
264 #if defined(SAFERHANGUP)
265         CloseHandle(hConIn); /* trigger WAIT_FAILED */
266         return TRUE;
267 #endif
268     default:
269         return FALSE;
270     }
271 }
272
273 /* called by init_tty in wintty.c for WIN32 port only */
274 void
275 nttty_open(mode)
276 int mode;
277 {
278     HANDLE hStdOut;
279     DWORD cmode;
280     long mask;
281
282     GUILaunched = 0;
283
284     try :
285         /* The following lines of code were suggested by
286          * Bob Landau of Microsoft WIN32 Developer support,
287          * as the only current means of determining whether
288          * we were launched from the command prompt, or from
289          * the NT program manager. M. Allison
290          */
291     hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
292
293     if (hStdOut) {
294         GetConsoleScreenBufferInfo(hStdOut, &origcsbi);
295     } else if (mode) {
296         HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
297         HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
298
299         if (!hStdOut && !hStdIn) {
300             /* Bool rval; */
301             AllocConsole();
302             AttachConsole(GetCurrentProcessId());
303             /*  rval = SetStdHandle(STD_OUTPUT_HANDLE, hWrite); */
304             freopen("CON", "w", stdout);
305             freopen("CON", "r", stdin);
306         }
307         mode = 0;
308         goto try;
309     } else {
310         return;
311     }
312
313     /* Obtain handles for the standard Console I/O devices */
314     hConIn = GetStdHandle(STD_INPUT_HANDLE);
315     hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
316
317     load_keyboard_handler();
318     /* Initialize the function pointer that points to
319     * the kbhit() equivalent, in this TTY case nttty_kbhit()
320     */
321     nt_kbhit = nttty_kbhit;
322
323     GetConsoleMode(hConIn, &cmode);
324 #ifdef NO_MOUSE_ALLOWED
325     mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT
326            | ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT;
327 #else
328     mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
329            | ENABLE_WINDOW_INPUT;
330 #endif
331     /* Turn OFF the settings specified in the mask */
332     cmode &= ~mask;
333 #ifndef NO_MOUSE_ALLOWED
334     cmode |= ENABLE_MOUSE_INPUT;
335 #endif
336     SetConsoleMode(hConIn, cmode);
337     if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) {
338         /* Unable to set control handler */
339         cmode = 0; /* just to have a statement to break on for debugger */
340     }
341     get_scr_size();
342     console.cursor.X = console.cursor.Y = 0;
343     really_move_cursor();
344 }
345
346 int
347 process_keystroke(ir, valid, numberpad, portdebug)
348 INPUT_RECORD *ir;
349 boolean *valid;
350 boolean numberpad;
351 int portdebug;
352 {
353     int ch = pProcessKeystroke(hConIn, ir, valid, numberpad, portdebug);
354     /* check for override */
355     if (ch && ch < MAX_OVERRIDES && key_overrides[ch])
356         ch = key_overrides[ch];
357     return ch;
358 }
359
360 int
361 nttty_kbhit()
362 {
363     return pNHkbhit(hConIn, &ir);
364 }
365
366 void
367 get_scr_size()
368 {
369     int lines, cols;
370     
371     GetConsoleScreenBufferInfo(hConOut, &csbi);
372     
373     lines = csbi.srWindow.Bottom - (csbi.srWindow.Top + 1);
374     cols = csbi.srWindow.Right - (csbi.srWindow.Left + 1);
375
376     LI = lines;
377     CO = min(cols, 80);
378     
379     if ((LI < 25) || (CO < 80)) {
380         COORD newcoord;
381
382         LI = 25;
383         CO = 80;
384
385         newcoord.Y = LI;
386         newcoord.X = CO;
387
388         SetConsoleScreenBufferSize(hConOut, newcoord);
389     }
390 }
391
392 int
393 tgetch()
394 {
395     int mod;
396     coord cc;
397     DWORD count;
398     really_move_cursor();
399     return (program_state.done_hup)
400                ? '\033'
401                : pCheckInput(hConIn, &ir, &count, iflags.num_pad, 0, &mod,
402                              &cc);
403 }
404
405 int
406 ntposkey(x, y, mod)
407 int *x, *y, *mod;
408 {
409     int ch;
410     coord cc;
411     DWORD count;
412     really_move_cursor();
413     ch = (program_state.done_hup)
414              ? '\033'
415              : pCheckInput(hConIn, &ir, &count, iflags.num_pad, 1, mod, &cc);
416     if (!ch) {
417         *x = cc.x;
418         *y = cc.y;
419     }
420     return ch;
421 }
422
423 static void
424 really_move_cursor()
425 {
426 #ifdef PORT_DEBUG
427     char oldtitle[BUFSZ], newtitle[BUFSZ];
428     if (display_cursor_info && wizard) {
429         oldtitle[0] = '\0';
430         if (GetConsoleTitle(oldtitle, BUFSZ)) {
431             oldtitle[39] = '\0';
432         }
433         Sprintf(newtitle, "%-55s tty=(%02d,%02d) nttty=(%02d,%02d)", oldtitle,
434                 ttyDisplay->curx, ttyDisplay->cury, console.cursor.X, console.cursor.Y);
435         (void) SetConsoleTitle(newtitle);
436     }
437 #endif
438     if (ttyDisplay) {
439         console.cursor.X = ttyDisplay->curx;
440         console.cursor.Y = ttyDisplay->cury;
441     }
442     SetConsoleCursorPosition(hConOut, console.cursor);
443 }
444
445 void
446 cmov(x, y)
447 register int x, y;
448 {
449     ttyDisplay->cury = y;
450     ttyDisplay->curx = x;
451     console.cursor.X = x;
452     console.cursor.Y = y;
453 }
454
455 void
456 nocmov(x, y)
457 int x, y;
458 {
459     console.cursor.X = x;
460     console.cursor.Y = y;
461     ttyDisplay->curx = x;
462     ttyDisplay->cury = y;
463 }
464
465 void
466 xputc(ch)
467 char ch;
468 {
469     console.cursor.X = ttyDisplay->curx;
470     console.cursor.Y = ttyDisplay->cury;
471     xputc_core(ch);
472 }
473
474 #if 1 /*JP*/
475 void
476 xputc2_core(ch1, ch2)
477 int ch1;
478 int ch2;
479 {
480     unsigned char buf[2];
481     WORD attrs[2];
482
483     buf[0] = ch1;
484     buf[1] = ch2;
485
486     attrs[0] = attrs[1] = console.attr;
487
488     WriteConsoleOutputAttribute(hConOut, (WORD *)(&attrs), 2,
489                                 console.cursor, &acount);
490     WriteConsoleOutputCharacter(hConOut, buf, 2,
491                                 console.cursor, &ccount);
492     console.cursor.X += 2;
493 }
494
495 void
496 xputc2(ch1, ch2)
497 int ch1;
498 int ch2;
499 {
500     /* wintty.c \82Å\82Í 1 \83o\83C\83g\96\88\82É curx \82ð\89Á\8eZ\82·\82é\82ª\81A\82±\82±\82Í
501        2 \83o\83C\83g\82½\82Ü\82Á\82Ä\82©\82ç\8cÄ\82Ñ\8fo\82³\82ê\82é\82Ì\82Å\81A1 \95\8e\9a\95ª\90æ\82É\90i\82ñ\82Å
502       \82µ\82Ü\82Á\82Ä\82¢\82é\81B\8f]\82Á\82Ä 1 \82ð\88ø\82­\81B */
503     console.cursor.X = ttyDisplay->curx - 1;
504     console.cursor.Y = ttyDisplay->cury;
505
506     xputc2_core(ch1, ch2);
507 }
508 #endif
509
510 void
511 xputs(s)
512 const char *s;
513 {
514     int k;
515     int slen = strlen(s);
516
517     if (ttyDisplay) {
518         console.cursor.X = ttyDisplay->curx;
519         console.cursor.Y = ttyDisplay->cury;
520     }
521
522     if (s) {
523         for (k = 0; k < slen && s[k]; ++k)
524             xputc_core(s[k]);
525     }
526 }
527
528 /* xputc_core() and g_putch() are the only
529  * two routines that actually place output
530  * on the display.
531  */
532 void
533 xputc_core(ch)
534 char ch;
535 {
536     boolean inverse = FALSE;
537     switch (ch) {
538     case '\n':
539         console.cursor.Y++;
540     /* fall through */
541     case '\r':
542         console.cursor.X = 1;
543         break;
544     case '\b':
545         console.cursor.X--;
546         break;
547     default:
548         inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse);
549         console.attr = (inverse) ?
550                         ttycolors_inv[console.current_nhcolor] :
551                         ttycolors[console.current_nhcolor];
552         if (console.current_nhattr[ATR_BOLD])
553                 console.attr |= (inverse) ?
554                                 BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
555         WriteConsoleOutputAttribute(hConOut, &console.attr, 1, console.cursor, &acount);
556         if (has_unicode) {
557             /* Avoid bug in ANSI API on WinNT */
558             WCHAR c2[2];
559             int rc;
560             rc = MultiByteToWideChar(GetConsoleOutputCP(), 0, &ch, 1, c2, 2);
561             WriteConsoleOutputCharacterW(hConOut, c2, rc, console.cursor, &ccount);
562         } else {
563             WriteConsoleOutputCharacterA(hConOut, &ch, 1, console.cursor, &ccount);
564         }
565         console.cursor.X++;
566     }
567 }
568
569 /*
570  * Overrides wintty.c function of the same name
571  * for win32. It is used for glyphs only, not text.
572  */
573
574 /* CP437 to Unicode mapping according to the Unicode Consortium */
575 static const WCHAR cp437[] = {
576     0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
577     0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
578     0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
579     0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
580     0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
581     0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
582     0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
583     0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
584     0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
585     0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
586     0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
587     0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
588     0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
589     0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
590     0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
591     0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
592     0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
593     0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
594     0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
595     0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
596     0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
597     0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
598     0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
599     0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
600     0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
601     0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
602     0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
603     0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
604     0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
605     0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
606     0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
607     0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
608 };
609
610 void
611 g_putch(in_ch)
612 int in_ch;
613 {
614     boolean inverse = FALSE;
615     unsigned char ch = (unsigned char) in_ch;
616
617     console.cursor.X = ttyDisplay->curx;
618     console.cursor.Y = ttyDisplay->cury;
619
620     inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse);
621     console.attr = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse) ?
622                     ttycolors_inv[console.current_nhcolor] :
623                     ttycolors[console.current_nhcolor];
624     if (console.current_nhattr[ATR_BOLD])
625         console.attr |= (inverse) ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
626     WriteConsoleOutputAttribute(hConOut, &console.attr, 1, console.cursor, &acount);
627
628     if (has_unicode)
629         WriteConsoleOutputCharacterW(hConOut, &cp437[ch], 1, console.cursor, &ccount);
630     else
631         WriteConsoleOutputCharacterA(hConOut, &ch, 1, console.cursor, &ccount);
632 }
633
634 void
635 cl_end()
636 {
637     int cx;
638     console.cursor.X = ttyDisplay->curx;
639     console.cursor.Y = ttyDisplay->cury;
640     cx = CO - console.cursor.X;
641     FillConsoleOutputAttribute(hConOut, DEFTEXTCOLOR, cx, console.cursor, &acount);
642     FillConsoleOutputCharacter(hConOut, ' ', cx, console.cursor, &ccount);
643     tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury);
644 }
645
646 void
647 raw_clear_screen()
648 {
649     if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
650         DWORD ccnt;
651         COORD newcoord;
652
653         newcoord.X = 0;
654         newcoord.Y = 0;
655         FillConsoleOutputAttribute(
656             hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
657             csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
658         FillConsoleOutputCharacter(
659             hConOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
660     }
661 }
662
663 void
664 clear_screen()
665 {
666     raw_clear_screen();
667     home();
668 }
669
670 void
671 home()
672 {
673     console.cursor.X = console.cursor.Y = 0;
674     ttyDisplay->curx = ttyDisplay->cury = 0;
675 }
676
677 void
678 backsp()
679 {
680     console.cursor.X = ttyDisplay->curx;
681     console.cursor.Y = ttyDisplay->cury;
682     xputc_core('\b');
683 }
684
685 void
686 cl_eos()
687 {
688     int cy = ttyDisplay->cury + 1;
689     if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
690         DWORD ccnt;
691         COORD newcoord;
692
693         newcoord.X = ttyDisplay->curx;
694         newcoord.Y = ttyDisplay->cury;
695         FillConsoleOutputAttribute(
696             hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
697             csbi.dwSize.X * csbi.dwSize.Y - cy, newcoord, &ccnt);
698         FillConsoleOutputCharacter(hConOut, ' ',
699                                    csbi.dwSize.X * csbi.dwSize.Y - cy,
700                                    newcoord, &ccnt);
701     }
702     tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury);
703 }
704
705 void
706 tty_nhbell()
707 {
708     if (flags.silent)
709         return;
710     Beep(8000, 500);
711 }
712
713 volatile int junk; /* prevent optimizer from eliminating loop below */
714
715 void
716 tty_delay_output()
717 {
718     /* delay 50 ms - uses ANSI C clock() function now */
719     clock_t goal;
720     int k;
721
722     goal = 50 + clock();
723     while (goal > clock()) {
724         k = junk; /* Do nothing */
725     }
726 }
727
728 #ifdef TEXTCOLOR
729 /*
730  * CLR_BLACK            0
731  * CLR_RED              1
732  * CLR_GREEN            2
733  * CLR_BROWN            3       low-intensity yellow
734  * CLR_BLUE             4
735  * CLR_MAGENTA          5
736  * CLR_CYAN             6
737  * CLR_GRAY             7       low-intensity white
738  * NO_COLOR             8
739  * CLR_ORANGE           9
740  * CLR_BRIGHT_GREEN     10
741  * CLR_YELLOW           11
742  * CLR_BRIGHT_BLUE      12
743  * CLR_BRIGHT_MAGENTA   13
744  * CLR_BRIGHT_CYAN      14
745  * CLR_WHITE            15
746  * CLR_MAX              16
747  * BRIGHT               8
748  */
749
750 static void
751 init_ttycolor()
752 {
753 #ifdef TEXTCOLOR
754     ttycolors[CLR_BLACK]        = FOREGROUND_INTENSITY; /* fix by Quietust */
755     ttycolors[CLR_RED]          = FOREGROUND_RED;
756     ttycolors[CLR_GREEN]        = FOREGROUND_GREEN;
757     ttycolors[CLR_BROWN]        = FOREGROUND_GREEN | FOREGROUND_RED;
758     ttycolors[CLR_BLUE]         = FOREGROUND_BLUE;
759     ttycolors[CLR_MAGENTA]      = FOREGROUND_BLUE | FOREGROUND_RED;
760     ttycolors[CLR_CYAN]         = FOREGROUND_GREEN | FOREGROUND_BLUE;
761     ttycolors[CLR_GRAY]         = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE;
762     ttycolors[NO_COLOR]         = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
763     ttycolors[CLR_ORANGE]       = FOREGROUND_RED | FOREGROUND_INTENSITY;
764     ttycolors[CLR_BRIGHT_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
765     ttycolors[CLR_YELLOW]       = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
766     ttycolors[CLR_BRIGHT_BLUE]  = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
767     ttycolors[CLR_BRIGHT_MAGENTA]=FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
768     ttycolors[CLR_BRIGHT_CYAN]  = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
769     ttycolors[CLR_WHITE]        = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED
770                                     | FOREGROUND_INTENSITY;
771
772     ttycolors_inv[CLR_BLACK]       = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
773                                         | BACKGROUND_INTENSITY;
774     ttycolors_inv[CLR_RED]         = BACKGROUND_RED | BACKGROUND_INTENSITY;
775     ttycolors_inv[CLR_GREEN]       = BACKGROUND_GREEN;
776     ttycolors_inv[CLR_BROWN]       = BACKGROUND_GREEN | BACKGROUND_RED;
777     ttycolors_inv[CLR_BLUE]        = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
778     ttycolors_inv[CLR_MAGENTA]     = BACKGROUND_BLUE | BACKGROUND_RED;
779     ttycolors_inv[CLR_CYAN]        = BACKGROUND_GREEN | BACKGROUND_BLUE;
780     ttycolors_inv[CLR_GRAY]        = BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE;
781     ttycolors_inv[NO_COLOR]        = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
782     ttycolors_inv[CLR_ORANGE]      = BACKGROUND_RED | BACKGROUND_INTENSITY;
783     ttycolors_inv[CLR_BRIGHT_GREEN]= BACKGROUND_GREEN | BACKGROUND_INTENSITY;
784     ttycolors_inv[CLR_YELLOW]      = BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
785     ttycolors_inv[CLR_BRIGHT_BLUE] = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
786     ttycolors_inv[CLR_BRIGHT_MAGENTA] =BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY;
787     ttycolors_inv[CLR_BRIGHT_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
788     ttycolors_inv[CLR_WHITE]       = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
789                                        | BACKGROUND_INTENSITY;
790 #else
791     int k;
792     ttycolors[0] = FOREGROUND_INTENSITY;
793     ttycolors_inv[0] = BACKGROUND_INTENSITY;
794     for (k = 1; k < SIZE(ttycolors); ++k) {
795         ttycolors[k] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
796         ttycolors_inv[k] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
797     }
798 #endif
799     init_ttycolor_completed = TRUE;
800 }
801 #endif /* TEXTCOLOR */
802
803 int
804 has_color(int color)
805 {
806 #ifdef TEXTCOLOR
807     if ((color >= 0) && (color < CLR_MAX))
808         return 1;
809 #else
810     if ((color == CLR_BLACK) || (color == CLR_WHITE) || (color == NO_COLOR))
811         return 1;
812 #endif
813     else
814         return 0;
815 }
816
817 void
818 term_start_attr(int attrib)
819 {
820     console.current_nhattr[attrib] = TRUE;
821     if (attrib) console.current_nhattr[ATR_NONE] = FALSE;
822 }
823
824 void
825 term_end_attr(int attrib)
826 {
827     int k;
828
829     switch (attrib) {
830     case ATR_INVERSE:
831     case ATR_ULINE:
832     case ATR_BLINK:
833     case ATR_BOLD:
834         break;
835     }
836     console.current_nhattr[attrib] = FALSE;
837     console.current_nhattr[ATR_NONE] = TRUE;
838     /* re-evaluate all attr now for performance at output time */
839     for (k=ATR_NONE; k <= ATR_INVERSE; ++k) {
840         if (console.current_nhattr[k])
841             console.current_nhattr[ATR_NONE] = FALSE;
842     }
843 }
844
845 void
846 term_end_raw_bold(void)
847 {
848     term_end_attr(ATR_BOLD);
849 }
850
851 void
852 term_start_raw_bold(void)
853 {
854     term_start_attr(ATR_BOLD);
855 }
856
857 void
858 term_start_color(int color)
859 {
860 #ifdef TEXTCOLOR
861     if (color >= 0 && color < CLR_MAX) {
862         console.current_nhcolor = color;
863     } else
864 #endif
865     console.current_nhcolor = NO_COLOR;
866 }
867
868 void
869 term_end_color(void)
870 {
871 #ifdef TEXTCOLOR
872     console.foreground = DEFTEXTCOLOR;
873 #endif
874     console.attr = (console.foreground | console.background);
875     console.current_nhcolor = NO_COLOR;
876 }
877
878 void
879 standoutbeg()
880 {
881     term_start_attr(ATR_BOLD);
882 }
883
884 void
885 standoutend()
886 {
887     term_end_attr(ATR_BOLD);
888 }
889
890 #ifndef NO_MOUSE_ALLOWED
891 void
892 toggle_mouse_support()
893 {
894     DWORD cmode;
895     GetConsoleMode(hConIn, &cmode);
896     if (iflags.wc_mouse_support)
897         cmode |= ENABLE_MOUSE_INPUT;
898     else
899         cmode &= ~ENABLE_MOUSE_INPUT;
900     SetConsoleMode(hConIn, cmode);
901 }
902 #endif
903
904 /* handle tty options updates here */
905 void
906 nttty_preference_update(pref)
907 const char *pref;
908 {
909     if (stricmp(pref, "mouse_support") == 0) {
910 #ifndef NO_MOUSE_ALLOWED
911         toggle_mouse_support();
912 #endif
913     }
914     if (stricmp(pref, "symset") == 0)
915         check_and_set_font();
916     return;
917 }
918
919 #ifdef PORT_DEBUG
920 void
921 win32con_debug_keystrokes()
922 {
923     DWORD count;
924     boolean valid = 0;
925     int ch;
926     xputs("\n");
927     while (!valid || ch != 27) {
928         nocmov(ttyDisplay->curx, ttyDisplay->cury);
929         ReadConsoleInput(hConIn, &ir, 1, &count);
930         if ((ir.EventType == KEY_EVENT) && ir.Event.KeyEvent.bKeyDown)
931             ch = process_keystroke(&ir, &valid, iflags.num_pad, 1);
932     }
933     (void) doredraw();
934 }
935 void
936 win32con_handler_info()
937 {
938     char *buf;
939     int ci;
940     if (!pSourceAuthor && !pSourceWhere)
941         pline("Keyboard handler source info and author unavailable.");
942     else {
943         if (pKeyHandlerName && pKeyHandlerName(&buf, 1)) {
944             xputs("\n");
945             xputs("Keystroke handler loaded: \n    ");
946             xputs(buf);
947         }
948         if (pSourceAuthor && pSourceAuthor(&buf)) {
949             xputs("\n");
950             xputs("Keystroke handler Author: \n    ");
951             xputs(buf);
952         }
953         if (pSourceWhere && pSourceWhere(&buf)) {
954             xputs("\n");
955             xputs("Keystroke handler source code available at:\n    ");
956             xputs(buf);
957         }
958         xputs("\nPress any key to resume.");
959         ci = nhgetch();
960         (void) doredraw();
961     }
962 }
963
964 void
965 win32con_toggle_cursor_info()
966 {
967     display_cursor_info = !display_cursor_info;
968 }
969 #endif
970
971 void
972 map_subkeyvalue(op)
973 register char *op;
974 {
975     char digits[] = "0123456789";
976     int length, i, idx, val;
977     char *kp;
978
979     idx = -1;
980     val = -1;
981     kp = index(op, '/');
982     if (kp) {
983         *kp = '\0';
984         kp++;
985         length = strlen(kp);
986         if (length < 1 || length > 3)
987             return;
988         for (i = 0; i < length; i++)
989             if (!index(digits, kp[i]))
990                 return;
991         val = atoi(kp);
992         length = strlen(op);
993         if (length < 1 || length > 3)
994             return;
995         for (i = 0; i < length; i++)
996             if (!index(digits, op[i]))
997                 return;
998         idx = atoi(op);
999     }
1000     if (idx >= MAX_OVERRIDES || idx < 0 || val >= MAX_OVERRIDES || val < 1)
1001         return;
1002     key_overrides[idx] = val;
1003 }
1004
1005 void
1006 load_keyboard_handler()
1007 {
1008     char suffx[] = ".dll";
1009     char *truncspot;
1010 #define MAX_DLLNAME 25
1011     char kh[MAX_ALTKEYHANDLER];
1012     if (iflags.altkeyhandler[0]) {
1013         if (hLibrary) { /* already one loaded apparently */
1014             FreeLibrary(hLibrary);
1015             hLibrary = (HANDLE) 0;
1016             pNHkbhit = (NHKBHIT) 0;
1017             pCheckInput = (CHECKINPUT) 0;
1018             pSourceWhere = (SOURCEWHERE) 0;
1019             pSourceAuthor = (SOURCEAUTHOR) 0;
1020             pKeyHandlerName = (KEYHANDLERNAME) 0;
1021             pProcessKeystroke = (PROCESS_KEYSTROKE) 0;
1022         }
1023         if ((truncspot = strstri(iflags.altkeyhandler, suffx)) != 0)
1024             *truncspot = '\0';
1025         (void) strncpy(kh, iflags.altkeyhandler,
1026                        (MAX_ALTKEYHANDLER - sizeof suffx) - 1);
1027         kh[(MAX_ALTKEYHANDLER - sizeof suffx) - 1] = '\0';
1028         Strcat(kh, suffx);
1029         Strcpy(iflags.altkeyhandler, kh);
1030         hLibrary = LoadLibrary(kh);
1031         if (hLibrary) {
1032             pProcessKeystroke = (PROCESS_KEYSTROKE) GetProcAddress(
1033                 hLibrary, TEXT("ProcessKeystroke"));
1034             pNHkbhit = (NHKBHIT) GetProcAddress(hLibrary, TEXT("NHkbhit"));
1035             pCheckInput =
1036                 (CHECKINPUT) GetProcAddress(hLibrary, TEXT("CheckInput"));
1037             pSourceWhere =
1038                 (SOURCEWHERE) GetProcAddress(hLibrary, TEXT("SourceWhere"));
1039             pSourceAuthor =
1040                 (SOURCEAUTHOR) GetProcAddress(hLibrary, TEXT("SourceAuthor"));
1041             pKeyHandlerName = (KEYHANDLERNAME) GetProcAddress(
1042                 hLibrary, TEXT("KeyHandlerName"));
1043         }
1044     }
1045     if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) {
1046         if (hLibrary) {
1047             FreeLibrary(hLibrary);
1048             hLibrary = (HANDLE) 0;
1049             pNHkbhit = (NHKBHIT) 0;
1050             pCheckInput = (CHECKINPUT) 0;
1051             pSourceWhere = (SOURCEWHERE) 0;
1052             pSourceAuthor = (SOURCEAUTHOR) 0;
1053             pKeyHandlerName = (KEYHANDLERNAME) 0;
1054             pProcessKeystroke = (PROCESS_KEYSTROKE) 0;
1055         }
1056         (void) strncpy(kh, "nhdefkey.dll",
1057                        (MAX_ALTKEYHANDLER - sizeof suffx) - 1);
1058         kh[(MAX_ALTKEYHANDLER - sizeof suffx) - 1] = '\0';
1059         Strcpy(iflags.altkeyhandler, kh);
1060         hLibrary = LoadLibrary(kh);
1061         if (hLibrary) {
1062             pProcessKeystroke = (PROCESS_KEYSTROKE) GetProcAddress(
1063                 hLibrary, TEXT("ProcessKeystroke"));
1064             pCheckInput =
1065                 (CHECKINPUT) GetProcAddress(hLibrary, TEXT("CheckInput"));
1066             pNHkbhit = (NHKBHIT) GetProcAddress(hLibrary, TEXT("NHkbhit"));
1067             pSourceWhere =
1068                 (SOURCEWHERE) GetProcAddress(hLibrary, TEXT("SourceWhere"));
1069             pSourceAuthor =
1070                 (SOURCEAUTHOR) GetProcAddress(hLibrary, TEXT("SourceAuthor"));
1071             pKeyHandlerName = (KEYHANDLERNAME) GetProcAddress(
1072                 hLibrary, TEXT("KeyHandlerName"));
1073         }
1074     }
1075     if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) {
1076         if (!hLibrary)
1077             raw_printf("\nNetHack was unable to load keystroke handler.\n");
1078         else {
1079             FreeLibrary(hLibrary);
1080             hLibrary = (HANDLE) 0;
1081             raw_printf("\nNetHack keystroke handler is invalid.\n");
1082         }
1083         exit(EXIT_FAILURE);
1084     }
1085 }
1086
1087 /* this is used as a printf() replacement when the window
1088  * system isn't initialized yet
1089  */
1090 void msmsg
1091 VA_DECL(const char *, fmt)
1092 {
1093     char buf[ROWNO * COLNO]; /* worst case scenario */
1094     VA_START(fmt);
1095     VA_INIT(fmt, const char *);
1096     Vsprintf(buf, fmt, VA_ARGS);
1097     if (redirect_stdout)
1098         fprintf(stdout, "%s", buf);
1099     else {
1100         if(!init_ttycolor_completed)
1101             init_ttycolor();
1102
1103 #if 0 /*JP*/
1104         xputs(buf);
1105 #else
1106         if(ttyDisplay){
1107             console.cursor.X = ttyDisplay->curx;
1108             console.cursor.Y = ttyDisplay->cury;
1109         }
1110         {
1111             char *str = buf;
1112             while(*str){
1113                 jbuffer(*(str++), NULL, xputc_core, xputc2_core);
1114             }
1115         }
1116 #endif
1117         if (ttyDisplay)
1118             curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y);
1119     }
1120     VA_END();
1121     return;
1122 }
1123
1124 /* fatal error */
1125 /*VARARGS1*/
1126 void nttty_error
1127 VA_DECL(const char *, s)
1128 {
1129     char buf[BUFSZ];
1130     VA_START(s);
1131     VA_INIT(s, const char *);
1132     /* error() may get called before tty is initialized */
1133     if (iflags.window_inited)
1134         end_screen();
1135     buf[0] = '\n';
1136     (void) vsprintf(&buf[1], s, VA_ARGS);
1137     msmsg(buf);
1138     really_move_cursor();
1139     VA_END();
1140     exit(EXIT_FAILURE);
1141 }
1142
1143 void
1144 synch_cursor()
1145 {
1146     really_move_cursor();
1147 }
1148
1149 #ifdef CHANGE_COLOR
1150 void
1151 tty_change_color(color_number, rgb, reverse)
1152 int color_number, reverse;
1153 long rgb;
1154 {
1155     /* Map NetHack color index to NT Console palette index */
1156     int idx, win32_color_number[] = {
1157         0,  /* CLR_BLACK           0 */
1158         4,  /* CLR_RED             1 */
1159         2,  /* CLR_GREEN           2 */
1160         6,  /* CLR_BROWN           3 */
1161         1,  /* CLR_BLUE            4 */
1162         5,  /* CLR_MAGENTA         5 */
1163         3,  /* CLR_CYAN            6 */
1164         7,  /* CLR_GRAY            7 */
1165         8,  /* NO_COLOR            8 */
1166         12, /* CLR_ORANGE          9 */
1167         10, /* CLR_BRIGHT_GREEN   10 */
1168         14, /* CLR_YELLOW         11 */
1169         9,  /* CLR_BRIGHT_BLUE    12 */
1170         13, /* CLR_BRIGHT_MAGENTA 13 */
1171         11, /* CLR_BRIGHT_CYAN    14 */
1172         15  /* CLR_WHITE          15 */
1173     };
1174     int k;
1175     if (color_number < 0) { /* indicates OPTIONS=palette with no value */
1176         /* copy the NetHack palette into UserDefinedColors */
1177         for (k = 0; k < CLR_MAX; k++)
1178             UserDefinedColors[k] = NetHackColors[k];
1179     } else if (color_number >= 0 && color_number < CLR_MAX) {
1180         if (!altered_palette) {
1181             /* make sure a full suite is available */
1182             for (k = 0; k < CLR_MAX; k++)
1183                 UserDefinedColors[k] = DefaultColors[k];
1184         }
1185         idx = win32_color_number[color_number];
1186         UserDefinedColors[idx] = rgb;
1187     }
1188     altered_palette = TRUE;
1189 }
1190
1191 char *
1192 tty_get_color_string()
1193 {
1194     return "";
1195 }
1196
1197 int
1198 match_color_name(c)
1199 const char *c;
1200 {
1201     const struct others {
1202         int idx;
1203         const char *colorname;
1204     } othernames[] = {
1205         { CLR_MAGENTA, "purple" },
1206         { CLR_BRIGHT_MAGENTA, "bright purple" },
1207         { NO_COLOR, "dark gray" },
1208         { NO_COLOR, "dark grey" },
1209         { CLR_GRAY, "grey" },
1210     };
1211
1212     int cnt;
1213     for (cnt = 0; cnt < CLR_MAX; ++cnt) {
1214         if (!strcmpi(c, c_obj_colors[cnt]))
1215             return cnt;
1216     }
1217     for (cnt = 0; cnt < SIZE(othernames); ++cnt) {
1218         if (!strcmpi(c, othernames[cnt].colorname))
1219             return othernames[cnt].idx;
1220     }
1221     return -1;
1222 }
1223
1224 /*
1225  * Returns 0 if badoption syntax
1226  */
1227 int
1228 alternative_palette(op)
1229 char *op;
1230 {
1231     /*
1232      *  palette:color-R-G-B
1233      *  OPTIONS=palette:green-4-3-1, palette:0-0-0-0
1234      */
1235     int fieldcnt, color_number, rgb, red, green, blue;
1236     char *fields[4], *cp;
1237
1238     if (!op) {
1239         change_color(-1, 0, 0); /* indicates palette option with
1240                                    no value meaning "load an entire
1241                                    hard-coded NetHack palette." */
1242         return 1;
1243     }
1244
1245     cp = fields[0] = op;
1246     for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) {
1247         cp = index(cp, '-');
1248         if (!cp)
1249             return 0;
1250         fields[fieldcnt] = cp;
1251         cp++;
1252     }
1253     for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) {
1254         *(fields[fieldcnt]) = '\0';
1255         ++fields[fieldcnt];
1256     }
1257     rgb = 0;
1258     for (fieldcnt = 0; fieldcnt < 4; ++fieldcnt) {
1259         if (fieldcnt == 0 && isalpha(*(fields[0]))) {
1260             color_number = match_color_name(fields[0]);
1261             if (color_number == -1)
1262                 return 0;
1263         } else {
1264             int dcount = 0, cval = 0;
1265             cp = fields[fieldcnt];
1266             if (*cp == '\\' && index("0123456789xXoO", cp[1])) {
1267                 const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
1268
1269                 cp++;
1270                 if (*cp == 'x' || *cp == 'X')
1271                     for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
1272                         cval = (int) ((cval * 16) + (dp - hex) / 2);
1273                 else if (*cp == 'o' || *cp == 'O')
1274                     for (++cp; (index("01234567", *cp)) && (dcount++ < 3);
1275                          cp++)
1276                         cval = (cval * 8) + (*cp - '0');
1277                 else
1278                     return 0;
1279             } else {
1280                 for (; *cp && (index("0123456789", *cp)) && (dcount++ < 3);
1281                      cp++)
1282                     cval = (cval * 10) + (*cp - '0');
1283             }
1284             switch (fieldcnt) {
1285             case 0:
1286                 color_number = cval;
1287                 break;
1288             case 1:
1289                 red = cval;
1290                 break;
1291             case 2:
1292                 green = cval;
1293                 break;
1294             case 3:
1295                 blue = cval;
1296                 break;
1297             }
1298         }
1299     }
1300     rgb = RGB(red, green, blue);
1301     if (color_number >= 0 && color_number < CLR_MAX)
1302         change_color(color_number, rgb, 0);
1303     return 1;
1304 }
1305
1306 /*
1307  *  This uses an undocumented method to set console attributes
1308  *  at runtime including console palette
1309  *
1310  *      VOID WINAPI SetConsolePalette(COLORREF palette[16])
1311  *
1312  *  Author: James Brown at www.catch22.net
1313  *
1314  *  Set palette of current console.
1315  *  Palette should be of the form:
1316  *
1317  *      COLORREF DefaultColors[CLR_MAX] =
1318  *      {
1319  *              0x00000000, 0x00800000, 0x00008000, 0x00808000,
1320  *              0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0,
1321  *              0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00,
1322  *              0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
1323  *       };
1324  */
1325
1326 #pragma pack(push, 1)
1327
1328 /*
1329  *      Structure to send console via WM_SETCONSOLEINFO
1330  */
1331 typedef struct _CONSOLE_INFO {
1332     ULONG Length;
1333     COORD ScreenBufferSize;
1334     COORD WindowSize;
1335     ULONG WindowPosX;
1336     ULONG WindowPosY;
1337
1338     COORD FontSize;
1339     ULONG FontFamily;
1340     ULONG FontWeight;
1341     WCHAR FaceName[32];
1342
1343     ULONG CursorSize;
1344     ULONG FullScreen;
1345     ULONG QuickEdit;
1346     ULONG AutoPosition;
1347     ULONG InsertMode;
1348
1349     USHORT ScreenColors;
1350     USHORT PopupColors;
1351     ULONG HistoryNoDup;
1352     ULONG HistoryBufferSize;
1353     ULONG NumberOfHistoryBuffers;
1354
1355     COLORREF ColorTable[16];
1356
1357     ULONG CodePage;
1358     HWND Hwnd;
1359
1360     WCHAR ConsoleTitle[0x100];
1361 } CONSOLE_INFO;
1362
1363 #pragma pack(pop)
1364
1365 BOOL SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci);
1366 static void GetConsoleSizeInfo(CONSOLE_INFO *pci);
1367 VOID WINAPI SetConsolePalette(COLORREF crPalette[16]);
1368
1369 void
1370 adjust_palette(VOID_ARGS)
1371 {
1372     SetConsolePalette(UserDefinedColors);
1373     altered_palette = 0;
1374 }
1375
1376 /*
1377 /* only in Win2k+  (use FindWindow for NT4) */
1378 /* HWND WINAPI GetConsoleWindow(); */
1379
1380 /*  Undocumented console message */
1381 #define WM_SETCONSOLEINFO (WM_USER + 201)
1382
1383 VOID WINAPI
1384 SetConsolePalette(COLORREF palette[16])
1385 {
1386     CONSOLE_INFO ci = { sizeof(ci) };
1387     int i;
1388     HWND hwndConsole = GetConsoleHandle();
1389
1390     /* get current size/position settings rather than using defaults.. */
1391     GetConsoleSizeInfo(&ci);
1392
1393     /* set these to zero to keep current settings */
1394     ci.FontSize.X = 0; /* def = 8  */
1395     ci.FontSize.Y = 0; /* def = 12 */
1396     ci.FontFamily = 0; /* def = 0x30 = FF_MODERN|FIXED_PITCH */
1397     ci.FontWeight = 0; /* 0x400;   */
1398     /* lstrcpyW(ci.FaceName, L"Terminal"); */
1399     ci.FaceName[0] = L'\0';
1400
1401     ci.CursorSize = 25;
1402     ci.FullScreen = FALSE;
1403     ci.QuickEdit = TRUE;
1404     ci.AutoPosition = 0x10000;
1405     ci.InsertMode = TRUE;
1406     ci.ScreenColors = MAKEWORD(0x7, 0x0);
1407     ci.PopupColors = MAKEWORD(0x5, 0xf);
1408
1409     ci.HistoryNoDup = FALSE;
1410     ci.HistoryBufferSize = 50;
1411     ci.NumberOfHistoryBuffers = 4;
1412
1413     // colour table
1414     for (i = 0; i < 16; i++)
1415         ci.ColorTable[i] = palette[i];
1416
1417     ci.CodePage = GetConsoleOutputCP();
1418     ci.Hwnd = hwndConsole;
1419
1420     lstrcpyW(ci.ConsoleTitle, L"");
1421
1422     SetConsoleInfo(hwndConsole, &ci);
1423 }
1424
1425 /*
1426  *  Wrapper around WM_SETCONSOLEINFO. We need to create the
1427  *  necessary section (file-mapping) object in the context of the
1428  *  process which owns the console, before posting the message
1429  */
1430 BOOL
1431 SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci)
1432 {
1433     DWORD dwConsoleOwnerPid;
1434     HANDLE hProcess;
1435     HANDLE hSection, hDupSection;
1436     PVOID ptrView = 0;
1437     HANDLE hThread;
1438
1439     /*
1440      *  Open the process which "owns" the console
1441      */
1442     GetWindowThreadProcessId(hwndConsole, &dwConsoleOwnerPid);
1443     hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwConsoleOwnerPid);
1444
1445     /*
1446      * Create a SECTION object backed by page-file, then map a view of
1447      * this section into the owner process so we can write the contents
1448      * of the CONSOLE_INFO buffer into it
1449      */
1450     hSection = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0,
1451                                  pci->Length, 0);
1452
1453     /*
1454      *  Copy our console structure into the section-object
1455      */
1456     ptrView = MapViewOfFile(hSection, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0,
1457                             pci->Length);
1458     memcpy(ptrView, pci, pci->Length);
1459     UnmapViewOfFile(ptrView);
1460
1461     /*
1462      *  Map the memory into owner process
1463      */
1464     DuplicateHandle(GetCurrentProcess(), hSection, hProcess, &hDupSection, 0,
1465                     FALSE, DUPLICATE_SAME_ACCESS);
1466
1467     /*  Send console window the "update" message */
1468     SendMessage(hwndConsole, WM_SETCONSOLEINFO, (WPARAM) hDupSection, 0);
1469
1470     /*
1471      * clean up
1472      */
1473     hThread = CreateRemoteThread(hProcess, 0, 0,
1474                                  (LPTHREAD_START_ROUTINE) CloseHandle,
1475                                  hDupSection, 0, 0);
1476
1477     CloseHandle(hThread);
1478     CloseHandle(hSection);
1479     CloseHandle(hProcess);
1480
1481     return TRUE;
1482 }
1483
1484 /*
1485  *  Fill the CONSOLE_INFO structure with information
1486  *  about the current console window
1487  */
1488 static void
1489 GetConsoleSizeInfo(CONSOLE_INFO *pci)
1490 {
1491     CONSOLE_SCREEN_BUFFER_INFO csbi;
1492
1493     HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
1494
1495     GetConsoleScreenBufferInfo(hConsoleOut, &csbi);
1496
1497     pci->ScreenBufferSize = csbi.dwSize;
1498     pci->WindowSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
1499     pci->WindowSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
1500     pci->WindowPosX = csbi.srWindow.Left;
1501     pci->WindowPosY = csbi.srWindow.Top;
1502 }
1503
1504 static HWND
1505 GetConsoleHandle(void)
1506 {
1507     HMODULE hMod = GetModuleHandle("kernel32.dll");
1508     GETCONSOLEWINDOW pfnGetConsoleWindow =
1509         (GETCONSOLEWINDOW) GetProcAddress(hMod, "GetConsoleWindow");
1510     if (pfnGetConsoleWindow)
1511         return pfnGetConsoleWindow();
1512     else
1513         return GetConsoleHwnd();
1514 }
1515
1516 static HWND
1517 GetConsoleHwnd(void)
1518 {
1519     int iterations = 0;
1520     HWND hwndFound = 0;
1521     char OldTitle[1024], NewTitle[1024], TestTitle[1024];
1522
1523     /* Get current window title */
1524     GetConsoleTitle(OldTitle, sizeof OldTitle);
1525
1526     (void) sprintf(NewTitle, "NETHACK%d/%d", GetTickCount(),
1527                    GetCurrentProcessId());
1528     SetConsoleTitle(NewTitle);
1529
1530     GetConsoleTitle(TestTitle, sizeof TestTitle);
1531     while (strcmp(TestTitle, NewTitle) != 0) {
1532         iterations++;
1533         /* sleep(0); */
1534         GetConsoleTitle(TestTitle, sizeof TestTitle);
1535     }
1536     hwndFound = FindWindow(NULL, NewTitle);
1537     SetConsoleTitle(OldTitle);
1538     /*       printf("%d iterations\n", iterations); */
1539     return hwndFound;
1540 }
1541 #endif /*CHANGE_COLOR*/
1542
1543 static int CALLBACK EnumFontCallback(
1544     const LOGFONTW * lf, const TEXTMETRICW * tm, DWORD fontType, LPARAM lParam)
1545 {
1546     LOGFONTW * lf_ptr = (LOGFONTW *) lParam;
1547     *lf_ptr = *lf;
1548     return 0;
1549 }
1550
1551 /* check_and_set_font ensures that the current font will render the symbols
1552  * that are currently being used correctly.  If they will not be rendered
1553  * correctly, then it will change the font to a known good font.
1554  */
1555 void
1556 check_and_set_font()
1557 {
1558     if (!check_font_widths()) {
1559         raw_print("WARNING: glyphs too wide in console font."
1560                   "  Changing code page to 437 and font to Consolas\n");
1561         set_known_good_console_font();
1562     }
1563 }
1564
1565 /* check_font_widths returns TRUE if all glyphs in current console font
1566  * fit within the width of a single console cell.
1567  */
1568 boolean
1569 check_font_widths()
1570 {
1571     CONSOLE_FONT_INFOEX console_font_info;
1572     console_font_info.cbSize = sizeof(console_font_info);
1573     BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE,
1574                                             &console_font_info);
1575
1576     /* get console window and DC
1577      * NOTE: the DC from the console window does not have the correct
1578      *       font selected at this point.
1579      */
1580     HWND hWnd = GetConsoleWindow();
1581     HDC hDC = GetDC(hWnd);
1582
1583     LOGFONTW logical_font;
1584     logical_font.lfCharSet = DEFAULT_CHARSET;
1585     wcscpy(logical_font.lfFaceName, console_font_info.FaceName);
1586     logical_font.lfPitchAndFamily = 0;
1587
1588     /* getting matching console font */
1589     LOGFONTW matching_log_font = { 0 };
1590     EnumFontFamiliesExW(hDC, &logical_font, EnumFontCallback,
1591                                         (LPARAM) &matching_log_font, 0);
1592
1593     if (matching_log_font.lfHeight == 0) {
1594         raw_print("Unable to enumerate system fonts\n");
1595         return FALSE;
1596     }
1597
1598     /* create font matching console font */
1599     LOGFONTW console_font_log_font = matching_log_font;
1600     console_font_log_font.lfWeight = console_font_info.FontWeight;
1601     console_font_log_font.lfHeight = console_font_info.dwFontSize.Y;
1602     console_font_log_font.lfWidth = 0;
1603     HFONT console_font = CreateFontIndirectW(&console_font_log_font);
1604
1605     if (console_font == NULL) {
1606         raw_print("Unable to create console font\n");
1607         return FALSE;
1608     }
1609
1610     /* select font */
1611     HGDIOBJ saved_font = SelectObject(hDC, console_font);
1612
1613     /* determine whether it is a true type font */
1614     TEXTMETRICA tm;
1615     success = GetTextMetricsA(hDC, &tm);
1616
1617     if (!success) {
1618         raw_print("Unable to get console font text metrics\n");
1619         goto clean_up;
1620     }
1621
1622     boolean isTrueType = (tm.tmPitchAndFamily & TMPF_TRUETYPE) != 0;
1623
1624     /* determine which glyphs are used */
1625     boolean used[256];
1626     memset(used, 0, sizeof(used));
1627     for (int i = 0; i < SYM_MAX; i++) {
1628         used[l_syms[i]] = TRUE;
1629         used[r_syms[i]] = TRUE;
1630     }
1631
1632     int wcUsedCount = 0;
1633     wchar_t wcUsed[256];
1634     for (int i = 0; i < sizeof(used); i++)
1635         if (used[i])
1636             wcUsed[wcUsedCount++] = cp437[i];
1637
1638     /* measure the set of used glyphs to ensure they fit */
1639     boolean all_glyphs_fit = TRUE;
1640
1641     for (int i = 0; i < wcUsedCount; i++) {
1642         int width;
1643         if (isTrueType) {
1644             ABC abc;
1645             success = GetCharABCWidthsW(hDC, wcUsed[i], wcUsed[i], &abc);
1646             width = abc.abcA + abc.abcB + abc.abcC;
1647         } else {
1648             success = GetCharWidthW(hDC, wcUsed[i], wcUsed[i], &width);
1649         }
1650
1651         if (success && width > console_font_info.dwFontSize.X) {
1652             all_glyphs_fit = FALSE;
1653             break;
1654         }
1655     }
1656
1657 clean_up:
1658
1659     SelectObject(hDC, saved_font);
1660     DeleteObject(console_font);
1661
1662     return all_glyphs_fit;
1663 }
1664
1665 /* set_known_good_console_font sets the code page and font used by the console
1666  * to settings know to work well with NetHack.  It also saves the original
1667  * settings so that they can be restored prior to NetHack exit.
1668  */
1669 void
1670 set_known_good_console_font()
1671 {
1672     CONSOLE_FONT_INFOEX console_font_info;
1673     console_font_info.cbSize = sizeof(console_font_info);
1674     BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE,
1675                                             &console_font_info);
1676
1677     console_font_changed = TRUE;
1678     original_console_font_info = console_font_info;
1679     original_console_code_page = GetConsoleOutputCP();
1680
1681     wcscpy_s(console_font_info.FaceName,
1682         sizeof(console_font_info.FaceName)
1683             / sizeof(console_font_info.FaceName[0]),
1684         L"Consolas");
1685
1686     success = SetConsoleOutputCP(437);
1687     if (!success)
1688         raw_print("Unable to set console code page to 437\n");
1689
1690     success = SetCurrentConsoleFontEx(hConOut, FALSE, &console_font_info);
1691     if (!success)
1692         raw_print("Unable to set console font to Consolas\n");
1693 }
1694
1695 /* restore_original_console_font will restore the console font and code page
1696  * settings to what they were when NetHack was launched.
1697  */
1698 void
1699 restore_original_console_font()
1700 {
1701     if (console_font_changed) {
1702         BOOL success;
1703         raw_print("Restoring original font and code page\n");
1704         success = SetConsoleOutputCP(original_console_code_page);
1705         if (!success)
1706             raw_print("Unable to restore original code page\n");
1707
1708         success = SetCurrentConsoleFontEx(hConOut, FALSE,
1709                                             &original_console_font_info);
1710         if (!success)
1711             raw_print("Unable to restore original font\n");
1712
1713         console_font_changed = FALSE;
1714     }
1715 }
1716
1717 #endif /* WIN32 */