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