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