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