OSDN Git Service

upgrade to 3.6.1
[jnethack/source.git] / win / win32 / mswproc.c
1 /* NetHack 3.6  mswproc.c       $NHDT-Date: 1451611595 2016/01/01 01:26:35 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.98 $ */
2 /* Copyright (C) 2001 by Alex Kompel     */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* For 3.6-, Copyright (c) SHIRAKATA Kentaro, 2016                 */
7 /* JNetHack may be freely redistributed.  See license for details. */
8
9 /*
10  * This file implements the interface between the window port specific
11  * code in the mswin port and the rest of the nethack game engine.
12 */
13
14 #include "hack.h"
15 #include "color.h"
16 #include "dlb.h"
17 #include "func_tab.h" /* for extended commands */
18 #include "winMS.h"
19 #include <assert.h>
20 #include <mmsystem.h>
21 #include "mhmap.h"
22 #include "mhstatus.h"
23 #include "mhtext.h"
24 #include "mhmsgwnd.h"
25 #include "mhmenu.h"
26 #include "mhsplash.h"
27 #include "mhmsg.h"
28 #include "mhinput.h"
29 #include "mhaskyn.h"
30 #include "mhdlg.h"
31 #include "mhrip.h"
32 #include "mhmain.h"
33 #include "mhfont.h"
34 #include "resource.h"
35
36 #define LLEN 128
37
38 #define NHTRACE_LOG "nhtrace.log"
39
40 #ifdef DEBUG
41 # ifdef _DEBUG
42 static FILE* _s_debugfp = NULL;
43 extern void logDebug(const char *fmt, ...);
44 # endif
45 #endif
46
47 #ifndef _DEBUG
48 void
49 logDebug(const char *fmt, ...)
50 {
51 }
52 #endif
53
54 static void mswin_main_loop(void);
55 static BOOL initMapTiles(void);
56 static void mswin_color_from_string(char *colorstring, HBRUSH *brushptr,
57                                     COLORREF *colorptr);
58 static void prompt_for_player_selection(void);
59
60 #define TOTAL_BRUSHES 10
61 HBRUSH brush_table[TOTAL_BRUSHES];
62 int max_brush = 0;
63
64 HBRUSH menu_bg_brush = NULL;
65 HBRUSH menu_fg_brush = NULL;
66 HBRUSH text_bg_brush = NULL;
67 HBRUSH text_fg_brush = NULL;
68 HBRUSH status_bg_brush = NULL;
69 HBRUSH status_fg_brush = NULL;
70 HBRUSH message_bg_brush = NULL;
71 HBRUSH message_fg_brush = NULL;
72
73 COLORREF menu_bg_color = RGB(0, 0, 0);
74 COLORREF menu_fg_color = RGB(0xFF, 0xFF, 0xFF);
75 COLORREF text_bg_color = RGB(0, 0, 0);
76 COLORREF text_fg_color = RGB(0xFF, 0xFF, 0xFF);
77 COLORREF status_bg_color = RGB(0, 0, 0);
78 COLORREF status_fg_color = RGB(0xFF, 0xFF, 0xFF);
79 COLORREF message_bg_color = RGB(0, 0, 0);
80 COLORREF message_fg_color = RGB(0xFF, 0xFF, 0xFF);
81
82 strbuf_t raw_print_strbuf = { 0 };
83
84 /* Interface definition, for windows.c */
85 struct window_procs mswin_procs = {
86     "MSWIN",
87     WC_COLOR | WC_HILITE_PET | WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_INVERSE
88         | WC_SCROLL_AMOUNT | WC_SCROLL_MARGIN | WC_MAP_MODE | WC_FONT_MESSAGE
89         | WC_FONT_STATUS | WC_FONT_MENU | WC_FONT_TEXT | WC_FONT_MAP
90         | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_MENU
91         | WC_FONTSIZ_TEXT | WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE
92         | WC_VARY_MSGCOUNT | WC_WINDOWCOLORS | WC_PLAYER_SELECTION
93         | WC_SPLASH_SCREEN | WC_POPUP_DIALOG | WC_MOUSE_SUPPORT,
94 #ifdef STATUS_HILITES
95     WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_HILITE_STATUS |
96 #endif
97     0L, mswin_init_nhwindows, mswin_player_selection, mswin_askname,
98     mswin_get_nh_event, mswin_exit_nhwindows, mswin_suspend_nhwindows,
99     mswin_resume_nhwindows, mswin_create_nhwindow, mswin_clear_nhwindow,
100     mswin_display_nhwindow, mswin_destroy_nhwindow, mswin_curs, mswin_putstr,
101     genl_putmixed, mswin_display_file, mswin_start_menu, mswin_add_menu,
102     mswin_end_menu, mswin_select_menu,
103     genl_message_menu, /* no need for X-specific handling */
104     mswin_update_inventory, mswin_mark_synch, mswin_wait_synch,
105 #ifdef CLIPPING
106     mswin_cliparound,
107 #endif
108 #ifdef POSITIONBAR
109     donull,
110 #endif
111     mswin_print_glyph, mswin_raw_print, mswin_raw_print_bold, mswin_nhgetch,
112     mswin_nh_poskey, mswin_nhbell, mswin_doprev_message, mswin_yn_function,
113     mswin_getlin, mswin_get_ext_cmd, mswin_number_pad, mswin_delay_output,
114 #ifdef CHANGE_COLOR /* only a Mac option currently */
115     mswin, mswin_change_background,
116 #endif
117     /* other defs that really should go away (they're tty specific) */
118     mswin_start_screen, mswin_end_screen, mswin_outrip,
119     mswin_preference_update, mswin_getmsghistory, mswin_putmsghistory,
120     mswin_status_init, mswin_status_finish, mswin_status_enablefield,
121     mswin_status_update,
122     genl_can_suspend_yes,
123 };
124
125 /*
126 init_nhwindows(int* argcp, char** argv)
127                 -- Initialize the windows used by NetHack.  This can also
128                    create the standard windows listed at the top, but does
129                    not display them.
130                 -- Any commandline arguments relevant to the windowport
131                    should be interpreted, and *argcp and *argv should
132                    be changed to remove those arguments.
133                 -- When the message window is created, the variable
134                    iflags.window_inited needs to be set to TRUE.  Otherwise
135                    all plines() will be done via raw_print().
136                 ** Why not have init_nhwindows() create all of the "standard"
137                 ** windows?  Or at least all but WIN_INFO?      -dean
138 */
139 void
140 mswin_init_nhwindows(int *argc, char **argv)
141 {
142     UNREFERENCED_PARAMETER(argc);
143     UNREFERENCED_PARAMETER(argv);
144
145 #ifdef DEBUG
146 # ifdef _DEBUG
147     if (showdebug(NHTRACE_LOG) && !_s_debugfp) {
148         /* truncate trace file */
149         _s_debugfp = fopen(NHTRACE_LOG, "w");
150     }
151 # endif
152 #endif
153     logDebug("mswin_init_nhwindows()\n");
154
155     mswin_nh_input_init();
156
157     /* set it to WIN_ERR so we can detect attempts to
158        use this ID before it is inialized */
159     WIN_MAP = WIN_ERR;
160
161     /* Read Windows settings from the reqistry */
162     /* First set safe defaults */
163     GetNHApp()->regMainMinX = CW_USEDEFAULT;
164     mswin_read_reg();
165     /* Create the main window */
166     GetNHApp()->hMainWnd = mswin_init_main_window();
167     if (!GetNHApp()->hMainWnd) {
168         panic("Cannot create main window");
169     }
170
171     /* Set menu check mark for interface mode */
172     mswin_menu_check_intf_mode();
173
174     /* check default values */
175     if (iflags.wc_fontsiz_status < NHFONT_SIZE_MIN
176         || iflags.wc_fontsiz_status > NHFONT_SIZE_MAX)
177         iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE;
178
179     if (iflags.wc_fontsiz_message < NHFONT_SIZE_MIN
180         || iflags.wc_fontsiz_message > NHFONT_SIZE_MAX)
181         iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE;
182
183     if (iflags.wc_fontsiz_text < NHFONT_SIZE_MIN
184         || iflags.wc_fontsiz_text > NHFONT_SIZE_MAX)
185         iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE;
186
187     if (iflags.wc_fontsiz_menu < NHFONT_SIZE_MIN
188         || iflags.wc_fontsiz_menu > NHFONT_SIZE_MAX)
189         iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE;
190
191     if (iflags.wc_align_message == 0)
192         iflags.wc_align_message = ALIGN_TOP;
193     if (iflags.wc_align_status == 0)
194         iflags.wc_align_status = ALIGN_BOTTOM;
195     if (iflags.wc_scroll_margin == 0)
196         iflags.wc_scroll_margin = DEF_CLIPAROUND_MARGIN;
197     if (iflags.wc_scroll_amount == 0)
198         iflags.wc_scroll_amount = DEF_CLIPAROUND_AMOUNT;
199     if (iflags.wc_tile_width == 0)
200         iflags.wc_tile_width = TILE_X;
201     if (iflags.wc_tile_height == 0)
202         iflags.wc_tile_height = TILE_Y;
203
204     if (iflags.wc_vary_msgcount == 0)
205         iflags.wc_vary_msgcount = 4;
206
207     /* force tabs in menus */
208     iflags.menu_tab_sep = 1;
209
210     /* force toptenwin to be true.  toptenwin is the option that decides
211      * whether to
212      * write output to a window or stdout.  stdout doesn't make sense on
213      * Windows
214      * non-console applications
215      */
216     iflags.toptenwin = 1;
217     set_option_mod_status("toptenwin", SET_IN_FILE);
218     //set_option_mod_status("perm_invent", SET_IN_FILE);
219     set_option_mod_status("mouse_support", SET_IN_GAME);
220
221     /* initialize map tiles bitmap */
222     initMapTiles();
223
224     /* set tile-related options to readonly */
225     set_wc_option_mod_status(WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE,
226                              DISP_IN_GAME);
227
228     /* set font-related options to change in the game */
229     set_wc_option_mod_status(
230         WC_HILITE_PET | WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_SCROLL_AMOUNT
231             | WC_SCROLL_MARGIN | WC_MAP_MODE | WC_FONT_MESSAGE
232             | WC_FONT_STATUS | WC_FONT_MENU | WC_FONT_TEXT
233             | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_MENU
234             | WC_FONTSIZ_TEXT | WC_VARY_MSGCOUNT,
235         SET_IN_GAME);
236
237     mswin_color_from_string(iflags.wc_foregrnd_menu, &menu_fg_brush,
238                             &menu_fg_color);
239     mswin_color_from_string(iflags.wc_foregrnd_message, &message_fg_brush,
240                             &message_fg_color);
241     mswin_color_from_string(iflags.wc_foregrnd_status, &status_fg_brush,
242                             &status_fg_color);
243     mswin_color_from_string(iflags.wc_foregrnd_text, &text_fg_brush,
244                             &text_fg_color);
245     mswin_color_from_string(iflags.wc_backgrnd_menu, &menu_bg_brush,
246                             &menu_bg_color);
247     mswin_color_from_string(iflags.wc_backgrnd_message, &message_bg_brush,
248                             &message_bg_color);
249     mswin_color_from_string(iflags.wc_backgrnd_status, &status_bg_brush,
250                             &status_bg_color);
251     mswin_color_from_string(iflags.wc_backgrnd_text, &text_bg_brush,
252                             &text_bg_color);
253
254     if (iflags.wc_splash_screen)
255         mswin_display_splash_window(FALSE);
256
257     iflags.window_inited = TRUE;
258 }
259
260 /* Do a window-port specific player type selection. If player_selection()
261    offers a Quit option, it is its responsibility to clean up and terminate
262    the process. You need to fill in pl_character[0].
263 */
264 void
265 mswin_player_selection(void)
266 {
267     logDebug("mswin_player_selection()\n");
268
269     if (iflags.wc_player_selection == VIA_DIALOG) {
270         /* pick player type randomly (use pre-selected
271          * role/race/gender/alignment) */
272         if (flags.randomall) {
273             if (flags.initrole < 0) {
274                 flags.initrole = pick_role(flags.initrace, flags.initgend,
275                                            flags.initalign, PICK_RANDOM);
276                 if (flags.initrole < 0) {
277                     raw_print("Incompatible role!");
278                     flags.initrole = randrole();
279                 }
280             }
281
282             if (flags.initrace < 0
283                 || !validrace(flags.initrole, flags.initrace)) {
284                 flags.initrace = pick_race(flags.initrole, flags.initgend,
285                                            flags.initalign, PICK_RANDOM);
286                 if (flags.initrace < 0) {
287                     raw_print("Incompatible race!");
288                     flags.initrace = randrace(flags.initrole);
289                 }
290             }
291
292             if (flags.initgend < 0
293                 || !validgend(flags.initrole, flags.initrace,
294                               flags.initgend)) {
295                 flags.initgend = pick_gend(flags.initrole, flags.initrace,
296                                            flags.initalign, PICK_RANDOM);
297                 if (flags.initgend < 0) {
298                     raw_print("Incompatible gender!");
299                     flags.initgend = randgend(flags.initrole, flags.initrace);
300                 }
301             }
302
303             if (flags.initalign < 0
304                 || !validalign(flags.initrole, flags.initrace,
305                                flags.initalign)) {
306                 flags.initalign = pick_align(flags.initrole, flags.initrace,
307                                              flags.initgend, PICK_RANDOM);
308                 if (flags.initalign < 0) {
309                     raw_print("Incompatible alignment!");
310                     flags.initalign =
311                         randalign(flags.initrole, flags.initrace);
312                 }
313             }
314         } else {
315             /* select a role */
316             if (!mswin_player_selection_window()) {
317                 bail(0);
318             }
319         }
320     } else { /* iflags.wc_player_selection == VIA_PROMPTS */
321         prompt_for_player_selection();
322     }
323 }
324
325 void
326 prompt_for_player_selection(void)
327 {
328     int i, k, n;
329     char pick4u = 'n', thisch, lastch = 0;
330     char pbuf[QBUFSZ], plbuf[QBUFSZ];
331     winid win;
332     anything any;
333     menu_item *selected = 0;
334     DWORD box_result;
335
336     logDebug("prompt_for_player_selection()\n");
337
338     /* prevent an unnecessary prompt */
339     rigid_role_checks();
340
341     /* Should we randomly pick for the player? */
342     if (!flags.randomall
343         && (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE
344             || flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
345         /* int echoline; */
346         char *prompt = build_plselection_prompt(
347             pbuf, QBUFSZ, flags.initrole, flags.initrace, flags.initgend,
348             flags.initalign);
349
350         /* tty_putstr(BASE_WINDOW, 0, ""); */
351         /* echoline = wins[BASE_WINDOW]->cury; */
352         box_result = NHMessageBox(NULL, prompt, MB_YESNOCANCEL | MB_DEFBUTTON1
353                                                     | MB_ICONQUESTION);
354         pick4u =
355             (box_result == IDYES) ? 'y' : (box_result == IDNO) ? 'n' : '\033';
356         /* tty_putstr(BASE_WINDOW, 0, prompt); */
357         do {
358             /* pick4u = lowc(readchar()); */
359             if (index(quitchars, pick4u))
360                 pick4u = 'y';
361         } while (!index(ynqchars, pick4u));
362         if ((int) strlen(prompt) + 1 < CO) {
363             /* Echo choice and move back down line */
364             /* tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline,
365              * pick4u); */
366             /* tty_putstr(BASE_WINDOW, 0, ""); */
367         } else
368             /* Otherwise it's hard to tell where to echo, and things are
369              * wrapping a bit messily anyway, so (try to) make sure the next
370              * question shows up well and doesn't get wrapped at the
371              * bottom of the window.
372              */
373             /* tty_clear_nhwindow(BASE_WINDOW) */;
374
375         if (pick4u != 'y' && pick4u != 'n') {
376         give_up: /* Quit */
377             if (selected)
378                 free((genericptr_t) selected);
379             bail((char *) 0);
380             /*NOTREACHED*/
381             return;
382         }
383     }
384
385     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
386                                    flags.initrace, flags.initgend,
387                                    flags.initalign);
388
389     /* Select a role, if necessary */
390     /* we'll try to be compatible with pre-selected race/gender/alignment,
391      * but may not succeed */
392     if (flags.initrole < 0) {
393         char rolenamebuf[QBUFSZ];
394         /* Process the choice */
395         if (pick4u == 'y' || flags.initrole == ROLE_RANDOM
396             || flags.randomall) {
397             /* Pick a random role */
398             flags.initrole = pick_role(flags.initrace, flags.initgend,
399                                        flags.initalign, PICK_RANDOM);
400             if (flags.initrole < 0) {
401                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible role!"); */
402                 flags.initrole = randrole();
403             }
404         } else {
405             /* tty_clear_nhwindow(BASE_WINDOW); */
406             /* tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role"); */
407             /* Prompt for a role */
408             win = create_nhwindow(NHW_MENU);
409             start_menu(win);
410             any = zeroany; /* zero out all bits */
411             for (i = 0; roles[i].name.m; i++) {
412                 if (ok_role(i, flags.initrace, flags.initgend,
413                             flags.initalign)) {
414                     any.a_int = i + 1; /* must be non-zero */
415                     thisch = lowc(roles[i].name.m[0]);
416                     if (thisch == lastch)
417                         thisch = highc(thisch);
418                     if (flags.initgend != ROLE_NONE
419                         && flags.initgend != ROLE_RANDOM) {
420                         if (flags.initgend == 1 && roles[i].name.f)
421                             Strcpy(rolenamebuf, roles[i].name.f);
422                         else
423                             Strcpy(rolenamebuf, roles[i].name.m);
424                     } else {
425                         if (roles[i].name.f) {
426                             Strcpy(rolenamebuf, roles[i].name.m);
427                             Strcat(rolenamebuf, "/");
428                             Strcat(rolenamebuf, roles[i].name.f);
429                         } else
430                             Strcpy(rolenamebuf, roles[i].name.m);
431                     }
432                     add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE,
433                              an(rolenamebuf), MENU_UNSELECTED);
434                     lastch = thisch;
435                 }
436             }
437             any.a_int = pick_role(flags.initrace, flags.initgend,
438                                   flags.initalign, PICK_RANDOM) + 1;
439             if (any.a_int == 0) /* must be non-zero */
440                 any.a_int = randrole() + 1;
441             add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
442                      MENU_UNSELECTED);
443             any.a_int = i + 1; /* must be non-zero */
444             add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
445                      MENU_UNSELECTED);
446             Sprintf(pbuf, "Pick a role for your %s", plbuf);
447             end_menu(win, pbuf);
448             n = select_menu(win, PICK_ONE, &selected);
449             destroy_nhwindow(win);
450
451             /* Process the choice */
452             if (n != 1 || selected[0].item.a_int == any.a_int)
453                 goto give_up; /* Selected quit */
454
455             flags.initrole = selected[0].item.a_int - 1;
456             free((genericptr_t) selected), selected = 0;
457         }
458         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
459                                        flags.initrace, flags.initgend,
460                                        flags.initalign);
461     }
462
463     /* Select a race, if necessary */
464     /* force compatibility with role, try for compatibility with
465      * pre-selected gender/alignment */
466     if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
467         /* pre-selected race not valid */
468         if (pick4u == 'y' || flags.initrace == ROLE_RANDOM
469             || flags.randomall) {
470             flags.initrace = pick_race(flags.initrole, flags.initgend,
471                                        flags.initalign, PICK_RANDOM);
472             if (flags.initrace < 0) {
473                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible race!"); */
474                 flags.initrace = randrace(flags.initrole);
475             }
476         } else { /* pick4u == 'n' */
477             /* Count the number of valid races */
478             n = 0; /* number valid */
479             k = 0; /* valid race */
480             for (i = 0; races[i].noun; i++) {
481                 if (ok_race(flags.initrole, i, flags.initgend,
482                             flags.initalign)) {
483                     n++;
484                     k = i;
485                 }
486             }
487             if (n == 0) {
488                 for (i = 0; races[i].noun; i++) {
489                     if (validrace(flags.initrole, i)) {
490                         n++;
491                         k = i;
492                     }
493                 }
494             }
495
496             /* Permit the user to pick, if there is more than one */
497             if (n > 1) {
498                 /* tty_clear_nhwindow(BASE_WINDOW); */
499                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Race"); */
500                 win = create_nhwindow(NHW_MENU);
501                 start_menu(win);
502                 any = zeroany; /* zero out all bits */
503                 for (i = 0; races[i].noun; i++)
504                     if (ok_race(flags.initrole, i, flags.initgend,
505                                 flags.initalign)) {
506                         any.a_int = i + 1; /* must be non-zero */
507                         add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0,
508                                  ATR_NONE, races[i].noun, MENU_UNSELECTED);
509                     }
510                 any.a_int = pick_race(flags.initrole, flags.initgend,
511                                       flags.initalign, PICK_RANDOM) + 1;
512                 if (any.a_int == 0) /* must be non-zero */
513                     any.a_int = randrace(flags.initrole) + 1;
514                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
515                          MENU_UNSELECTED);
516                 any.a_int = i + 1; /* must be non-zero */
517                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
518                          MENU_UNSELECTED);
519                 Sprintf(pbuf, "Pick the race of your %s", plbuf);
520                 end_menu(win, pbuf);
521                 n = select_menu(win, PICK_ONE, &selected);
522                 destroy_nhwindow(win);
523                 if (n != 1 || selected[0].item.a_int == any.a_int)
524                     goto give_up; /* Selected quit */
525
526                 k = selected[0].item.a_int - 1;
527                 free((genericptr_t) selected), selected = 0;
528             }
529             flags.initrace = k;
530         }
531         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
532                                        flags.initrace, flags.initgend,
533                                        flags.initalign);
534     }
535
536     /* Select a gender, if necessary */
537     /* force compatibility with role/race, try for compatibility with
538      * pre-selected alignment */
539     if (flags.initgend < 0
540         || !validgend(flags.initrole, flags.initrace, flags.initgend)) {
541         /* pre-selected gender not valid */
542         if (pick4u == 'y' || flags.initgend == ROLE_RANDOM
543             || flags.randomall) {
544             flags.initgend = pick_gend(flags.initrole, flags.initrace,
545                                        flags.initalign, PICK_RANDOM);
546             if (flags.initgend < 0) {
547                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible gender!"); */
548                 flags.initgend = randgend(flags.initrole, flags.initrace);
549             }
550         } else { /* pick4u == 'n' */
551             /* Count the number of valid genders */
552             n = 0; /* number valid */
553             k = 0; /* valid gender */
554             for (i = 0; i < ROLE_GENDERS; i++) {
555                 if (ok_gend(flags.initrole, flags.initrace, i,
556                             flags.initalign)) {
557                     n++;
558                     k = i;
559                 }
560             }
561             if (n == 0) {
562                 for (i = 0; i < ROLE_GENDERS; i++) {
563                     if (validgend(flags.initrole, flags.initrace, i)) {
564                         n++;
565                         k = i;
566                     }
567                 }
568             }
569
570             /* Permit the user to pick, if there is more than one */
571             if (n > 1) {
572                 /* tty_clear_nhwindow(BASE_WINDOW); */
573                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Gender"); */
574                 win = create_nhwindow(NHW_MENU);
575                 start_menu(win);
576                 any = zeroany; /* zero out all bits */
577                 for (i = 0; i < ROLE_GENDERS; i++)
578                     if (ok_gend(flags.initrole, flags.initrace, i,
579                                 flags.initalign)) {
580                         any.a_int = i + 1;
581                         add_menu(win, NO_GLYPH, &any, genders[i].adj[0], 0,
582                                  ATR_NONE, genders[i].adj, MENU_UNSELECTED);
583                     }
584                 any.a_int = pick_gend(flags.initrole, flags.initrace,
585                                       flags.initalign, PICK_RANDOM) + 1;
586                 if (any.a_int == 0) /* must be non-zero */
587                     any.a_int = randgend(flags.initrole, flags.initrace) + 1;
588                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
589                          MENU_UNSELECTED);
590                 any.a_int = i + 1; /* must be non-zero */
591                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
592                          MENU_UNSELECTED);
593                 Sprintf(pbuf, "Pick the gender of your %s", plbuf);
594                 end_menu(win, pbuf);
595                 n = select_menu(win, PICK_ONE, &selected);
596                 destroy_nhwindow(win);
597                 if (n != 1 || selected[0].item.a_int == any.a_int)
598                     goto give_up; /* Selected quit */
599
600                 k = selected[0].item.a_int - 1;
601                 free((genericptr_t) selected), selected = 0;
602             }
603             flags.initgend = k;
604         }
605         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
606                                        flags.initrace, flags.initgend,
607                                        flags.initalign);
608     }
609
610     /* Select an alignment, if necessary */
611     /* force compatibility with role/race/gender */
612     if (flags.initalign < 0
613         || !validalign(flags.initrole, flags.initrace, flags.initalign)) {
614         /* pre-selected alignment not valid */
615         if (pick4u == 'y' || flags.initalign == ROLE_RANDOM
616             || flags.randomall) {
617             flags.initalign = pick_align(flags.initrole, flags.initrace,
618                                          flags.initgend, PICK_RANDOM);
619             if (flags.initalign < 0) {
620                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); */
621                 flags.initalign = randalign(flags.initrole, flags.initrace);
622             }
623         } else { /* pick4u == 'n' */
624             /* Count the number of valid alignments */
625             n = 0; /* number valid */
626             k = 0; /* valid alignment */
627             for (i = 0; i < ROLE_ALIGNS; i++) {
628                 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
629                              i)) {
630                     n++;
631                     k = i;
632                 }
633             }
634             if (n == 0) {
635                 for (i = 0; i < ROLE_ALIGNS; i++) {
636                     if (validalign(flags.initrole, flags.initrace, i)) {
637                         n++;
638                         k = i;
639                     }
640                 }
641             }
642
643             /* Permit the user to pick, if there is more than one */
644             if (n > 1) {
645                 /* tty_clear_nhwindow(BASE_WINDOW); */
646                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Alignment"); */
647                 win = create_nhwindow(NHW_MENU);
648                 start_menu(win);
649                 any = zeroany; /* zero out all bits */
650                 for (i = 0; i < ROLE_ALIGNS; i++)
651                     if (ok_align(flags.initrole, flags.initrace,
652                                  flags.initgend, i)) {
653                         any.a_int = i + 1;
654                         add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0,
655                                  ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
656                     }
657                 any.a_int = pick_align(flags.initrole, flags.initrace,
658                                        flags.initgend, PICK_RANDOM) + 1;
659                 if (any.a_int == 0) /* must be non-zero */
660                     any.a_int = randalign(flags.initrole, flags.initrace) + 1;
661                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
662                          MENU_UNSELECTED);
663                 any.a_int = i + 1; /* must be non-zero */
664                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
665                          MENU_UNSELECTED);
666                 Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
667                 end_menu(win, pbuf);
668                 n = select_menu(win, PICK_ONE, &selected);
669                 destroy_nhwindow(win);
670                 if (n != 1 || selected[0].item.a_int == any.a_int)
671                     goto give_up; /* Selected quit */
672
673                 k = selected[0].item.a_int - 1;
674                 free((genericptr_t) selected), selected = 0;
675             }
676             flags.initalign = k;
677         }
678     }
679     /* Success! */
680     /* tty_display_nhwindow(BASE_WINDOW, FALSE); */
681 }
682
683 /* Ask the user for a player name. */
684 void
685 mswin_askname(void)
686 {
687     logDebug("mswin_askname()\n");
688
689     if (mswin_getlin_window("Who are you?", plname, PL_NSIZ) == IDCANCEL) {
690         bail("bye-bye");
691         /* not reached */
692     }
693 }
694
695 /* Does window event processing (e.g. exposure events).
696    A noop for the tty and X window-ports.
697 */
698 void
699 mswin_get_nh_event(void)
700 {
701     MSG msg;
702
703     logDebug("mswin_get_nh_event()\n");
704
705     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
706         if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) {
707             TranslateMessage(&msg);
708             DispatchMessage(&msg);
709         }
710     }
711     return;
712 }
713
714 /* Exits the window system.  This should dismiss all windows,
715    except the "window" used for raw_print().  str is printed if possible.
716 */
717 void
718 mswin_exit_nhwindows(const char *str)
719 {
720     logDebug("mswin_exit_nhwindows(%s)\n", str);
721
722     /* Write Window settings to the registry */
723     mswin_write_reg();
724
725 }
726
727 /* Prepare the window to be suspended. */
728 void
729 mswin_suspend_nhwindows(const char *str)
730 {
731     logDebug("mswin_suspend_nhwindows(%s)\n", str);
732
733     return;
734 }
735
736 /* Restore the windows after being suspended. */
737 void
738 mswin_resume_nhwindows()
739 {
740     logDebug("mswin_resume_nhwindows()\n");
741
742     return;
743 }
744
745 /*  Create a window of type "type" which can be
746         NHW_MESSAGE     (top line)
747         NHW_STATUS      (bottom lines)
748         NHW_MAP         (main dungeon)
749         NHW_MENU        (inventory or other "corner" windows)
750         NHW_TEXT        (help/text, full screen paged window)
751 */
752 winid
753 mswin_create_nhwindow(int type)
754 {
755     winid i = 0;
756     MSNHMsgAddWnd data;
757
758     logDebug("mswin_create_nhwindow(%d)\n", type);
759
760     /* Return the next available winid
761      */
762
763     for (i = 1; i < MAXWINDOWS; i++)
764         if (GetNHApp()->windowlist[i].win == NULL
765             && !GetNHApp()->windowlist[i].dead)
766             break;
767     if (i == MAXWINDOWS)
768         panic("ERROR:  No windows available...\n");
769
770     switch (type) {
771     case NHW_MAP: {
772         GetNHApp()->windowlist[i].win = mswin_init_map_window();
773         GetNHApp()->windowlist[i].type = type;
774         GetNHApp()->windowlist[i].dead = 0;
775         break;
776     }
777     case NHW_MESSAGE: {
778         GetNHApp()->windowlist[i].win = mswin_init_message_window();
779         GetNHApp()->windowlist[i].type = type;
780         GetNHApp()->windowlist[i].dead = 0;
781         break;
782     }
783     case NHW_STATUS: {
784         GetNHApp()->windowlist[i].win = mswin_init_status_window();
785         GetNHApp()->windowlist[i].type = type;
786         GetNHApp()->windowlist[i].dead = 0;
787         break;
788     }
789     case NHW_MENU: {
790         GetNHApp()->windowlist[i].win = NULL; // will create later
791         GetNHApp()->windowlist[i].type = type;
792         GetNHApp()->windowlist[i].dead = 1;
793         break;
794     }
795     case NHW_TEXT: {
796         GetNHApp()->windowlist[i].win = mswin_init_text_window();
797         GetNHApp()->windowlist[i].type = type;
798         GetNHApp()->windowlist[i].dead = 0;
799         break;
800     }
801     }
802
803     ZeroMemory(&data, sizeof(data));
804     data.wid = i;
805     SendMessage(GetNHApp()->hMainWnd, WM_MSNH_COMMAND,
806                 (WPARAM) MSNH_MSG_ADDWND, (LPARAM) &data);
807     return i;
808 }
809
810 /* Clear the given window, when asked to. */
811 void
812 mswin_clear_nhwindow(winid wid)
813 {
814     logDebug("mswin_clear_nhwindow(%d)\n", wid);
815
816     if ((wid >= 0) && (wid < MAXWINDOWS)
817         && (GetNHApp()->windowlist[wid].win != NULL)) {
818         if (GetNHApp()->windowlist[wid].type == NHW_MAP) {
819             if (Is_rogue_level(&u.uz))
820                 mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
821                                ROGUE_LEVEL_MAP_MODE);
822             else
823                 mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
824                                iflags.wc_map_mode);
825         }
826
827         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
828                     (WPARAM) MSNH_MSG_CLEAR_WINDOW, (LPARAM) NULL);
829     }
830 }
831
832 /* -- Display the window on the screen.  If there is data
833                    pending for output in that window, it should be sent.
834                    If blocking is TRUE, display_nhwindow() will not
835                    return until the data has been displayed on the screen,
836                    and acknowledged by the user where appropriate.
837                 -- All calls are blocking in the tty window-port.
838                 -- Calling display_nhwindow(WIN_MESSAGE,???) will do a
839                    --more--, if necessary, in the tty window-port.
840 */
841 void
842 mswin_display_nhwindow(winid wid, BOOLEAN_P block)
843 {
844     logDebug("mswin_display_nhwindow(%d, %d)\n", wid, block);
845     if (GetNHApp()->windowlist[wid].win != NULL) {
846         ShowWindow(GetNHApp()->windowlist[wid].win, SW_SHOW);
847         mswin_layout_main_window(GetNHApp()->windowlist[wid].win);
848         if (GetNHApp()->windowlist[wid].type == NHW_MENU) {
849             MENU_ITEM_P *p;
850             mswin_menu_window_select_menu(GetNHApp()->windowlist[wid].win,
851                                           PICK_NONE, &p, TRUE);
852         }
853         if (GetNHApp()->windowlist[wid].type == NHW_TEXT) {
854             mswin_display_text_window(GetNHApp()->windowlist[wid].win);
855         }
856         if (GetNHApp()->windowlist[wid].type == NHW_RIP) {
857             mswin_display_RIP_window(GetNHApp()->windowlist[wid].win);
858         } else {
859             if (!block) {
860                 UpdateWindow(GetNHApp()->windowlist[wid].win);
861             } else {
862                 if (GetNHApp()->windowlist[wid].type == NHW_MAP) {
863                     (void) mswin_nhgetch();
864                 }
865             }
866         }
867         SetFocus(GetNHApp()->hMainWnd);
868     }
869 }
870
871 HWND
872 mswin_hwnd_from_winid(winid wid)
873 {
874     if (wid >= 0 && wid < MAXWINDOWS) {
875         return GetNHApp()->windowlist[wid].win;
876     } else {
877         return NULL;
878     }
879 }
880
881 winid
882 mswin_winid_from_handle(HWND hWnd)
883 {
884     winid i = 0;
885
886     for (i = 1; i < MAXWINDOWS; i++)
887         if (GetNHApp()->windowlist[i].win == hWnd)
888             return i;
889     return -1;
890 }
891
892 winid
893 mswin_winid_from_type(int type)
894 {
895     winid i = 0;
896
897     for (i = 1; i < MAXWINDOWS; i++)
898         if (GetNHApp()->windowlist[i].type == type)
899             return i;
900     return -1;
901 }
902
903 void
904 mswin_window_mark_dead(winid wid)
905 {
906     if (wid >= 0 && wid < MAXWINDOWS) {
907         GetNHApp()->windowlist[wid].win = NULL;
908         GetNHApp()->windowlist[wid].dead = 1;
909     }
910 }
911
912 /* Destroy will dismiss the window if the window has not
913  * already been dismissed.
914 */
915 void
916 mswin_destroy_nhwindow(winid wid)
917 {
918     logDebug("mswin_destroy_nhwindow(%d)\n", wid);
919
920     if ((GetNHApp()->windowlist[wid].type == NHW_MAP)
921         || (GetNHApp()->windowlist[wid].type == NHW_MESSAGE)
922         || (GetNHApp()->windowlist[wid].type == NHW_STATUS)) {
923         /* main windows is going to take care of those */
924         return;
925     }
926
927     if (wid != -1) {
928         if (!GetNHApp()->windowlist[wid].dead
929             && GetNHApp()->windowlist[wid].win != NULL)
930             DestroyWindow(GetNHApp()->windowlist[wid].win);
931         GetNHApp()->windowlist[wid].win = NULL;
932         GetNHApp()->windowlist[wid].type = 0;
933         GetNHApp()->windowlist[wid].dead = 0;
934     }
935 }
936
937 /* Next output to window will start at (x,y), also moves
938  displayable cursor to (x,y).  For backward compatibility,
939  1 <= x < cols, 0 <= y < rows, where cols and rows are
940  the size of window.
941 */
942 void
943 mswin_curs(winid wid, int x, int y)
944 {
945     logDebug("mswin_curs(%d, %d, %d)\n", wid, x, y);
946
947     if ((wid >= 0) && (wid < MAXWINDOWS)
948         && (GetNHApp()->windowlist[wid].win != NULL)) {
949         MSNHMsgCursor data;
950         data.x = x;
951         data.y = y;
952         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
953                     (WPARAM) MSNH_MSG_CURSOR, (LPARAM) &data);
954     }
955 }
956
957 /*
958 putstr(window, attr, str)
959                 -- Print str on the window with the given attribute.  Only
960                    printable ASCII characters (040-0126) must be supported.
961                    Multiple putstr()s are output on separate lines.
962 Attributes
963                    can be one of
964                         ATR_NONE (or 0)
965                         ATR_ULINE
966                         ATR_BOLD
967                         ATR_BLINK
968                         ATR_INVERSE
969                    If a window-port does not support all of these, it may map
970                    unsupported attributes to a supported one (e.g. map them
971                    all to ATR_INVERSE).  putstr() may compress spaces out of
972                    str, break str, or truncate str, if necessary for the
973                    display.  Where putstr() breaks a line, it has to clear
974                    to end-of-line.
975                 -- putstr should be implemented such that if two putstr()s
976                    are done consecutively the user will see the first and
977                    then the second.  In the tty port, pline() achieves this
978                    by calling more() or displaying both on the same line.
979 */
980 void
981 mswin_putstr(winid wid, int attr, const char *text)
982 {
983     logDebug("mswin_putstr(%d, %d, %s)\n", wid, attr, text);
984
985     mswin_putstr_ex(wid, attr, text, 0);
986 }
987
988 void
989 mswin_putstr_ex(winid wid, int attr, const char *text, int app)
990 {
991     if ((wid >= 0) && (wid < MAXWINDOWS)) {
992         if (GetNHApp()->windowlist[wid].win == NULL
993             && GetNHApp()->windowlist[wid].type == NHW_MENU) {
994             GetNHApp()->windowlist[wid].win =
995                 mswin_init_menu_window(MENU_TYPE_TEXT);
996             GetNHApp()->windowlist[wid].dead = 0;
997         }
998
999         if (GetNHApp()->windowlist[wid].win != NULL) {
1000             MSNHMsgPutstr data;
1001             ZeroMemory(&data, sizeof(data));
1002             data.attr = attr;
1003             data.text = text;
1004             data.append = app;
1005             SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1006                         (WPARAM) MSNH_MSG_PUTSTR, (LPARAM) &data);
1007         }
1008         /* yield a bit so it gets done immediately */
1009         mswin_get_nh_event();
1010     } else {
1011         // build text to display later in message box
1012         GetNHApp()->saved_text =
1013             realloc(GetNHApp()->saved_text,
1014                     strlen(text) + strlen(GetNHApp()->saved_text) + 1);
1015         strcat(GetNHApp()->saved_text, text);
1016     }
1017 }
1018
1019 /* Display the file named str.  Complain about missing files
1020                    iff complain is TRUE.
1021 */
1022 void
1023 mswin_display_file(const char *filename, BOOLEAN_P must_exist)
1024 {
1025     dlb *f;
1026     TCHAR wbuf[BUFSZ];
1027
1028     logDebug("mswin_display_file(%s, %d)\n", filename, must_exist);
1029
1030     f = dlb_fopen(filename, RDTMODE);
1031     if (!f) {
1032         if (must_exist) {
1033             TCHAR message[90];
1034             _stprintf(message, TEXT("Warning! Could not find file: %s\n"),
1035                       NH_A2W(filename, wbuf, sizeof(wbuf)));
1036             NHMessageBox(GetNHApp()->hMainWnd, message,
1037                          MB_OK | MB_ICONEXCLAMATION);
1038         }
1039     } else {
1040         winid text;
1041         char line[LLEN];
1042
1043         text = mswin_create_nhwindow(NHW_TEXT);
1044
1045         while (dlb_fgets(line, LLEN, f)) {
1046             size_t len;
1047             len = strlen(line);
1048             if (line[len - 1] == '\n')
1049                 line[len - 1] = '\x0';
1050             mswin_putstr(text, ATR_NONE, line);
1051         }
1052         (void) dlb_fclose(f);
1053
1054         mswin_display_nhwindow(text, 1);
1055         mswin_destroy_nhwindow(text);
1056     }
1057 }
1058
1059 /* Start using window as a menu.  You must call start_menu()
1060    before add_menu().  After calling start_menu() you may not
1061    putstr() to the window.  Only windows of type NHW_MENU may
1062    be used for menus.
1063 */
1064 void
1065 mswin_start_menu(winid wid)
1066 {
1067     logDebug("mswin_start_menu(%d)\n", wid);
1068     if ((wid >= 0) && (wid < MAXWINDOWS)) {
1069         if (GetNHApp()->windowlist[wid].win == NULL
1070             && GetNHApp()->windowlist[wid].type == NHW_MENU) {
1071             GetNHApp()->windowlist[wid].win =
1072                 mswin_init_menu_window(MENU_TYPE_MENU);
1073             GetNHApp()->windowlist[wid].dead = 0;
1074         }
1075
1076         if (GetNHApp()->windowlist[wid].win != NULL) {
1077             SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1078                         (WPARAM) MSNH_MSG_STARTMENU, (LPARAM) NULL);
1079         }
1080     }
1081 }
1082
1083 /*
1084 add_menu(windid window, int glyph, const anything identifier,
1085                                 char accelerator, char groupacc,
1086                                 int attr, char *str, boolean preselected)
1087                 -- Add a text line str to the given menu window.  If
1088 identifier
1089                    is 0, then the line cannot be selected (e.g. a title).
1090                    Otherwise, identifier is the value returned if the line is
1091                    selected.  Accelerator is a keyboard key that can be used
1092                    to select the line.  If the accelerator of a selectable
1093                    item is 0, the window system is free to select its own
1094                    accelerator.  It is up to the window-port to make the
1095                    accelerator visible to the user (e.g. put "a - " in front
1096                    of str).  The value attr is the same as in putstr().
1097                    Glyph is an optional glyph to accompany the line.  If
1098                    window port cannot or does not want to display it, this
1099                    is OK.  If there is no glyph applicable, then this
1100                    value will be NO_GLYPH.
1101                 -- All accelerators should be in the range [A-Za-z].
1102                 -- It is expected that callers do not mix accelerator
1103                    choices.  Either all selectable items have an accelerator
1104                    or let the window system pick them.  Don't do both.
1105                 -- Groupacc is a group accelerator.  It may be any character
1106                    outside of the standard accelerator (see above) or a
1107                    number.  If 0, the item is unaffected by any group
1108                    accelerator.  If this accelerator conflicts with
1109                    the menu command (or their user defined alises), it loses.
1110                    The menu commands and aliases take care not to interfere
1111                    with the default object class symbols.
1112                 -- If you want this choice to be preselected when the
1113                    menu is displayed, set preselected to TRUE.
1114 */
1115 void
1116 mswin_add_menu(winid wid, int glyph, const ANY_P *identifier,
1117                CHAR_P accelerator, CHAR_P group_accel, int attr,
1118                const char *str, BOOLEAN_P presel)
1119 {
1120     logDebug("mswin_add_menu(%d, %d, %p, %c, %c, %d, %s, %d)\n", wid, glyph,
1121              identifier, (char) accelerator, (char) group_accel, attr, str,
1122              presel);
1123     if ((wid >= 0) && (wid < MAXWINDOWS)
1124         && (GetNHApp()->windowlist[wid].win != NULL)) {
1125         MSNHMsgAddMenu data;
1126         ZeroMemory(&data, sizeof(data));
1127         data.glyph = glyph;
1128         data.identifier = identifier;
1129         data.accelerator = accelerator;
1130         data.group_accel = group_accel;
1131         data.attr = attr;
1132         data.str = str;
1133         data.presel = presel;
1134
1135         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1136                     (WPARAM) MSNH_MSG_ADDMENU, (LPARAM) &data);
1137     }
1138 }
1139
1140 /*
1141 end_menu(window, prompt)
1142                 -- Stop adding entries to the menu and flushes the window
1143                    to the screen (brings to front?).  Prompt is a prompt
1144                    to give the user.  If prompt is NULL, no prompt will
1145                    be printed.
1146                 ** This probably shouldn't flush the window any more (if
1147                 ** it ever did).  That should be select_menu's job.  -dean
1148 */
1149 void
1150 mswin_end_menu(winid wid, const char *prompt)
1151 {
1152     logDebug("mswin_end_menu(%d, %s)\n", wid, prompt);
1153     if ((wid >= 0) && (wid < MAXWINDOWS)
1154         && (GetNHApp()->windowlist[wid].win != NULL)) {
1155         MSNHMsgEndMenu data;
1156         ZeroMemory(&data, sizeof(data));
1157         data.text = prompt;
1158
1159         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1160                     (WPARAM) MSNH_MSG_ENDMENU, (LPARAM) &data);
1161     }
1162 }
1163
1164 /*
1165 int select_menu(windid window, int how, menu_item **selected)
1166                 -- Return the number of items selected; 0 if none were chosen,
1167                    -1 when explicitly cancelled.  If items were selected, then
1168                    selected is filled in with an allocated array of menu_item
1169                    structures, one for each selected line.  The caller must
1170                    free this array when done with it.  The "count" field
1171                    of selected is a user supplied count.  If the user did
1172                    not supply a count, then the count field is filled with
1173                    -1 (meaning all).  A count of zero is equivalent to not
1174                    being selected and should not be in the list.  If no items
1175                    were selected, then selected is NULL'ed out.  How is the
1176                    mode of the menu.  Three valid values are PICK_NONE,
1177                    PICK_ONE, and PICK_N, meaning: nothing is selectable,
1178                    only one thing is selectable, and any number valid items
1179                    may selected.  If how is PICK_NONE, this function should
1180                    never return anything but 0 or -1.
1181                 -- You may call select_menu() on a window multiple times --
1182                    the menu is saved until start_menu() or destroy_nhwindow()
1183                    is called on the window.
1184                 -- Note that NHW_MENU windows need not have select_menu()
1185                    called for them. There is no way of knowing whether
1186                    select_menu() will be called for the window at
1187                    create_nhwindow() time.
1188 */
1189 int
1190 mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected)
1191 {
1192     int nReturned = -1;
1193
1194     logDebug("mswin_select_menu(%d, %d)\n", wid, how);
1195
1196     if ((wid >= 0) && (wid < MAXWINDOWS)
1197         && (GetNHApp()->windowlist[wid].win != NULL)) {
1198         ShowWindow(GetNHApp()->windowlist[wid].win, SW_SHOW);
1199         nReturned = mswin_menu_window_select_menu(
1200             GetNHApp()->windowlist[wid].win, how, selected,
1201             !(flags.perm_invent && wid == WIN_INVEN
1202               && how == PICK_NONE) /* don't activate inventory window if
1203                                       perm_invent is on */
1204             );
1205     }
1206     return nReturned;
1207 }
1208
1209 /*
1210     -- Indicate to the window port that the inventory has been changed.
1211     -- Merely calls display_inventory() for window-ports that leave the
1212         window up, otherwise empty.
1213 */
1214 void
1215 mswin_update_inventory()
1216 {
1217     logDebug("mswin_update_inventory()\n");
1218     if (flags.perm_invent && program_state.something_worth_saving
1219         && iflags.window_inited && WIN_INVEN != WIN_ERR)
1220         display_inventory(NULL, FALSE);
1221 }
1222
1223 /*
1224 mark_synch()    -- Don't go beyond this point in I/O on any channel until
1225                    all channels are caught up to here.  Can be an empty call
1226                    for the moment
1227 */
1228 void
1229 mswin_mark_synch()
1230 {
1231     logDebug("mswin_mark_synch()\n");
1232 }
1233
1234 /*
1235 wait_synch()    -- Wait until all pending output is complete (*flush*() for
1236                    streams goes here).
1237                 -- May also deal with exposure events etc. so that the
1238                    display is OK when return from wait_synch().
1239 */
1240 void
1241 mswin_wait_synch()
1242 {
1243     logDebug("mswin_wait_synch()\n");
1244     mswin_raw_print_flush();
1245 }
1246
1247 /*
1248 cliparound(x, y)-- Make sure that the user is more-or-less centered on the
1249                    screen if the playing area is larger than the screen.
1250                 -- This function is only defined if CLIPPING is defined.
1251 */
1252 void
1253 mswin_cliparound(int x, int y)
1254 {
1255     winid wid = WIN_MAP;
1256
1257     logDebug("mswin_cliparound(%d, %d)\n", x, y);
1258
1259     if ((wid >= 0) && (wid < MAXWINDOWS)
1260         && (GetNHApp()->windowlist[wid].win != NULL)) {
1261         MSNHMsgClipAround data;
1262         data.x = x;
1263         data.y = y;
1264         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1265                     (WPARAM) MSNH_MSG_CLIPAROUND, (LPARAM) &data);
1266     }
1267 }
1268
1269 /*
1270 print_glyph(window, x, y, glyph, bkglyph)
1271                 -- Print the glyph at (x,y) on the given window.  Glyphs are
1272                    integers at the interface, mapped to whatever the window-
1273                    port wants (symbol, font, color, attributes, ...there's
1274                    a 1-1 map between glyphs and distinct things on the map).
1275                 -- bkglyph is a background glyph for potential use by some
1276                    graphical or tiled environments to allow the depiction
1277                    to fall against a background consistent with the grid 
1278                    around x,y.
1279                    
1280 */
1281 void
1282 mswin_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)
1283 {
1284     logDebug("mswin_print_glyph(%d, %d, %d, %d, %d)\n", wid, x, y, glyph, bkglyph);
1285
1286     if ((wid >= 0) && (wid < MAXWINDOWS)
1287         && (GetNHApp()->windowlist[wid].win != NULL)) {
1288         MSNHMsgPrintGlyph data;
1289
1290         ZeroMemory(&data, sizeof(data));
1291         data.x = x;
1292         data.y = y;
1293         data.glyph = glyph;
1294         data.bkglyph = bkglyph;
1295         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1296                     (WPARAM) MSNH_MSG_PRINT_GLYPH, (LPARAM) &data);
1297     }
1298 }
1299
1300 /*
1301  * mswin_raw_print_accumulate() accumulate the given text into
1302  *   raw_print_strbuf.
1303  */
1304 void
1305 mswin_raw_print_accumulate(const char * str, boolean bold)
1306 {
1307     bold; // ignored for now
1308
1309     if (raw_print_strbuf.str != NULL) strbuf_append(&raw_print_strbuf, "\n");
1310     strbuf_append(&raw_print_strbuf, str);
1311 }
1312
1313 /*
1314  * mswin_raw_print_flush() - display any text found in raw_print_strbuf in a
1315  *   dialog box and clear raw_print_strbuf.
1316  */
1317 void
1318 mswin_raw_print_flush()
1319 {
1320     if (raw_print_strbuf.str != NULL) {
1321         int wlen = strlen(raw_print_strbuf.str) + 1;
1322         TCHAR * wbuf = (TCHAR *) alloc(wlen * sizeof(TCHAR));
1323         if (wbuf != NULL) {
1324             NHMessageBox(GetNHApp()->hMainWnd,
1325                             NH_A2W(raw_print_strbuf.str, wbuf, wlen),
1326                             MB_ICONINFORMATION | MB_OK);
1327             free(wbuf);
1328         }
1329         strbuf_empty(&raw_print_strbuf);
1330     }
1331 }
1332
1333
1334 /*
1335 raw_print(str)  -- Print directly to a screen, or otherwise guarantee that
1336                    the user sees str.  raw_print() appends a newline to str.
1337                    It need not recognize ASCII control characters.  This is
1338                    used during startup (before windowing system initialization
1339                    -- maybe this means only error startup messages are raw),
1340                    for error messages, and maybe other "msg" uses.  E.g.
1341                    updating status for micros (i.e, "saving").
1342 */
1343 void
1344 mswin_raw_print(const char *str)
1345 {
1346     logDebug("mswin_raw_print(%s)\n", str);
1347
1348     if (str && *str) {
1349         extern int redirect_stdout;
1350         if (!redirect_stdout)
1351             mswin_raw_print_accumulate(str, FALSE);
1352         else
1353             fprintf(stdout, "%s", str);
1354     }
1355 }
1356
1357 /*
1358 raw_print_bold(str)
1359                 -- Like raw_print(), but prints in bold/standout (if
1360 possible).
1361 */
1362 void
1363 mswin_raw_print_bold(const char *str)
1364 {
1365     logDebug("mswin_raw_print_bold(%s)\n", str);
1366     if (str && *str) {
1367         extern int redirect_stdout;
1368         if (!redirect_stdout)
1369             mswin_raw_print_accumulate(str, TRUE);
1370         else
1371             fprintf(stdout, "%s", str);
1372     }
1373 }
1374
1375 /*
1376 int nhgetch()   -- Returns a single character input from the user.
1377                 -- In the tty window-port, nhgetch() assumes that tgetch()
1378                    will be the routine the OS provides to read a character.
1379                    Returned character _must_ be non-zero.
1380 */
1381 int
1382 mswin_nhgetch()
1383 {
1384     PMSNHEvent event;
1385     int key = 0;
1386
1387     logDebug("mswin_nhgetch()\n");
1388
1389     while ((event = mswin_input_pop()) == NULL || event->type != NHEVENT_CHAR)
1390         mswin_main_loop();
1391
1392     key = event->kbd.ch;
1393     return (key);
1394 }
1395
1396 /*
1397 int nh_poskey(int *x, int *y, int *mod)
1398                 -- Returns a single character input from the user or a
1399                    a positioning event (perhaps from a mouse).  If the
1400                    return value is non-zero, a character was typed, else,
1401                    a position in the MAP window is returned in x, y and mod.
1402                    mod may be one of
1403
1404                         CLICK_1         -- mouse click type 1
1405                         CLICK_2         -- mouse click type 2
1406
1407                    The different click types can map to whatever the
1408                    hardware supports.  If no mouse is supported, this
1409                    routine always returns a non-zero character.
1410 */
1411 int
1412 mswin_nh_poskey(int *x, int *y, int *mod)
1413 {
1414     PMSNHEvent event;
1415     int key;
1416
1417     logDebug("mswin_nh_poskey()\n");
1418
1419     while ((event = mswin_input_pop()) == NULL)
1420         mswin_main_loop();
1421
1422     if (event->type == NHEVENT_MOUSE) {
1423         if (iflags.wc_mouse_support) {
1424             *mod = event->ms.mod;
1425             *x = event->ms.x;
1426             *y = event->ms.y;
1427         }
1428         key = 0;
1429     } else {
1430         key = event->kbd.ch;
1431     }
1432     return (key);
1433 }
1434
1435 /*
1436 nhbell()        -- Beep at user.  [This will exist at least until sounds are
1437                    redone, since sounds aren't attributable to windows
1438 anyway.]
1439 */
1440 void
1441 mswin_nhbell()
1442 {
1443     logDebug("mswin_nhbell()\n");
1444 }
1445
1446 /*
1447 doprev_message()
1448                 -- Display previous messages.  Used by the ^P command.
1449                 -- On the tty-port this scrolls WIN_MESSAGE back one line.
1450 */
1451 int
1452 mswin_doprev_message()
1453 {
1454     logDebug("mswin_doprev_message()\n");
1455     SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_VSCROLL,
1456                 MAKEWPARAM(SB_LINEUP, 0), (LPARAM) NULL);
1457     return 0;
1458 }
1459
1460 /*
1461 char yn_function(const char *ques, const char *choices, char default)
1462                 -- Print a prompt made up of ques, choices and default.
1463                    Read a single character response that is contained in
1464                    choices or default.  If choices is NULL, all possible
1465                    inputs are accepted and returned.  This overrides
1466                    everything else.  The choices are expected to be in
1467                    lower case.  Entering ESC always maps to 'q', or 'n',
1468                    in that order, if present in choices, otherwise it maps
1469                    to default.  Entering any other quit character (SPACE,
1470                    RETURN, NEWLINE) maps to default.
1471                 -- If the choices string contains ESC, then anything after
1472                    it is an acceptable response, but the ESC and whatever
1473                    follows is not included in the prompt.
1474                 -- If the choices string contains a '#' then accept a count.
1475                    Place this value in the global "yn_number" and return '#'.
1476                 -- This uses the top line in the tty window-port, other
1477                    ports might use a popup.
1478 */
1479 char
1480 mswin_yn_function(const char *question, const char *choices, CHAR_P def)
1481 {
1482     char ch;
1483     char yn_esc_map = '\033';
1484     char message[BUFSZ];
1485     char res_ch[2];
1486     int createcaret;
1487     boolean digit_ok, allow_num;
1488
1489     logDebug("mswin_yn_function(%s, %s, %d)\n", question, choices, def);
1490
1491     if (WIN_MESSAGE == WIN_ERR && choices == ynchars) {
1492         char *text =
1493             realloc(strdup(GetNHApp()->saved_text),
1494                     strlen(question) + strlen(GetNHApp()->saved_text) + 1);
1495         DWORD box_result;
1496         strcat(text, question);
1497         box_result =
1498             NHMessageBox(NULL, NH_W2A(text, message, sizeof(message)),
1499                          MB_ICONQUESTION | MB_YESNOCANCEL
1500                              | ((def == 'y') ? MB_DEFBUTTON1
1501                                              : (def == 'n') ? MB_DEFBUTTON2
1502                                                             : MB_DEFBUTTON3));
1503         free(text);
1504         GetNHApp()->saved_text = strdup("");
1505         return box_result == IDYES ? 'y' : box_result == IDNO ? 'n' : '\033';
1506     }
1507
1508     if (choices) {
1509         char *cb, choicebuf[QBUFSZ];
1510
1511         allow_num = (index(choices, '#') != 0);
1512
1513         Strcpy(choicebuf, choices);
1514         if ((cb = index(choicebuf, '\033')) != 0) {
1515             /* anything beyond <esc> is hidden */
1516             *cb = '\0';
1517         }
1518         (void) strncpy(message, question, QBUFSZ - 1);
1519         message[QBUFSZ - 1] = '\0';
1520         sprintf(eos(message), " [%s]", choicebuf);
1521         if (def)
1522             sprintf(eos(message), " (%c)", def);
1523         Strcat(message, " ");
1524         /* escape maps to 'q' or 'n' or default, in that order */
1525         yn_esc_map =
1526             (index(choices, 'q') ? 'q' : (index(choices, 'n') ? 'n' : def));
1527     } else {
1528         Strcpy(message, question);
1529         Strcat(message, " ");
1530     }
1531
1532     createcaret = 1;
1533     SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1534                 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1535
1536     mswin_clear_nhwindow(WIN_MESSAGE);
1537     mswin_putstr(WIN_MESSAGE, ATR_BOLD, message);
1538
1539     /* Only here if main window is not present */
1540     ch = 0;
1541     do {
1542         ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1543         ch = mswin_nhgetch();
1544         HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1545         if (choices)
1546             ch = lowc(ch);
1547         else
1548             break; /* If choices is NULL, all possible inputs are accepted and
1549                       returned. */
1550
1551         digit_ok = allow_num && digit(ch);
1552         if (ch == '\033') {
1553             if (index(choices, 'q'))
1554                 ch = 'q';
1555             else if (index(choices, 'n'))
1556                 ch = 'n';
1557             else
1558                 ch = def;
1559             break;
1560         } else if (index(quitchars, ch)) {
1561             ch = def;
1562             break;
1563         } else if (!index(choices, ch) && !digit_ok) {
1564             mswin_nhbell();
1565             ch = (char) 0;
1566             /* and try again... */
1567         } else if (ch == '#' || digit_ok) {
1568             char z, digit_string[2];
1569             int n_len = 0;
1570             long value = 0;
1571             mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, ("#"), 1);
1572             n_len++;
1573             digit_string[1] = '\0';
1574             if (ch != '#') {
1575                 digit_string[0] = ch;
1576                 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, 1);
1577                 n_len++;
1578                 value = ch - '0';
1579                 ch = '#';
1580             }
1581             do { /* loop until we get a non-digit */
1582                 z = lowc(readchar());
1583                 if (digit(z)) {
1584                     value = (10 * value) + (z - '0');
1585                     if (value < 0)
1586                         break; /* overflow: try again */
1587                     digit_string[0] = z;
1588                     mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, 1);
1589                     n_len++;
1590                 } else if (z == 'y' || index(quitchars, z)) {
1591                     if (z == '\033')
1592                         value = -1; /* abort */
1593                     z = '\n';       /* break */
1594                 } else if (z == '\b') {
1595                     if (n_len <= 1) {
1596                         value = -1;
1597                         break;
1598                     } else {
1599                         value /= 10;
1600                         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string,
1601                                         -1);
1602                         n_len--;
1603                     }
1604                 } else {
1605                     value = -1; /* abort */
1606                     mswin_nhbell();
1607                     break;
1608                 }
1609             } while (z != '\n');
1610             if (value > 0)
1611                 yn_number = value;
1612             else if (value == 0)
1613                 ch = 'n'; /* 0 => "no" */
1614             else {        /* remove number from top line, then try again */
1615                 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, -n_len);
1616                 n_len = 0;
1617                 ch = (char) 0;
1618             }
1619         }
1620     } while (!ch);
1621
1622     createcaret = 0;
1623     SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1624                 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1625
1626     /* display selection in the message window */
1627     if (isprint((uchar) ch) && ch != '#') {
1628         res_ch[0] = ch;
1629         res_ch[1] = '\x0';
1630         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, res_ch, 1);
1631     }
1632
1633     return ch;
1634 }
1635
1636 /*
1637 getlin(const char *ques, char *input)
1638             -- Prints ques as a prompt and reads a single line of text,
1639                up to a newline.  The string entered is returned without the
1640                newline.  ESC is used to cancel, in which case the string
1641                "\033\000" is returned.
1642             -- getlin() must call flush_screen(1) before doing anything.
1643             -- This uses the top line in the tty window-port, other
1644                ports might use a popup.
1645 */
1646 void
1647 mswin_getlin(const char *question, char *input)
1648 {
1649     logDebug("mswin_getlin(%s, %p)\n", question, input);
1650
1651     if (!iflags.wc_popup_dialog) {
1652 #if 0 /*JP*/
1653         char c;
1654 #else
1655         int c;
1656 #endif
1657         int len;
1658         int done;
1659         int createcaret;
1660
1661         createcaret = 1;
1662         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1663                     (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1664
1665         /* mswin_clear_nhwindow(WIN_MESSAGE); */
1666         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, question, 0);
1667         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, " ", 1);
1668 #ifdef EDIT_GETLIN
1669         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, input, 0);
1670         len = strlen(input);
1671 #else
1672         input[0] = '\0';
1673         len = 0;
1674 #endif
1675         ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1676         done = FALSE;
1677         while (!done) {
1678             c = mswin_nhgetch();
1679             switch (c) {
1680             case VK_ESCAPE:
1681                 strcpy(input, "\033");
1682                 done = TRUE;
1683                 break;
1684             case '\n':
1685             case '\r':
1686             case -115:
1687                 done = TRUE;
1688                 break;
1689             default:
1690                 if (input[0])
1691                     mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, input, -len);
1692                 if (c == VK_BACK) {
1693                     if (len > 0)
1694                         len--;
1695 #if 1 /*JP*//*2\83o\83C\83g\95\8e\9a\82È\82ç\82à\82¤1\83o\83C\83g\8fÁ\82·*/
1696                     if (len > 0 && is_kanji2(input, len))
1697                         len--;
1698 #endif
1699                     input[len] = '\0';
1700                 } else if (len>=(BUFSZ-1)) {
1701                     PlaySound((LPCSTR)SND_ALIAS_SYSTEMEXCLAMATION, NULL, SND_ALIAS_ID|SND_ASYNC);
1702                 } else {
1703                     input[len++] = c;
1704 #if 1 /*JP*//*2\83o\83C\83g\95\8e\9a\82È\82ç\82»\82Ì\8fê\82Å\82à\82¤1\83o\83C\83g\93Ç\82Ý\8d\9e\82Þ*/
1705                     if (is_kanji(c)){
1706                         c = mswin_nhgetch();
1707                         input[len++] = c;
1708                     }
1709 #endif
1710                     input[len] = '\0';
1711                 }
1712                 mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, input, 1);
1713                 break;
1714             }
1715         }
1716         HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1717         createcaret = 0;
1718         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1719                     (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1720     } else {
1721         if (mswin_getlin_window(question, input, BUFSZ) == IDCANCEL) {
1722             strcpy(input, "\033");
1723         }
1724     }
1725 }
1726
1727 /*
1728 int get_ext_cmd(void)
1729             -- Get an extended command in a window-port specific way.
1730                An index into extcmdlist[] is returned on a successful
1731                selection, -1 otherwise.
1732 */
1733 int
1734 mswin_get_ext_cmd()
1735 {
1736     int ret;
1737     logDebug("mswin_get_ext_cmd()\n");
1738
1739     if (!iflags.wc_popup_dialog) {
1740         char c;
1741         char cmd[BUFSZ];
1742         int i, len;
1743         int createcaret;
1744
1745         createcaret = 1;
1746         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1747                     (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1748
1749         cmd[0] = '\0';
1750         i = -2;
1751         mswin_clear_nhwindow(WIN_MESSAGE);
1752         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, "#", 0);
1753         len = 0;
1754         ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1755         while (i == -2) {
1756             int oindex, com_index;
1757             c = mswin_nhgetch();
1758             switch (c) {
1759             case VK_ESCAPE:
1760                 i = -1;
1761                 break;
1762             case '\n':
1763             case '\r':
1764             case -115:
1765                 for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++)
1766                     if (!strcmpi(cmd, extcmdlist[i].ef_txt))
1767                         break;
1768
1769                 if (extcmdlist[i].ef_txt == (char *) 0) {
1770                     pline("%s: unknown extended command.", cmd);
1771                     i = -1;
1772                 }
1773                 break;
1774             default:
1775                 if (cmd[0])
1776                     mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, cmd,
1777                                     -(int) strlen(cmd));
1778                 if (c == VK_BACK) {
1779                     if (len > 0)
1780                         len--;
1781                     cmd[len] = '\0';
1782                 } else {
1783                     cmd[len++] = c;
1784                     cmd[len] = '\0';
1785                     /* Find a command with this prefix in extcmdlist */
1786                     com_index = -1;
1787                     for (oindex = 0; extcmdlist[oindex].ef_txt != (char *) 0;
1788                          oindex++) {
1789                         if ((extcmdlist[oindex].flags & AUTOCOMPLETE)
1790                             && !(!wizard && (extcmdlist[oindex].flags & WIZMODECMD))
1791                             && !strncmpi(cmd, extcmdlist[oindex].ef_txt, len)) {
1792                             if (com_index == -1) /* no matches yet */
1793                                 com_index = oindex;
1794                             else
1795                                 com_index =
1796                                     -2; /* two matches, don't complete */
1797                         }
1798                     }
1799                     if (com_index >= 0) {
1800                         Strcpy(cmd, extcmdlist[com_index].ef_txt);
1801                     }
1802                 }
1803                 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, cmd, 1);
1804                 break;
1805             }
1806         }
1807         HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1808         createcaret = 0;
1809         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1810                     (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1811         return i;
1812     } else {
1813         if (mswin_ext_cmd_window(&ret) == IDCANCEL)
1814             return -1;
1815         else
1816             return ret;
1817     }
1818 }
1819
1820 /*
1821 number_pad(state)
1822             -- Initialize the number pad to the given state.
1823 */
1824 void
1825 mswin_number_pad(int state)
1826 {
1827     /* Do Nothing */
1828     logDebug("mswin_number_pad(%d)\n", state);
1829 }
1830
1831 /*
1832 delay_output()  -- Causes a visible delay of 50ms in the output.
1833                Conceptually, this is similar to wait_synch() followed
1834                by a nap(50ms), but allows asynchronous operation.
1835 */
1836 void
1837 mswin_delay_output()
1838 {
1839     logDebug("mswin_delay_output()\n");
1840     Sleep(50);
1841 }
1842
1843 void
1844 mswin_change_color()
1845 {
1846     logDebug("mswin_change_color()\n");
1847 }
1848
1849 char *
1850 mswin_get_color_string()
1851 {
1852     logDebug("mswin_get_color_string()\n");
1853     return ("");
1854 }
1855
1856 /*
1857 start_screen()  -- Only used on Unix tty ports, but must be declared for
1858                completeness.  Sets up the tty to work in full-screen
1859                graphics mode.  Look at win/tty/termcap.c for an
1860                example.  If your window-port does not need this function
1861                just declare an empty function.
1862 */
1863 void
1864 mswin_start_screen()
1865 {
1866     /* Do Nothing */
1867     logDebug("mswin_start_screen()\n");
1868 }
1869
1870 /*
1871 end_screen()    -- Only used on Unix tty ports, but must be declared for
1872                completeness.  The complement of start_screen().
1873 */
1874 void
1875 mswin_end_screen()
1876 {
1877     /* Do Nothing */
1878     logDebug("mswin_end_screen()\n");
1879 }
1880
1881 /*
1882 outrip(winid, int, when)
1883             -- The tombstone code.  If you want the traditional code use
1884                genl_outrip for the value and check the #if in rip.c.
1885 */
1886 #define STONE_LINE_LEN 16
1887 void
1888 mswin_outrip(winid wid, int how, time_t when)
1889 {
1890     char buf[BUFSZ];
1891     long year;
1892
1893     logDebug("mswin_outrip(%d, %d, %ld)\n", wid, how, (long) when);
1894     if ((wid >= 0) && (wid < MAXWINDOWS)) {
1895         DestroyWindow(GetNHApp()->windowlist[wid].win);
1896         GetNHApp()->windowlist[wid].win = mswin_init_RIP_window();
1897         GetNHApp()->windowlist[wid].type = NHW_RIP;
1898         GetNHApp()->windowlist[wid].dead = 0;
1899     }
1900
1901     /* Put name on stone */
1902     Sprintf(buf, "%s", plname);
1903     buf[STONE_LINE_LEN] = 0;
1904     putstr(wid, 0, buf);
1905
1906     /* Put $ on stone */
1907     Sprintf(buf, "%ld Au", done_money);
1908     buf[STONE_LINE_LEN] = 0; /* It could be a *lot* of gold :-) */
1909     putstr(wid, 0, buf);
1910
1911     /* Put together death description */
1912     formatkiller(buf, sizeof buf, how, FALSE);
1913
1914     /* Put death type on stone */
1915     putstr(wid, 0, buf);
1916
1917     /* Put year on stone */
1918     year = yyyymmdd(when) / 10000L;
1919     Sprintf(buf, "%4ld", year);
1920     putstr(wid, 0, buf);
1921     mswin_finish_rip_text(wid);
1922 }
1923
1924 /* handle options updates here */
1925 void
1926 mswin_preference_update(const char *pref)
1927 {
1928     HDC hdc;
1929     int i;
1930
1931     if (stricmp(pref, "font_menu") == 0
1932         || stricmp(pref, "font_size_menu") == 0) {
1933         if (iflags.wc_fontsiz_menu < NHFONT_SIZE_MIN
1934             || iflags.wc_fontsiz_menu > NHFONT_SIZE_MAX)
1935             iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE;
1936
1937         hdc = GetDC(GetNHApp()->hMainWnd);
1938         mswin_get_font(NHW_MENU, ATR_NONE, hdc, TRUE);
1939         mswin_get_font(NHW_MENU, ATR_BOLD, hdc, TRUE);
1940         mswin_get_font(NHW_MENU, ATR_DIM, hdc, TRUE);
1941         mswin_get_font(NHW_MENU, ATR_ULINE, hdc, TRUE);
1942         mswin_get_font(NHW_MENU, ATR_BLINK, hdc, TRUE);
1943         mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, TRUE);
1944         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1945
1946         mswin_layout_main_window(NULL);
1947         return;
1948     }
1949
1950     if (stricmp(pref, "font_status") == 0
1951         || stricmp(pref, "font_size_status") == 0) {
1952         if (iflags.wc_fontsiz_status < NHFONT_SIZE_MIN
1953             || iflags.wc_fontsiz_status > NHFONT_SIZE_MAX)
1954             iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE;
1955
1956         hdc = GetDC(GetNHApp()->hMainWnd);
1957         mswin_get_font(NHW_STATUS, ATR_NONE, hdc, TRUE);
1958         mswin_get_font(NHW_STATUS, ATR_BOLD, hdc, TRUE);
1959         mswin_get_font(NHW_STATUS, ATR_DIM, hdc, TRUE);
1960         mswin_get_font(NHW_STATUS, ATR_ULINE, hdc, TRUE);
1961         mswin_get_font(NHW_STATUS, ATR_BLINK, hdc, TRUE);
1962         mswin_get_font(NHW_STATUS, ATR_INVERSE, hdc, TRUE);
1963         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1964
1965         for (i = 1; i < MAXWINDOWS; i++) {
1966             if (GetNHApp()->windowlist[i].type == NHW_STATUS
1967                 && GetNHApp()->windowlist[i].win != NULL) {
1968                 InvalidateRect(GetNHApp()->windowlist[i].win, NULL, TRUE);
1969             }
1970         }
1971         mswin_layout_main_window(NULL);
1972         return;
1973     }
1974
1975     if (stricmp(pref, "font_message") == 0
1976         || stricmp(pref, "font_size_message") == 0) {
1977         if (iflags.wc_fontsiz_message < NHFONT_SIZE_MIN
1978             || iflags.wc_fontsiz_message > NHFONT_SIZE_MAX)
1979             iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE;
1980
1981         hdc = GetDC(GetNHApp()->hMainWnd);
1982         mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, TRUE);
1983         mswin_get_font(NHW_MESSAGE, ATR_BOLD, hdc, TRUE);
1984         mswin_get_font(NHW_MESSAGE, ATR_DIM, hdc, TRUE);
1985         mswin_get_font(NHW_MESSAGE, ATR_ULINE, hdc, TRUE);
1986         mswin_get_font(NHW_MESSAGE, ATR_BLINK, hdc, TRUE);
1987         mswin_get_font(NHW_MESSAGE, ATR_INVERSE, hdc, TRUE);
1988         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1989
1990         InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE);
1991         mswin_layout_main_window(NULL);
1992         return;
1993     }
1994
1995     if (stricmp(pref, "font_text") == 0
1996         || stricmp(pref, "font_size_text") == 0) {
1997         if (iflags.wc_fontsiz_text < NHFONT_SIZE_MIN
1998             || iflags.wc_fontsiz_text > NHFONT_SIZE_MAX)
1999             iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE;
2000
2001         hdc = GetDC(GetNHApp()->hMainWnd);
2002         mswin_get_font(NHW_TEXT, ATR_NONE, hdc, TRUE);
2003         mswin_get_font(NHW_TEXT, ATR_BOLD, hdc, TRUE);
2004         mswin_get_font(NHW_TEXT, ATR_DIM, hdc, TRUE);
2005         mswin_get_font(NHW_TEXT, ATR_ULINE, hdc, TRUE);
2006         mswin_get_font(NHW_TEXT, ATR_BLINK, hdc, TRUE);
2007         mswin_get_font(NHW_TEXT, ATR_INVERSE, hdc, TRUE);
2008         ReleaseDC(GetNHApp()->hMainWnd, hdc);
2009
2010         mswin_layout_main_window(NULL);
2011         return;
2012     }
2013
2014     if (stricmp(pref, "scroll_amount") == 0) {
2015         mswin_cliparound(u.ux, u.uy);
2016         return;
2017     }
2018
2019     if (stricmp(pref, "scroll_margin") == 0) {
2020         mswin_cliparound(u.ux, u.uy);
2021         return;
2022     }
2023
2024     if (stricmp(pref, "map_mode") == 0) {
2025         mswin_select_map_mode(iflags.wc_map_mode);
2026         return;
2027     }
2028
2029     if (stricmp(pref, "hilite_pet") == 0) {
2030         InvalidateRect(mswin_hwnd_from_winid(WIN_MAP), NULL, TRUE);
2031         return;
2032     }
2033
2034     if (stricmp(pref, "align_message") == 0
2035         || stricmp(pref, "align_status") == 0) {
2036         mswin_layout_main_window(NULL);
2037         return;
2038     }
2039
2040     if (stricmp(pref, "vary_msgcount") == 0) {
2041         InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE);
2042         mswin_layout_main_window(NULL);
2043         return;
2044     }
2045
2046     if (stricmp(pref, "perm_invent") == 0) {
2047         mswin_update_inventory();
2048         return;
2049     }
2050 }
2051
2052 #define TEXT_BUFFER_SIZE 4096
2053 char *
2054 mswin_getmsghistory(BOOLEAN_P init)
2055 {
2056     static PMSNHMsgGetText text = 0;
2057     static char *next_message = 0;
2058
2059     if (init) {
2060         text = (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText)
2061                                         + TEXT_BUFFER_SIZE);
2062         text->max_size =
2063             TEXT_BUFFER_SIZE
2064             - 1; /* make sure we always have 0 at the end of the buffer */
2065
2066         ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
2067         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
2068                     (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
2069
2070         next_message = text->buffer;
2071     }
2072
2073     if (!(next_message && next_message[0])) {
2074         free(text);
2075         next_message = 0;
2076         return (char *) 0;
2077     } else {
2078         char *retval = next_message;
2079         char *p;
2080         next_message = p = strchr(next_message, '\n');
2081         if (next_message)
2082             next_message++;
2083         if (p)
2084             while (p >= retval && isspace((uchar) *p))
2085                 *p-- = (char) 0; /* delete trailing whitespace */
2086         return retval;
2087     }
2088 }
2089
2090 void
2091 mswin_putmsghistory(const char *msg, BOOLEAN_P restoring)
2092 {
2093     BOOL save_sound_opt;
2094
2095     UNREFERENCED_PARAMETER(restoring);
2096
2097     if (!msg)
2098         return; /* end of message history restore */
2099     save_sound_opt = GetNHApp()->bNoSounds;
2100     GetNHApp()->bNoSounds =
2101         TRUE; /* disable sounds while restoring message history */
2102     mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, msg, 0);
2103     clear_nhwindow(WIN_MESSAGE); /* it is in fact end-of-turn indication so
2104                                     each message will print on the new line */
2105     GetNHApp()->bNoSounds = save_sound_opt; /* restore sounds option */
2106 }
2107
2108 void
2109 mswin_main_loop()
2110 {
2111     MSG msg;
2112
2113     while (!mswin_have_input() && GetMessage(&msg, NULL, 0, 0) != 0) {
2114         if (GetNHApp()->regNetHackMode
2115             || !TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable,
2116                                      &msg)) {
2117             TranslateMessage(&msg);
2118             DispatchMessage(&msg);
2119         }
2120     }
2121 }
2122
2123 /* clean up and quit */
2124 void
2125 bail(const char *mesg)
2126 {
2127     clearlocks();
2128     mswin_exit_nhwindows(mesg);
2129     nh_terminate(EXIT_SUCCESS);
2130     /*NOTREACHED*/
2131 }
2132
2133 BOOL
2134 initMapTiles(void)
2135 {
2136     HBITMAP hBmp;
2137     BITMAP bm;
2138     TCHAR wbuf[MAX_PATH];
2139     int tl_num;
2140     SIZE map_size;
2141     extern int total_tiles_used;
2142
2143     /* no file - no tile */
2144     if (!(iflags.wc_tile_file && *iflags.wc_tile_file))
2145         return TRUE;
2146
2147     /* load bitmap */
2148     hBmp = LoadImage(GetNHApp()->hApp,
2149                      NH_A2W(iflags.wc_tile_file, wbuf, MAX_PATH),
2150                      IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
2151     if (hBmp == NULL) {
2152         raw_print(
2153             "Cannot load tiles from the file. Reverting back to default.");
2154         return FALSE;
2155     }
2156
2157     /* calculate tile dimensions */
2158     GetObject(hBmp, sizeof(BITMAP), (LPVOID) &bm);
2159     if (bm.bmWidth % iflags.wc_tile_width
2160         || bm.bmHeight % iflags.wc_tile_height) {
2161         DeleteObject(hBmp);
2162         raw_print("Tiles bitmap does not match tile_width and tile_height "
2163                   "options. Reverting back to default.");
2164         return FALSE;
2165     }
2166
2167     tl_num = (bm.bmWidth / iflags.wc_tile_width)
2168              * (bm.bmHeight / iflags.wc_tile_height);
2169     if (tl_num < total_tiles_used) {
2170         DeleteObject(hBmp);
2171         raw_print("Number of tiles in the bitmap is less than required by "
2172                   "the game. Reverting back to default.");
2173         return FALSE;
2174     }
2175
2176     /* set the tile information */
2177     if (GetNHApp()->bmpMapTiles != GetNHApp()->bmpTiles) {
2178         DeleteObject(GetNHApp()->bmpMapTiles);
2179     }
2180
2181     GetNHApp()->bmpMapTiles = hBmp;
2182     GetNHApp()->mapTile_X = iflags.wc_tile_width;
2183     GetNHApp()->mapTile_Y = iflags.wc_tile_height;
2184     GetNHApp()->mapTilesPerLine = bm.bmWidth / iflags.wc_tile_width;
2185
2186     map_size.cx = GetNHApp()->mapTile_X * COLNO;
2187     map_size.cy = GetNHApp()->mapTile_Y * ROWNO;
2188     mswin_map_stretch(mswin_hwnd_from_winid(WIN_MAP), &map_size, TRUE);
2189     return TRUE;
2190 }
2191
2192 void
2193 mswin_popup_display(HWND hWnd, int *done_indicator)
2194 {
2195     MSG msg;
2196     HWND hChild;
2197     HMENU hMenu;
2198     int mi_count;
2199     int i;
2200
2201     /* activate the menu window */
2202     GetNHApp()->hPopupWnd = hWnd;
2203
2204     mswin_layout_main_window(hWnd);
2205
2206     /* disable game windows */
2207     for (hChild = GetWindow(GetNHApp()->hMainWnd, GW_CHILD); hChild;
2208          hChild = GetWindow(hChild, GW_HWNDNEXT)) {
2209         if (hChild != hWnd)
2210             EnableWindow(hChild, FALSE);
2211     }
2212
2213     /* disable menu */
2214     hMenu = GetMenu(GetNHApp()->hMainWnd);
2215     mi_count = GetMenuItemCount(hMenu);
2216     for (i = 0; i < mi_count; i++) {
2217         EnableMenuItem(hMenu, i, MF_BYPOSITION | MF_GRAYED);
2218     }
2219     DrawMenuBar(GetNHApp()->hMainWnd);
2220
2221     /* bring menu window on top */
2222     SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,
2223                  SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
2224     SetFocus(hWnd);
2225
2226     /* go into message loop */
2227     while (IsWindow(hWnd) && (done_indicator == NULL || !*done_indicator)
2228            && GetMessage(&msg, NULL, 0, 0) != 0) {
2229         if (!IsDialogMessage(hWnd, &msg)) {
2230             if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable,
2231                                       &msg)) {
2232                 TranslateMessage(&msg);
2233                 DispatchMessage(&msg);
2234             }
2235         }
2236     }
2237 }
2238
2239 void
2240 mswin_popup_destroy(HWND hWnd)
2241 {
2242     HWND hChild;
2243     HMENU hMenu;
2244     int mi_count;
2245     int i;
2246
2247     /* enable game windows */
2248     for (hChild = GetWindow(GetNHApp()->hMainWnd, GW_CHILD); hChild;
2249          hChild = GetWindow(hChild, GW_HWNDNEXT)) {
2250         if (hChild != hWnd) {
2251             EnableWindow(hChild, TRUE);
2252         }
2253     }
2254
2255     /* enable menu */
2256     hMenu = GetMenu(GetNHApp()->hMainWnd);
2257     mi_count = GetMenuItemCount(hMenu);
2258     for (i = 0; i < mi_count; i++) {
2259         EnableMenuItem(hMenu, i, MF_BYPOSITION | MF_ENABLED);
2260     }
2261     DrawMenuBar(GetNHApp()->hMainWnd);
2262
2263     /* Don't hide the permanent inventory window ... leave it showing */
2264     if (!flags.perm_invent || mswin_winid_from_handle(hWnd) != WIN_INVEN)
2265         ShowWindow(hWnd, SW_HIDE);
2266
2267     GetNHApp()->hPopupWnd = NULL;
2268
2269     mswin_layout_main_window(hWnd);
2270
2271     SetFocus(GetNHApp()->hMainWnd);
2272 }
2273
2274 #ifdef DEBUG
2275 # ifdef _DEBUG
2276 #include <stdarg.h>
2277
2278 void
2279 logDebug(const char *fmt, ...)
2280 {
2281     va_list args;
2282
2283     if (!showdebug(NHTRACE_LOG) || !_s_debugfp)
2284         return;
2285
2286     va_start(args, fmt);
2287     vfprintf(_s_debugfp, fmt, args);
2288     va_end(args);
2289     fflush(_s_debugfp);
2290 }
2291 # endif
2292 #endif
2293
2294 /* Reading and writing settings from the registry. */
2295 #define CATEGORYKEY "Software"
2296 #define COMPANYKEY "NetHack"
2297 #define PRODUCTKEY "NetHack 3.6.1"
2298 #define SETTINGSKEY "Settings"
2299 #define MAINSHOWSTATEKEY "MainShowState"
2300 #define MAINMINXKEY "MainMinX"
2301 #define MAINMINYKEY "MainMinY"
2302 #define MAINMAXXKEY "MainMaxX"
2303 #define MAINMAXYKEY "MainMaxY"
2304 #define MAINLEFTKEY "MainLeft"
2305 #define MAINRIGHTKEY "MainRight"
2306 #define MAINTOPKEY "MainTop"
2307 #define MAINBOTTOMKEY "MainBottom"
2308 #define MAINAUTOLAYOUT "AutoLayout"
2309 #define MAPLEFT "MapLeft"
2310 #define MAPRIGHT "MapRight"
2311 #define MAPTOP "MapTop"
2312 #define MAPBOTTOM "MapBottom"
2313 #define MSGLEFT "MsgLeft"
2314 #define MSGRIGHT "MsgRight"
2315 #define MSGTOP "MsgTop"
2316 #define MSGBOTTOM "MsgBottom"
2317 #define STATUSLEFT "StatusLeft"
2318 #define STATUSRIGHT "StatusRight"
2319 #define STATUSTOP "StatusTop"
2320 #define STATUSBOTTOM "StatusBottom"
2321 #define MENULEFT "MenuLeft"
2322 #define MENURIGHT "MenuRight"
2323 #define MENUTOP "MenuTop"
2324 #define MENUBOTTOM "MenuBottom"
2325 #define TEXTLEFT "TextLeft"
2326 #define TEXTRIGHT "TextRight"
2327 #define TEXTTOP "TextTop"
2328 #define TEXTBOTTOM "TextBottom"
2329 #define INVENTLEFT "InventLeft"
2330 #define INVENTRIGHT "InventRight"
2331 #define INVENTTOP "InventTop"
2332 #define INVENTBOTTOM "InventBottom"
2333
2334 /* #define all the subkeys here */
2335 #define INTFKEY "Interface"
2336
2337 void
2338 mswin_read_reg()
2339 {
2340     HKEY key;
2341     DWORD size;
2342     DWORD safe_buf;
2343     char keystring[MAX_PATH];
2344     int i;
2345     COLORREF default_mapcolors[CLR_MAX] = {
2346         RGB(0x55, 0x55, 0x55), /* CLR_BLACK */
2347         RGB(0xFF, 0x00, 0x00), /* CLR_RED */
2348         RGB(0x00, 0x80, 0x00), /* CLR_GREEN */
2349         RGB(0xA5, 0x2A, 0x2A), /* CLR_BROWN */
2350         RGB(0x00, 0x00, 0xFF), /* CLR_BLUE */
2351         RGB(0xFF, 0x00, 0xFF), /* CLR_MAGENTA */
2352         RGB(0x00, 0xFF, 0xFF), /* CLR_CYAN */
2353         RGB(0xC0, 0xC0, 0xC0), /* CLR_GRAY */
2354         RGB(0xFF, 0xFF, 0xFF), /* NO_COLOR */
2355         RGB(0xFF, 0xA5, 0x00), /* CLR_ORANGE */
2356         RGB(0x00, 0xFF, 0x00), /* CLR_BRIGHT_GREEN */
2357         RGB(0xFF, 0xFF, 0x00), /* CLR_YELLOW */
2358         RGB(0x00, 0xC0, 0xFF), /* CLR_BRIGHT_BLUE */
2359         RGB(0xFF, 0x80, 0xFF), /* CLR_BRIGHT_MAGENTA */
2360         RGB(0x80, 0xFF, 0xFF), /* CLR_BRIGHT_CYAN */
2361         RGB(0xFF, 0xFF, 0xFF)  /* CLR_WHITE */
2362     };
2363
2364     sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY,
2365             SETTINGSKEY);
2366
2367     /* Set the defaults here. The very first time the app is started, nothing
2368        is
2369        read from the registry, so these defaults apply. */
2370     GetNHApp()->saveRegistrySettings = 1; /* Normally, we always save */
2371     GetNHApp()->regNetHackMode = TRUE;
2372
2373     for (i = 0; i < CLR_MAX; i++)
2374         GetNHApp()->regMapColors[i] = default_mapcolors[i];
2375
2376     if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key)
2377         != ERROR_SUCCESS)
2378         return;
2379
2380     size = sizeof(DWORD);
2381
2382 #define NHGETREG_DWORD(name, val)                                       \
2383     RegQueryValueEx(key, (name), 0, NULL, (unsigned char *)(&safe_buf), \
2384                     &size);                                             \
2385     (val) = safe_buf;
2386
2387     /* read the keys here */
2388     NHGETREG_DWORD(INTFKEY, GetNHApp()->regNetHackMode);
2389
2390     /* read window placement */
2391     NHGETREG_DWORD(MAINSHOWSTATEKEY, GetNHApp()->regMainShowState);
2392     NHGETREG_DWORD(MAINMINXKEY, GetNHApp()->regMainMinX);
2393     NHGETREG_DWORD(MAINMINYKEY, GetNHApp()->regMainMinY);
2394     NHGETREG_DWORD(MAINMAXXKEY, GetNHApp()->regMainMaxX);
2395     NHGETREG_DWORD(MAINMAXYKEY, GetNHApp()->regMainMaxY);
2396     NHGETREG_DWORD(MAINLEFTKEY, GetNHApp()->regMainLeft);
2397     NHGETREG_DWORD(MAINRIGHTKEY, GetNHApp()->regMainRight);
2398     NHGETREG_DWORD(MAINTOPKEY, GetNHApp()->regMainTop);
2399     NHGETREG_DWORD(MAINBOTTOMKEY, GetNHApp()->regMainBottom);
2400
2401     NHGETREG_DWORD(MAINAUTOLAYOUT, GetNHApp()->bAutoLayout);
2402     NHGETREG_DWORD(MAPLEFT, GetNHApp()->rtMapWindow.left);
2403     NHGETREG_DWORD(MAPRIGHT, GetNHApp()->rtMapWindow.right);
2404     NHGETREG_DWORD(MAPTOP, GetNHApp()->rtMapWindow.top);
2405     NHGETREG_DWORD(MAPBOTTOM, GetNHApp()->rtMapWindow.bottom);
2406     NHGETREG_DWORD(MSGLEFT, GetNHApp()->rtMsgWindow.left);
2407     NHGETREG_DWORD(MSGRIGHT, GetNHApp()->rtMsgWindow.right);
2408     NHGETREG_DWORD(MSGTOP, GetNHApp()->rtMsgWindow.top);
2409     NHGETREG_DWORD(MSGBOTTOM, GetNHApp()->rtMsgWindow.bottom);
2410     NHGETREG_DWORD(STATUSLEFT, GetNHApp()->rtStatusWindow.left);
2411     NHGETREG_DWORD(STATUSRIGHT, GetNHApp()->rtStatusWindow.right);
2412     NHGETREG_DWORD(STATUSTOP, GetNHApp()->rtStatusWindow.top);
2413     NHGETREG_DWORD(STATUSBOTTOM, GetNHApp()->rtStatusWindow.bottom);
2414     NHGETREG_DWORD(MENULEFT, GetNHApp()->rtMenuWindow.left);
2415     NHGETREG_DWORD(MENURIGHT, GetNHApp()->rtMenuWindow.right);
2416     NHGETREG_DWORD(MENUTOP, GetNHApp()->rtMenuWindow.top);
2417     NHGETREG_DWORD(MENUBOTTOM, GetNHApp()->rtMenuWindow.bottom);
2418     NHGETREG_DWORD(TEXTLEFT, GetNHApp()->rtTextWindow.left);
2419     NHGETREG_DWORD(TEXTRIGHT, GetNHApp()->rtTextWindow.right);
2420     NHGETREG_DWORD(TEXTTOP, GetNHApp()->rtTextWindow.top);
2421     NHGETREG_DWORD(TEXTBOTTOM, GetNHApp()->rtTextWindow.bottom);
2422     NHGETREG_DWORD(INVENTLEFT, GetNHApp()->rtInvenWindow.left);
2423     NHGETREG_DWORD(INVENTRIGHT, GetNHApp()->rtInvenWindow.right);
2424     NHGETREG_DWORD(INVENTTOP, GetNHApp()->rtInvenWindow.top);
2425     NHGETREG_DWORD(INVENTBOTTOM, GetNHApp()->rtInvenWindow.bottom);
2426 #undef NHGETREG_DWORD
2427
2428     for (i = 0; i < CLR_MAX; i++) {
2429         COLORREF cl;
2430         char mapcolorkey[64];
2431         sprintf(mapcolorkey, "MapColor%02d", i);
2432         if (RegQueryValueEx(key, mapcolorkey, NULL, NULL, (BYTE *)&cl, &size) == ERROR_SUCCESS)
2433             GetNHApp()->regMapColors[i] = cl;
2434     }
2435
2436     RegCloseKey(key);
2437
2438     /* check the data for validity */
2439     if (IsRectEmpty(&GetNHApp()->rtMapWindow)
2440         || IsRectEmpty(&GetNHApp()->rtMsgWindow)
2441         || IsRectEmpty(&GetNHApp()->rtStatusWindow)
2442         || IsRectEmpty(&GetNHApp()->rtMenuWindow)
2443         || IsRectEmpty(&GetNHApp()->rtTextWindow)
2444         || IsRectEmpty(&GetNHApp()->rtInvenWindow)) {
2445         GetNHApp()->bAutoLayout = TRUE;
2446     }
2447 }
2448
2449 void
2450 mswin_write_reg()
2451 {
2452     HKEY key;
2453     DWORD disposition;
2454     int i;
2455
2456     if (GetNHApp()->saveRegistrySettings) {
2457         char keystring[MAX_PATH];
2458         DWORD safe_buf;
2459
2460         sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY,
2461                 PRODUCTKEY, SETTINGSKEY);
2462
2463         if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_WRITE, &key)
2464             != ERROR_SUCCESS) {
2465             RegCreateKeyEx(HKEY_CURRENT_USER, keystring, 0, "",
2466                            REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
2467                            &key, &disposition);
2468         }
2469
2470 #define NHSETREG_DWORD(name, val)                                   \
2471     RegSetValueEx(key, (name), 0, REG_DWORD,                        \
2472                   (unsigned char *)((safe_buf = (val)), &safe_buf), \
2473                   sizeof(DWORD));
2474
2475         /* Write the keys here */
2476         NHSETREG_DWORD(INTFKEY, GetNHApp()->regNetHackMode);
2477
2478         /* Main window placement */
2479         NHSETREG_DWORD(MAINSHOWSTATEKEY, GetNHApp()->regMainShowState);
2480         NHSETREG_DWORD(MAINMINXKEY, GetNHApp()->regMainMinX);
2481         NHSETREG_DWORD(MAINMINYKEY, GetNHApp()->regMainMinY);
2482         NHSETREG_DWORD(MAINMAXXKEY, GetNHApp()->regMainMaxX);
2483         NHSETREG_DWORD(MAINMAXYKEY, GetNHApp()->regMainMaxY);
2484         NHSETREG_DWORD(MAINLEFTKEY, GetNHApp()->regMainLeft);
2485         NHSETREG_DWORD(MAINRIGHTKEY, GetNHApp()->regMainRight);
2486         NHSETREG_DWORD(MAINTOPKEY, GetNHApp()->regMainTop);
2487         NHSETREG_DWORD(MAINBOTTOMKEY, GetNHApp()->regMainBottom);
2488
2489         NHSETREG_DWORD(MAINAUTOLAYOUT, GetNHApp()->bAutoLayout);
2490         NHSETREG_DWORD(MAPLEFT, GetNHApp()->rtMapWindow.left);
2491         NHSETREG_DWORD(MAPRIGHT, GetNHApp()->rtMapWindow.right);
2492         NHSETREG_DWORD(MAPTOP, GetNHApp()->rtMapWindow.top);
2493         NHSETREG_DWORD(MAPBOTTOM, GetNHApp()->rtMapWindow.bottom);
2494         NHSETREG_DWORD(MSGLEFT, GetNHApp()->rtMsgWindow.left);
2495         NHSETREG_DWORD(MSGRIGHT, GetNHApp()->rtMsgWindow.right);
2496         NHSETREG_DWORD(MSGTOP, GetNHApp()->rtMsgWindow.top);
2497         NHSETREG_DWORD(MSGBOTTOM, GetNHApp()->rtMsgWindow.bottom);
2498         NHSETREG_DWORD(STATUSLEFT, GetNHApp()->rtStatusWindow.left);
2499         NHSETREG_DWORD(STATUSRIGHT, GetNHApp()->rtStatusWindow.right);
2500         NHSETREG_DWORD(STATUSTOP, GetNHApp()->rtStatusWindow.top);
2501         NHSETREG_DWORD(STATUSBOTTOM, GetNHApp()->rtStatusWindow.bottom);
2502         NHSETREG_DWORD(MENULEFT, GetNHApp()->rtMenuWindow.left);
2503         NHSETREG_DWORD(MENURIGHT, GetNHApp()->rtMenuWindow.right);
2504         NHSETREG_DWORD(MENUTOP, GetNHApp()->rtMenuWindow.top);
2505         NHSETREG_DWORD(MENUBOTTOM, GetNHApp()->rtMenuWindow.bottom);
2506         NHSETREG_DWORD(TEXTLEFT, GetNHApp()->rtTextWindow.left);
2507         NHSETREG_DWORD(TEXTRIGHT, GetNHApp()->rtTextWindow.right);
2508         NHSETREG_DWORD(TEXTTOP, GetNHApp()->rtTextWindow.top);
2509         NHSETREG_DWORD(TEXTBOTTOM, GetNHApp()->rtTextWindow.bottom);
2510         NHSETREG_DWORD(INVENTLEFT, GetNHApp()->rtInvenWindow.left);
2511         NHSETREG_DWORD(INVENTRIGHT, GetNHApp()->rtInvenWindow.right);
2512         NHSETREG_DWORD(INVENTTOP, GetNHApp()->rtInvenWindow.top);
2513         NHSETREG_DWORD(INVENTBOTTOM, GetNHApp()->rtInvenWindow.bottom);
2514 #undef NHSETREG_DWORD
2515
2516         for (i = 0; i < CLR_MAX; i++) {
2517             COLORREF cl = GetNHApp()->regMapColors[i];
2518             char mapcolorkey[64];
2519             sprintf(mapcolorkey, "MapColor%02d", i);
2520             RegSetValueEx(key, mapcolorkey, 0, REG_DWORD, (BYTE *)&cl, sizeof(DWORD));
2521         }
2522
2523         RegCloseKey(key);
2524     }
2525 }
2526
2527 void
2528 mswin_destroy_reg()
2529 {
2530     char keystring[MAX_PATH];
2531     HKEY key;
2532     DWORD nrsubkeys;
2533
2534     /* Delete keys one by one, as NT does not delete trees */
2535     sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY,
2536             SETTINGSKEY);
2537     RegDeleteKey(HKEY_CURRENT_USER, keystring);
2538     sprintf(keystring, "%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY);
2539     RegDeleteKey(HKEY_CURRENT_USER, keystring);
2540     /* The company key will also contain information about newer versions
2541        of nethack (e.g. a subkey called NetHack 4.0), so only delete that
2542        if it's empty now. */
2543     sprintf(keystring, "%s\\%s", CATEGORYKEY, COMPANYKEY);
2544     /* If we cannot open it, we probably cannot delete it either... Just
2545        go on and see what happens. */
2546     RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key);
2547     nrsubkeys = 0;
2548     RegQueryInfoKey(key, NULL, NULL, NULL, &nrsubkeys, NULL, NULL, NULL, NULL,
2549                     NULL, NULL, NULL);
2550     RegCloseKey(key);
2551     if (nrsubkeys == 0)
2552         RegDeleteKey(HKEY_CURRENT_USER, keystring);
2553
2554     /* Prevent saving on exit */
2555     GetNHApp()->saveRegistrySettings = 0;
2556 }
2557
2558 typedef struct ctv {
2559     const char *colorstring;
2560     COLORREF colorvalue;
2561 } color_table_value;
2562
2563 /*
2564  * The color list here is a combination of:
2565  * NetHack colors.  (See mhmap.c)
2566  * HTML colors. (See http://www.w3.org/TR/REC-html40/types.html#h-6.5 )
2567  */
2568
2569 static color_table_value color_table[] = {
2570     /* NetHack colors */
2571     { "black", RGB(0x55, 0x55, 0x55) },
2572     { "red", RGB(0xFF, 0x00, 0x00) },
2573     { "green", RGB(0x00, 0x80, 0x00) },
2574     { "brown", RGB(0xA5, 0x2A, 0x2A) },
2575     { "blue", RGB(0x00, 0x00, 0xFF) },
2576     { "magenta", RGB(0xFF, 0x00, 0xFF) },
2577     { "cyan", RGB(0x00, 0xFF, 0xFF) },
2578     { "orange", RGB(0xFF, 0xA5, 0x00) },
2579     { "brightgreen", RGB(0x00, 0xFF, 0x00) },
2580     { "yellow", RGB(0xFF, 0xFF, 0x00) },
2581     { "brightblue", RGB(0x00, 0xC0, 0xFF) },
2582     { "brightmagenta", RGB(0xFF, 0x80, 0xFF) },
2583     { "brightcyan", RGB(0x80, 0xFF, 0xFF) },
2584     { "white", RGB(0xFF, 0xFF, 0xFF) },
2585     /* Remaining HTML colors */
2586     { "trueblack", RGB(0x00, 0x00, 0x00) },
2587     { "gray", RGB(0x80, 0x80, 0x80) },
2588     { "grey", RGB(0x80, 0x80, 0x80) },
2589     { "purple", RGB(0x80, 0x00, 0x80) },
2590     { "silver", RGB(0xC0, 0xC0, 0xC0) },
2591     { "maroon", RGB(0x80, 0x00, 0x00) },
2592     { "fuchsia", RGB(0xFF, 0x00, 0xFF) }, /* = NetHack magenta */
2593     { "lime", RGB(0x00, 0xFF, 0x00) },    /* = NetHack bright green */
2594     { "olive", RGB(0x80, 0x80, 0x00) },
2595     { "navy", RGB(0x00, 0x00, 0x80) },
2596     { "teal", RGB(0x00, 0x80, 0x80) },
2597     { "aqua", RGB(0x00, 0xFF, 0xFF) }, /* = NetHack cyan */
2598     { "", RGB(0x00, 0x00, 0x00) },
2599 };
2600
2601 typedef struct ctbv {
2602     char *colorstring;
2603     int syscolorvalue;
2604 } color_table_brush_value;
2605
2606 static color_table_brush_value color_table_brush[] = {
2607     { "activeborder", COLOR_ACTIVEBORDER },
2608     { "activecaption", COLOR_ACTIVECAPTION },
2609     { "appworkspace", COLOR_APPWORKSPACE },
2610     { "background", COLOR_BACKGROUND },
2611     { "btnface", COLOR_BTNFACE },
2612     { "btnshadow", COLOR_BTNSHADOW },
2613     { "btntext", COLOR_BTNTEXT },
2614     { "captiontext", COLOR_CAPTIONTEXT },
2615     { "graytext", COLOR_GRAYTEXT },
2616     { "greytext", COLOR_GRAYTEXT },
2617     { "highlight", COLOR_HIGHLIGHT },
2618     { "highlighttext", COLOR_HIGHLIGHTTEXT },
2619     { "inactiveborder", COLOR_INACTIVEBORDER },
2620     { "inactivecaption", COLOR_INACTIVECAPTION },
2621     { "menu", COLOR_MENU },
2622     { "menutext", COLOR_MENUTEXT },
2623     { "scrollbar", COLOR_SCROLLBAR },
2624     { "window", COLOR_WINDOW },
2625     { "windowframe", COLOR_WINDOWFRAME },
2626     { "windowtext", COLOR_WINDOWTEXT },
2627     { "", -1 },
2628 };
2629
2630 static void
2631 mswin_color_from_string(char *colorstring, HBRUSH *brushptr,
2632                         COLORREF *colorptr)
2633 {
2634     color_table_value *ctv_ptr = color_table;
2635     color_table_brush_value *ctbv_ptr = color_table_brush;
2636     int red_value, blue_value, green_value;
2637     static char *hexadecimals = "0123456789abcdef";
2638
2639     if (colorstring == NULL)
2640         return;
2641     if (*colorstring == '#') {
2642         if (strlen(++colorstring) != 6)
2643             return;
2644
2645         red_value = (int) (index(hexadecimals, tolower((uchar) *colorstring))
2646                            - hexadecimals);
2647         ++colorstring;
2648         red_value *= 16;
2649         red_value += (int) (index(hexadecimals, tolower((uchar) *colorstring))
2650                             - hexadecimals);
2651         ++colorstring;
2652
2653         green_value = (int) (index(hexadecimals,
2654                                    tolower((uchar) *colorstring))
2655                              - hexadecimals);
2656         ++colorstring;
2657         green_value *= 16;
2658         green_value += (int) (index(hexadecimals,
2659                                     tolower((uchar) *colorstring))
2660                               - hexadecimals);
2661         ++colorstring;
2662
2663         blue_value = (int) (index(hexadecimals, tolower((uchar) *colorstring))
2664                             - hexadecimals);
2665         ++colorstring;
2666         blue_value *= 16;
2667         blue_value += (int) (index(hexadecimals,
2668                                    tolower((uchar) *colorstring))
2669                              - hexadecimals);
2670         ++colorstring;
2671
2672         *colorptr = RGB(red_value, green_value, blue_value);
2673     } else {
2674         while (*ctv_ptr->colorstring
2675                && stricmp(ctv_ptr->colorstring, colorstring))
2676             ++ctv_ptr;
2677         if (*ctv_ptr->colorstring) {
2678             *colorptr = ctv_ptr->colorvalue;
2679         } else {
2680             while (*ctbv_ptr->colorstring
2681                    && stricmp(ctbv_ptr->colorstring, colorstring))
2682                 ++ctbv_ptr;
2683             if (*ctbv_ptr->colorstring) {
2684                 *brushptr = SYSCLR_TO_BRUSH(ctbv_ptr->syscolorvalue);
2685                 *colorptr = GetSysColor(ctbv_ptr->syscolorvalue);
2686             }
2687         }
2688     }
2689     if (max_brush > TOTAL_BRUSHES)
2690         panic("Too many colors!");
2691     *brushptr = CreateSolidBrush(*colorptr);
2692     brush_table[max_brush++] = *brushptr;
2693 }
2694
2695 void
2696 mswin_get_window_placement(int type, LPRECT rt)
2697 {
2698     switch (type) {
2699     case NHW_MAP:
2700         *rt = GetNHApp()->rtMapWindow;
2701         break;
2702
2703     case NHW_MESSAGE:
2704         *rt = GetNHApp()->rtMsgWindow;
2705         break;
2706
2707     case NHW_STATUS:
2708         *rt = GetNHApp()->rtStatusWindow;
2709         break;
2710
2711     case NHW_MENU:
2712         *rt = GetNHApp()->rtMenuWindow;
2713         break;
2714
2715     case NHW_TEXT:
2716         *rt = GetNHApp()->rtTextWindow;
2717         break;
2718
2719     case NHW_INVEN:
2720         *rt = GetNHApp()->rtInvenWindow;
2721         break;
2722
2723     default:
2724         SetRect(rt, 0, 0, 0, 0);
2725         break;
2726     }
2727 }
2728
2729 void
2730 mswin_update_window_placement(int type, LPRECT rt)
2731 {
2732     LPRECT rt_conf = NULL;
2733
2734     switch (type) {
2735     case NHW_MAP:
2736         rt_conf = &GetNHApp()->rtMapWindow;
2737         break;
2738
2739     case NHW_MESSAGE:
2740         rt_conf = &GetNHApp()->rtMsgWindow;
2741         break;
2742
2743     case NHW_STATUS:
2744         rt_conf = &GetNHApp()->rtStatusWindow;
2745         break;
2746
2747     case NHW_MENU:
2748         rt_conf = &GetNHApp()->rtMenuWindow;
2749         break;
2750
2751     case NHW_TEXT:
2752         rt_conf = &GetNHApp()->rtTextWindow;
2753         break;
2754
2755     case NHW_INVEN:
2756         rt_conf = &GetNHApp()->rtInvenWindow;
2757         break;
2758     }
2759
2760     if (rt_conf && !IsRectEmpty(rt) && !EqualRect(rt_conf, rt)) {
2761         *rt_conf = *rt;
2762     }
2763 }
2764
2765 int
2766 NHMessageBox(HWND hWnd, LPCTSTR text, UINT type)
2767 {
2768     TCHAR title[MAX_LOADSTRING];
2769
2770     LoadString(GetNHApp()->hApp, IDS_APP_TITLE_SHORT, title, MAX_LOADSTRING);
2771
2772     return MessageBox(hWnd, text, title, type);
2773 }
2774
2775 static const char *_status_fieldnm[MAXBLSTATS];
2776 static const char *_status_fieldfmt[MAXBLSTATS];
2777 static char *_status_vals[MAXBLSTATS];
2778 static int _status_colors[MAXBLSTATS];
2779 static int _status_percents[MAXBLSTATS];
2780 static boolean _status_activefields[MAXBLSTATS];
2781 extern winid WIN_STATUS;
2782
2783 #ifdef STATUS_HILITES
2784 typedef struct hilite_data_struct {
2785     int thresholdtype;
2786     anything value;
2787     int behavior;
2788     int under;
2789     int over;
2790 } hilite_data_t;
2791 static hilite_data_t _status_hilites[MAXBLSTATS];
2792 #endif /* STATUS_HILITES */
2793 /*
2794 status_init()   -- core calls this to notify the window port that a status
2795                    display is required. The window port should perform
2796                    the necessary initialization in here, allocate memory, etc.
2797 */
2798 void
2799 mswin_status_init(void)
2800 {
2801     int i;
2802     logDebug("mswin_status_init()\n");
2803     for (i = 0; i < MAXBLSTATS; ++i) {
2804         _status_vals[i] = (char *) alloc(BUFSZ);
2805         *_status_vals[i] = '\0';
2806         _status_activefields[i] = FALSE;
2807         _status_fieldfmt[i] = (const char *) 0;
2808         _status_colors[i] = CLR_MAX; /* no color */
2809         _status_percents[i] = 0;
2810 #ifdef STATUS_HILITES
2811         _status_hilites[i].thresholdtype = 0;
2812         _status_hilites[i].behavior = BL_TH_NONE;
2813         _status_hilites[i].under = BL_HILITE_NONE;
2814         _status_hilites[i].over = BL_HILITE_NONE;
2815 #endif /* STATUS_HILITES */
2816     }
2817     /* Use a window for the genl version; backward port compatibility */
2818     WIN_STATUS = create_nhwindow(NHW_STATUS);
2819     display_nhwindow(WIN_STATUS, FALSE);
2820 }
2821
2822 /*
2823 status_finish() -- called when it is time for the window port to tear down
2824                    the status display and free allocated memory, etc.
2825 */
2826 void
2827 mswin_status_finish(void)
2828 {
2829     /* tear down routine */
2830     int i;
2831
2832     logDebug("mswin_status_finish()\n");
2833
2834     /* free alloc'd memory here */
2835     for (i = 0; i < MAXBLSTATS; ++i) {
2836         if (_status_vals[i])
2837             free((genericptr_t) _status_vals[i]);
2838         _status_vals[i] = (char *) 0;
2839     }
2840 }
2841
2842 /*
2843 status_enablefield(int fldindex, char fldname, char fieldfmt, boolean enable)
2844                 -- notifies the window port which fields it is authorized to
2845                    display.
2846                 -- This may be called at any time, and is used
2847                    to disable as well as enable fields, depending on the
2848                    value of the final argument (TRUE = enable).
2849                 -- fldindex could be one of the following from botl.h:
2850                    BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
2851                    BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
2852                    BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
2853                    BL_LEVELDESC, BL_EXP, BL_CONDITION
2854                 -- There are MAXBLSTATS status fields (from botl.h)
2855 */
2856 void
2857 mswin_status_enablefield(int fieldidx, const char *nm, const char *fmt,
2858                          boolean enable)
2859 {
2860     logDebug("mswin_status_enablefield(%d, %s, %s, %d)\n", fieldidx, nm, fmt,
2861              (int) enable);
2862     _status_fieldfmt[fieldidx] = fmt;
2863     _status_fieldnm[fieldidx] = nm;
2864     _status_activefields[fieldidx] = enable;
2865 }
2866
2867 /*
2868
2869 status_update(int fldindex, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks)
2870                 -- update the value of a status field.
2871                 -- the fldindex identifies which field is changing and
2872                    is an integer index value from botl.h
2873                 -- fldindex could be any one of the following from botl.h:
2874                    BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
2875                    BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
2876                    BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
2877                    BL_LEVELDESC, BL_EXP, BL_CONDITION
2878                 -- fldindex could also be BL_FLUSH (-1), which is not really
2879                    a field index, but is a special trigger to tell the
2880                    windowport that it should redisplay all its status fields,
2881                    even if no changes have been presented to it.
2882                 -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
2883                    If fldindex is BL_CONDITION, then ptr is a long value with
2884                    any or none of the following bits set (from botl.h):
2885                         BL_MASK_STONE           0x00000001L
2886                         BL_MASK_SLIME           0x00000002L
2887                         BL_MASK_STRNGL          0x00000004L
2888                         BL_MASK_FOODPOIS        0x00000008L
2889                         BL_MASK_TERMILL         0x00000010L
2890                         BL_MASK_BLIND           0x00000020L
2891                         BL_MASK_DEAF            0x00000040L
2892                         BL_MASK_STUN            0x00000080L
2893                         BL_MASK_CONF            0x00000100L
2894                         BL_MASK_HALLU           0x00000200L
2895                         BL_MASK_LEV             0x00000400L
2896                         BL_MASK_FLY             0x00000800L
2897                         BL_MASK_RIDE            0x00001000L
2898                 -- The value passed for BL_GOLD includes an encoded leading
2899                    symbol for GOLD "\GXXXXNNNN:nnn". If window port needs
2900                    textual gold amount without the leading "$:" the port will
2901                    have to skip past ':' in passed "ptr" for the BL_GOLD case.
2902                 -- color is the color that the NetHack core is telling you to
2903                    use to display the text.
2904                 -- colormasks is a pointer to a set of CLR_MAX unsigned longs
2905                    telling you which fields should be displayed in each color.
2906 */
2907 void
2908 mswin_status_update(int idx, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks)
2909 {
2910     long cond, *condptr = (long *) ptr;
2911     char *text = (char *) ptr;
2912     MSNHMsgUpdateStatus update_cmd_data;
2913     int ocolor, ochar;
2914     unsigned ospecial;
2915     long value = -1;
2916
2917     logDebug("mswin_status_update(%d, %p, %d, %d, %x, %p)\n", idx, ptr, chg, percent, color, colormasks);
2918
2919     if (idx != BL_FLUSH) {
2920         if (!_status_activefields[idx])
2921             return;
2922         _status_percents[idx] = percent;
2923         switch (idx) {
2924         case BL_CONDITION: {
2925             cond = *condptr;
2926             *_status_vals[idx] = '\0';
2927             if (cond & BL_MASK_STONE)
2928                 Strcat(_status_vals[idx], " Stone");
2929             if (cond & BL_MASK_SLIME)
2930                 Strcat(_status_vals[idx], " Slime");
2931             if (cond & BL_MASK_STRNGL)
2932                 Strcat(_status_vals[idx], " Strngl");
2933             if (cond & BL_MASK_FOODPOIS)
2934                 Strcat(_status_vals[idx], " FoodPois");
2935             if (cond & BL_MASK_TERMILL)
2936                 Strcat(_status_vals[idx], " TermIll");
2937             if (cond & BL_MASK_BLIND)
2938                 Strcat(_status_vals[idx], " Blind");
2939             if (cond & BL_MASK_DEAF)
2940                 Strcat(_status_vals[idx], " Deaf");
2941             if (cond & BL_MASK_STUN)
2942                 Strcat(_status_vals[idx], " Stun");
2943             if (cond & BL_MASK_CONF)
2944                 Strcat(_status_vals[idx], " Conf");
2945             if (cond & BL_MASK_HALLU)
2946                 Strcat(_status_vals[idx], " Hallu");
2947             if (cond & BL_MASK_LEV)
2948                 Strcat(_status_vals[idx], " Lev");
2949             if (cond & BL_MASK_FLY)
2950                 Strcat(_status_vals[idx], " Fly");
2951             if (cond & BL_MASK_RIDE)
2952                 Strcat(_status_vals[idx], " Ride");
2953             value = cond;
2954         } break;
2955         case BL_GOLD: {
2956             char buf[BUFSZ];
2957             char *p;
2958             ZeroMemory(buf, sizeof(buf));
2959             mapglyph(objnum_to_glyph(GOLD_PIECE), &ochar, &ocolor, &ospecial,
2960                      0, 0);
2961             buf[0] = ochar;
2962             p = strchr(text, ':');
2963             if (p) {
2964                 strncpy(buf + 1, p, sizeof(buf) - 2);
2965                 value = atol(p + 1);
2966             } else {
2967                 buf[1] = ':';
2968                 strncpy(buf + 2, text, sizeof(buf) - 2);
2969                 value = atol(text);
2970             }
2971             Sprintf(_status_vals[idx],
2972                     _status_fieldfmt[idx] ? _status_fieldfmt[idx] : "%s",
2973                     buf);
2974         } break;
2975         default: {
2976             value = atol(text);
2977             Sprintf(_status_vals[idx],
2978                     _status_fieldfmt[idx] ? _status_fieldfmt[idx] : "%s",
2979                     text);
2980         } break;
2981         }
2982     }
2983
2984     _status_colors[idx] = color;
2985
2986     /* send command to status window */
2987     ZeroMemory(&update_cmd_data, sizeof(update_cmd_data));
2988     update_cmd_data.n_fields = MAXBLSTATS;
2989     update_cmd_data.vals = _status_vals;
2990     update_cmd_data.activefields = _status_activefields;
2991     update_cmd_data.percents = _status_percents;
2992     update_cmd_data.colors = _status_colors;
2993     SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND,
2994                 (WPARAM) MSNH_MSG_UPDATE_STATUS, (LPARAM) &update_cmd_data);
2995 }
2996