1 /* NetHack 3.6 nttty.c $NHDT-Date: 1554215932 2019/04/02 14:38:52 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.99 $ */
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 * Use of back buffer to improve performance B. House 2018/05/06
13 *
14 */
15
16 #ifdef WIN32
17 #define NEED_VARARGS /* Uses ... */
18 #include "win32api.h"
19 #include "winos.h"
20 #include "hack.h"
21 #include "wintty.h"
22 #include <sys\types.h>
23 #include <sys\stat.h>
24
25 extern boolean getreturn_enabled; /* from sys/share/pcsys.c */
26 extern int redirect_stdout;
27
28 #ifdef TTY_GRAPHICS
29 /*
30 * Console Buffer Flipping Support
31 *
32 * To minimize the number of calls into the WriteConsoleOutputXXX methods,
33 * we implement a notion of a console back buffer which keeps the next frame
34 * of console output as it is being composed. When ready to show the new
35 * frame, we compare this next frame to what is currently being output and
36 * only call WriteConsoleOutputXXX for those console values that need to
37 * change.
38 *
39 */
40
41 #define CONSOLE_CLEAR_ATTRIBUTE (FOREGROUND_RED | FOREGROUND_GREEN \
42 | FOREGROUND_BLUE)
43 #define CONSOLE_CLEAR_CHARACTER (' ')
44
45 #define CONSOLE_UNDEFINED_ATTRIBUTE (0)
46 #define CONSOLE_UNDEFINED_CHARACTER ('\0')
47
48 typedef struct {
49 WCHAR character;
50 WORD attribute;
51 } cell_t;
52
53 cell_t clear_cell = { CONSOLE_CLEAR_CHARACTER, CONSOLE_CLEAR_ATTRIBUTE };
54 cell_t undefined_cell = { CONSOLE_UNDEFINED_CHARACTER,
55 CONSOLE_UNDEFINED_ATTRIBUTE };
56
57 /*
58 * The following WIN32 Console API routines are used in this file.
59 *
60 * CreateFile
61 * GetConsoleScreenBufferInfo
62 * GetStdHandle
63 * SetConsoleCursorPosition
64 * SetConsoleTextAttribute
65 * SetConsoleCtrlHandler
66 * PeekConsoleInput
67 * ReadConsoleInput
68 * WriteConsoleOutputCharacter
69 * FillConsoleOutputAttribute
70 * GetConsoleOutputCP
71 */
72
73 static BOOL FDECL(CtrlHandler, (DWORD));
74 static void FDECL(xputc_core, (char));
75 void FDECL(cmov, (int, int));
76 void FDECL(nocmov, (int, int));
77 int FDECL(process_keystroke,
78 (INPUT_RECORD *, boolean *, BOOLEAN_P numberpad, int portdebug));
79 static void NDECL(init_ttycolor);
80 static void NDECL(really_move_cursor);
81 static void NDECL(check_and_set_font);
82 static boolean NDECL(check_font_widths);
83 static void NDECL(set_known_good_console_font);
84 static void NDECL(restore_original_console_font);
85 extern void NDECL(safe_routines);
86
87 /* Win32 Screen buffer,coordinate,console I/O information */
88 COORD ntcoord;
89 INPUT_RECORD ir;
90 static boolean orig_QuickEdit;
91
92 /* Support for changing console font if existing glyph widths are too wide */
93
94 /* Flag for whether NetHack was launched via the GUI, not the command line.
95 * The reason we care at all, is so that we can get
96 * a final RETURN at the end of the game when launched from the GUI
97 * to prevent the scoreboard (or panic message :-|) from vanishing
98 * immediately after it is displayed, yet not bother when started
99 * from the command line.
100 */
101 int GUILaunched = FALSE;
102 /* Flag for whether unicode is supported */
103 static boolean init_ttycolor_completed;
104 #ifdef PORT_DEBUG
105 static boolean display_cursor_info = FALSE;
106 #endif
107 #ifdef CHANGE_COLOR
108 static void NDECL(adjust_palette);
109 static int FDECL(match_color_name, (const char *));
110 typedef HWND(WINAPI *GETCONSOLEWINDOW)();
111 static HWND GetConsoleHandle(void);
112 static HWND GetConsoleHwnd(void);
113 static boolean altered_palette;
114 static COLORREF UserDefinedColors[CLR_MAX];
115 static COLORREF NetHackColors[CLR_MAX] = {
116 0x00000000, 0x00c80000, 0x0000c850, 0x00b4b432, 0x000000d2, 0x00800080,
117 0x000064b4, 0x00c0c0c0, 0x00646464, 0x00f06464, 0x0000ff00, 0x00ffff00,
118 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
119 };
120 static COLORREF DefaultColors[CLR_MAX] = {
121 0x00000000, 0x00800000, 0x00008000, 0x00808000, 0x00000080, 0x00800080,
122 0x00008080, 0x00c0c0c0, 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00,
123 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
124 };
125 #endif
126 struct console_t {
127 WORD background;
128 WORD foreground;
129 WORD attr;
130 int current_nhcolor;
131 int current_nhattr[ATR_INVERSE+1];
132 COORD cursor;
133 HANDLE hConOut;
134 HANDLE hConIn;
135 CONSOLE_SCREEN_BUFFER_INFO origcsbi;
136 int width;
137 int height;
138 boolean has_unicode;
139 int buffer_size;
140 cell_t * front_buffer;
141 cell_t * back_buffer;
142 WCHAR cpMap[256];
143 boolean font_changed;
144 CONSOLE_FONT_INFOEX original_font_info;
145 UINT original_code_page;
146 } console = {
147 0,
148 (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED),
149 (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED),
150 NO_COLOR,
151 {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE},
152 {0, 0},
153 NULL,
154 NULL,
155 { 0 },
156 0,
157 0,
158 FALSE,
159 0,
160 NULL,
161 NULL,
162 { 0 },
163 FALSE,
164 { 0 },
165 0
166 };
167
168 static DWORD ccount, acount;
169 #ifndef CLR_MAX
170 #define CLR_MAX 16
171 #endif
172
173 int ttycolors[CLR_MAX];
174 int ttycolors_inv[CLR_MAX];
175
176 #define MAX_OVERRIDES 256
177 unsigned char key_overrides[MAX_OVERRIDES];
178 static char nullstr[] = "";
179 char erase_char, kill_char;
180 #define DEFTEXTCOLOR ttycolors[7]
181
182 /* dynamic keystroke handling .DLL support */
183 typedef int(__stdcall *PROCESS_KEYSTROKE)(HANDLE, INPUT_RECORD *, boolean *,
184 BOOLEAN_P, int);
185
186 typedef int(__stdcall *NHKBHIT)(HANDLE, INPUT_RECORD *);
187
188 typedef int(__stdcall *CHECKINPUT)(HANDLE, INPUT_RECORD *, DWORD *, BOOLEAN_P,
189 int, int *, coord *);
190
191 typedef int(__stdcall *SOURCEWHERE)(char **);
192
193 typedef int(__stdcall *SOURCEAUTHOR)(char **);
194
195 typedef int(__stdcall *KEYHANDLERNAME)(char **, int);
196
197 typedef struct {
198 char * name; // name without DLL extension
199 HANDLE hLibrary;
200 PROCESS_KEYSTROKE pProcessKeystroke;
201 NHKBHIT pNHkbhit;
202 CHECKINPUT pCheckInput;
203 SOURCEWHERE pSourceWhere;
204 SOURCEAUTHOR pSourceAuthor;
205 KEYHANDLERNAME pKeyHandlerName;
206 } keyboard_handler_t;
207
208 keyboard_handler_t keyboard_handler;
209
210
211 /* Console buffer flipping support */
212
back_buffer_flip()213 static void back_buffer_flip()
214 {
215 cell_t * back = console.back_buffer;
216 cell_t * front = console.front_buffer;
217 COORD pos;
218 DWORD unused;
219
220 for (pos.Y = 0; pos.Y < console.height; pos.Y++) {
221 for (pos.X = 0; pos.X < console.width; pos.X++) {
222 if (back->attribute != front->attribute) {
223 WriteConsoleOutputAttribute(console.hConOut, &back->attribute,
224 1, pos, &unused);
225 front->attribute = back->attribute;
226 }
227 if (back->character != front->character) {
228 if (console.has_unicode) {
229 WriteConsoleOutputCharacterW(console.hConOut,
230 &back->character, 1, pos, &unused);
231 } else {
232 char ch = (char)back->character;
233 WriteConsoleOutputCharacterA(console.hConOut, &ch, 1, pos,
234 &unused);
235 }
236 *front = *back;
237 }
238 back++;
239 front++;
240 }
241 }
242 }
243
buffer_fill_to_end(cell_t * buffer,cell_t * fill,int x,int y)244 void buffer_fill_to_end(cell_t * buffer, cell_t * fill, int x, int y)
245 {
246 nhassert(x >= 0 && x < console.width);
247 nhassert(y >= 0 && ((y < console.height) || (y == console.height &&
248 x == 0)));
249
250 cell_t * dst = buffer + console.width * y + x;
251 cell_t * sentinel = buffer + console.buffer_size;
252 while (dst != sentinel)
253 *dst++ = *fill;
254
255 if (iflags.debug.immediateflips && buffer == console.back_buffer)
256 back_buffer_flip();
257 }
258
buffer_clear_to_end_of_line(cell_t * buffer,int x,int y)259 static void buffer_clear_to_end_of_line(cell_t * buffer, int x, int y)
260 {
261 nhassert(x >= 0 && x < console.width);
262 nhassert(y >= 0 && ((y < console.height) || (y == console.height &&
263 x == 0)));
264 cell_t * dst = buffer + console.width * y + x;
265 cell_t *sentinel = buffer + console.width * (y + 1);
266
267 while (dst != sentinel)
268 *dst++ = clear_cell;
269
270 if (iflags.debug.immediateflips)
271 back_buffer_flip();
272 }
273
buffer_write(cell_t * buffer,cell_t * cell,COORD pos)274 void buffer_write(cell_t * buffer, cell_t * cell, COORD pos)
275 {
276 nhassert(pos.X >= 0 && pos.X < console.width);
277 nhassert(pos.Y >= 0 && pos.Y < console.height);
278
279 cell_t * dst = buffer + (console.width * pos.Y) + pos.X;
280 *dst = *cell;
281
282 if (iflags.debug.immediateflips && buffer == console.back_buffer)
283 back_buffer_flip();
284 }
285
286 /*
287 * Called after returning from ! or ^Z
288 */
289 void
gettty()290 gettty()
291 {
292 #ifndef TEXTCOLOR
293 int k;
294 #endif
295 erase_char = '\b';
296 kill_char = 21; /* cntl-U */
297 iflags.cbreak = TRUE;
298 #ifdef TEXTCOLOR
299 init_ttycolor();
300 #else
301 for (k = 0; k < CLR_MAX; ++k)
302 ttycolors[k] = NO_COLOR;
303 #endif
304 }
305
306 /* reset terminal to original state */
307 void
settty(s)308 settty(s)
309 const char *s;
310 {
311 cmov(ttyDisplay->curx, ttyDisplay->cury);
312 end_screen();
313 if (s)
314 raw_print(s);
315 restore_original_console_font();
316 if (orig_QuickEdit) {
317 DWORD cmode;
318
319 GetConsoleMode(console.hConIn, &cmode);
320 cmode |= (ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
321 SetConsoleMode(console.hConIn, cmode);
322 }
323 }
324
325 /* called by init_nhwindows() and resume_nhwindows() */
326 void
setftty()327 setftty()
328 {
329 #ifdef CHANGE_COLOR
330 if (altered_palette)
331 adjust_palette();
332 #endif
333 start_screen();
334 }
335
336 void
tty_startup(wid,hgt)337 tty_startup(wid, hgt)
338 int *wid, *hgt;
339 {
340 *wid = console.width;
341 *hgt = console.height;
342 set_option_mod_status("mouse_support", SET_IN_GAME);
343 }
344
345 void
tty_number_pad(state)346 tty_number_pad(state)
347 int state;
348 {
349 // do nothing
350 }
351
352 void
tty_start_screen()353 tty_start_screen()
354 {
355 if (iflags.num_pad)
356 tty_number_pad(1); /* make keypad send digits */
357 }
358
359 void
tty_end_screen()360 tty_end_screen()
361 {
362 clear_screen();
363 really_move_cursor();
364 buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0);
365 back_buffer_flip();
366 FlushConsoleInputBuffer(console.hConIn);
367 }
368
369 static BOOL
CtrlHandler(ctrltype)370 CtrlHandler(ctrltype)
371 DWORD ctrltype;
372 {
373 switch (ctrltype) {
374 /* case CTRL_C_EVENT: */
375 case CTRL_BREAK_EVENT:
376 clear_screen();
377 case CTRL_CLOSE_EVENT:
378 case CTRL_LOGOFF_EVENT:
379 case CTRL_SHUTDOWN_EVENT:
380 getreturn_enabled = FALSE;
381 #ifndef NOSAVEONHANGUP
382 hangup(0);
383 #endif
384 #if defined(SAFERHANGUP)
385 CloseHandle(console.hConIn); /* trigger WAIT_FAILED */
386 return TRUE;
387 #endif
388 default:
389 return FALSE;
390 }
391 }
392
393 /* called by pcmain() and process_options() */
394 void
nttty_open(mode)395 nttty_open(mode)
396 int mode; // unused
397 {
398 DWORD cmode;
399
400 /* Initialize the function pointer that points to
401 * the kbhit() equivalent, in this TTY case nttty_kbhit()
402 */
403 nt_kbhit = nttty_kbhit;
404
405 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) {
406 /* Unable to set control handler */
407 cmode = 0; /* just to have a statement to break on for debugger */
408 }
409
410 LI = console.height;
411 CO = console.width;
412
413 really_move_cursor();
414 }
415
416 void
nttty_exit()417 nttty_exit()
418 {
419 /* go back to using the safe routines */
420 safe_routines();
421 }
422
423 int
process_keystroke(ir,valid,numberpad,portdebug)424 process_keystroke(ir, valid, numberpad, portdebug)
425 INPUT_RECORD *ir;
426 boolean *valid;
427 boolean numberpad;
428 int portdebug;
429 {
430 int ch;
431
432 #ifdef QWERTZ_SUPPORT
433 if (Cmd.swap_yz)
434 numberpad |= 0x10;
435 #endif
436 ch = keyboard_handler.pProcessKeystroke(
437 console.hConIn, ir, valid, numberpad, portdebug);
438 #ifdef QWERTZ_SUPPORT
439 numberpad &= ~0x10;
440 #endif
441 /* check for override */
442 if (ch && ch < MAX_OVERRIDES && key_overrides[ch])
443 ch = key_overrides[ch];
444 return ch;
445 }
446
447 int
nttty_kbhit()448 nttty_kbhit()
449 {
450 return keyboard_handler.pNHkbhit(console.hConIn, &ir);
451 }
452
453 int
tgetch()454 tgetch()
455 {
456 int mod;
457 coord cc;
458 DWORD count;
459 boolean numpad = iflags.num_pad;
460
461 really_move_cursor();
462 if (iflags.debug_fuzzer)
463 return randomkey();
464 #ifdef QWERTZ_SUPPORT
465 if (Cmd.swap_yz)
466 numpad |= 0x10;
467 #endif
468
469 return (program_state.done_hup)
470 ? '\033'
471 : keyboard_handler.pCheckInput(
472 console.hConIn, &ir, &count, numpad, 0, &mod, &cc);
473 }
474
475 int
ntposkey(x,y,mod)476 ntposkey(x, y, mod)
477 int *x, *y, *mod;
478 {
479 int ch;
480 coord cc;
481 DWORD count;
482 boolean numpad = iflags.num_pad;
483
484 really_move_cursor();
485 if (iflags.debug_fuzzer)
486 return randomkey();
487 #ifdef QWERTZ_SUPPORT
488 if (Cmd.swap_yz)
489 numpad |= 0x10;
490 #endif
491 ch = (program_state.done_hup)
492 ? '\033'
493 : keyboard_handler.pCheckInput(
494 console.hConIn, &ir, &count, numpad, 1, mod, &cc);
495 #ifdef QWERTZ_SUPPORT
496 numpad &= ~0x10;
497 #endif
498 if (!ch) {
499 *x = cc.x;
500 *y = cc.y;
501 }
502 return ch;
503 }
504
set_console_cursor(int x,int y)505 static void set_console_cursor(int x, int y)
506 {
507 nhassert(x >= 0 && x < console.width);
508 nhassert(y >= 0 && y < console.height);
509
510 console.cursor.X = max(0, min(console.width - 1, x));
511 console.cursor.Y = max(0, min(console.height - 1, y));
512 }
513
514 static void
really_move_cursor()515 really_move_cursor()
516 {
517 #ifdef PORT_DEBUG
518 char oldtitle[BUFSZ], newtitle[BUFSZ];
519 if (display_cursor_info && wizard) {
520 oldtitle[0] = '\0';
521 if (GetConsoleTitle(oldtitle, BUFSZ)) {
522 oldtitle[39] = '\0';
523 }
524 Sprintf(newtitle, "%-55s tty=(%02d,%02d) nttty=(%02d,%02d)", oldtitle,
525 ttyDisplay->curx, ttyDisplay->cury,
526 console.cursor.X, console.cursor.Y);
527 (void) SetConsoleTitle(newtitle);
528 }
529 #endif
530 if (ttyDisplay)
531 set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
532
533 back_buffer_flip();
534 SetConsoleCursorPosition(console.hConOut, console.cursor);
535 }
536
537 void
cmov(x,y)538 cmov(x, y)
539 register int x, y;
540 {
541 ttyDisplay->cury = y;
542 ttyDisplay->curx = x;
543
544 set_console_cursor(x, y);
545 }
546
547 void
nocmov(x,y)548 nocmov(x, y)
549 int x, y;
550 {
551 ttyDisplay->curx = x;
552 ttyDisplay->cury = y;
553
554 set_console_cursor(x, y);
555 }
556
557 /* same signature as 'putchar()' with potential failure result ignored */
558 int
xputc(ch)559 xputc(ch)
560 int ch;
561 {
562 set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
563 xputc_core((char) ch);
564 return 0;
565 }
566
567 void
xputs(s)568 xputs(s)
569 const char *s;
570 {
571 int k;
572 int slen = (int) strlen(s);
573
574 if (ttyDisplay)
575 set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
576
577 if (s) {
578 for (k = 0; k < slen && s[k]; ++k)
579 xputc_core(s[k]);
580 }
581 }
582
583 /* xputc_core() and g_putch() are the only
584 * two routines that actually place output
585 * on the display.
586 */
587 void
xputc_core(ch)588 xputc_core(ch)
589 char ch;
590 {
591 nhassert(console.cursor.X >= 0 && console.cursor.X < console.width);
592 nhassert(console.cursor.Y >= 0 && console.cursor.Y < console.height);
593
594 boolean inverse = FALSE;
595 cell_t cell;
596
597 switch (ch) {
598 case '\n':
599 if (console.cursor.Y < console.height - 1)
600 console.cursor.Y++;
601 /* fall through */
602 case '\r':
603 console.cursor.X = 1;
604 break;
605 case '\b':
606 if (console.cursor.X > 1) {
607 console.cursor.X--;
608 } else if(console.cursor.Y > 0) {
609 console.cursor.X = console.width - 1;
610 console.cursor.Y--;
611 }
612 break;
613 default:
614
615 inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse);
616 console.attr = (inverse) ?
617 ttycolors_inv[console.current_nhcolor] :
618 ttycolors[console.current_nhcolor];
619 if (console.current_nhattr[ATR_BOLD])
620 console.attr |= (inverse) ?
621 BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
622
623 cell.attribute = console.attr;
624 cell.character = (console.has_unicode ? console.cpMap[ch] : ch);
625
626 buffer_write(console.back_buffer, &cell, console.cursor);
627
628 if (console.cursor.X == console.width - 1) {
629 if (console.cursor.Y < console.height - 1) {
630 console.cursor.X = 1;
631 console.cursor.Y++;
632 }
633 } else {
634 console.cursor.X++;
635 }
636 }
637
638 nhassert(console.cursor.X >= 0 && console.cursor.X < console.width);
639 nhassert(console.cursor.Y >= 0 && console.cursor.Y < console.height);
640 }
641
642 /*
643 * Overrides wintty.c function of the same name
644 * for win32. It is used for glyphs only, not text.
645 */
646
647 void
g_putch(in_ch)648 g_putch(in_ch)
649 int in_ch;
650 {
651 boolean inverse = FALSE;
652 unsigned char ch = (unsigned char) in_ch;
653
654 set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
655
656 inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse);
657 console.attr = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse) ?
658 ttycolors_inv[console.current_nhcolor] :
659 ttycolors[console.current_nhcolor];
660 if (console.current_nhattr[ATR_BOLD])
661 console.attr |= (inverse) ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
662
663 cell_t cell;
664
665 cell.attribute = console.attr;
666 cell.character = (console.has_unicode ? cp437[ch] : ch);
667
668 buffer_write(console.back_buffer, &cell, console.cursor);
669 }
670
671 void
cl_end()672 cl_end()
673 {
674 set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
675 buffer_clear_to_end_of_line(console.back_buffer, console.cursor.X,
676 console.cursor.Y);
677 tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury);
678 }
679
680 void
raw_clear_screen()681 raw_clear_screen()
682 {
683 if (WINDOWPORT("tty")) {
684 cell_t * back = console.back_buffer;
685 cell_t * front = console.front_buffer;
686 COORD pos;
687 DWORD unused;
688
689 for (pos.Y = 0; pos.Y < console.height; pos.Y++) {
690 for (pos.X = 0; pos.X < console.width; pos.X++) {
691 WriteConsoleOutputAttribute(console.hConOut, &back->attribute,
692 1, pos, &unused);
693 front->attribute = back->attribute;
694 if (console.has_unicode) {
695 WriteConsoleOutputCharacterW(console.hConOut,
696 &back->character, 1, pos, &unused);
697 } else {
698 char ch = (char)back->character;
699 WriteConsoleOutputCharacterA(console.hConOut, &ch, 1, pos,
700 &unused);
701 }
702 *front = *back;
703 back++;
704 front++;
705 }
706 }
707 }
708 }
709
710 void
clear_screen()711 clear_screen()
712 {
713 buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0);
714 home();
715 }
716
717 void
home()718 home()
719 {
720 ttyDisplay->curx = ttyDisplay->cury = 0;
721 set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
722 }
723
724 void
backsp()725 backsp()
726 {
727 set_console_cursor(ttyDisplay->curx, ttyDisplay->cury);
728 xputc_core('\b');
729 }
730
731 void
cl_eos()732 cl_eos()
733 {
734 buffer_fill_to_end(console.back_buffer, &clear_cell, ttyDisplay->curx,
735 ttyDisplay->cury);
736 tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury);
737 }
738
739 void
tty_nhbell()740 tty_nhbell()
741 {
742 if (flags.silent || iflags.debug_fuzzer)
743 return;
744 Beep(8000, 500);
745 }
746
747 volatile int junk; /* prevent optimizer from eliminating loop below */
748
749 void
tty_delay_output()750 tty_delay_output()
751 {
752 /* delay 50 ms - uses ANSI C clock() function now */
753 clock_t goal;
754 int k;
755
756 goal = 50 + clock();
757 back_buffer_flip();
758 if (iflags.debug_fuzzer)
759 return;
760
761 while (goal > clock()) {
762 k = junk; /* Do nothing */
763 }
764 }
765
766 /*
767 * CLR_BLACK 0
768 * CLR_RED 1
769 * CLR_GREEN 2
770 * CLR_BROWN 3 low-intensity yellow
771 * CLR_BLUE 4
772 * CLR_MAGENTA 5
773 * CLR_CYAN 6
774 * CLR_GRAY 7 low-intensity white
775 * NO_COLOR 8
776 * CLR_ORANGE 9
777 * CLR_BRIGHT_GREEN 10
778 * CLR_YELLOW 11
779 * CLR_BRIGHT_BLUE 12
780 * CLR_BRIGHT_MAGENTA 13
781 * CLR_BRIGHT_CYAN 14
782 * CLR_WHITE 15
783 * CLR_MAX 16
784 * BRIGHT 8
785 */
786
787 static void
init_ttycolor()788 init_ttycolor()
789 {
790 #ifdef TEXTCOLOR
791 ttycolors[CLR_BLACK] = FOREGROUND_INTENSITY; /* fix by Quietust */
792 ttycolors[CLR_RED] = FOREGROUND_RED;
793 ttycolors[CLR_GREEN] = FOREGROUND_GREEN;
794 ttycolors[CLR_BROWN] = FOREGROUND_GREEN | FOREGROUND_RED;
795 ttycolors[CLR_BLUE] = FOREGROUND_BLUE;
796 ttycolors[CLR_MAGENTA] = FOREGROUND_BLUE | FOREGROUND_RED;
797 ttycolors[CLR_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE;
798 ttycolors[CLR_GRAY] = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE;
799 ttycolors[NO_COLOR] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
800 ttycolors[CLR_ORANGE] = FOREGROUND_RED | FOREGROUND_INTENSITY;
801 ttycolors[CLR_BRIGHT_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
802 ttycolors[CLR_YELLOW] = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
803 ttycolors[CLR_BRIGHT_BLUE] = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
804 ttycolors[CLR_BRIGHT_MAGENTA]=FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
805 ttycolors[CLR_BRIGHT_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
806 ttycolors[CLR_WHITE] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED
807 | FOREGROUND_INTENSITY;
808
809 ttycolors_inv[CLR_BLACK] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
810 | BACKGROUND_INTENSITY;
811 ttycolors_inv[CLR_RED] = BACKGROUND_RED | BACKGROUND_INTENSITY;
812 ttycolors_inv[CLR_GREEN] = BACKGROUND_GREEN;
813 ttycolors_inv[CLR_BROWN] = BACKGROUND_GREEN | BACKGROUND_RED;
814 ttycolors_inv[CLR_BLUE] = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
815 ttycolors_inv[CLR_MAGENTA] = BACKGROUND_BLUE | BACKGROUND_RED;
816 ttycolors_inv[CLR_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE;
817 ttycolors_inv[CLR_GRAY] = BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE;
818 ttycolors_inv[NO_COLOR] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
819 ttycolors_inv[CLR_ORANGE] = BACKGROUND_RED | BACKGROUND_INTENSITY;
820 ttycolors_inv[CLR_BRIGHT_GREEN]= BACKGROUND_GREEN | BACKGROUND_INTENSITY;
821 ttycolors_inv[CLR_YELLOW] = BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
822 ttycolors_inv[CLR_BRIGHT_BLUE] = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
823 ttycolors_inv[CLR_BRIGHT_MAGENTA] =BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY;
824 ttycolors_inv[CLR_BRIGHT_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
825 ttycolors_inv[CLR_WHITE] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
826 | BACKGROUND_INTENSITY;
827 #else
828 int k;
829 ttycolors[0] = FOREGROUND_INTENSITY;
830 ttycolors_inv[0] = BACKGROUND_INTENSITY;
831 for (k = 1; k < SIZE(ttycolors); ++k) {
832 ttycolors[k] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED;
833 ttycolors_inv[k] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED;
834 }
835 #endif
836 init_ttycolor_completed = TRUE;
837 }
838
839 #if 0
840 int
841 has_color(int color) /* this function is commented out */
842 {
843 #ifdef TEXTCOLOR
844 if ((color >= 0) && (color < CLR_MAX))
845 return 1;
846 #else
847 if ((color == CLR_BLACK) || (color == CLR_WHITE) || (color == NO_COLOR))
848 return 1;
849 #endif
850 else
851 return 0;
852 }
853 #endif
854
855 int
term_attr_fixup(int attrmask)856 term_attr_fixup(int attrmask)
857 {
858 return attrmask;
859 }
860
861 void
term_start_attr(int attrib)862 term_start_attr(int attrib)
863 {
864 console.current_nhattr[attrib] = TRUE;
865 if (attrib) console.current_nhattr[ATR_NONE] = FALSE;
866 }
867
868 void
term_end_attr(int attrib)869 term_end_attr(int attrib)
870 {
871 int k;
872
873 switch (attrib) {
874 case ATR_INVERSE:
875 case ATR_ULINE:
876 case ATR_BLINK:
877 case ATR_BOLD:
878 break;
879 }
880 console.current_nhattr[attrib] = FALSE;
881 console.current_nhattr[ATR_NONE] = TRUE;
882 /* re-evaluate all attr now for performance at output time */
883 for (k=ATR_NONE; k <= ATR_INVERSE; ++k) {
884 if (console.current_nhattr[k])
885 console.current_nhattr[ATR_NONE] = FALSE;
886 }
887 }
888
889 void
term_end_raw_bold(void)890 term_end_raw_bold(void)
891 {
892 term_end_attr(ATR_BOLD);
893 }
894
895 void
term_start_raw_bold(void)896 term_start_raw_bold(void)
897 {
898 term_start_attr(ATR_BOLD);
899 }
900
901 void
term_start_color(int color)902 term_start_color(int color)
903 {
904 #ifdef TEXTCOLOR
905 if (color >= 0 && color < CLR_MAX) {
906 console.current_nhcolor = color;
907 } else
908 #endif
909 console.current_nhcolor = NO_COLOR;
910 }
911
912 void
term_end_color(void)913 term_end_color(void)
914 {
915 #ifdef TEXTCOLOR
916 console.foreground = DEFTEXTCOLOR;
917 #endif
918 console.attr = (console.foreground | console.background);
919 console.current_nhcolor = NO_COLOR;
920 }
921
922 void
standoutbeg()923 standoutbeg()
924 {
925 term_start_attr(ATR_BOLD);
926 }
927
928 void
standoutend()929 standoutend()
930 {
931 term_end_attr(ATR_BOLD);
932 }
933
934 #ifndef NO_MOUSE_ALLOWED
935 void
toggle_mouse_support()936 toggle_mouse_support()
937 {
938 static int qeinit = 0;
939 DWORD cmode;
940
941 GetConsoleMode(console.hConIn, &cmode);
942 if (!qeinit) {
943 qeinit = 1;
944 orig_QuickEdit = ((cmode & ENABLE_QUICK_EDIT_MODE) != 0);
945 }
946 switch(iflags.wc_mouse_support) {
947 case 2:
948 cmode |= ENABLE_MOUSE_INPUT;
949 break;
950 case 1:
951 cmode |= ENABLE_MOUSE_INPUT;
952 cmode &= ~ENABLE_QUICK_EDIT_MODE;
953 cmode |= ENABLE_EXTENDED_FLAGS;
954 break;
955 case 0:
956 /*FALLTHRU*/
957 default:
958 cmode &= ~ENABLE_MOUSE_INPUT;
959 if (orig_QuickEdit)
960 cmode |= (ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
961 }
962 SetConsoleMode(console.hConIn, cmode);
963 }
964 #endif
965
966 /* handle tty options updates here */
967 void
nttty_preference_update(pref)968 nttty_preference_update(pref)
969 const char *pref;
970 {
971 if (stricmp(pref, "mouse_support") == 0) {
972 #ifndef NO_MOUSE_ALLOWED
973 toggle_mouse_support();
974 #endif
975 }
976 if (stricmp(pref, "symset") == 0)
977 check_and_set_font();
978 return;
979 }
980
981 #ifdef PORT_DEBUG
982 void
win32con_debug_keystrokes()983 win32con_debug_keystrokes()
984 {
985 DWORD count;
986 boolean valid = 0;
987 int ch;
988 xputs("\n");
989 while (!valid || ch != 27) {
990 nocmov(ttyDisplay->curx, ttyDisplay->cury);
991 ReadConsoleInput(console.hConIn, &ir, 1, &count);
992 if ((ir.EventType == KEY_EVENT) && ir.Event.KeyEvent.bKeyDown)
993 ch = process_keystroke(&ir, &valid, iflags.num_pad, 1);
994 }
995 (void) doredraw();
996 }
997 void
win32con_handler_info()998 win32con_handler_info()
999 {
1000 char *buf;
1001 int ci;
1002 if (!keyboard_handler.pSourceAuthor && !keyboard_handler.pSourceWhere)
1003 pline("Keyboard handler source info and author unavailable.");
1004 else {
1005 if (keyboard_handler.pKeyHandlerName &&
1006 keyboard_handler.pKeyHandlerName(&buf, 1)) {
1007 xputs("\n");
1008 xputs("Keystroke handler loaded: \n ");
1009 xputs(buf);
1010 }
1011 if (keyboard_handler.pSourceAuthor &&
1012 keyboard_handler.pSourceAuthor(&buf)) {
1013 xputs("\n");
1014 xputs("Keystroke handler Author: \n ");
1015 xputs(buf);
1016 }
1017 if (keyboard_handler.pSourceWhere &&
1018 keyboard_handler.pSourceWhere(&buf)) {
1019 xputs("\n");
1020 xputs("Keystroke handler source code available at:\n ");
1021 xputs(buf);
1022 }
1023 xputs("\nPress any key to resume.");
1024 ci = nhgetch();
1025 (void) doredraw();
1026 }
1027 }
1028
1029 void
win32con_toggle_cursor_info()1030 win32con_toggle_cursor_info()
1031 {
1032 display_cursor_info = !display_cursor_info;
1033 }
1034 #endif
1035
1036 void
map_subkeyvalue(op)1037 map_subkeyvalue(op)
1038 register char *op;
1039 {
1040 char digits[] = "0123456789";
1041 int length, i, idx, val;
1042 char *kp;
1043
1044 idx = -1;
1045 val = -1;
1046 kp = index(op, '/');
1047 if (kp) {
1048 *kp = '\0';
1049 kp++;
1050 length = strlen(kp);
1051 if (length < 1 || length > 3)
1052 return;
1053 for (i = 0; i < length; i++)
1054 if (!index(digits, kp[i]))
1055 return;
1056 val = atoi(kp);
1057 length = strlen(op);
1058 if (length < 1 || length > 3)
1059 return;
1060 for (i = 0; i < length; i++)
1061 if (!index(digits, op[i]))
1062 return;
1063 idx = atoi(op);
1064 }
1065 if (idx >= MAX_OVERRIDES || idx < 0 || val >= MAX_OVERRIDES || val < 1)
1066 return;
1067 key_overrides[idx] = val;
1068 }
1069
unload_keyboard_handler()1070 void unload_keyboard_handler()
1071 {
1072 nhassert(keyboard_handler.hLibrary != NULL);
1073
1074 FreeLibrary(keyboard_handler.hLibrary);
1075 memset(&keyboard_handler, 0, sizeof(keyboard_handler_t));
1076 }
1077
1078 boolean
load_keyboard_handler(const char * inName)1079 load_keyboard_handler(const char * inName)
1080 {
1081 char path[MAX_ALTKEYHANDLER + 4];
1082 strcpy(path, inName);
1083 strcat(path, ".dll");
1084
1085 HANDLE hLibrary = LoadLibrary(path);
1086
1087 if (hLibrary == NULL)
1088 return FALSE;
1089
1090 PROCESS_KEYSTROKE pProcessKeystroke = (PROCESS_KEYSTROKE) GetProcAddress(
1091 hLibrary, TEXT("ProcessKeystroke"));
1092 NHKBHIT pNHkbhit = (NHKBHIT) GetProcAddress(
1093 hLibrary, TEXT("NHkbhit"));
1094 CHECKINPUT pCheckInput =
1095 (CHECKINPUT) GetProcAddress(hLibrary, TEXT("CheckInput"));
1096
1097 if (!pProcessKeystroke || !pNHkbhit || !pCheckInput)
1098 {
1099 return FALSE;
1100 } else {
1101 if (keyboard_handler.hLibrary != NULL)
1102 unload_keyboard_handler();
1103
1104 keyboard_handler.hLibrary = hLibrary;
1105
1106 keyboard_handler.pProcessKeystroke = pProcessKeystroke;
1107 keyboard_handler.pNHkbhit = pNHkbhit;
1108 keyboard_handler.pCheckInput = pCheckInput;
1109
1110 keyboard_handler.pSourceWhere =
1111 (SOURCEWHERE) GetProcAddress(hLibrary, TEXT("SourceWhere"));
1112 keyboard_handler.pSourceAuthor =
1113 (SOURCEAUTHOR) GetProcAddress(hLibrary, TEXT("SourceAuthor"));
1114 keyboard_handler.pKeyHandlerName = (KEYHANDLERNAME) GetProcAddress(
1115 hLibrary, TEXT("KeyHandlerName"));
1116 }
1117
1118 return TRUE;
1119 }
1120
set_altkeyhandler(const char * inName)1121 void set_altkeyhandler(const char * inName)
1122 {
1123 if (strlen(inName) >= MAX_ALTKEYHANDLER) {
1124 config_error_add("altkeyhandler name '%s' is too long", inName);
1125 return;
1126 }
1127
1128 char name[MAX_ALTKEYHANDLER];
1129 strcpy(name, inName);
1130
1131 /* We support caller mistakenly giving name with '.dll' extension */
1132 char * ext = strchr(name, '.');
1133 if (ext != NULL) *ext = '\0';
1134
1135 if (load_keyboard_handler(name))
1136 strcpy(iflags.altkeyhandler, name);
1137 else {
1138 config_error_add("unable to load altkeyhandler '%s'", name);
1139 return;
1140 }
1141
1142 return;
1143 }
1144
1145
1146 /* fatal error */
1147 /*VARARGS1*/
1148 void nttty_error
VA_DECL(const char *,s)1149 VA_DECL(const char *, s)
1150 {
1151 char buf[BUFSZ];
1152 VA_START(s);
1153 VA_INIT(s, const char *);
1154 /* error() may get called before tty is initialized */
1155 if (iflags.window_inited)
1156 end_screen();
1157 buf[0] = '\n';
1158 (void) vsnprintf(&buf[1], sizeof buf - 1, s, VA_ARGS);
1159 msmsg(buf);
1160 really_move_cursor();
1161 VA_END();
1162 exit(EXIT_FAILURE);
1163 }
1164
1165 void
synch_cursor()1166 synch_cursor()
1167 {
1168 really_move_cursor();
1169 }
1170
1171 #ifdef CHANGE_COLOR
1172 void
tty_change_color(color_number,rgb,reverse)1173 tty_change_color(color_number, rgb, reverse)
1174 int color_number, reverse;
1175 long rgb;
1176 {
1177 /* Map NetHack color index to NT Console palette index */
1178 int idx, win32_color_number[] = {
1179 0, /* CLR_BLACK 0 */
1180 4, /* CLR_RED 1 */
1181 2, /* CLR_GREEN 2 */
1182 6, /* CLR_BROWN 3 */
1183 1, /* CLR_BLUE 4 */
1184 5, /* CLR_MAGENTA 5 */
1185 3, /* CLR_CYAN 6 */
1186 7, /* CLR_GRAY 7 */
1187 8, /* NO_COLOR 8 */
1188 12, /* CLR_ORANGE 9 */
1189 10, /* CLR_BRIGHT_GREEN 10 */
1190 14, /* CLR_YELLOW 11 */
1191 9, /* CLR_BRIGHT_BLUE 12 */
1192 13, /* CLR_BRIGHT_MAGENTA 13 */
1193 11, /* CLR_BRIGHT_CYAN 14 */
1194 15 /* CLR_WHITE 15 */
1195 };
1196 int k;
1197 if (color_number < 0) { /* indicates OPTIONS=palette with no value */
1198 /* copy the NetHack palette into UserDefinedColors */
1199 for (k = 0; k < CLR_MAX; k++)
1200 UserDefinedColors[k] = NetHackColors[k];
1201 } else if (color_number >= 0 && color_number < CLR_MAX) {
1202 if (!altered_palette) {
1203 /* make sure a full suite is available */
1204 for (k = 0; k < CLR_MAX; k++)
1205 UserDefinedColors[k] = DefaultColors[k];
1206 }
1207 idx = win32_color_number[color_number];
1208 UserDefinedColors[idx] = rgb;
1209 }
1210 altered_palette = TRUE;
1211 }
1212
1213 char *
tty_get_color_string()1214 tty_get_color_string()
1215 {
1216 return "";
1217 }
1218
1219 int
match_color_name(c)1220 match_color_name(c)
1221 const char *c;
1222 {
1223 const struct others {
1224 int idx;
1225 const char *colorname;
1226 } othernames[] = {
1227 { CLR_MAGENTA, "purple" },
1228 { CLR_BRIGHT_MAGENTA, "bright purple" },
1229 { NO_COLOR, "dark gray" },
1230 { NO_COLOR, "dark grey" },
1231 { CLR_GRAY, "grey" },
1232 };
1233
1234 int cnt;
1235 for (cnt = 0; cnt < CLR_MAX; ++cnt) {
1236 if (!strcmpi(c, c_obj_colors[cnt]))
1237 return cnt;
1238 }
1239 for (cnt = 0; cnt < SIZE(othernames); ++cnt) {
1240 if (!strcmpi(c, othernames[cnt].colorname))
1241 return othernames[cnt].idx;
1242 }
1243 return -1;
1244 }
1245
1246 /*
1247 * Returns 0 if badoption syntax
1248 */
1249 int
alternative_palette(op)1250 alternative_palette(op)
1251 char *op;
1252 {
1253 /*
1254 * palette:color-R-G-B
1255 * OPTIONS=palette:green-4-3-1, palette:0-0-0-0
1256 */
1257 int fieldcnt, color_number, rgb, red, green, blue;
1258 char *fields[4], *cp;
1259
1260 if (!op) {
1261 change_color(-1, 0, 0); /* indicates palette option with
1262 no value meaning "load an entire
1263 hard-coded NetHack palette." */
1264 return 1;
1265 }
1266
1267 cp = fields[0] = op;
1268 for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) {
1269 cp = index(cp, '-');
1270 if (!cp)
1271 return 0;
1272 fields[fieldcnt] = cp;
1273 cp++;
1274 }
1275 for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) {
1276 *(fields[fieldcnt]) = '\0';
1277 ++fields[fieldcnt];
1278 }
1279 rgb = 0;
1280 for (fieldcnt = 0; fieldcnt < 4; ++fieldcnt) {
1281 if (fieldcnt == 0 && isalpha(*(fields[0]))) {
1282 color_number = match_color_name(fields[0]);
1283 if (color_number == -1)
1284 return 0;
1285 } else {
1286 int dcount = 0, cval = 0;
1287 cp = fields[fieldcnt];
1288 if (*cp == '\\' && index("0123456789xXoO", cp[1])) {
1289 const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
1290
1291 cp++;
1292 if (*cp == 'x' || *cp == 'X')
1293 for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
1294 cval = (int) ((cval * 16) + (dp - hex) / 2);
1295 else if (*cp == 'o' || *cp == 'O')
1296 for (++cp; (index("01234567", *cp)) && (dcount++ < 3);
1297 cp++)
1298 cval = (cval * 8) + (*cp - '0');
1299 else
1300 return 0;
1301 } else {
1302 for (; *cp && (index("0123456789", *cp)) && (dcount++ < 3);
1303 cp++)
1304 cval = (cval * 10) + (*cp - '0');
1305 }
1306 switch (fieldcnt) {
1307 case 0:
1308 color_number = cval;
1309 break;
1310 case 1:
1311 red = cval;
1312 break;
1313 case 2:
1314 green = cval;
1315 break;
1316 case 3:
1317 blue = cval;
1318 break;
1319 }
1320 }
1321 }
1322 rgb = RGB(red, green, blue);
1323 if (color_number >= 0 && color_number < CLR_MAX)
1324 change_color(color_number, rgb, 0);
1325 return 1;
1326 }
1327
1328 /*
1329 * This uses an undocumented method to set console attributes
1330 * at runtime including console palette
1331 *
1332 * VOID WINAPI SetConsolePalette(COLORREF palette[16])
1333 *
1334 * Author: James Brown at www.catch22.net
1335 *
1336 * Set palette of current console.
1337 * Palette should be of the form:
1338 *
1339 * COLORREF DefaultColors[CLR_MAX] =
1340 * {
1341 * 0x00000000, 0x00800000, 0x00008000, 0x00808000,
1342 * 0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0,
1343 * 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00,
1344 * 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff
1345 * };
1346 */
1347
1348 #pragma pack(push, 1)
1349
1350 /*
1351 * Structure to send console via WM_SETCONSOLEINFO
1352 */
1353 typedef struct _CONSOLE_INFO {
1354 ULONG Length;
1355 COORD ScreenBufferSize;
1356 COORD WindowSize;
1357 ULONG WindowPosX;
1358 ULONG WindowPosY;
1359
1360 COORD FontSize;
1361 ULONG FontFamily;
1362 ULONG FontWeight;
1363 WCHAR FaceName[32];
1364
1365 ULONG CursorSize;
1366 ULONG FullScreen;
1367 ULONG QuickEdit;
1368 ULONG AutoPosition;
1369 ULONG InsertMode;
1370
1371 USHORT ScreenColors;
1372 USHORT PopupColors;
1373 ULONG HistoryNoDup;
1374 ULONG HistoryBufferSize;
1375 ULONG NumberOfHistoryBuffers;
1376
1377 COLORREF ColorTable[16];
1378
1379 ULONG CodePage;
1380 HWND Hwnd;
1381
1382 WCHAR ConsoleTitle[0x100];
1383 } CONSOLE_INFO;
1384
1385 #pragma pack(pop)
1386
1387 BOOL SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci);
1388 static void GetConsoleSizeInfo(CONSOLE_INFO *pci);
1389 VOID WINAPI SetConsolePalette(COLORREF crPalette[16]);
1390
1391 void
adjust_palette(VOID_ARGS)1392 adjust_palette(VOID_ARGS)
1393 {
1394 SetConsolePalette(UserDefinedColors);
1395 altered_palette = 0;
1396 }
1397
1398 /*
1399 /* only in Win2k+ (use FindWindow for NT4) */
1400 /* HWND WINAPI GetConsoleWindow(); */
1401
1402 /* Undocumented console message */
1403 #define WM_SETCONSOLEINFO (WM_USER + 201)
1404
1405 VOID WINAPI
SetConsolePalette(COLORREF palette[16])1406 SetConsolePalette(COLORREF palette[16])
1407 {
1408 CONSOLE_INFO ci = { sizeof(ci) };
1409 int i;
1410 HWND hwndConsole = GetConsoleHandle();
1411
1412 /* get current size/position settings rather than using defaults.. */
1413 GetConsoleSizeInfo(&ci);
1414
1415 /* set these to zero to keep current settings */
1416 ci.FontSize.X = 0; /* def = 8 */
1417 ci.FontSize.Y = 0; /* def = 12 */
1418 ci.FontFamily = 0; /* def = 0x30 = FF_MODERN|FIXED_PITCH */
1419 ci.FontWeight = 0; /* 0x400; */
1420 /* lstrcpyW(ci.FaceName, L"Terminal"); */
1421 ci.FaceName[0] = L'\0';
1422
1423 ci.CursorSize = 25;
1424 ci.FullScreen = FALSE;
1425 ci.QuickEdit = TRUE;
1426 ci.AutoPosition = 0x10000;
1427 ci.InsertMode = TRUE;
1428 ci.ScreenColors = MAKEWORD(0x7, 0x0);
1429 ci.PopupColors = MAKEWORD(0x5, 0xf);
1430
1431 ci.HistoryNoDup = FALSE;
1432 ci.HistoryBufferSize = 50;
1433 ci.NumberOfHistoryBuffers = 4;
1434
1435 // colour table
1436 for (i = 0; i < 16; i++)
1437 ci.ColorTable[i] = palette[i];
1438
1439 ci.CodePage = GetConsoleOutputCP();
1440 ci.Hwnd = hwndConsole;
1441
1442 lstrcpyW(ci.ConsoleTitle, L"");
1443
1444 SetConsoleInfo(hwndConsole, &ci);
1445 }
1446
1447 /*
1448 * Wrapper around WM_SETCONSOLEINFO. We need to create the
1449 * necessary section (file-mapping) object in the context of the
1450 * process which owns the console, before posting the message
1451 */
1452 BOOL
SetConsoleInfo(HWND hwndConsole,CONSOLE_INFO * pci)1453 SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci)
1454 {
1455 DWORD dwConsoleOwnerPid;
1456 HANDLE hProcess;
1457 HANDLE hSection, hDupSection;
1458 PVOID ptrView = 0;
1459 HANDLE hThread;
1460
1461 /*
1462 * Open the process which "owns" the console
1463 */
1464 GetWindowThreadProcessId(hwndConsole, &dwConsoleOwnerPid);
1465 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwConsoleOwnerPid);
1466
1467 /*
1468 * Create a SECTION object backed by page-file, then map a view of
1469 * this section into the owner process so we can write the contents
1470 * of the CONSOLE_INFO buffer into it
1471 */
1472 hSection = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0,
1473 pci->Length, 0);
1474
1475 /*
1476 * Copy our console structure into the section-object
1477 */
1478 ptrView = MapViewOfFile(hSection, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0,
1479 pci->Length);
1480 memcpy(ptrView, pci, pci->Length);
1481 UnmapViewOfFile(ptrView);
1482
1483 /*
1484 * Map the memory into owner process
1485 */
1486 DuplicateHandle(GetCurrentProcess(), hSection, hProcess, &hDupSection, 0,
1487 FALSE, DUPLICATE_SAME_ACCESS);
1488
1489 /* Send console window the "update" message */
1490 SendMessage(hwndConsole, WM_SETCONSOLEINFO, (WPARAM) hDupSection, 0);
1491
1492 /*
1493 * clean up
1494 */
1495 hThread = CreateRemoteThread(hProcess, 0, 0,
1496 (LPTHREAD_START_ROUTINE) CloseHandle,
1497 hDupSection, 0, 0);
1498
1499 CloseHandle(hThread);
1500 CloseHandle(hSection);
1501 CloseHandle(hProcess);
1502
1503 return TRUE;
1504 }
1505
1506 /*
1507 * Fill the CONSOLE_INFO structure with information
1508 * about the current console window
1509 */
1510 static void
GetConsoleSizeInfo(CONSOLE_INFO * pci)1511 GetConsoleSizeInfo(CONSOLE_INFO *pci)
1512 {
1513 CONSOLE_SCREEN_BUFFER_INFO csbi;
1514
1515 HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
1516
1517 GetConsoleScreenBufferInfo(hConsoleOut, &csbi);
1518
1519 pci->ScreenBufferSize = csbi.dwSize;
1520 pci->WindowSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
1521 pci->WindowSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
1522 pci->WindowPosX = csbi.srWindow.Left;
1523 pci->WindowPosY = csbi.srWindow.Top;
1524 }
1525
1526 static HWND
GetConsoleHandle(void)1527 GetConsoleHandle(void)
1528 {
1529 HMODULE hMod = GetModuleHandle("kernel32.dll");
1530 GETCONSOLEWINDOW pfnGetConsoleWindow =
1531 (GETCONSOLEWINDOW) GetProcAddress(hMod, "GetConsoleWindow");
1532 if (pfnGetConsoleWindow)
1533 return pfnGetConsoleWindow();
1534 else
1535 return GetConsoleHwnd();
1536 }
1537
1538 static HWND
GetConsoleHwnd(void)1539 GetConsoleHwnd(void)
1540 {
1541 int iterations = 0;
1542 HWND hwndFound = 0;
1543 char OldTitle[1024], NewTitle[1024], TestTitle[1024];
1544
1545 /* Get current window title */
1546 GetConsoleTitle(OldTitle, sizeof OldTitle);
1547
1548 (void) sprintf(NewTitle, "NETHACK%d/%d", GetTickCount(),
1549 GetCurrentProcessId());
1550 SetConsoleTitle(NewTitle);
1551
1552 GetConsoleTitle(TestTitle, sizeof TestTitle);
1553 while (strcmp(TestTitle, NewTitle) != 0) {
1554 iterations++;
1555 /* sleep(0); */
1556 GetConsoleTitle(TestTitle, sizeof TestTitle);
1557 }
1558 hwndFound = FindWindow(NULL, NewTitle);
1559 SetConsoleTitle(OldTitle);
1560 /* printf("%d iterations\n", iterations); */
1561 return hwndFound;
1562 }
1563 #endif /*CHANGE_COLOR*/
1564
EnumFontCallback(const LOGFONTW * lf,const TEXTMETRICW * tm,DWORD fontType,LPARAM lParam)1565 static int CALLBACK EnumFontCallback(
1566 const LOGFONTW * lf, const TEXTMETRICW * tm, DWORD fontType, LPARAM lParam)
1567 {
1568 LOGFONTW * lf_ptr = (LOGFONTW *) lParam;
1569 *lf_ptr = *lf;
1570 return 0;
1571 }
1572
1573 /* check_and_set_font ensures that the current font will render the symbols
1574 * that are currently being used correctly. If they will not be rendered
1575 * correctly, then it will change the font to a known good font.
1576 */
1577 void
check_and_set_font()1578 check_and_set_font()
1579 {
1580 if (!check_font_widths()) {
1581 raw_print("WARNING: glyphs too wide in console font."
1582 " Changing code page to 437 and font to Consolas\n");
1583 set_known_good_console_font();
1584 }
1585 }
1586
1587 /* check_font_widths returns TRUE if all glyphs in current console font
1588 * fit within the width of a single console cell.
1589 */
1590 boolean
check_font_widths()1591 check_font_widths()
1592 {
1593 CONSOLE_FONT_INFOEX console_font_info;
1594 console_font_info.cbSize = sizeof(console_font_info);
1595 BOOL success = GetCurrentConsoleFontEx(console.hConOut, FALSE,
1596 &console_font_info);
1597
1598 /* get console window and DC
1599 * NOTE: the DC from the console window does not have the correct
1600 * font selected at this point.
1601 */
1602 HWND hWnd = GetConsoleWindow();
1603 HDC hDC = GetDC(hWnd);
1604
1605 LOGFONTW logical_font;
1606 logical_font.lfCharSet = DEFAULT_CHARSET;
1607 wcscpy(logical_font.lfFaceName, console_font_info.FaceName);
1608 logical_font.lfPitchAndFamily = 0;
1609
1610 /* getting matching console font */
1611 LOGFONTW matching_log_font = { 0 };
1612 EnumFontFamiliesExW(hDC, &logical_font, EnumFontCallback,
1613 (LPARAM) &matching_log_font, 0);
1614
1615 if (matching_log_font.lfHeight == 0) {
1616 raw_print("Unable to enumerate system fonts\n");
1617 return FALSE;
1618 }
1619
1620 /* create font matching console font */
1621 LOGFONTW console_font_log_font = matching_log_font;
1622 console_font_log_font.lfWeight = console_font_info.FontWeight;
1623 console_font_log_font.lfHeight = console_font_info.dwFontSize.Y;
1624 console_font_log_font.lfWidth = console_font_info.dwFontSize.X;
1625 HFONT console_font = CreateFontIndirectW(&console_font_log_font);
1626
1627 if (console_font == NULL) {
1628 raw_print("Unable to create console font\n");
1629 return FALSE;
1630 }
1631
1632 /* select font */
1633 HGDIOBJ saved_font = SelectObject(hDC, console_font);
1634
1635 /* determine whether it is a true type font */
1636 TEXTMETRICA tm;
1637 success = GetTextMetricsA(hDC, &tm);
1638
1639 if (!success) {
1640 raw_print("Unable to get console font text metrics\n");
1641 goto clean_up;
1642 }
1643
1644 boolean isTrueType = (tm.tmPitchAndFamily & TMPF_TRUETYPE) != 0;
1645
1646 /* determine which glyphs are used */
1647 boolean used[256];
1648 memset(used, 0, sizeof(used));
1649 for (int i = 0; i < SYM_MAX; i++) {
1650 used[primary_syms[i]] = TRUE;
1651 used[rogue_syms[i]] = TRUE;
1652 }
1653
1654 int wcUsedCount = 0;
1655 wchar_t wcUsed[256];
1656 for (int i = 0; i < sizeof(used); i++)
1657 if (used[i])
1658 wcUsed[wcUsedCount++] = cp437[i];
1659
1660 /* measure the set of used glyphs to ensure they fit */
1661 boolean all_glyphs_fit = TRUE;
1662
1663 for (int i = 0; i < wcUsedCount; i++) {
1664 int width;
1665 if (isTrueType) {
1666 ABC abc;
1667 success = GetCharABCWidthsW(hDC, wcUsed[i], wcUsed[i], &abc);
1668 width = abc.abcA + abc.abcB + abc.abcC;
1669 } else {
1670 success = GetCharWidthW(hDC, wcUsed[i], wcUsed[i], &width);
1671 }
1672
1673 if (success && width > console_font_info.dwFontSize.X) {
1674 all_glyphs_fit = FALSE;
1675 break;
1676 }
1677 }
1678
1679 clean_up:
1680
1681 SelectObject(hDC, saved_font);
1682 DeleteObject(console_font);
1683
1684 return all_glyphs_fit;
1685 }
1686
1687 /* set_known_good_console_font sets the code page and font used by the console
1688 * to settings know to work well with NetHack. It also saves the original
1689 * settings so that they can be restored prior to NetHack exit.
1690 */
1691 void
set_known_good_console_font()1692 set_known_good_console_font()
1693 {
1694 CONSOLE_FONT_INFOEX console_font_info;
1695 console_font_info.cbSize = sizeof(console_font_info);
1696 BOOL success = GetCurrentConsoleFontEx(console.hConOut, FALSE,
1697 &console_font_info);
1698
1699 console.font_changed = TRUE;
1700 console.original_font_info = console_font_info;
1701 console.original_code_page = GetConsoleOutputCP();
1702
1703 wcscpy_s(console_font_info.FaceName,
1704 sizeof(console_font_info.FaceName)
1705 / sizeof(console_font_info.FaceName[0]),
1706 L"Consolas");
1707
1708 success = SetConsoleOutputCP(437);
1709 nhassert(success);
1710
1711 success = SetCurrentConsoleFontEx(console.hConOut, FALSE, &console_font_info);
1712 nhassert(success);
1713 }
1714
1715 /* restore_original_console_font will restore the console font and code page
1716 * settings to what they were when NetHack was launched.
1717 */
1718 void
restore_original_console_font()1719 restore_original_console_font()
1720 {
1721 if (console.font_changed) {
1722 BOOL success;
1723 raw_print("Restoring original font and code page\n");
1724 success = SetConsoleOutputCP(console.original_code_page);
1725 if (!success)
1726 raw_print("Unable to restore original code page\n");
1727
1728 success = SetCurrentConsoleFontEx(console.hConOut, FALSE,
1729 &console.original_font_info);
1730 if (!success)
1731 raw_print("Unable to restore original font\n");
1732
1733 console.font_changed = FALSE;
1734 }
1735 }
1736
1737 /* set_cp_map() creates a mapping of every possible character of a code
1738 * page to its corresponding WCHAR. This is necessary due to the high
1739 * cost of making calls to MultiByteToWideChar() for every character we
1740 * wish to print to the console.
1741 */
1742
set_cp_map()1743 void set_cp_map()
1744 {
1745 if (console.has_unicode) {
1746 UINT codePage = GetConsoleOutputCP();
1747
1748 if (codePage == 437) {
1749 memcpy(console.cpMap, cp437, sizeof(console.cpMap));
1750 } else {
1751 for (int i = 0; i < 256; i++) {
1752 char c = (char)i;
1753 int count = MultiByteToWideChar(codePage, 0, &c, 1,
1754 &console.cpMap[i], 1);
1755 nhassert(count == 1);
1756
1757 // If a character was mapped to unicode control codes,
1758 // remap to the appropriate unicode character per our
1759 // code page 437 mappings.
1760 if (console.cpMap[i] < 32)
1761 console.cpMap[i] = cp437[console.cpMap[i]];
1762 }
1763 }
1764
1765 }
1766 }
1767
1768 #if 0
1769 /* early_raw_print() is used during early game intialization prior to the
1770 * setting up of the windowing system. This allows early errors and panics
1771 * to have there messages displayed.
1772 *
1773 * early_raw_print() eventually gets replaced by tty_raw_print().
1774 *
1775 */
1776
1777 void early_raw_print(const char *s)
1778 {
1779 if (console.hConOut == NULL)
1780 return;
1781
1782 nhassert(console.cursor.X >= 0 && console.cursor.X < console.width);
1783 nhassert(console.cursor.Y >= 0 && console.cursor.Y < console.height);
1784
1785 WORD attribute = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE;
1786 DWORD unused;
1787
1788 while (*s != '\0') {
1789 switch (*s) {
1790 case '\n':
1791 if (console.cursor.Y < console.height - 1)
1792 console.cursor.Y++;
1793 /* fall through */
1794 case '\r':
1795 console.cursor.X = 1;
1796 break;
1797 case '\b':
1798 if (console.cursor.X > 1) {
1799 console.cursor.X--;
1800 } else if(console.cursor.Y > 0) {
1801 console.cursor.X = console.width - 1;
1802 console.cursor.Y--;
1803 }
1804 break;
1805 default:
1806 WriteConsoleOutputAttribute(console.hConOut, &attribute,
1807 1, console.cursor, &unused);
1808 WriteConsoleOutputCharacterA(console.hConOut, s,
1809 1, console.cursor, &unused);
1810 if (console.cursor.X == console.width - 1) {
1811 if (console.cursor.Y < console.height - 1) {
1812 console.cursor.X = 1;
1813 console.cursor.Y++;
1814 }
1815 } else {
1816 console.cursor.X++;
1817 }
1818 }
1819 s++;
1820 }
1821
1822 nhassert(console.cursor.X >= 0 && console.cursor.X < console.width);
1823 nhassert(console.cursor.Y >= 0 && console.cursor.Y < console.height);
1824
1825 SetConsoleCursorPosition(console.hConOut, console.cursor);
1826
1827 }
1828 #endif
1829
1830 /* nethack_enter_nttty() is the first thing that is called from main
1831 * once the tty port is confirmed.
1832 *
1833 * We initialize all console state to support rendering to the console
1834 * through out flipping support at this time. This allows us to support
1835 * raw_print prior to our returning.
1836 *
1837 * During this early initialization, we also determine the width and
1838 * height of the console that will be used. This width and height will
1839 * not later change.
1840 *
1841 * We also check and set the console font to a font that we know will work
1842 * well with nethack.
1843 *
1844 * The intent of this early initialization is to get all state that is
1845 * not dependent upon game options initialized allowing us to simplify
1846 * any additional initialization that might be needed when we are actually
1847 * asked to open.
1848 *
1849 * Other then the call below which clears the entire console buffer, no
1850 * other code outputs directly to the console other then the code that
1851 * handles flipping the back buffer.
1852 *
1853 */
1854
nethack_enter_nttty()1855 void nethack_enter_nttty()
1856 {
1857 #if 0
1858 /* set up state needed by early_raw_print() */
1859 windowprocs.win_raw_print = early_raw_print;
1860 #endif
1861 console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
1862 nhassert(console.hConOut != NULL); // NOTE: this assert will not print
1863
1864 GetConsoleScreenBufferInfo(console.hConOut, &console.origcsbi);
1865
1866 /* Testing of widths != COLNO has not turned up any problems. Need
1867 * to do a bit more testing and then we are likely to enable having
1868 * console width match window width.
1869 */
1870 #if 0
1871 console.width = console.origcsbi.srWindow.Right -
1872 console.origcsbi.srWindow.Left + 1;
1873 console.Width = max(console.Width, COLNO);
1874 #else
1875 console.width = COLNO;
1876 #endif
1877
1878 console.height = console.origcsbi.srWindow.Bottom -
1879 console.origcsbi.srWindow.Top + 1;
1880 console.height = max(console.height, ROWNO + 3);
1881
1882 console.buffer_size = console.width * console.height;
1883
1884
1885 /* clear the entire console buffer */
1886 int size = console.origcsbi.dwSize.X * console.origcsbi.dwSize.Y;
1887 DWORD unused;
1888 set_console_cursor(0, 0);
1889 FillConsoleOutputAttribute(
1890 console.hConOut, CONSOLE_CLEAR_ATTRIBUTE,
1891 size, console.cursor, &unused);
1892
1893 FillConsoleOutputCharacter(
1894 console.hConOut, CONSOLE_CLEAR_CHARACTER,
1895 size, console.cursor, &unused);
1896
1897 set_console_cursor(1, 0);
1898 SetConsoleCursorPosition(console.hConOut, console.cursor);
1899
1900 /* At this point early_raw_print will work */
1901
1902 console.hConIn = GetStdHandle(STD_INPUT_HANDLE);
1903 nhassert(console.hConIn != NULL);
1904
1905 /* grow the size of the console buffer if it is not wide enough */
1906 if (console.origcsbi.dwSize.X < console.width) {
1907 COORD size = {
1908 size.Y = console.origcsbi.dwSize.Y,
1909 size.X = console.width
1910 };
1911
1912 SetConsoleScreenBufferSize(console.hConOut, size);
1913 }
1914
1915 /* setup front and back buffers */
1916 int buffer_size_bytes = sizeof(cell_t) * console.buffer_size;
1917
1918 console.front_buffer = (cell_t *)malloc(buffer_size_bytes);
1919 buffer_fill_to_end(console.front_buffer, &undefined_cell, 0, 0);
1920
1921 console.back_buffer = (cell_t *)malloc(buffer_size_bytes);
1922 buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0);
1923
1924 /* determine whether OS version has unicode support */
1925 console.has_unicode = ((GetVersion() & 0x80000000) == 0);
1926
1927 /* check the font before we capture the code page map */
1928 check_and_set_font();
1929 set_cp_map();
1930
1931 /* Set console mode */
1932 DWORD cmode, mask;
1933 GetConsoleMode(console.hConIn, &cmode);
1934 #ifdef NO_MOUSE_ALLOWED
1935 mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT
1936 | ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT;
1937 #else
1938 mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
1939 | ENABLE_WINDOW_INPUT;
1940 #endif
1941 /* Turn OFF the settings specified in the mask */
1942 cmode &= ~mask;
1943 #ifndef NO_MOUSE_ALLOWED
1944 cmode |= ENABLE_MOUSE_INPUT;
1945 #endif
1946 SetConsoleMode(console.hConIn, cmode);
1947
1948 /* load default keyboard handler */
1949 HKL keyboard_layout = GetKeyboardLayout(0);
1950 DWORD primary_language = (UINT_PTR) keyboard_layout & 0x3f;
1951
1952 /* This was overriding the handler that had already
1953 been loaded during options parsing. Needs to
1954 check first */
1955 if (!iflags.altkeyhandler[0]) {
1956 if (primary_language == LANG_ENGLISH) {
1957 if (!load_keyboard_handler("nhdefkey"))
1958 error("Unable to load nhdefkey.dll");
1959 } else {
1960 if (!load_keyboard_handler("nhraykey"))
1961 error("Unable to load nhraykey.dll");
1962 }
1963 }
1964 }
1965 #endif /* TTY_GRAPHICS */
1966
1967 /* this is used as a printf() replacement when the window
1968 * system isn't initialized yet
1969 */
1970 void msmsg
VA_DECL(const char *,fmt)1971 VA_DECL(const char *, fmt)
1972 {
1973 char buf[ROWNO * COLNO]; /* worst case scenario */
1974 VA_START(fmt);
1975 VA_INIT(fmt, const char *);
1976 (void) vsnprintf(buf, sizeof buf, fmt, VA_ARGS);
1977 if (redirect_stdout)
1978 fprintf(stdout, "%s", buf);
1979 else {
1980 #ifdef TTY_GRAPHICS
1981 if(!init_ttycolor_completed)
1982 init_ttycolor();
1983 /* if we have generated too many messages ... ask the user to
1984 * confirm and then clear.
1985 */
1986 if (console.cursor.Y > console.height - 4) {
1987 xputs("Hit <Enter> to continue.");
1988 while (pgetchar() != '\n')
1989 ;
1990 raw_clear_screen();
1991 set_console_cursor(1, 0);
1992 }
1993 xputs(buf);
1994 if (ttyDisplay)
1995 curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y);
1996 #else
1997 fprintf(stdout, "%s", buf);
1998 #endif
1999 }
2000 VA_END();
2001 return;
2002 }
2003
2004 #endif /* WIN32 */
2005