OSDN Git Service

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