1 /* NetHack 3.6 mswproc.c $NHDT-Date: 1575245201 2019/12/02 00:06:41 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.137 $ */
2 /* Copyright (C) 2001 by Alex Kompel */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* JNetHack Copyright */
6 /* For 3.6-, Copyright (c) SHIRAKATA Kentaro, 2016-2023 */
7 /* JNetHack may be freely redistributed. See license for details. */
10 * This file implements the interface between the window port specific
11 * code in the mswin port and the rest of the nethack game engine.
17 #include "func_tab.h" /* for extended commands */
38 #define NHTRACE_LOG "nhtrace.log"
42 static FILE* _s_debugfp = NULL;
43 extern void logDebug(const char *fmt, ...);
49 logDebug(const char *fmt, ...)
54 static void mswin_main_loop(void);
55 static BOOL initMapTiles(void);
56 static void mswin_color_from_string(char *colorstring, HBRUSH *brushptr,
58 static void prompt_for_player_selection(void);
60 #define TOTAL_BRUSHES 10
61 HBRUSH brush_table[TOTAL_BRUSHES];
64 HBRUSH menu_bg_brush = NULL;
65 HBRUSH menu_fg_brush = NULL;
66 HBRUSH text_bg_brush = NULL;
67 HBRUSH text_fg_brush = NULL;
68 HBRUSH status_bg_brush = NULL;
69 HBRUSH status_fg_brush = NULL;
70 HBRUSH message_bg_brush = NULL;
71 HBRUSH message_fg_brush = NULL;
73 COLORREF menu_bg_color = RGB(0, 0, 0);
74 COLORREF menu_fg_color = RGB(0xFF, 0xFF, 0xFF);
75 COLORREF text_bg_color = RGB(0, 0, 0);
76 COLORREF text_fg_color = RGB(0xFF, 0xFF, 0xFF);
77 COLORREF status_bg_color = RGB(0, 0, 0);
78 COLORREF status_fg_color = RGB(0xFF, 0xFF, 0xFF);
79 COLORREF message_bg_color = RGB(0, 0, 0);
80 COLORREF message_fg_color = RGB(0xFF, 0xFF, 0xFF);
82 strbuf_t raw_print_strbuf = { 0 };
84 /* Interface definition, for windows.c */
85 struct window_procs mswin_procs = {
87 WC_COLOR | WC_HILITE_PET | WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_INVERSE
88 | WC_SCROLL_AMOUNT | WC_SCROLL_MARGIN | WC_MAP_MODE | WC_FONT_MESSAGE
89 | WC_FONT_STATUS | WC_FONT_MENU | WC_FONT_TEXT | WC_FONT_MAP
90 | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_MENU
91 | WC_FONTSIZ_TEXT | WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE
92 | WC_VARY_MSGCOUNT | WC_WINDOWCOLORS | WC_PLAYER_SELECTION
93 | WC_SPLASH_SCREEN | WC_POPUP_DIALOG | WC_MOUSE_SUPPORT,
95 WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_RESET_STATUS | WC2_HILITE_STATUS |
98 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */
99 mswin_init_nhwindows, mswin_player_selection, mswin_askname,
100 mswin_get_nh_event, mswin_exit_nhwindows, mswin_suspend_nhwindows,
101 mswin_resume_nhwindows, mswin_create_nhwindow, mswin_clear_nhwindow,
102 mswin_display_nhwindow, mswin_destroy_nhwindow, mswin_curs, mswin_putstr,
103 genl_putmixed, mswin_display_file, mswin_start_menu, mswin_add_menu,
104 mswin_end_menu, mswin_select_menu,
105 genl_message_menu, /* no need for X-specific handling */
106 mswin_update_inventory, mswin_mark_synch, mswin_wait_synch,
113 mswin_print_glyph, mswin_raw_print, mswin_raw_print_bold, mswin_nhgetch,
114 mswin_nh_poskey, mswin_nhbell, mswin_doprev_message, mswin_yn_function,
115 mswin_getlin, mswin_get_ext_cmd, mswin_number_pad, mswin_delay_output,
116 #ifdef CHANGE_COLOR /* only a Mac option currently */
117 mswin, mswin_change_background,
119 /* other defs that really should go away (they're tty specific) */
120 mswin_start_screen, mswin_end_screen, mswin_outrip,
121 mswin_preference_update, mswin_getmsghistory, mswin_putmsghistory,
122 mswin_status_init, mswin_status_finish, mswin_status_enablefield,
124 genl_can_suspend_yes,
128 init_nhwindows(int* argcp, char** argv)
129 -- Initialize the windows used by NetHack. This can also
130 create the standard windows listed at the top, but does
132 -- Any commandline arguments relevant to the windowport
133 should be interpreted, and *argcp and *argv should
134 be changed to remove those arguments.
135 -- When the message window is created, the variable
136 iflags.window_inited needs to be set to TRUE. Otherwise
137 all plines() will be done via raw_print().
138 ** Why not have init_nhwindows() create all of the "standard"
139 ** windows? Or at least all but WIN_INFO? -dean
142 mswin_init_nhwindows(int *argc, char **argv)
144 UNREFERENCED_PARAMETER(argc);
145 UNREFERENCED_PARAMETER(argv);
149 if (showdebug(NHTRACE_LOG) && !_s_debugfp) {
150 /* truncate trace file */
151 /* BUG: this relies on current working directory */
152 _s_debugfp = fopen(NHTRACE_LOG, "w");
156 logDebug("mswin_init_nhwindows()\n");
158 mswin_nh_input_init();
160 /* set it to WIN_ERR so we can detect attempts to
161 use this ID before it is inialized */
164 /* Read Windows settings from the reqistry */
165 /* First set safe defaults */
166 GetNHApp()->regMainMinX = CW_USEDEFAULT;
168 /* Create the main window */
169 GetNHApp()->hMainWnd = mswin_init_main_window();
170 if (!GetNHApp()->hMainWnd) {
171 panic("Cannot create main window");
174 /* Set menu check mark for interface mode */
175 mswin_menu_check_intf_mode();
177 /* check default values */
178 if (iflags.wc_fontsiz_status < NHFONT_SIZE_MIN
179 || iflags.wc_fontsiz_status > NHFONT_SIZE_MAX)
180 iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE;
182 if (iflags.wc_fontsiz_message < NHFONT_SIZE_MIN
183 || iflags.wc_fontsiz_message > NHFONT_SIZE_MAX)
184 iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE;
186 if (iflags.wc_fontsiz_text < NHFONT_SIZE_MIN
187 || iflags.wc_fontsiz_text > NHFONT_SIZE_MAX)
188 iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE;
190 if (iflags.wc_fontsiz_menu < NHFONT_SIZE_MIN
191 || iflags.wc_fontsiz_menu > NHFONT_SIZE_MAX)
192 iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE;
194 if (iflags.wc_align_message == 0)
195 iflags.wc_align_message = ALIGN_TOP;
196 if (iflags.wc_align_status == 0)
197 iflags.wc_align_status = ALIGN_BOTTOM;
198 if (iflags.wc_scroll_margin == 0)
199 iflags.wc_scroll_margin = DEF_CLIPAROUND_MARGIN;
200 if (iflags.wc_scroll_amount == 0)
201 iflags.wc_scroll_amount = DEF_CLIPAROUND_AMOUNT;
202 if (iflags.wc_tile_width == 0)
203 iflags.wc_tile_width = TILE_X;
204 if (iflags.wc_tile_height == 0)
205 iflags.wc_tile_height = TILE_Y;
207 if (iflags.wc_vary_msgcount == 0)
208 iflags.wc_vary_msgcount = 4;
210 /* force tabs in menus */
211 iflags.menu_tab_sep = 1;
213 /* force toptenwin to be true. toptenwin is the option that decides
215 * write output to a window or stdout. stdout doesn't make sense on
217 * non-console applications
219 iflags.toptenwin = 1;
220 set_option_mod_status("toptenwin", SET_IN_FILE);
221 //set_option_mod_status("perm_invent", SET_IN_FILE);
222 set_option_mod_status("mouse_support", SET_IN_GAME);
224 /* initialize map tiles bitmap */
227 /* set tile-related options to readonly */
228 set_wc_option_mod_status(WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE,
231 /* set font-related options to change in the game */
232 set_wc_option_mod_status(
233 WC_HILITE_PET | WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_SCROLL_AMOUNT
234 | WC_SCROLL_MARGIN | WC_MAP_MODE | WC_FONT_MESSAGE
235 | WC_FONT_STATUS | WC_FONT_MENU | WC_FONT_TEXT
236 | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_MENU
237 | WC_FONTSIZ_TEXT | WC_VARY_MSGCOUNT,
240 mswin_color_from_string(iflags.wc_foregrnd_menu, &menu_fg_brush,
242 mswin_color_from_string(iflags.wc_foregrnd_message, &message_fg_brush,
244 mswin_color_from_string(iflags.wc_foregrnd_status, &status_fg_brush,
246 mswin_color_from_string(iflags.wc_foregrnd_text, &text_fg_brush,
248 mswin_color_from_string(iflags.wc_backgrnd_menu, &menu_bg_brush,
250 mswin_color_from_string(iflags.wc_backgrnd_message, &message_bg_brush,
252 mswin_color_from_string(iflags.wc_backgrnd_status, &status_bg_brush,
254 mswin_color_from_string(iflags.wc_backgrnd_text, &text_bg_brush,
257 if (iflags.wc_splash_screen)
258 mswin_display_splash_window(FALSE);
260 iflags.window_inited = TRUE;
263 /* Do a window-port specific player type selection. If player_selection()
264 offers a Quit option, it is its responsibility to clean up and terminate
265 the process. You need to fill in pl_character[0].
268 mswin_player_selection(void)
270 logDebug("mswin_player_selection()\n");
272 if (iflags.wc_player_selection == VIA_DIALOG) {
273 /* pick player type randomly (use pre-selected
274 * role/race/gender/alignment) */
275 if (flags.randomall) {
276 if (flags.initrole < 0) {
277 flags.initrole = pick_role(flags.initrace, flags.initgend,
278 flags.initalign, PICK_RANDOM);
279 if (flags.initrole < 0) {
280 raw_print("Incompatible role!");
281 flags.initrole = randrole(FALSE);
285 if (flags.initrace < 0
286 || !validrace(flags.initrole, flags.initrace)) {
287 flags.initrace = pick_race(flags.initrole, flags.initgend,
288 flags.initalign, PICK_RANDOM);
289 if (flags.initrace < 0) {
290 raw_print("Incompatible race!");
291 flags.initrace = randrace(flags.initrole);
295 if (flags.initgend < 0
296 || !validgend(flags.initrole, flags.initrace,
298 flags.initgend = pick_gend(flags.initrole, flags.initrace,
299 flags.initalign, PICK_RANDOM);
300 if (flags.initgend < 0) {
301 raw_print("Incompatible gender!");
302 flags.initgend = randgend(flags.initrole, flags.initrace);
306 if (flags.initalign < 0
307 || !validalign(flags.initrole, flags.initrace,
309 flags.initalign = pick_align(flags.initrole, flags.initrace,
310 flags.initgend, PICK_RANDOM);
311 if (flags.initalign < 0) {
312 raw_print("Incompatible alignment!");
314 randalign(flags.initrole, flags.initrace);
319 if (!mswin_player_selection_window()) {
323 } else { /* iflags.wc_player_selection == VIA_PROMPTS */
324 prompt_for_player_selection();
329 prompt_for_player_selection(void)
332 char pick4u = 'n', thisch, lastch = 0;
333 char pbuf[QBUFSZ], plbuf[QBUFSZ];
336 menu_item *selected = 0;
339 logDebug("prompt_for_player_selection()\n");
341 /* prevent an unnecessary prompt */
344 /* Should we randomly pick for the player? */
346 && (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE
347 || flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
349 char *prompt = build_plselection_prompt(
350 pbuf, QBUFSZ, flags.initrole, flags.initrace, flags.initgend,
353 /* tty_putstr(BASE_WINDOW, 0, ""); */
354 /* echoline = wins[BASE_WINDOW]->cury; */
355 box_result = NHMessageBox(NULL, prompt, MB_YESNOCANCEL | MB_DEFBUTTON1
358 (box_result == IDYES) ? 'y' : (box_result == IDNO) ? 'n' : '\033';
359 /* tty_putstr(BASE_WINDOW, 0, prompt); */
361 /* pick4u = lowc(readchar()); */
362 if (index(quitchars, pick4u))
364 } while (!index(ynqchars, pick4u));
365 if ((int) strlen(prompt) + 1 < CO) {
366 /* Echo choice and move back down line */
367 /* tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline,
369 /* tty_putstr(BASE_WINDOW, 0, ""); */
371 /* Otherwise it's hard to tell where to echo, and things are
372 * wrapping a bit messily anyway, so (try to) make sure the next
373 * question shows up well and doesn't get wrapped at the
374 * bottom of the window.
376 /* tty_clear_nhwindow(BASE_WINDOW) */;
378 if (pick4u != 'y' && pick4u != 'n') {
381 free((genericptr_t) selected);
388 (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
389 flags.initrace, flags.initgend,
392 /* Select a role, if necessary */
393 /* we'll try to be compatible with pre-selected race/gender/alignment,
394 * but may not succeed */
395 if (flags.initrole < 0) {
396 char rolenamebuf[QBUFSZ];
397 /* Process the choice */
398 if (pick4u == 'y' || flags.initrole == ROLE_RANDOM
399 || flags.randomall) {
400 /* Pick a random role */
401 flags.initrole = pick_role(flags.initrace, flags.initgend,
402 flags.initalign, PICK_RANDOM);
403 if (flags.initrole < 0) {
404 /* tty_putstr(BASE_WINDOW, 0, "Incompatible role!"); */
405 flags.initrole = randrole(FALSE);
408 /* tty_clear_nhwindow(BASE_WINDOW); */
409 /* tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role"); */
410 /* Prompt for a role */
411 win = create_nhwindow(NHW_MENU);
413 any = zeroany; /* zero out all bits */
414 for (i = 0; roles[i].name.m; i++) {
415 if (ok_role(i, flags.initrace, flags.initgend,
417 any.a_int = i + 1; /* must be non-zero */
418 thisch = lowc(roles[i].name.m[0]);
419 if (thisch == lastch)
420 thisch = highc(thisch);
421 if (flags.initgend != ROLE_NONE
422 && flags.initgend != ROLE_RANDOM) {
423 if (flags.initgend == 1 && roles[i].name.f)
424 Strcpy(rolenamebuf, roles[i].name.f);
426 Strcpy(rolenamebuf, roles[i].name.m);
428 if (roles[i].name.f) {
429 Strcpy(rolenamebuf, roles[i].name.m);
430 Strcat(rolenamebuf, "/");
431 Strcat(rolenamebuf, roles[i].name.f);
433 Strcpy(rolenamebuf, roles[i].name.m);
435 add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE,
436 an(rolenamebuf), MENU_UNSELECTED);
440 any.a_int = pick_role(flags.initrace, flags.initgend,
441 flags.initalign, PICK_RANDOM) + 1;
442 if (any.a_int == 0) /* must be non-zero */
443 any.a_int = randrole(FALSE) + 1;
444 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
446 any.a_int = i + 1; /* must be non-zero */
447 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
449 Sprintf(pbuf, "Pick a role for your %s", plbuf);
451 n = select_menu(win, PICK_ONE, &selected);
452 destroy_nhwindow(win);
454 /* Process the choice */
455 if (n != 1 || selected[0].item.a_int == any.a_int)
456 goto give_up; /* Selected quit */
458 flags.initrole = selected[0].item.a_int - 1;
459 free((genericptr_t) selected), selected = 0;
461 (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
462 flags.initrace, flags.initgend,
466 /* Select a race, if necessary */
467 /* force compatibility with role, try for compatibility with
468 * pre-selected gender/alignment */
469 if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
470 /* pre-selected race not valid */
471 if (pick4u == 'y' || flags.initrace == ROLE_RANDOM
472 || flags.randomall) {
473 flags.initrace = pick_race(flags.initrole, flags.initgend,
474 flags.initalign, PICK_RANDOM);
475 if (flags.initrace < 0) {
476 /* tty_putstr(BASE_WINDOW, 0, "Incompatible race!"); */
477 flags.initrace = randrace(flags.initrole);
479 } else { /* pick4u == 'n' */
480 /* Count the number of valid races */
481 n = 0; /* number valid */
482 k = 0; /* valid race */
483 for (i = 0; races[i].noun; i++) {
484 if (ok_race(flags.initrole, i, flags.initgend,
491 for (i = 0; races[i].noun; i++) {
492 if (validrace(flags.initrole, i)) {
499 /* Permit the user to pick, if there is more than one */
501 /* tty_clear_nhwindow(BASE_WINDOW); */
502 /* tty_putstr(BASE_WINDOW, 0, "Choosing Race"); */
503 win = create_nhwindow(NHW_MENU);
505 any = zeroany; /* zero out all bits */
506 for (i = 0; races[i].noun; i++)
507 if (ok_race(flags.initrole, i, flags.initgend,
509 any.a_int = i + 1; /* must be non-zero */
510 add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0,
511 ATR_NONE, races[i].noun, MENU_UNSELECTED);
513 any.a_int = pick_race(flags.initrole, flags.initgend,
514 flags.initalign, PICK_RANDOM) + 1;
515 if (any.a_int == 0) /* must be non-zero */
516 any.a_int = randrace(flags.initrole) + 1;
517 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
519 any.a_int = i + 1; /* must be non-zero */
520 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
522 Sprintf(pbuf, "Pick the race of your %s", plbuf);
524 n = select_menu(win, PICK_ONE, &selected);
525 destroy_nhwindow(win);
526 if (n != 1 || selected[0].item.a_int == any.a_int)
527 goto give_up; /* Selected quit */
529 k = selected[0].item.a_int - 1;
530 free((genericptr_t) selected), selected = 0;
534 (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
535 flags.initrace, flags.initgend,
539 /* Select a gender, if necessary */
540 /* force compatibility with role/race, try for compatibility with
541 * pre-selected alignment */
542 if (flags.initgend < 0
543 || !validgend(flags.initrole, flags.initrace, flags.initgend)) {
544 /* pre-selected gender not valid */
545 if (pick4u == 'y' || flags.initgend == ROLE_RANDOM
546 || flags.randomall) {
547 flags.initgend = pick_gend(flags.initrole, flags.initrace,
548 flags.initalign, PICK_RANDOM);
549 if (flags.initgend < 0) {
550 /* tty_putstr(BASE_WINDOW, 0, "Incompatible gender!"); */
551 flags.initgend = randgend(flags.initrole, flags.initrace);
553 } else { /* pick4u == 'n' */
554 /* Count the number of valid genders */
555 n = 0; /* number valid */
556 k = 0; /* valid gender */
557 for (i = 0; i < ROLE_GENDERS; i++) {
558 if (ok_gend(flags.initrole, flags.initrace, i,
565 for (i = 0; i < ROLE_GENDERS; i++) {
566 if (validgend(flags.initrole, flags.initrace, i)) {
573 /* Permit the user to pick, if there is more than one */
575 /* tty_clear_nhwindow(BASE_WINDOW); */
576 /* tty_putstr(BASE_WINDOW, 0, "Choosing Gender"); */
577 win = create_nhwindow(NHW_MENU);
579 any = zeroany; /* zero out all bits */
580 for (i = 0; i < ROLE_GENDERS; i++)
581 if (ok_gend(flags.initrole, flags.initrace, i,
584 add_menu(win, NO_GLYPH, &any, genders[i].adj[0], 0,
585 ATR_NONE, genders[i].adj, MENU_UNSELECTED);
587 any.a_int = pick_gend(flags.initrole, flags.initrace,
588 flags.initalign, PICK_RANDOM) + 1;
589 if (any.a_int == 0) /* must be non-zero */
590 any.a_int = randgend(flags.initrole, flags.initrace) + 1;
591 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
593 any.a_int = i + 1; /* must be non-zero */
594 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
596 Sprintf(pbuf, "Pick the gender of your %s", plbuf);
598 n = select_menu(win, PICK_ONE, &selected);
599 destroy_nhwindow(win);
600 if (n != 1 || selected[0].item.a_int == any.a_int)
601 goto give_up; /* Selected quit */
603 k = selected[0].item.a_int - 1;
604 free((genericptr_t) selected), selected = 0;
608 (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
609 flags.initrace, flags.initgend,
613 /* Select an alignment, if necessary */
614 /* force compatibility with role/race/gender */
615 if (flags.initalign < 0
616 || !validalign(flags.initrole, flags.initrace, flags.initalign)) {
617 /* pre-selected alignment not valid */
618 if (pick4u == 'y' || flags.initalign == ROLE_RANDOM
619 || flags.randomall) {
620 flags.initalign = pick_align(flags.initrole, flags.initrace,
621 flags.initgend, PICK_RANDOM);
622 if (flags.initalign < 0) {
623 /* tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); */
624 flags.initalign = randalign(flags.initrole, flags.initrace);
626 } else { /* pick4u == 'n' */
627 /* Count the number of valid alignments */
628 n = 0; /* number valid */
629 k = 0; /* valid alignment */
630 for (i = 0; i < ROLE_ALIGNS; i++) {
631 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
638 for (i = 0; i < ROLE_ALIGNS; i++) {
639 if (validalign(flags.initrole, flags.initrace, i)) {
646 /* Permit the user to pick, if there is more than one */
648 /* tty_clear_nhwindow(BASE_WINDOW); */
649 /* tty_putstr(BASE_WINDOW, 0, "Choosing Alignment"); */
650 win = create_nhwindow(NHW_MENU);
652 any = zeroany; /* zero out all bits */
653 for (i = 0; i < ROLE_ALIGNS; i++)
654 if (ok_align(flags.initrole, flags.initrace,
655 flags.initgend, i)) {
657 add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0,
658 ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
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, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
666 any.a_int = i + 1; /* must be non-zero */
667 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
669 Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
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 */
676 k = selected[0].item.a_int - 1;
677 free((genericptr_t) selected), selected = 0;
683 /* tty_display_nhwindow(BASE_WINDOW, FALSE); */
686 /* Ask the user for a player name. */
690 logDebug("mswin_askname()\n");
692 if (mswin_getlin_window("Who are you?", plname, PL_NSIZ) == IDCANCEL) {
698 /* Does window event processing (e.g. exposure events).
699 A noop for the tty and X window-ports.
702 mswin_get_nh_event(void)
706 logDebug("mswin_get_nh_event()\n");
708 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
709 if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) {
710 TranslateMessage(&msg);
711 DispatchMessage(&msg);
717 /* Exits the window system. This should dismiss all windows,
718 except the "window" used for raw_print(). str is printed if possible.
721 mswin_exit_nhwindows(const char *str)
723 logDebug("mswin_exit_nhwindows(%s)\n", str);
725 /* Write Window settings to the registry */
728 /* set things back to failsafes */
729 windowprocs = *get_safe_procs(0);
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;
737 /* Prepare the window to be suspended. */
739 mswin_suspend_nhwindows(const char *str)
741 logDebug("mswin_suspend_nhwindows(%s)\n", str);
746 /* Restore the windows after being suspended. */
748 mswin_resume_nhwindows()
750 logDebug("mswin_resume_nhwindows()\n");
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)
763 mswin_create_nhwindow(int type)
768 logDebug("mswin_create_nhwindow(%d)\n", type);
770 /* Return the next available winid
773 for (i = 1; i < MAXWINDOWS; i++)
774 if (GetNHApp()->windowlist[i].win == NULL
775 && !GetNHApp()->windowlist[i].dead)
778 panic("ERROR: No windows available...\n");
782 GetNHApp()->windowlist[i].win = mswin_init_map_window();
783 GetNHApp()->windowlist[i].type = type;
784 GetNHApp()->windowlist[i].dead = 0;
788 GetNHApp()->windowlist[i].win = mswin_init_message_window();
789 GetNHApp()->windowlist[i].type = type;
790 GetNHApp()->windowlist[i].dead = 0;
794 GetNHApp()->windowlist[i].win = mswin_init_status_window();
795 GetNHApp()->windowlist[i].type = type;
796 GetNHApp()->windowlist[i].dead = 0;
800 GetNHApp()->windowlist[i].win = NULL; // will create later
801 GetNHApp()->windowlist[i].type = type;
802 GetNHApp()->windowlist[i].dead = 1;
806 GetNHApp()->windowlist[i].win = mswin_init_text_window();
807 GetNHApp()->windowlist[i].type = type;
808 GetNHApp()->windowlist[i].dead = 0;
813 ZeroMemory(&data, sizeof(data));
815 SendMessage(GetNHApp()->hMainWnd, WM_MSNH_COMMAND,
816 (WPARAM) MSNH_MSG_ADDWND, (LPARAM) &data);
820 /* Clear the given window, when asked to. */
822 mswin_clear_nhwindow(winid wid)
824 logDebug("mswin_clear_nhwindow(%d)\n", wid);
826 if ((wid >= 0) && (wid < MAXWINDOWS)
827 && (GetNHApp()->windowlist[wid].win != NULL)) {
828 if (GetNHApp()->windowlist[wid].type == NHW_MAP) {
829 if (Is_rogue_level(&u.uz))
830 if (iflags.wc_map_mode == MAP_MODE_ASCII_FIT_TO_SCREEN ||
831 iflags.wc_map_mode == MAP_MODE_TILES_FIT_TO_SCREEN)
833 mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
834 ROGUE_LEVEL_MAP_MODE_FIT_TO_SCREEN);
836 mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
837 ROGUE_LEVEL_MAP_MODE);
839 mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
843 SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
844 (WPARAM) MSNH_MSG_CLEAR_WINDOW, (LPARAM) NULL);
848 /* -- Display the window on the screen. If there is data
849 pending for output in that window, it should be sent.
850 If blocking is TRUE, display_nhwindow() will not
851 return until the data has been displayed on the screen,
852 and acknowledged by the user where appropriate.
853 -- All calls are blocking in the tty window-port.
854 -- Calling display_nhwindow(WIN_MESSAGE,???) will do a
855 --more--, if necessary, in the tty window-port.
858 mswin_display_nhwindow(winid wid, BOOLEAN_P block)
860 logDebug("mswin_display_nhwindow(%d, %d)\n", wid, block);
861 if (GetNHApp()->windowlist[wid].win != NULL) {
862 ShowWindow(GetNHApp()->windowlist[wid].win, SW_SHOW);
863 mswin_layout_main_window(GetNHApp()->windowlist[wid].win);
864 if (GetNHApp()->windowlist[wid].type == NHW_MENU) {
866 mswin_menu_window_select_menu(GetNHApp()->windowlist[wid].win,
867 PICK_NONE, &p, TRUE);
869 if (GetNHApp()->windowlist[wid].type == NHW_TEXT) {
870 mswin_display_text_window(GetNHApp()->windowlist[wid].win);
872 if (GetNHApp()->windowlist[wid].type == NHW_RIP) {
873 mswin_display_RIP_window(GetNHApp()->windowlist[wid].win);
876 UpdateWindow(GetNHApp()->windowlist[wid].win);
878 if (GetNHApp()->windowlist[wid].type == NHW_MAP) {
879 (void) mswin_nhgetch();
883 SetFocus(GetNHApp()->hMainWnd);
888 mswin_hwnd_from_winid(winid wid)
890 if (wid >= 0 && wid < MAXWINDOWS) {
891 return GetNHApp()->windowlist[wid].win;
898 mswin_winid_from_handle(HWND hWnd)
902 for (i = 1; i < MAXWINDOWS; i++)
903 if (GetNHApp()->windowlist[i].win == hWnd)
909 mswin_winid_from_type(int type)
913 for (i = 1; i < MAXWINDOWS; i++)
914 if (GetNHApp()->windowlist[i].type == type)
920 mswin_window_mark_dead(winid wid)
922 if (wid >= 0 && wid < MAXWINDOWS) {
923 GetNHApp()->windowlist[wid].win = NULL;
924 GetNHApp()->windowlist[wid].dead = 1;
928 /* Destroy will dismiss the window if the window has not
929 * already been dismissed.
932 mswin_destroy_nhwindow(winid wid)
934 logDebug("mswin_destroy_nhwindow(%d)\n", wid);
936 if ((GetNHApp()->windowlist[wid].type == NHW_MAP)
937 || (GetNHApp()->windowlist[wid].type == NHW_MESSAGE)
938 || (GetNHApp()->windowlist[wid].type == NHW_STATUS)) {
939 /* main windows is going to take care of those */
944 if (!GetNHApp()->windowlist[wid].dead
945 && GetNHApp()->windowlist[wid].win != NULL)
946 DestroyWindow(GetNHApp()->windowlist[wid].win);
947 GetNHApp()->windowlist[wid].win = NULL;
948 GetNHApp()->windowlist[wid].type = 0;
949 GetNHApp()->windowlist[wid].dead = 0;
953 /* Next output to window will start at (x,y), also moves
954 displayable cursor to (x,y). For backward compatibility,
955 1 <= x < cols, 0 <= y < rows, where cols and rows are
959 mswin_curs(winid wid, int x, int y)
961 logDebug("mswin_curs(%d, %d, %d)\n", wid, x, y);
963 if ((wid >= 0) && (wid < MAXWINDOWS)
964 && (GetNHApp()->windowlist[wid].win != NULL)) {
968 SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
969 (WPARAM) MSNH_MSG_CURSOR, (LPARAM) &data);
974 putstr(window, attr, str)
975 -- Print str on the window with the given attribute. Only
976 printable ASCII characters (040-0126) must be supported.
977 Multiple putstr()s are output on separate lines.
985 If a window-port does not support all of these, it may map
986 unsupported attributes to a supported one (e.g. map them
987 all to ATR_INVERSE). putstr() may compress spaces out of
988 str, break str, or truncate str, if necessary for the
989 display. Where putstr() breaks a line, it has to clear
991 -- putstr should be implemented such that if two putstr()s
992 are done consecutively the user will see the first and
993 then the second. In the tty port, pline() achieves this
994 by calling more() or displaying both on the same line.
997 mswin_putstr(winid wid, int attr, const char *text)
999 logDebug("mswin_putstr(%d, %d, %s)\n", wid, attr, text);
1001 mswin_putstr_ex(wid, attr, text, 0);
1005 mswin_putstr_ex(winid wid, int attr, const char *text, int app)
1007 if ((wid >= 0) && (wid < MAXWINDOWS)) {
1008 if (GetNHApp()->windowlist[wid].win == NULL
1009 && GetNHApp()->windowlist[wid].type == NHW_MENU) {
1010 GetNHApp()->windowlist[wid].win =
1011 mswin_init_menu_window(MENU_TYPE_TEXT);
1012 GetNHApp()->windowlist[wid].dead = 0;
1015 if (GetNHApp()->windowlist[wid].win != NULL) {
1017 ZeroMemory(&data, sizeof(data));
1021 SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1022 (WPARAM) MSNH_MSG_PUTSTR, (LPARAM) &data);
1024 /* yield a bit so it gets done immediately */
1025 mswin_get_nh_event();
1027 // build text to display later in message box
1028 GetNHApp()->saved_text =
1029 realloc(GetNHApp()->saved_text,
1030 strlen(text) + strlen(GetNHApp()->saved_text) + 1);
1031 strcat(GetNHApp()->saved_text, text);
1035 /* Display the file named str. Complain about missing files
1036 iff complain is TRUE.
1039 mswin_display_file(const char *filename, BOOLEAN_P must_exist)
1044 logDebug("mswin_display_file(%s, %d)\n", filename, must_exist);
1046 f = dlb_fopen(filename, RDTMODE);
1050 _stprintf(message, TEXT("Warning! Could not find file: %s\n"),
1051 NH_A2W(filename, wbuf, sizeof(wbuf)));
1052 NHMessageBox(GetNHApp()->hMainWnd, message,
1053 MB_OK | MB_ICONEXCLAMATION);
1059 text = mswin_create_nhwindow(NHW_TEXT);
1061 while (dlb_fgets(line, LLEN, f)) {
1064 if (line[len - 1] == '\n')
1065 line[len - 1] = '\x0';
1066 mswin_putstr(text, ATR_NONE, line);
1068 (void) dlb_fclose(f);
1070 mswin_display_nhwindow(text, 1);
1071 mswin_destroy_nhwindow(text);
1075 /* Start using window as a menu. You must call start_menu()
1076 before add_menu(). After calling start_menu() you may not
1077 putstr() to the window. Only windows of type NHW_MENU may
1081 mswin_start_menu(winid wid)
1083 logDebug("mswin_start_menu(%d)\n", wid);
1084 if ((wid >= 0) && (wid < MAXWINDOWS)) {
1085 if (GetNHApp()->windowlist[wid].win == NULL
1086 && GetNHApp()->windowlist[wid].type == NHW_MENU) {
1087 GetNHApp()->windowlist[wid].win =
1088 mswin_init_menu_window(MENU_TYPE_MENU);
1089 GetNHApp()->windowlist[wid].dead = 0;
1092 if (GetNHApp()->windowlist[wid].win != NULL) {
1093 SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1094 (WPARAM) MSNH_MSG_STARTMENU, (LPARAM) NULL);
1100 add_menu(windid window, int glyph, const anything identifier,
1101 char accelerator, char groupacc,
1102 int attr, char *str, boolean preselected)
1103 -- Add a text line str to the given menu window. If
1105 is 0, then the line cannot be selected (e.g. a title).
1106 Otherwise, identifier is the value returned if the line is
1107 selected. Accelerator is a keyboard key that can be used
1108 to select the line. If the accelerator of a selectable
1109 item is 0, the window system is free to select its own
1110 accelerator. It is up to the window-port to make the
1111 accelerator visible to the user (e.g. put "a - " in front
1112 of str). The value attr is the same as in putstr().
1113 Glyph is an optional glyph to accompany the line. If
1114 window port cannot or does not want to display it, this
1115 is OK. If there is no glyph applicable, then this
1116 value will be NO_GLYPH.
1117 -- All accelerators should be in the range [A-Za-z].
1118 -- It is expected that callers do not mix accelerator
1119 choices. Either all selectable items have an accelerator
1120 or let the window system pick them. Don't do both.
1121 -- Groupacc is a group accelerator. It may be any character
1122 outside of the standard accelerator (see above) or a
1123 number. If 0, the item is unaffected by any group
1124 accelerator. If this accelerator conflicts with
1125 the menu command (or their user defined aliases), it loses.
1126 The menu commands and aliases take care not to interfere
1127 with the default object class symbols.
1128 -- If you want this choice to be preselected when the
1129 menu is displayed, set preselected to TRUE.
1132 mswin_add_menu(winid wid, int glyph, const ANY_P *identifier,
1133 CHAR_P accelerator, CHAR_P group_accel, int attr,
1134 const char *str, BOOLEAN_P presel)
1136 logDebug("mswin_add_menu(%d, %d, %p, %c, %c, %d, %s, %d)\n", wid, glyph,
1137 identifier, (char) accelerator, (char) group_accel, attr, str,
1139 if ((wid >= 0) && (wid < MAXWINDOWS)
1140 && (GetNHApp()->windowlist[wid].win != NULL)) {
1141 MSNHMsgAddMenu data;
1142 ZeroMemory(&data, sizeof(data));
1144 data.identifier = identifier;
1145 data.accelerator = accelerator;
1146 data.group_accel = group_accel;
1149 data.presel = presel;
1151 SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1152 (WPARAM) MSNH_MSG_ADDMENU, (LPARAM) &data);
1157 end_menu(window, prompt)
1158 -- Stop adding entries to the menu and flushes the window
1159 to the screen (brings to front?). Prompt is a prompt
1160 to give the user. If prompt is NULL, no prompt will
1162 ** This probably shouldn't flush the window any more (if
1163 ** it ever did). That should be select_menu's job. -dean
1166 mswin_end_menu(winid wid, const char *prompt)
1168 logDebug("mswin_end_menu(%d, %s)\n", wid, prompt);
1169 if ((wid >= 0) && (wid < MAXWINDOWS)
1170 && (GetNHApp()->windowlist[wid].win != NULL)) {
1171 MSNHMsgEndMenu data;
1172 ZeroMemory(&data, sizeof(data));
1175 SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1176 (WPARAM) MSNH_MSG_ENDMENU, (LPARAM) &data);
1181 int select_menu(windid window, int how, menu_item **selected)
1182 -- Return the number of items selected; 0 if none were chosen,
1183 -1 when explicitly cancelled. If items were selected, then
1184 selected is filled in with an allocated array of menu_item
1185 structures, one for each selected line. The caller must
1186 free this array when done with it. The "count" field
1187 of selected is a user supplied count. If the user did
1188 not supply a count, then the count field is filled with
1189 -1 (meaning all). A count of zero is equivalent to not
1190 being selected and should not be in the list. If no items
1191 were selected, then selected is NULL'ed out. How is the
1192 mode of the menu. Three valid values are PICK_NONE,
1193 PICK_ONE, and PICK_N, meaning: nothing is selectable,
1194 only one thing is selectable, and any number valid items
1195 may selected. If how is PICK_NONE, this function should
1196 never return anything but 0 or -1.
1197 -- You may call select_menu() on a window multiple times --
1198 the menu is saved until start_menu() or destroy_nhwindow()
1199 is called on the window.
1200 -- Note that NHW_MENU windows need not have select_menu()
1201 called for them. There is no way of knowing whether
1202 select_menu() will be called for the window at
1203 create_nhwindow() time.
1206 mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected)
1210 logDebug("mswin_select_menu(%d, %d)\n", wid, how);
1212 if ((wid >= 0) && (wid < MAXWINDOWS)
1213 && (GetNHApp()->windowlist[wid].win != NULL)) {
1214 ShowWindow(GetNHApp()->windowlist[wid].win, SW_SHOW);
1215 nReturned = mswin_menu_window_select_menu(
1216 GetNHApp()->windowlist[wid].win, how, selected,
1217 !(iflags.perm_invent && wid == WIN_INVEN
1218 && how == PICK_NONE) /* don't activate inventory window if
1219 perm_invent is on */
1226 -- Indicate to the window port that the inventory has been changed.
1227 -- Merely calls display_inventory() for window-ports that leave the
1228 window up, otherwise empty.
1231 mswin_update_inventory()
1233 logDebug("mswin_update_inventory()\n");
1234 if (iflags.perm_invent && program_state.something_worth_saving
1235 && iflags.window_inited && WIN_INVEN != WIN_ERR)
1236 display_inventory(NULL, FALSE);
1240 mark_synch() -- Don't go beyond this point in I/O on any channel until
1241 all channels are caught up to here. Can be an empty call
1247 logDebug("mswin_mark_synch()\n");
1251 wait_synch() -- Wait until all pending output is complete (*flush*() for
1253 -- May also deal with exposure events etc. so that the
1254 display is OK when return from wait_synch().
1259 logDebug("mswin_wait_synch()\n");
1260 mswin_raw_print_flush();
1264 cliparound(x, y)-- Make sure that the user is more-or-less centered on the
1265 screen if the playing area is larger than the screen.
1266 -- This function is only defined if CLIPPING is defined.
1269 mswin_cliparound(int x, int y)
1271 winid wid = WIN_MAP;
1273 logDebug("mswin_cliparound(%d, %d)\n", x, y);
1275 if ((wid >= 0) && (wid < MAXWINDOWS)
1276 && (GetNHApp()->windowlist[wid].win != NULL)) {
1277 MSNHMsgClipAround data;
1280 SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1281 (WPARAM) MSNH_MSG_CLIPAROUND, (LPARAM) &data);
1286 print_glyph(window, x, y, glyph, bkglyph)
1287 -- Print the glyph at (x,y) on the given window. Glyphs are
1288 integers at the interface, mapped to whatever the window-
1289 port wants (symbol, font, color, attributes, ...there's
1290 a 1-1 map between glyphs and distinct things on the map).
1291 -- bkglyph is a background glyph for potential use by some
1292 graphical or tiled environments to allow the depiction
1293 to fall against a background consistent with the grid
1298 mswin_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)
1300 logDebug("mswin_print_glyph(%d, %d, %d, %d, %d)\n", wid, x, y, glyph, bkglyph);
1302 if ((wid >= 0) && (wid < MAXWINDOWS)
1303 && (GetNHApp()->windowlist[wid].win != NULL)) {
1304 MSNHMsgPrintGlyph data;
1306 ZeroMemory(&data, sizeof(data));
1310 data.bkglyph = bkglyph;
1311 SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1312 (WPARAM) MSNH_MSG_PRINT_GLYPH, (LPARAM) &data);
1317 * mswin_raw_print_accumulate() accumulate the given text into
1321 mswin_raw_print_accumulate(const char * str, boolean bold)
1323 bold; // ignored for now
1325 if (raw_print_strbuf.str != NULL) strbuf_append(&raw_print_strbuf, "\n");
1326 strbuf_append(&raw_print_strbuf, str);
1330 * mswin_raw_print_flush() - display any text found in raw_print_strbuf in a
1331 * dialog box and clear raw_print_strbuf.
1334 mswin_raw_print_flush()
1336 if (raw_print_strbuf.str != NULL) {
1337 int wlen = strlen(raw_print_strbuf.str) + 1;
1338 TCHAR * wbuf = (TCHAR *) alloc(wlen * sizeof(TCHAR));
1340 NHMessageBox(GetNHApp()->hMainWnd,
1341 NH_A2W(raw_print_strbuf.str, wbuf, wlen),
1342 MB_ICONINFORMATION | MB_OK);
1345 strbuf_empty(&raw_print_strbuf);
1351 raw_print(str) -- Print directly to a screen, or otherwise guarantee that
1352 the user sees str. raw_print() appends a newline to str.
1353 It need not recognize ASCII control characters. This is
1354 used during startup (before windowing system initialization
1355 -- maybe this means only error startup messages are raw),
1356 for error messages, and maybe other "msg" uses. E.g.
1357 updating status for micros (i.e, "saving").
1360 mswin_raw_print(const char *str)
1362 logDebug("mswin_raw_print(%s)\n", str);
1365 extern int redirect_stdout;
1366 if (!redirect_stdout)
1367 mswin_raw_print_accumulate(str, FALSE);
1369 fprintf(stdout, "%s", str);
1375 -- Like raw_print(), but prints in bold/standout (if
1379 mswin_raw_print_bold(const char *str)
1381 logDebug("mswin_raw_print_bold(%s)\n", str);
1383 extern int redirect_stdout;
1384 if (!redirect_stdout)
1385 mswin_raw_print_accumulate(str, TRUE);
1387 fprintf(stdout, "%s", str);
1392 int nhgetch() -- Returns a single character input from the user.
1393 -- In the tty window-port, nhgetch() assumes that tgetch()
1394 will be the routine the OS provides to read a character.
1395 Returned character _must_ be non-zero.
1403 logDebug("mswin_nhgetch()\n");
1405 while ((event = mswin_input_pop()) == NULL || event->type != NHEVENT_CHAR)
1408 key = event->kbd.ch;
1413 int nh_poskey(int *x, int *y, int *mod)
1414 -- Returns a single character input from the user or a
1415 a positioning event (perhaps from a mouse). If the
1416 return value is non-zero, a character was typed, else,
1417 a position in the MAP window is returned in x, y and mod.
1420 CLICK_1 -- mouse click type 1
1421 CLICK_2 -- mouse click type 2
1423 The different click types can map to whatever the
1424 hardware supports. If no mouse is supported, this
1425 routine always returns a non-zero character.
1428 mswin_nh_poskey(int *x, int *y, int *mod)
1433 logDebug("mswin_nh_poskey()\n");
1435 while ((event = mswin_input_pop()) == NULL)
1438 if (event->type == NHEVENT_MOUSE) {
1439 if (iflags.wc_mouse_support) {
1440 *mod = event->ms.mod;
1446 key = event->kbd.ch;
1452 nhbell() -- Beep at user. [This will exist at least until sounds are
1453 redone, since sounds aren't attributable to windows
1459 logDebug("mswin_nhbell()\n");
1464 -- Display previous messages. Used by the ^P command.
1465 -- On the tty-port this scrolls WIN_MESSAGE back one line.
1468 mswin_doprev_message()
1470 logDebug("mswin_doprev_message()\n");
1471 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_VSCROLL,
1472 MAKEWPARAM(SB_LINEUP, 0), (LPARAM) NULL);
1477 char yn_function(const char *ques, const char *choices, char default)
1478 -- Print a prompt made up of ques, choices and default.
1479 Read a single character response that is contained in
1480 choices or default. If choices is NULL, all possible
1481 inputs are accepted and returned. This overrides
1482 everything else. The choices are expected to be in
1483 lower case. Entering ESC always maps to 'q', or 'n',
1484 in that order, if present in choices, otherwise it maps
1485 to default. Entering any other quit character (SPACE,
1486 RETURN, NEWLINE) maps to default.
1487 -- If the choices string contains ESC, then anything after
1488 it is an acceptable response, but the ESC and whatever
1489 follows is not included in the prompt.
1490 -- If the choices string contains a '#' then accept a count.
1491 Place this value in the global "yn_number" and return '#'.
1492 -- This uses the top line in the tty window-port, other
1493 ports might use a popup.
1496 mswin_yn_function(const char *question, const char *choices, CHAR_P def)
1499 char yn_esc_map = '\033';
1500 char message[BUFSZ];
1503 boolean digit_ok, allow_num;
1505 logDebug("mswin_yn_function(%s, %s, %d)\n", question, choices, def);
1507 if (WIN_MESSAGE == WIN_ERR && choices == ynchars) {
1509 realloc(strdup(GetNHApp()->saved_text),
1510 strlen(question) + strlen(GetNHApp()->saved_text) + 1);
1512 strcat(text, question);
1514 NHMessageBox(NULL, NH_W2A(text, message, sizeof(message)),
1515 MB_ICONQUESTION | MB_YESNOCANCEL
1516 | ((def == 'y') ? MB_DEFBUTTON1
1517 : (def == 'n') ? MB_DEFBUTTON2
1520 GetNHApp()->saved_text = strdup("");
1521 return box_result == IDYES ? 'y' : box_result == IDNO ? 'n' : '\033';
1525 char *cb, choicebuf[QBUFSZ];
1527 allow_num = (index(choices, '#') != 0);
1529 Strcpy(choicebuf, choices);
1530 if ((cb = index(choicebuf, '\033')) != 0) {
1531 /* anything beyond <esc> is hidden */
1534 (void) strncpy(message, question, QBUFSZ - 1);
1535 message[QBUFSZ - 1] = '\0';
1536 sprintf(eos(message), " [%s]", choicebuf);
1538 sprintf(eos(message), " (%c)", def);
1539 Strcat(message, " ");
1540 /* escape maps to 'q' or 'n' or default, in that order */
1542 (index(choices, 'q') ? 'q' : (index(choices, 'n') ? 'n' : def));
1544 Strcpy(message, question);
1545 Strcat(message, " ");
1549 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1550 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1552 mswin_clear_nhwindow(WIN_MESSAGE);
1553 mswin_putstr(WIN_MESSAGE, ATR_BOLD, message);
1555 /* Only here if main window is not present */
1558 ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1559 ch = mswin_nhgetch();
1560 HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1564 break; /* If choices is NULL, all possible inputs are accepted and
1567 digit_ok = allow_num && digit(ch);
1569 if (index(choices, 'q'))
1571 else if (index(choices, 'n'))
1576 } else if (index(quitchars, ch)) {
1579 } else if (!index(choices, ch) && !digit_ok) {
1582 /* and try again... */
1583 } else if (ch == '#' || digit_ok) {
1584 char z, digit_string[2];
1587 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, ("#"), 1);
1589 digit_string[1] = '\0';
1591 digit_string[0] = ch;
1592 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, 1);
1597 do { /* loop until we get a non-digit */
1598 z = lowc(readchar());
1600 value = (10 * value) + (z - '0');
1602 break; /* overflow: try again */
1603 digit_string[0] = z;
1604 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, 1);
1606 } else if (z == 'y' || index(quitchars, z)) {
1608 value = -1; /* abort */
1609 z = '\n'; /* break */
1610 } else if (z == '\b') {
1616 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string,
1621 value = -1; /* abort */
1625 } while (z != '\n');
1628 else if (value == 0)
1629 ch = 'n'; /* 0 => "no" */
1630 else { /* remove number from top line, then try again */
1631 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, -n_len);
1639 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1640 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1642 /* display selection in the message window */
1643 if (isprint((uchar) ch) && ch != '#') {
1646 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, res_ch, 1);
1653 getlin(const char *ques, char *input)
1654 -- Prints ques as a prompt and reads a single line of text,
1655 up to a newline. The string entered is returned without the
1656 newline. ESC is used to cancel, in which case the string
1657 "\033\000" is returned.
1658 -- getlin() must call flush_screen(1) before doing anything.
1659 -- This uses the top line in the tty window-port, other
1660 ports might use a popup.
1663 mswin_getlin(const char *question, char *input)
1665 logDebug("mswin_getlin(%s, %p)\n", question, input);
1667 if (!iflags.wc_popup_dialog) {
1678 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1679 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1681 /* mswin_clear_nhwindow(WIN_MESSAGE); */
1682 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, question, 0);
1683 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, " ", 1);
1685 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, input, 0);
1686 len = strlen(input);
1691 ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1694 c = mswin_nhgetch();
1697 strcpy(input, "\033");
1707 mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, input, -len);
1711 #if 1 /*JP*//*2
\83o
\83C
\83g
\95¶
\8e\9a\82È
\82ç
\82à
\82¤1
\83o
\83C
\83g
\8fÁ
\82·*/
1712 if (len > 0 && is_kanji2(input, len))
1716 } else if (len>=(BUFSZ-1)) {
1717 PlaySound((LPCSTR)SND_ALIAS_SYSTEMEXCLAMATION, NULL, SND_ALIAS_ID|SND_ASYNC);
1720 #if 1 /*JP*//*2
\83o
\83C
\83g
\95¶
\8e\9a\82È
\82ç
\82»
\82Ì
\8fê
\82Å
\82à
\82¤1
\83o
\83C
\83g
\93Ç
\82Ý
\8d\9e\82Þ*/
1722 c = mswin_nhgetch();
1728 mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, input, 1);
1732 HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1734 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1735 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1737 if (mswin_getlin_window(question, input, BUFSZ) == IDCANCEL) {
1738 strcpy(input, "\033");
1744 int get_ext_cmd(void)
1745 -- Get an extended command in a window-port specific way.
1746 An index into extcmdlist[] is returned on a successful
1747 selection, -1 otherwise.
1753 logDebug("mswin_get_ext_cmd()\n");
1755 if (!iflags.wc_popup_dialog) {
1762 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1763 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1767 mswin_clear_nhwindow(WIN_MESSAGE);
1768 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, "#", 0);
1770 ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1772 int oindex, com_index;
1773 c = mswin_nhgetch();
1781 for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++)
1782 if (!strcmpi(cmd, extcmdlist[i].ef_txt))
1785 if (extcmdlist[i].ef_txt == (char *) 0) {
1786 pline("%s: unknown extended command.", cmd);
1792 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, cmd,
1793 -(int) strlen(cmd));
1801 /* Find a command with this prefix in extcmdlist */
1803 for (oindex = 0; extcmdlist[oindex].ef_txt != (char *) 0;
1805 if ((extcmdlist[oindex].flags & AUTOCOMPLETE)
1806 && !(!wizard && (extcmdlist[oindex].flags & WIZMODECMD))
1807 && !strncmpi(cmd, extcmdlist[oindex].ef_txt, len)) {
1808 if (com_index == -1) /* no matches yet */
1812 -2; /* two matches, don't complete */
1815 if (com_index >= 0) {
1816 Strcpy(cmd, extcmdlist[com_index].ef_txt);
1819 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, cmd, 1);
1823 HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1825 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1826 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1829 if (mswin_ext_cmd_window(&ret) == IDCANCEL)
1838 -- Initialize the number pad to the given state.
1841 mswin_number_pad(int state)
1844 logDebug("mswin_number_pad(%d)\n", state);
1848 delay_output() -- Causes a visible delay of 50ms in the output.
1849 Conceptually, this is similar to wait_synch() followed
1850 by a nap(50ms), but allows asynchronous operation.
1853 mswin_delay_output()
1855 logDebug("mswin_delay_output()\n");
1856 mswin_map_update(mswin_hwnd_from_winid(WIN_MAP));
1861 mswin_change_color()
1863 logDebug("mswin_change_color()\n");
1867 mswin_get_color_string()
1869 logDebug("mswin_get_color_string()\n");
1874 start_screen() -- Only used on Unix tty ports, but must be declared for
1875 completeness. Sets up the tty to work in full-screen
1876 graphics mode. Look at win/tty/termcap.c for an
1877 example. If your window-port does not need this function
1878 just declare an empty function.
1881 mswin_start_screen()
1884 logDebug("mswin_start_screen()\n");
1888 end_screen() -- Only used on Unix tty ports, but must be declared for
1889 completeness. The complement of start_screen().
1895 logDebug("mswin_end_screen()\n");
1899 outrip(winid, int, when)
1900 -- The tombstone code. If you want the traditional code use
1901 genl_outrip for the value and check the #if in rip.c.
1903 #define STONE_LINE_LEN 16
1905 mswin_outrip(winid wid, int how, time_t when)
1910 logDebug("mswin_outrip(%d, %d, %ld)\n", wid, how, (long) when);
1911 if ((wid >= 0) && (wid < MAXWINDOWS)) {
1912 DestroyWindow(GetNHApp()->windowlist[wid].win);
1913 GetNHApp()->windowlist[wid].win = mswin_init_RIP_window();
1914 GetNHApp()->windowlist[wid].type = NHW_RIP;
1915 GetNHApp()->windowlist[wid].dead = 0;
1918 /* Put name on stone */
1919 Sprintf(buf, "%s", plname);
1920 buf[STONE_LINE_LEN] = 0;
1921 putstr(wid, 0, buf);
1923 /* Put $ on stone */
1924 Sprintf(buf, "%ld Au", done_money);
1925 buf[STONE_LINE_LEN] = 0; /* It could be a *lot* of gold :-) */
1926 putstr(wid, 0, buf);
1928 /* Put together death description */
1929 formatkiller(buf, sizeof buf, how, FALSE);
1931 /* Put death type on stone */
1932 putstr(wid, 0, buf);
1934 /* Put year on stone */
1935 year = yyyymmdd(when) / 10000L;
1936 Sprintf(buf, "%4ld", year);
1937 putstr(wid, 0, buf);
1938 mswin_finish_rip_text(wid);
1941 /* handle options updates here */
1943 mswin_preference_update(const char *pref)
1945 if (stricmp(pref, "font_menu") == 0
1946 || stricmp(pref, "font_size_menu") == 0) {
1947 if (iflags.wc_fontsiz_menu < NHFONT_SIZE_MIN
1948 || iflags.wc_fontsiz_menu > NHFONT_SIZE_MAX)
1949 iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE;
1951 HDC hdc = GetDC(GetNHApp()->hMainWnd);
1952 mswin_get_font(NHW_MENU, ATR_NONE, hdc, TRUE);
1953 mswin_get_font(NHW_MENU, ATR_BOLD, hdc, TRUE);
1954 mswin_get_font(NHW_MENU, ATR_DIM, hdc, TRUE);
1955 mswin_get_font(NHW_MENU, ATR_ULINE, hdc, TRUE);
1956 mswin_get_font(NHW_MENU, ATR_BLINK, hdc, TRUE);
1957 mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, TRUE);
1958 ReleaseDC(GetNHApp()->hMainWnd, hdc);
1960 mswin_layout_main_window(NULL);
1964 if (stricmp(pref, "font_status") == 0
1965 || stricmp(pref, "font_size_status") == 0) {
1966 if (iflags.wc_fontsiz_status < NHFONT_SIZE_MIN
1967 || iflags.wc_fontsiz_status > NHFONT_SIZE_MAX)
1968 iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE;
1970 HDC hdc = GetDC(GetNHApp()->hMainWnd);
1971 mswin_get_font(NHW_STATUS, ATR_NONE, hdc, TRUE);
1972 mswin_get_font(NHW_STATUS, ATR_BOLD, hdc, TRUE);
1973 mswin_get_font(NHW_STATUS, ATR_DIM, hdc, TRUE);
1974 mswin_get_font(NHW_STATUS, ATR_ULINE, hdc, TRUE);
1975 mswin_get_font(NHW_STATUS, ATR_BLINK, hdc, TRUE);
1976 mswin_get_font(NHW_STATUS, ATR_INVERSE, hdc, TRUE);
1977 ReleaseDC(GetNHApp()->hMainWnd, hdc);
1979 for (int i = 1; i < MAXWINDOWS; i++) {
1980 if (GetNHApp()->windowlist[i].type == NHW_STATUS
1981 && GetNHApp()->windowlist[i].win != NULL) {
1982 InvalidateRect(GetNHApp()->windowlist[i].win, NULL, TRUE);
1985 mswin_layout_main_window(NULL);
1989 if (stricmp(pref, "font_message") == 0
1990 || stricmp(pref, "font_size_message") == 0) {
1991 if (iflags.wc_fontsiz_message < NHFONT_SIZE_MIN
1992 || iflags.wc_fontsiz_message > NHFONT_SIZE_MAX)
1993 iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE;
1995 HDC hdc = GetDC(GetNHApp()->hMainWnd);
1996 mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, TRUE);
1997 mswin_get_font(NHW_MESSAGE, ATR_BOLD, hdc, TRUE);
1998 mswin_get_font(NHW_MESSAGE, ATR_DIM, hdc, TRUE);
1999 mswin_get_font(NHW_MESSAGE, ATR_ULINE, hdc, TRUE);
2000 mswin_get_font(NHW_MESSAGE, ATR_BLINK, hdc, TRUE);
2001 mswin_get_font(NHW_MESSAGE, ATR_INVERSE, hdc, TRUE);
2002 ReleaseDC(GetNHApp()->hMainWnd, hdc);
2004 InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE);
2005 mswin_layout_main_window(NULL);
2009 if (stricmp(pref, "font_text") == 0
2010 || stricmp(pref, "font_size_text") == 0) {
2011 if (iflags.wc_fontsiz_text < NHFONT_SIZE_MIN
2012 || iflags.wc_fontsiz_text > NHFONT_SIZE_MAX)
2013 iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE;
2015 HDC hdc = GetDC(GetNHApp()->hMainWnd);
2016 mswin_get_font(NHW_TEXT, ATR_NONE, hdc, TRUE);
2017 mswin_get_font(NHW_TEXT, ATR_BOLD, hdc, TRUE);
2018 mswin_get_font(NHW_TEXT, ATR_DIM, hdc, TRUE);
2019 mswin_get_font(NHW_TEXT, ATR_ULINE, hdc, TRUE);
2020 mswin_get_font(NHW_TEXT, ATR_BLINK, hdc, TRUE);
2021 mswin_get_font(NHW_TEXT, ATR_INVERSE, hdc, TRUE);
2022 ReleaseDC(GetNHApp()->hMainWnd, hdc);
2024 mswin_layout_main_window(NULL);
2028 if (stricmp(pref, "scroll_amount") == 0) {
2029 mswin_cliparound(u.ux, u.uy);
2033 if (stricmp(pref, "scroll_margin") == 0) {
2034 mswin_cliparound(u.ux, u.uy);
2038 if (stricmp(pref, "map_mode") == 0) {
2039 mswin_select_map_mode(iflags.wc_map_mode);
2043 if (stricmp(pref, "hilite_pet") == 0) {
2044 InvalidateRect(mswin_hwnd_from_winid(WIN_MAP), NULL, TRUE);
2048 if (stricmp(pref, "align_message") == 0
2049 || stricmp(pref, "align_status") == 0) {
2050 mswin_layout_main_window(NULL);
2054 if (stricmp(pref, "vary_msgcount") == 0) {
2055 InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE);
2056 mswin_layout_main_window(NULL);
2060 if (stricmp(pref, "perm_invent") == 0) {
2061 mswin_update_inventory();
2066 #define TEXT_BUFFER_SIZE 4096
2068 mswin_getmsghistory(BOOLEAN_P init)
2070 static PMSNHMsgGetText text = 0;
2071 static char *next_message = 0;
2074 text = (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText)
2075 + TEXT_BUFFER_SIZE);
2078 - 1; /* make sure we always have 0 at the end of the buffer */
2080 ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
2081 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
2082 (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
2084 next_message = text->buffer;
2087 if (!(next_message && next_message[0])) {
2092 char *retval = next_message;
2094 next_message = p = strchr(next_message, '\n');
2098 while (p >= retval && isspace((uchar) *p))
2099 *p-- = (char) 0; /* delete trailing whitespace */
2105 mswin_putmsghistory(const char *msg, BOOLEAN_P restoring)
2107 BOOL save_sound_opt;
2109 UNREFERENCED_PARAMETER(restoring);
2112 return; /* end of message history restore */
2113 save_sound_opt = GetNHApp()->bNoSounds;
2114 GetNHApp()->bNoSounds =
2115 TRUE; /* disable sounds while restoring message history */
2116 mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, msg, 0);
2117 clear_nhwindow(WIN_MESSAGE); /* it is in fact end-of-turn indication so
2118 each message will print on the new line */
2119 GetNHApp()->bNoSounds = save_sound_opt; /* restore sounds option */
2125 while (!mswin_have_input()) {
2128 mswin_map_update(mswin_hwnd_from_winid(WIN_MAP));
2130 if (!iflags.debug_fuzzer || PeekMessage(&msg, NULL, 0, 0, FALSE)) {
2131 if(GetMessage(&msg, NULL, 0, 0) != 0) {
2132 if (GetNHApp()->regNetHackMode
2133 || !TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable,
2135 TranslateMessage(&msg);
2136 DispatchMessage(&msg);
2143 nhassert(iflags.debug_fuzzer);
2144 PostMessage(GetNHApp()->hMainWnd, WM_MSNH_COMMAND,
2145 MSNH_MSG_RANDOM_INPUT, 0);
2150 /* clean up and quit */
2152 bail(const char *mesg)
2155 mswin_exit_nhwindows(mesg);
2156 nh_terminate(EXIT_SUCCESS);
2165 TCHAR wbuf[MAX_PATH];
2169 extern int total_tiles_used;
2171 /* no file - no tile */
2172 if (!(iflags.wc_tile_file && *iflags.wc_tile_file))
2176 hBmp = LoadImage(GetNHApp()->hApp,
2177 NH_A2W(iflags.wc_tile_file, wbuf, MAX_PATH),
2178 IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
2182 errcode = GetLastError();
2183 Sprintf(errmsg, "%s (0x%x).",
2184 "Cannot load tiles from the file. Reverting back to default",
2190 /* calculate tile dimensions */
2191 GetObject(hBmp, sizeof(BITMAP), (LPVOID) &bm);
2192 if (bm.bmWidth % iflags.wc_tile_width
2193 || bm.bmHeight % iflags.wc_tile_height) {
2195 raw_print("Tiles bitmap does not match tile_width and tile_height "
2196 "options. Reverting back to default.");
2200 tl_num = (bm.bmWidth / iflags.wc_tile_width)
2201 * (bm.bmHeight / iflags.wc_tile_height);
2202 if (tl_num < total_tiles_used) {
2204 raw_print("Number of tiles in the bitmap is less than required by "
2205 "the game. Reverting back to default.");
2209 /* set the tile information */
2210 if (GetNHApp()->bmpMapTiles != GetNHApp()->bmpTiles) {
2211 DeleteObject(GetNHApp()->bmpMapTiles);
2214 GetNHApp()->bmpMapTiles = hBmp;
2215 GetNHApp()->mapTile_X = iflags.wc_tile_width;
2216 GetNHApp()->mapTile_Y = iflags.wc_tile_height;
2217 GetNHApp()->mapTilesPerLine = bm.bmWidth / iflags.wc_tile_width;
2219 map_size.cx = GetNHApp()->mapTile_X * COLNO;
2220 map_size.cy = GetNHApp()->mapTile_Y * ROWNO;
2221 mswin_map_layout(mswin_hwnd_from_winid(WIN_MAP), &map_size);
2226 mswin_popup_display(HWND hWnd, int *done_indicator)
2234 /* activate the menu window */
2235 GetNHApp()->hPopupWnd = hWnd;
2237 mswin_layout_main_window(hWnd);
2239 /* disable game windows */
2240 for (hChild = GetWindow(GetNHApp()->hMainWnd, GW_CHILD); hChild;
2241 hChild = GetWindow(hChild, GW_HWNDNEXT)) {
2243 EnableWindow(hChild, FALSE);
2247 hMenu = GetMenu(GetNHApp()->hMainWnd);
2248 mi_count = GetMenuItemCount(hMenu);
2249 for (i = 0; i < mi_count; i++) {
2250 EnableMenuItem(hMenu, i, MF_BYPOSITION | MF_GRAYED);
2252 DrawMenuBar(GetNHApp()->hMainWnd);
2254 /* bring menu window on top */
2255 SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,
2256 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
2259 /* go into message loop */
2260 while (IsWindow(hWnd) && (done_indicator == NULL || !*done_indicator)) {
2261 if (!iflags.debug_fuzzer || PeekMessage(&msg, NULL, 0, 0, FALSE)) {
2262 if(GetMessage(&msg, NULL, 0, 0) != 0) {
2263 if (msg.message == WM_MSNH_COMMAND ||
2264 !IsDialogMessage(hWnd, &msg)) {
2265 if (!TranslateAccelerator(msg.hwnd,
2266 GetNHApp()->hAccelTable, &msg)) {
2267 TranslateMessage(&msg);
2268 DispatchMessage(&msg);
2276 nhassert(iflags.debug_fuzzer);
2277 PostMessage(hWnd, WM_MSNH_COMMAND, MSNH_MSG_RANDOM_INPUT, 0);
2283 mswin_popup_destroy(HWND hWnd)
2290 /* enable game windows */
2291 for (hChild = GetWindow(GetNHApp()->hMainWnd, GW_CHILD); hChild;
2292 hChild = GetWindow(hChild, GW_HWNDNEXT)) {
2293 if (hChild != hWnd) {
2294 EnableWindow(hChild, TRUE);
2299 hMenu = GetMenu(GetNHApp()->hMainWnd);
2300 mi_count = GetMenuItemCount(hMenu);
2301 for (i = 0; i < mi_count; i++) {
2302 EnableMenuItem(hMenu, i, MF_BYPOSITION | MF_ENABLED);
2304 DrawMenuBar(GetNHApp()->hMainWnd);
2306 /* Don't hide the permanent inventory window ... leave it showing */
2307 if (!iflags.perm_invent || mswin_winid_from_handle(hWnd) != WIN_INVEN)
2308 ShowWindow(hWnd, SW_HIDE);
2310 GetNHApp()->hPopupWnd = NULL;
2312 mswin_layout_main_window(hWnd);
2314 SetFocus(GetNHApp()->hMainWnd);
2322 logDebug(const char *fmt, ...)
2326 if (!showdebug(NHTRACE_LOG) || !_s_debugfp)
2329 va_start(args, fmt);
2330 vfprintf(_s_debugfp, fmt, args);
2337 /* Reading and writing settings from the registry. */
2338 #define CATEGORYKEY "Software"
2339 #define COMPANYKEY "NetHack"
2340 #define PRODUCTKEY "NetHack 3.6.6"
2341 #define SETTINGSKEY "Settings"
2342 #define MAINSHOWSTATEKEY "MainShowState"
2343 #define MAINMINXKEY "MainMinX"
2344 #define MAINMINYKEY "MainMinY"
2345 #define MAINMAXXKEY "MainMaxX"
2346 #define MAINMAXYKEY "MainMaxY"
2347 #define MAINLEFTKEY "MainLeft"
2348 #define MAINRIGHTKEY "MainRight"
2349 #define MAINTOPKEY "MainTop"
2350 #define MAINBOTTOMKEY "MainBottom"
2351 #define MAINAUTOLAYOUT "AutoLayout"
2352 #define MAPLEFT "MapLeft"
2353 #define MAPRIGHT "MapRight"
2354 #define MAPTOP "MapTop"
2355 #define MAPBOTTOM "MapBottom"
2356 #define MSGLEFT "MsgLeft"
2357 #define MSGRIGHT "MsgRight"
2358 #define MSGTOP "MsgTop"
2359 #define MSGBOTTOM "MsgBottom"
2360 #define STATUSLEFT "StatusLeft"
2361 #define STATUSRIGHT "StatusRight"
2362 #define STATUSTOP "StatusTop"
2363 #define STATUSBOTTOM "StatusBottom"
2364 #define MENULEFT "MenuLeft"
2365 #define MENURIGHT "MenuRight"
2366 #define MENUTOP "MenuTop"
2367 #define MENUBOTTOM "MenuBottom"
2368 #define TEXTLEFT "TextLeft"
2369 #define TEXTRIGHT "TextRight"
2370 #define TEXTTOP "TextTop"
2371 #define TEXTBOTTOM "TextBottom"
2372 #define INVENTLEFT "InventLeft"
2373 #define INVENTRIGHT "InventRight"
2374 #define INVENTTOP "InventTop"
2375 #define INVENTBOTTOM "InventBottom"
2377 /* #define all the subkeys here */
2378 #define INTFKEY "Interface"
2386 char keystring[MAX_PATH];
2388 COLORREF default_mapcolors[CLR_MAX] = {
2389 RGB(0x55, 0x55, 0x55), /* CLR_BLACK */
2390 RGB(0xFF, 0x00, 0x00), /* CLR_RED */
2391 RGB(0x00, 0x80, 0x00), /* CLR_GREEN */
2392 RGB(0xA5, 0x2A, 0x2A), /* CLR_BROWN */
2393 RGB(0x00, 0x00, 0xFF), /* CLR_BLUE */
2394 RGB(0xFF, 0x00, 0xFF), /* CLR_MAGENTA */
2395 RGB(0x00, 0xFF, 0xFF), /* CLR_CYAN */
2396 RGB(0xC0, 0xC0, 0xC0), /* CLR_GRAY */
2397 RGB(0xFF, 0xFF, 0xFF), /* NO_COLOR */
2398 RGB(0xFF, 0xA5, 0x00), /* CLR_ORANGE */
2399 RGB(0x00, 0xFF, 0x00), /* CLR_BRIGHT_GREEN */
2400 RGB(0xFF, 0xFF, 0x00), /* CLR_YELLOW */
2401 RGB(0x00, 0xC0, 0xFF), /* CLR_BRIGHT_BLUE */
2402 RGB(0xFF, 0x80, 0xFF), /* CLR_BRIGHT_MAGENTA */
2403 RGB(0x80, 0xFF, 0xFF), /* CLR_BRIGHT_CYAN */
2404 RGB(0xFF, 0xFF, 0xFF) /* CLR_WHITE */
2407 sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY,
2410 /* Set the defaults here. The very first time the app is started, nothing
2412 read from the registry, so these defaults apply. */
2413 GetNHApp()->saveRegistrySettings = 1; /* Normally, we always save */
2414 GetNHApp()->regNetHackMode = TRUE;
2416 for (i = 0; i < CLR_MAX; i++)
2417 GetNHApp()->regMapColors[i] = default_mapcolors[i];
2419 if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key)
2423 size = sizeof(DWORD);
2425 #define NHGETREG_DWORD(name, val) \
2426 RegQueryValueEx(key, (name), 0, NULL, (unsigned char *)(&safe_buf), \
2430 /* read the keys here */
2431 NHGETREG_DWORD(INTFKEY, GetNHApp()->regNetHackMode);
2433 /* read window placement */
2434 NHGETREG_DWORD(MAINSHOWSTATEKEY, GetNHApp()->regMainShowState);
2435 NHGETREG_DWORD(MAINMINXKEY, GetNHApp()->regMainMinX);
2436 NHGETREG_DWORD(MAINMINYKEY, GetNHApp()->regMainMinY);
2437 NHGETREG_DWORD(MAINMAXXKEY, GetNHApp()->regMainMaxX);
2438 NHGETREG_DWORD(MAINMAXYKEY, GetNHApp()->regMainMaxY);
2439 NHGETREG_DWORD(MAINLEFTKEY, GetNHApp()->regMainLeft);
2440 NHGETREG_DWORD(MAINRIGHTKEY, GetNHApp()->regMainRight);
2441 NHGETREG_DWORD(MAINTOPKEY, GetNHApp()->regMainTop);
2442 NHGETREG_DWORD(MAINBOTTOMKEY, GetNHApp()->regMainBottom);
2444 NHGETREG_DWORD(MAINAUTOLAYOUT, GetNHApp()->bAutoLayout);
2445 NHGETREG_DWORD(MAPLEFT, GetNHApp()->rtMapWindow.left);
2446 NHGETREG_DWORD(MAPRIGHT, GetNHApp()->rtMapWindow.right);
2447 NHGETREG_DWORD(MAPTOP, GetNHApp()->rtMapWindow.top);
2448 NHGETREG_DWORD(MAPBOTTOM, GetNHApp()->rtMapWindow.bottom);
2449 NHGETREG_DWORD(MSGLEFT, GetNHApp()->rtMsgWindow.left);
2450 NHGETREG_DWORD(MSGRIGHT, GetNHApp()->rtMsgWindow.right);
2451 NHGETREG_DWORD(MSGTOP, GetNHApp()->rtMsgWindow.top);
2452 NHGETREG_DWORD(MSGBOTTOM, GetNHApp()->rtMsgWindow.bottom);
2453 NHGETREG_DWORD(STATUSLEFT, GetNHApp()->rtStatusWindow.left);
2454 NHGETREG_DWORD(STATUSRIGHT, GetNHApp()->rtStatusWindow.right);
2455 NHGETREG_DWORD(STATUSTOP, GetNHApp()->rtStatusWindow.top);
2456 NHGETREG_DWORD(STATUSBOTTOM, GetNHApp()->rtStatusWindow.bottom);
2457 NHGETREG_DWORD(MENULEFT, GetNHApp()->rtMenuWindow.left);
2458 NHGETREG_DWORD(MENURIGHT, GetNHApp()->rtMenuWindow.right);
2459 NHGETREG_DWORD(MENUTOP, GetNHApp()->rtMenuWindow.top);
2460 NHGETREG_DWORD(MENUBOTTOM, GetNHApp()->rtMenuWindow.bottom);
2461 NHGETREG_DWORD(TEXTLEFT, GetNHApp()->rtTextWindow.left);
2462 NHGETREG_DWORD(TEXTRIGHT, GetNHApp()->rtTextWindow.right);
2463 NHGETREG_DWORD(TEXTTOP, GetNHApp()->rtTextWindow.top);
2464 NHGETREG_DWORD(TEXTBOTTOM, GetNHApp()->rtTextWindow.bottom);
2465 NHGETREG_DWORD(INVENTLEFT, GetNHApp()->rtInvenWindow.left);
2466 NHGETREG_DWORD(INVENTRIGHT, GetNHApp()->rtInvenWindow.right);
2467 NHGETREG_DWORD(INVENTTOP, GetNHApp()->rtInvenWindow.top);
2468 NHGETREG_DWORD(INVENTBOTTOM, GetNHApp()->rtInvenWindow.bottom);
2469 #undef NHGETREG_DWORD
2471 for (i = 0; i < CLR_MAX; i++) {
2473 char mapcolorkey[64];
2474 sprintf(mapcolorkey, "MapColor%02d", i);
2475 if (RegQueryValueEx(key, mapcolorkey, NULL, NULL, (BYTE *)&cl, &size) == ERROR_SUCCESS)
2476 GetNHApp()->regMapColors[i] = cl;
2481 /* check the data for validity */
2482 if (IsRectEmpty(&GetNHApp()->rtMapWindow)
2483 || IsRectEmpty(&GetNHApp()->rtMsgWindow)
2484 || IsRectEmpty(&GetNHApp()->rtStatusWindow)
2485 || IsRectEmpty(&GetNHApp()->rtMenuWindow)
2486 || IsRectEmpty(&GetNHApp()->rtTextWindow)
2487 || IsRectEmpty(&GetNHApp()->rtInvenWindow)) {
2488 GetNHApp()->bAutoLayout = TRUE;
2499 if (GetNHApp()->saveRegistrySettings) {
2500 char keystring[MAX_PATH];
2503 sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY,
2504 PRODUCTKEY, SETTINGSKEY);
2506 if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_WRITE, &key)
2508 RegCreateKeyEx(HKEY_CURRENT_USER, keystring, 0, "",
2509 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
2510 &key, &disposition);
2513 #define NHSETREG_DWORD(name, val) \
2514 RegSetValueEx(key, (name), 0, REG_DWORD, \
2515 (unsigned char *)((safe_buf = (val)), &safe_buf), \
2518 /* Write the keys here */
2519 NHSETREG_DWORD(INTFKEY, GetNHApp()->regNetHackMode);
2521 /* Main window placement */
2522 NHSETREG_DWORD(MAINSHOWSTATEKEY, GetNHApp()->regMainShowState);
2523 NHSETREG_DWORD(MAINMINXKEY, GetNHApp()->regMainMinX);
2524 NHSETREG_DWORD(MAINMINYKEY, GetNHApp()->regMainMinY);
2525 NHSETREG_DWORD(MAINMAXXKEY, GetNHApp()->regMainMaxX);
2526 NHSETREG_DWORD(MAINMAXYKEY, GetNHApp()->regMainMaxY);
2527 NHSETREG_DWORD(MAINLEFTKEY, GetNHApp()->regMainLeft);
2528 NHSETREG_DWORD(MAINRIGHTKEY, GetNHApp()->regMainRight);
2529 NHSETREG_DWORD(MAINTOPKEY, GetNHApp()->regMainTop);
2530 NHSETREG_DWORD(MAINBOTTOMKEY, GetNHApp()->regMainBottom);
2532 NHSETREG_DWORD(MAINAUTOLAYOUT, GetNHApp()->bAutoLayout);
2533 NHSETREG_DWORD(MAPLEFT, GetNHApp()->rtMapWindow.left);
2534 NHSETREG_DWORD(MAPRIGHT, GetNHApp()->rtMapWindow.right);
2535 NHSETREG_DWORD(MAPTOP, GetNHApp()->rtMapWindow.top);
2536 NHSETREG_DWORD(MAPBOTTOM, GetNHApp()->rtMapWindow.bottom);
2537 NHSETREG_DWORD(MSGLEFT, GetNHApp()->rtMsgWindow.left);
2538 NHSETREG_DWORD(MSGRIGHT, GetNHApp()->rtMsgWindow.right);
2539 NHSETREG_DWORD(MSGTOP, GetNHApp()->rtMsgWindow.top);
2540 NHSETREG_DWORD(MSGBOTTOM, GetNHApp()->rtMsgWindow.bottom);
2541 NHSETREG_DWORD(STATUSLEFT, GetNHApp()->rtStatusWindow.left);
2542 NHSETREG_DWORD(STATUSRIGHT, GetNHApp()->rtStatusWindow.right);
2543 NHSETREG_DWORD(STATUSTOP, GetNHApp()->rtStatusWindow.top);
2544 NHSETREG_DWORD(STATUSBOTTOM, GetNHApp()->rtStatusWindow.bottom);
2545 NHSETREG_DWORD(MENULEFT, GetNHApp()->rtMenuWindow.left);
2546 NHSETREG_DWORD(MENURIGHT, GetNHApp()->rtMenuWindow.right);
2547 NHSETREG_DWORD(MENUTOP, GetNHApp()->rtMenuWindow.top);
2548 NHSETREG_DWORD(MENUBOTTOM, GetNHApp()->rtMenuWindow.bottom);
2549 NHSETREG_DWORD(TEXTLEFT, GetNHApp()->rtTextWindow.left);
2550 NHSETREG_DWORD(TEXTRIGHT, GetNHApp()->rtTextWindow.right);
2551 NHSETREG_DWORD(TEXTTOP, GetNHApp()->rtTextWindow.top);
2552 NHSETREG_DWORD(TEXTBOTTOM, GetNHApp()->rtTextWindow.bottom);
2553 NHSETREG_DWORD(INVENTLEFT, GetNHApp()->rtInvenWindow.left);
2554 NHSETREG_DWORD(INVENTRIGHT, GetNHApp()->rtInvenWindow.right);
2555 NHSETREG_DWORD(INVENTTOP, GetNHApp()->rtInvenWindow.top);
2556 NHSETREG_DWORD(INVENTBOTTOM, GetNHApp()->rtInvenWindow.bottom);
2557 #undef NHSETREG_DWORD
2559 for (i = 0; i < CLR_MAX; i++) {
2560 COLORREF cl = GetNHApp()->regMapColors[i];
2561 char mapcolorkey[64];
2562 sprintf(mapcolorkey, "MapColor%02d", i);
2563 RegSetValueEx(key, mapcolorkey, 0, REG_DWORD, (BYTE *)&cl, sizeof(DWORD));
2573 char keystring[MAX_PATH];
2577 /* Delete keys one by one, as NT does not delete trees */
2578 sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY,
2580 RegDeleteKey(HKEY_CURRENT_USER, keystring);
2581 sprintf(keystring, "%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY);
2582 RegDeleteKey(HKEY_CURRENT_USER, keystring);
2583 /* The company key will also contain information about newer versions
2584 of nethack (e.g. a subkey called NetHack 4.0), so only delete that
2585 if it's empty now. */
2586 sprintf(keystring, "%s\\%s", CATEGORYKEY, COMPANYKEY);
2587 /* If we cannot open it, we probably cannot delete it either... Just
2588 go on and see what happens. */
2589 RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key);
2591 RegQueryInfoKey(key, NULL, NULL, NULL, &nrsubkeys, NULL, NULL, NULL, NULL,
2595 RegDeleteKey(HKEY_CURRENT_USER, keystring);
2597 /* Prevent saving on exit */
2598 GetNHApp()->saveRegistrySettings = 0;
2601 typedef struct ctv {
2602 const char *colorstring;
2603 COLORREF colorvalue;
2604 } color_table_value;
2607 * The color list here is a combination of:
2608 * NetHack colors. (See mhmap.c)
2609 * HTML colors. (See http://www.w3.org/TR/REC-html40/types.html#h-6.5 )
2612 static color_table_value color_table[] = {
2613 /* NetHack colors */
2614 { "black", RGB(0x55, 0x55, 0x55) },
2615 { "red", RGB(0xFF, 0x00, 0x00) },
2616 { "green", RGB(0x00, 0x80, 0x00) },
2617 { "brown", RGB(0xA5, 0x2A, 0x2A) },
2618 { "blue", RGB(0x00, 0x00, 0xFF) },
2619 { "magenta", RGB(0xFF, 0x00, 0xFF) },
2620 { "cyan", RGB(0x00, 0xFF, 0xFF) },
2621 { "orange", RGB(0xFF, 0xA5, 0x00) },
2622 { "brightgreen", RGB(0x00, 0xFF, 0x00) },
2623 { "yellow", RGB(0xFF, 0xFF, 0x00) },
2624 { "brightblue", RGB(0x00, 0xC0, 0xFF) },
2625 { "brightmagenta", RGB(0xFF, 0x80, 0xFF) },
2626 { "brightcyan", RGB(0x80, 0xFF, 0xFF) },
2627 { "white", RGB(0xFF, 0xFF, 0xFF) },
2628 /* Remaining HTML colors */
2629 { "trueblack", RGB(0x00, 0x00, 0x00) },
2630 { "gray", RGB(0x80, 0x80, 0x80) },
2631 { "grey", RGB(0x80, 0x80, 0x80) },
2632 { "purple", RGB(0x80, 0x00, 0x80) },
2633 { "silver", RGB(0xC0, 0xC0, 0xC0) },
2634 { "maroon", RGB(0x80, 0x00, 0x00) },
2635 { "fuchsia", RGB(0xFF, 0x00, 0xFF) }, /* = NetHack magenta */
2636 { "lime", RGB(0x00, 0xFF, 0x00) }, /* = NetHack bright green */
2637 { "olive", RGB(0x80, 0x80, 0x00) },
2638 { "navy", RGB(0x00, 0x00, 0x80) },
2639 { "teal", RGB(0x00, 0x80, 0x80) },
2640 { "aqua", RGB(0x00, 0xFF, 0xFF) }, /* = NetHack cyan */
2641 { "", RGB(0x00, 0x00, 0x00) },
2644 typedef struct ctbv {
2647 } color_table_brush_value;
2649 static color_table_brush_value color_table_brush[] = {
2650 { "activeborder", COLOR_ACTIVEBORDER },
2651 { "activecaption", COLOR_ACTIVECAPTION },
2652 { "appworkspace", COLOR_APPWORKSPACE },
2653 { "background", COLOR_BACKGROUND },
2654 { "btnface", COLOR_BTNFACE },
2655 { "btnshadow", COLOR_BTNSHADOW },
2656 { "btntext", COLOR_BTNTEXT },
2657 { "captiontext", COLOR_CAPTIONTEXT },
2658 { "graytext", COLOR_GRAYTEXT },
2659 { "greytext", COLOR_GRAYTEXT },
2660 { "highlight", COLOR_HIGHLIGHT },
2661 { "highlighttext", COLOR_HIGHLIGHTTEXT },
2662 { "inactiveborder", COLOR_INACTIVEBORDER },
2663 { "inactivecaption", COLOR_INACTIVECAPTION },
2664 { "menu", COLOR_MENU },
2665 { "menutext", COLOR_MENUTEXT },
2666 { "scrollbar", COLOR_SCROLLBAR },
2667 { "window", COLOR_WINDOW },
2668 { "windowframe", COLOR_WINDOWFRAME },
2669 { "windowtext", COLOR_WINDOWTEXT },
2674 mswin_color_from_string(char *colorstring, HBRUSH *brushptr,
2677 color_table_value *ctv_ptr = color_table;
2678 color_table_brush_value *ctbv_ptr = color_table_brush;
2679 int red_value, blue_value, green_value;
2680 static char *hexadecimals = "0123456789abcdef";
2682 if (colorstring == NULL)
2684 if (*colorstring == '#') {
2685 if (strlen(++colorstring) != 6)
2688 red_value = (int) (index(hexadecimals, tolower((uchar) *colorstring))
2692 red_value += (int) (index(hexadecimals, tolower((uchar) *colorstring))
2696 green_value = (int) (index(hexadecimals,
2697 tolower((uchar) *colorstring))
2701 green_value += (int) (index(hexadecimals,
2702 tolower((uchar) *colorstring))
2706 blue_value = (int) (index(hexadecimals, tolower((uchar) *colorstring))
2710 blue_value += (int) (index(hexadecimals,
2711 tolower((uchar) *colorstring))
2715 *colorptr = RGB(red_value, green_value, blue_value);
2717 while (*ctv_ptr->colorstring
2718 && stricmp(ctv_ptr->colorstring, colorstring))
2720 if (*ctv_ptr->colorstring) {
2721 *colorptr = ctv_ptr->colorvalue;
2723 while (*ctbv_ptr->colorstring
2724 && stricmp(ctbv_ptr->colorstring, colorstring))
2726 if (*ctbv_ptr->colorstring) {
2727 *brushptr = SYSCLR_TO_BRUSH(ctbv_ptr->syscolorvalue);
2728 *colorptr = GetSysColor(ctbv_ptr->syscolorvalue);
2732 if (max_brush > TOTAL_BRUSHES)
2733 panic("Too many colors!");
2734 *brushptr = CreateSolidBrush(*colorptr);
2735 brush_table[max_brush++] = *brushptr;
2739 mswin_get_window_placement(int type, LPRECT rt)
2743 *rt = GetNHApp()->rtMapWindow;
2747 *rt = GetNHApp()->rtMsgWindow;
2751 *rt = GetNHApp()->rtStatusWindow;
2755 *rt = GetNHApp()->rtMenuWindow;
2759 *rt = GetNHApp()->rtTextWindow;
2763 *rt = GetNHApp()->rtInvenWindow;
2767 SetRect(rt, 0, 0, 0, 0);
2773 mswin_update_window_placement(int type, LPRECT rt)
2775 LPRECT rt_conf = NULL;
2779 rt_conf = &GetNHApp()->rtMapWindow;
2783 rt_conf = &GetNHApp()->rtMsgWindow;
2787 rt_conf = &GetNHApp()->rtStatusWindow;
2791 rt_conf = &GetNHApp()->rtMenuWindow;
2795 rt_conf = &GetNHApp()->rtTextWindow;
2799 rt_conf = &GetNHApp()->rtInvenWindow;
2803 if (rt_conf && !IsRectEmpty(rt) && !EqualRect(rt_conf, rt)) {
2809 NHMessageBox(HWND hWnd, LPCTSTR text, UINT type)
2811 TCHAR title[MAX_LOADSTRING];
2813 LoadString(GetNHApp()->hApp, IDS_APP_TITLE_SHORT, title, MAX_LOADSTRING);
2815 return MessageBox(hWnd, text, title, type);
2818 static mswin_status_lines _status_lines;
2819 static mswin_status_string _status_strings[MAXBLSTATS];
2820 static mswin_status_string _condition_strings[BL_MASK_BITS];
2821 static mswin_status_field _status_fields[MAXBLSTATS];
2823 static mswin_condition_field _condition_fields[BL_MASK_BITS] = {
2824 { BL_MASK_STONE, "Stone" },
2825 { BL_MASK_SLIME, "Slime" },
2826 { BL_MASK_STRNGL, "Strngl" },
2827 { BL_MASK_FOODPOIS, "FoodPois" },
2828 { BL_MASK_TERMILL, "TermIll" },
2829 { BL_MASK_BLIND, "Blind" },
2830 { BL_MASK_DEAF, "Deaf" },
2831 { BL_MASK_STUN, "Stun" },
2832 { BL_MASK_CONF, "Conf" },
2833 { BL_MASK_HALLU, "Hallu" },
2834 { BL_MASK_LEV, "Lev" },
2835 { BL_MASK_FLY, "Fly" },
2836 { BL_MASK_RIDE, "Ride" }
2840 extern winid WIN_STATUS;
2842 #ifdef STATUS_HILITES
2843 typedef struct hilite_data_struct {
2850 static hilite_data_t _status_hilites[MAXBLSTATS];
2851 #endif /* STATUS_HILITES */
2853 status_init() -- core calls this to notify the window port that a status
2854 display is required. The window port should perform
2855 the necessary initialization in here, allocate memory, etc.
2858 mswin_status_init(void)
2860 logDebug("mswin_status_init()\n");
2862 for (int i = 0; i < SIZE(_status_fields); i++) {
2863 mswin_status_field * status_field = &_status_fields[i];
2864 status_field->field_index = i;
2865 status_field->enabled = FALSE;
2868 for (int i = 0; i < SIZE(_condition_fields); i++) {
2869 mswin_condition_field * condition_field = &_condition_fields[i];
2870 nhassert(condition_field->mask == (1 << i));
2871 condition_field->bit_position = i;
2874 for (int i = 0; i < SIZE(_status_strings); i++) {
2875 mswin_status_string * status_string = &_status_strings[i];
2876 status_string->str = NULL;
2879 for (int i = 0; i < SIZE(_condition_strings); i++) {
2880 mswin_status_string * status_string = &_condition_strings[i];
2881 status_string->str = NULL;
2884 for (int lineIndex = 0; lineIndex < SIZE(_status_lines.lines); lineIndex++) {
2885 mswin_status_line * line = &_status_lines.lines[lineIndex];
2887 mswin_status_fields * status_fields = &line->status_fields;
2888 status_fields->count = 0;
2890 mswin_status_strings * status_strings = &line->status_strings;
2891 status_strings->count = 0;
2893 for (int i = 0; i < fieldcounts[lineIndex]; i++) {
2894 int field_index = fieldorders[lineIndex][i];
2895 nhassert(field_index <= SIZE(_status_fields));
2897 nhassert(status_fields->count <= SIZE(status_fields->status_fields));
2898 status_fields->status_fields[status_fields->count++] = &_status_fields[field_index];
2900 nhassert(status_strings->count <= SIZE(status_strings->status_strings));
2901 status_strings->status_strings[status_strings->count++] =
2902 &_status_strings[field_index];
2904 if (field_index == BL_CONDITION) {
2905 for (int j = 0; j < BL_MASK_BITS; j++) {
2906 nhassert(status_strings->count <= SIZE(status_strings->status_strings));
2907 status_strings->status_strings[status_strings->count++] =
2908 &_condition_strings[j];
2915 for (int i = 0; i < MAXBLSTATS; ++i) {
2916 #ifdef STATUS_HILITES
2917 _status_hilites[i].thresholdtype = 0;
2918 _status_hilites[i].behavior = BL_TH_NONE;
2919 _status_hilites[i].under = BL_HILITE_NONE;
2920 _status_hilites[i].over = BL_HILITE_NONE;
2921 #endif /* STATUS_HILITES */
2923 /* Use a window for the genl version; backward port compatibility */
2924 WIN_STATUS = create_nhwindow(NHW_STATUS);
2925 display_nhwindow(WIN_STATUS, FALSE);
2929 status_finish() -- called when it is time for the window port to tear down
2930 the status display and free allocated memory, etc.
2933 mswin_status_finish(void)
2935 logDebug("mswin_status_finish()\n");
2939 status_enablefield(int fldindex, char fldname, char fieldfmt, boolean enable)
2940 -- notifies the window port which fields it is authorized to
2942 -- This may be called at any time, and is used
2943 to disable as well as enable fields, depending on the
2944 value of the final argument (TRUE = enable).
2945 -- fldindex could be one of the following from botl.h:
2946 BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
2947 BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
2948 BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
2949 BL_LEVELDESC, BL_EXP, BL_CONDITION
2950 -- There are MAXBLSTATS status fields (from botl.h)
2953 mswin_status_enablefield(int fieldidx, const char *nm, const char *fmt,
2956 logDebug("mswin_status_enablefield(%d, %s, %s, %d)\n", fieldidx, nm, fmt,
2959 nhassert(fieldidx <= SIZE(_status_fields));
2960 mswin_status_field * field = &_status_fields[fieldidx];
2962 nhassert(fieldidx <= SIZE(_status_strings));
2963 mswin_status_string * string = &_status_strings[fieldidx];
2965 if (field != NULL) {
2966 field->format = fmt;
2967 field->space_in_front = (fmt[0] == ' ');
2968 if (field->space_in_front) field->format++;
2970 field->enabled = enable;
2972 string->str = (field->enabled ? field->string : NULL);
2973 string->space_in_front = field->space_in_front;
2975 if (field->field_index == BL_CONDITION)
2978 string->draw_bar = (field->enabled && field->field_index == BL_TITLE);
2982 /* TODO: turn this into a commmon helper; multiple identical implementations */
2984 mswin_condcolor(bm, bmarray)
2986 unsigned long *bmarray;
2991 for (i = 0; i < CLR_MAX; ++i) {
2992 if ((bm & bmarray[i]) != 0)
2999 mswin_condattr(bm, bmarray)
3001 unsigned long *bmarray;
3003 if (bm && bmarray) {
3004 if (bm & bmarray[HL_ATTCLR_DIM]) return HL_DIM;
3005 if (bm & bmarray[HL_ATTCLR_BLINK]) return HL_BLINK;
3006 if (bm & bmarray[HL_ATTCLR_ULINE]) return HL_ULINE;
3007 if (bm & bmarray[HL_ATTCLR_INVERSE]) return HL_INVERSE;
3008 if (bm & bmarray[HL_ATTCLR_BOLD]) return HL_BOLD;
3016 status_update(int fldindex, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks)
3017 -- update the value of a status field.
3018 -- the fldindex identifies which field is changing and
3019 is an integer index value from botl.h
3020 -- fldindex could be any one of the following from botl.h:
3021 BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3022 BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3023 BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3024 BL_LEVELDESC, BL_EXP, BL_CONDITION
3025 -- fldindex could also be BL_FLUSH, which is not really
3026 a field index, but is a special trigger to tell the
3027 windowport that it should output all changes received
3028 to this point. It marks the end of a bot() cycle.
3029 -- fldindex could also be BL_RESET, which is not really
3030 a field index, but is a special advisory to to tell the
3031 windowport that it should redisplay all its status fields,
3032 even if no changes have been presented to it.
3033 -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
3034 If fldindex is BL_CONDITION, then ptr is a long value with
3035 any or none of the following bits set (from botl.h):
3036 BL_MASK_STONE 0x00000001L
3037 BL_MASK_SLIME 0x00000002L
3038 BL_MASK_STRNGL 0x00000004L
3039 BL_MASK_FOODPOIS 0x00000008L
3040 BL_MASK_TERMILL 0x00000010L
3041 BL_MASK_BLIND 0x00000020L
3042 BL_MASK_DEAF 0x00000040L
3043 BL_MASK_STUN 0x00000080L
3044 BL_MASK_CONF 0x00000100L
3045 BL_MASK_HALLU 0x00000200L
3046 BL_MASK_LEV 0x00000400L
3047 BL_MASK_FLY 0x00000800L
3048 BL_MASK_RIDE 0x00001000L
3049 -- The value passed for BL_GOLD includes an encoded leading
3050 symbol for GOLD "\GXXXXNNNN:nnn". If window port needs
3051 textual gold amount without the leading "$:" the port will
3052 have to skip past ':' in passed "ptr" for the BL_GOLD case.
3053 -- color is the color that the NetHack core is telling you to
3054 use to display the text.
3055 -- condmasks is a pointer to a set of BL_ATTCLR_MAX unsigned
3056 longs telling which conditions should be displayed in each
3057 color and attriubte.
3060 mswin_status_update(int idx, genericptr_t ptr, int chg, int percent, int color, unsigned long *condmasks)
3062 long cond, *condptr = (long *) ptr;
3063 char *text = (char *) ptr;
3064 MSNHMsgUpdateStatus update_cmd_data;
3068 logDebug("mswin_status_update(%d, %p, %d, %d, %x, %p)\n", idx, ptr, chg, percent, color, condmasks);
3072 nhassert(idx <= SIZE(_status_fields));
3073 mswin_status_field * status_field = &_status_fields[idx];
3074 nhassert(status_field->field_index == idx);
3076 nhassert(idx <= SIZE(_status_strings));
3077 mswin_status_string * status_string = &_status_strings[idx];
3079 if (!status_field->enabled) {
3080 nhassert(status_string->str == NULL);
3084 status_field->color = status_string->color = color & 0xff;
3085 status_field->attribute = status_string->attribute = (color >> 8) & 0xff;
3088 case BL_CONDITION: {
3089 mswin_condition_field * condition_field = _condition_fields;
3091 nhassert(status_string->str == NULL);
3095 for (int i = 0; i < BL_MASK_BITS; i++, condition_field++) {
3096 status_string = &_condition_strings[i];
3098 if (condition_field->mask & cond) {
3099 status_string->str = condition_field->name;
3100 status_string->space_in_front = TRUE;
3101 status_string->color = mswin_condcolor(condition_field->mask, condmasks);
3102 status_string->attribute = mswin_condattr(condition_field->mask, condmasks);
3105 status_string->str = NULL;
3112 ZeroMemory(buf, sizeof(buf));
3113 if (iflags.invis_goldsym)
3116 mapglyph(objnum_to_glyph(GOLD_PIECE),
3117 &ochar, &ocolor, &ospecial, 0, 0, 0);
3119 p = strchr(text, ':');
3121 strncpy(buf + 1, p, sizeof(buf) - 2);
3124 strncpy(buf + 2, text, sizeof(buf) - 3);
3126 buf[sizeof buf - 1] = '\0';
3127 Sprintf(status_field->string,
3128 status_field->format ? status_field->format : "%s", buf);
3129 nhassert(status_string->str == status_field->string);
3132 Sprintf(status_field->string,
3133 status_field->format ? status_field->format : "%s", text);
3134 nhassert(status_string->str == status_field->string);
3138 /* if we received an update for the hp field, we must update the
3139 * bar percent and bar color for the title string */
3141 mswin_status_string * title_string = &_status_strings[BL_TITLE];
3143 title_string->bar_color = color & 0xff;
3144 title_string->bar_attribute = (color >> 8) & 0xff;
3145 title_string->bar_percent = percent;
3151 if (idx == BL_FLUSH || idx == BL_RESET) {
3152 /* send command to status window to update */
3153 ZeroMemory(&update_cmd_data, sizeof(update_cmd_data));
3154 update_cmd_data.status_lines = &_status_lines;
3155 SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND,
3156 (WPARAM)MSNH_MSG_UPDATE_STATUS, (LPARAM)&update_cmd_data);