OSDN Git Service

import nethack-3.6.0
[jnethack/source.git] / sys / wince / mswproc.c
1 /* NetHack 3.6  mswproc.c       $NHDT-Date: 1433806606 2015/06/08 23:36:46 $  $NHDT-Branch: master $:$NHDT-Revision: 1.60 $ */
2 /* Copyright (C) 2001 by Alex Kompel     */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /*
6  * This file implements the interface between the window port specific
7  * code in the mswin port and the rest of the nethack game engine.
8 */
9
10 #include "hack.h"
11 #include "dlb.h"
12 #include "winMS.h"
13 #include "mhmap.h"
14 #include "mhstatus.h"
15 #include "mhtext.h"
16 #include "mhmsgwnd.h"
17 #include "mhmenu.h"
18 #include "mhmsg.h"
19 #include "mhcmd.h"
20 #include "mhinput.h"
21 #include "mhaskyn.h"
22 #include "mhdlg.h"
23 #include "mhrip.h"
24 #include "mhmain.h"
25 #include "mhfont.h"
26 #include "mhcolor.h"
27
28 #define LLEN 128
29
30 #ifdef _DEBUG
31 extern void logDebug(const char *fmt, ...);
32 #else
33 void
34 logDebug(const char *fmt, ...)
35 {
36 }
37 #endif
38
39 static void mswin_main_loop();
40 static BOOL initMapTiles(void);
41 static void prompt_for_player_selection(void);
42
43 /* Interface definition, for windows.c */
44 struct window_procs mswin_procs = {
45     "MSWIN",
46     WC_COLOR | WC_HILITE_PET | WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_INVERSE
47         | WC_SCROLL_MARGIN | WC_MAP_MODE | WC_FONT_MESSAGE | WC_FONT_STATUS
48         | WC_FONT_MENU | WC_FONT_TEXT | WC_FONT_MAP | WC_FONTSIZ_MESSAGE
49         | WC_FONTSIZ_STATUS | WC_FONTSIZ_MENU | WC_FONTSIZ_TEXT
50         | WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE | WC_VARY_MSGCOUNT
51         | WC_WINDOWCOLORS | WC_PLAYER_SELECTION,
52     WC2_FULLSCREEN | WC2_SOFTKEYBOARD | WC2_WRAPTEXT, mswin_init_nhwindows,
53     mswin_player_selection, mswin_askname, mswin_get_nh_event,
54     mswin_exit_nhwindows, mswin_suspend_nhwindows, mswin_resume_nhwindows,
55     mswin_create_nhwindow, mswin_clear_nhwindow, mswin_display_nhwindow,
56     mswin_destroy_nhwindow, mswin_curs, mswin_putstr, genl_putmixed,
57     mswin_display_file, mswin_start_menu, mswin_add_menu, mswin_end_menu,
58     mswin_select_menu,
59     genl_message_menu, /* no need for X-specific handling */
60     mswin_update_inventory, mswin_mark_synch, mswin_wait_synch,
61 #ifdef CLIPPING
62     mswin_cliparound,
63 #endif
64 #ifdef POSITIONBAR
65     donull,
66 #endif
67     mswin_print_glyph, mswin_raw_print, mswin_raw_print_bold, mswin_nhgetch,
68     mswin_nh_poskey, mswin_nhbell, mswin_doprev_message, mswin_yn_function,
69     mswin_getlin, mswin_get_ext_cmd, mswin_number_pad, mswin_delay_output,
70 #ifdef CHANGE_COLOR /* only a Mac option currently */
71     mswin, mswin_change_background,
72 #endif
73     /* other defs that really should go away (they're tty specific) */
74     mswin_start_screen, mswin_end_screen, mswin_outrip,
75     mswin_preference_update, genl_getmsghistory, genl_putmsghistory,
76 #ifdef STATUS_VIA_WINDOWPORT
77     genl_status_init, genl_status_finish, genl_status_enablefield,
78     genl_status_update,
79 #ifdef STATUS_HILITES
80     genl_status_threshold,
81 #endif
82 #endif
83     genl_can_suspend_no,
84 };
85
86 /*
87 init_nhwindows(int* argcp, char** argv)
88                 -- Initialize the windows used by NetHack.  This can also
89                    create the standard windows listed at the top, but does
90                    not display them.
91                 -- Any commandline arguments relevant to the windowport
92                    should be interpreted, and *argcp and *argv should
93                    be changed to remove those arguments.
94                 -- When the message window is created, the variable
95                    iflags.window_inited needs to be set to TRUE.  Otherwise
96                    all plines() will be done via raw_print().
97                 ** Why not have init_nhwindows() create all of the "standard"
98                 ** windows?  Or at least all but WIN_INFO?      -dean
99 */
100 void
101 mswin_init_nhwindows(int *argc, char **argv)
102 {
103     HWND hWnd;
104     logDebug("mswin_init_nhwindows()\n");
105
106 #ifdef _DEBUG
107     {
108         /* truncate trace file */
109         FILE *dfp = fopen("nhtrace.log", "w");
110         fclose(dfp);
111     }
112 #endif
113
114     /* intialize input subsystem */
115     mswin_nh_input_init();
116
117     /* read registry settings */
118     mswin_read_reg();
119
120     /* set it to WIN_ERR so we can detect attempts to
121        use this ID before it is inialized */
122     WIN_MAP = WIN_ERR;
123
124     /* check default values */
125     if (iflags.wc_fontsiz_status < NHFONT_SIZE_MIN
126         || iflags.wc_fontsiz_status > NHFONT_SIZE_MAX)
127         iflags.wc_fontsiz_status = NHFONT_STATUS_DEFAULT_SIZE;
128
129     if (iflags.wc_fontsiz_message < NHFONT_SIZE_MIN
130         || iflags.wc_fontsiz_message > NHFONT_SIZE_MAX)
131         iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE;
132
133     if (iflags.wc_fontsiz_text < NHFONT_SIZE_MIN
134         || iflags.wc_fontsiz_text > NHFONT_SIZE_MAX)
135         iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE;
136
137     if (iflags.wc_fontsiz_menu < NHFONT_SIZE_MIN
138         || iflags.wc_fontsiz_menu > NHFONT_SIZE_MAX)
139         iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE;
140
141     if (iflags.wc_align_message == 0)
142         iflags.wc_align_message = ALIGN_BOTTOM;
143     if (iflags.wc_align_status == 0)
144         iflags.wc_align_status = ALIGN_TOP;
145     if (iflags.wc_scroll_margin == 0)
146         iflags.wc_scroll_margin = DEF_CLIPAROUND_MARGIN;
147     if (iflags.wc_tile_width == 0)
148         iflags.wc_tile_width = TILE_X;
149     if (iflags.wc_tile_height == 0)
150         iflags.wc_tile_height = TILE_Y;
151
152     if (iflags.wc_vary_msgcount == 0)
153         iflags.wc_vary_msgcount = 3;
154
155     /* force tabs in menus */
156     iflags.menu_tab_sep = 1;
157
158     /* force toptenwin to be true.  toptenwin is the option that decides
159      * whether to
160      * write output to a window or stdout.  stdout doesn't make sense on
161      * Windows
162      * non-console applications
163      */
164     iflags.toptenwin = 1;
165     set_option_mod_status("toptenwin", SET_IN_FILE);
166
167     /* initialize map tiles bitmap */
168     initMapTiles();
169
170     /* set tile-related options to readonly */
171     set_wc_option_mod_status(WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE,
172                              DISP_IN_GAME);
173
174     /* init color table */
175     mswin_init_color_table();
176
177     /* set font-related options to change in the game */
178     set_wc_option_mod_status(
179         WC_HILITE_PET | WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_SCROLL_MARGIN
180             | WC_MAP_MODE | WC_FONT_MESSAGE | WC_FONT_STATUS | WC_FONT_MENU
181             | WC_FONT_TEXT | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS
182             | WC_FONTSIZ_MENU | WC_FONTSIZ_TEXT | WC_VARY_MSGCOUNT,
183         SET_IN_GAME);
184
185     /* WC2 options */
186     set_wc2_option_mod_status(WC2_FULLSCREEN | WC2_SOFTKEYBOARD, SET_IN_FILE);
187     GetNHApp()->bFullScreen = iflags.wc2_fullscreen;
188     GetNHApp()->bUseSIP = iflags.wc2_softkeyboard;
189
190     set_wc2_option_mod_status(WC2_WRAPTEXT, SET_IN_GAME);
191     GetNHApp()->bWrapText = iflags.wc2_wraptext;
192
193     /* create the main nethack window */
194     hWnd = mswin_init_main_window();
195     if (!hWnd)
196         panic("Cannot create the main window.");
197     ShowWindow(hWnd, GetNHApp()->nCmdShow);
198     UpdateWindow(hWnd);
199     GetNHApp()->hMainWnd = hWnd;
200
201     /* set Full screen if requested */
202     mswin_set_fullscreen(GetNHApp()->bFullScreen);
203
204     /* let nethack code know that the window subsystem is ready */
205     iflags.window_inited = TRUE;
206 }
207
208 /* Do a window-port specific player type selection. If player_selection()
209    offers a Quit option, it is its responsibility to clean up and terminate
210    the process. You need to fill in pl_character[0].
211 */
212 void
213 mswin_player_selection(void)
214 {
215     logDebug("mswin_player_selection()\n");
216
217 #if defined(WIN_CE_SMARTPHONE)
218     /* SmartPhone does not supprt combo-boxes therefor we cannot
219        use dialog for player selection */
220     prompt_for_player_selection();
221 #else
222     if (iflags.wc_player_selection == VIA_DIALOG) {
223         int nRole;
224
225         /* pick player type randomly (use pre-selected
226          * role/race/gender/alignment) */
227         if (flags.randomall) {
228             if (flags.initrole < 0) {
229                 flags.initrole = pick_role(flags.initrace, flags.initgend,
230                                            flags.initalign, PICK_RANDOM);
231                 if (flags.initrole < 0) {
232                     raw_print("Incompatible role!");
233                     flags.initrole = randrole();
234                 }
235             }
236
237             if (flags.initrace < 0
238                 || !validrace(flags.initrole, flags.initrace)) {
239                 flags.initrace = pick_race(flags.initrole, flags.initgend,
240                                            flags.initalign, PICK_RANDOM);
241                 if (flags.initrace < 0) {
242                     raw_print("Incompatible race!");
243                     flags.initrace = randrace(flags.initrole);
244                 }
245             }
246
247             if (flags.initgend < 0
248                 || !validgend(flags.initrole, flags.initrace,
249                               flags.initgend)) {
250                 flags.initgend = pick_gend(flags.initrole, flags.initrace,
251                                            flags.initalign, PICK_RANDOM);
252                 if (flags.initgend < 0) {
253                     raw_print("Incompatible gender!");
254                     flags.initgend = randgend(flags.initrole, flags.initrace);
255                 }
256             }
257
258             if (flags.initalign < 0
259                 || !validalign(flags.initrole, flags.initrace,
260                                flags.initalign)) {
261                 flags.initalign = pick_align(flags.initrole, flags.initrace,
262                                              flags.initgend, PICK_RANDOM);
263                 if (flags.initalign < 0) {
264                     raw_print("Incompatible alignment!");
265                     flags.initalign =
266                         randalign(flags.initrole, flags.initrace);
267                 }
268             }
269         } else {
270             /* select a role */
271             if (mswin_player_selection_window(&nRole) == IDCANCEL) {
272                 bail(0);
273             }
274         }
275     } else { /* iflags.wc_player_selection == VIA_PROMPTS */
276         prompt_for_player_selection();
277     }
278 #endif /* defined(WIN_CE_SMARTPHONE) */
279 }
280
281 void
282 prompt_for_player_selection(void)
283 {
284     int i, k, n;
285     char pick4u = 'n', thisch, lastch = 0;
286     char pbuf[QBUFSZ], plbuf[QBUFSZ];
287     winid win;
288     anything any;
289     menu_item *selected = 0;
290     int box_result;
291     TCHAR wbuf[BUFSZ];
292
293     logDebug("prompt_for_player_selection()\n");
294
295     /* prevent an unnecessary prompt */
296     rigid_role_checks();
297
298     /* Should we randomly pick for the player? */
299     if (!flags.randomall
300         && (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE
301             || flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
302         /* int echoline; */
303         char *prompt = build_plselection_prompt(
304             pbuf, QBUFSZ, flags.initrole, flags.initrace, flags.initgend,
305             flags.initalign);
306
307         /* tty_putstr(BASE_WINDOW, 0, ""); */
308         /* echoline = wins[BASE_WINDOW]->cury; */
309         box_result = MessageBox(NULL, NH_A2W(prompt, wbuf, BUFSZ),
310                                 TEXT("NetHack for Windows"),
311 #if defined(WIN_CE_SMARTPHONE)
312                                 MB_YESNO | MB_DEFBUTTON1
313 #else
314                                 MB_YESNOCANCEL | MB_DEFBUTTON1
315 #endif
316                                 );
317
318         pick4u =
319             (box_result == IDYES) ? 'y' : (box_result == IDNO) ? 'n' : '\033';
320         /* tty_putstr(BASE_WINDOW, 0, prompt); */
321         do {
322             /* pick4u = lowc(readchar()); */
323             if (index(quitchars, pick4u))
324                 pick4u = 'y';
325         } while (!index(ynqchars, pick4u));
326         if ((int) strlen(prompt) + 1 < CO) {
327             /* Echo choice and move back down line */
328             /* tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline,
329              * pick4u); */
330             /* tty_putstr(BASE_WINDOW, 0, ""); */
331         } else
332             /* Otherwise it's hard to tell where to echo, and things are
333              * wrapping a bit messily anyway, so (try to) make sure the next
334              * question shows up well and doesn't get wrapped at the
335              * bottom of the window.
336              */
337             /* tty_clear_nhwindow(BASE_WINDOW) */;
338
339         if (pick4u != 'y' && pick4u != 'n') {
340         give_up: /* Quit */
341             if (selected)
342                 free((genericptr_t) selected);
343             bail((char *) 0);
344             /*NOTREACHED*/
345             return;
346         }
347     }
348
349     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
350                                    flags.initrace, flags.initgend,
351                                    flags.initalign);
352
353     /* Select a role, if necessary */
354     /* we'll try to be compatible with pre-selected race/gender/alignment,
355      * but may not succeed */
356     if (flags.initrole < 0) {
357         char rolenamebuf[QBUFSZ];
358         /* Process the choice */
359         if (pick4u == 'y' || flags.initrole == ROLE_RANDOM
360             || flags.randomall) {
361             /* Pick a random role */
362             flags.initrole = pick_role(flags.initrace, flags.initgend,
363                                        flags.initalign, PICK_RANDOM);
364             if (flags.initrole < 0) {
365                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible role!"); */
366                 flags.initrole = randrole();
367             }
368         } else {
369             /* tty_clear_nhwindow(BASE_WINDOW); */
370             /* tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role"); */
371             /* Prompt for a role */
372             win = create_nhwindow(NHW_MENU);
373             start_menu(win);
374             any.a_void = 0; /* zero out all bits */
375             for (i = 0; roles[i].name.m; i++) {
376                 if (ok_role(i, flags.initrace, flags.initgend,
377                             flags.initalign)) {
378                     any.a_int = i + 1; /* must be non-zero */
379                     thisch = lowc(roles[i].name.m[0]);
380                     if (thisch == lastch)
381                         thisch = highc(thisch);
382                     if (flags.initgend != ROLE_NONE
383                         && flags.initgend != ROLE_RANDOM) {
384                         if (flags.initgend == 1 && roles[i].name.f)
385                             Strcpy(rolenamebuf, roles[i].name.f);
386                         else
387                             Strcpy(rolenamebuf, roles[i].name.m);
388                     } else {
389                         if (roles[i].name.f) {
390                             Strcpy(rolenamebuf, roles[i].name.m);
391                             Strcat(rolenamebuf, "/");
392                             Strcat(rolenamebuf, roles[i].name.f);
393                         } else
394                             Strcpy(rolenamebuf, roles[i].name.m);
395                     }
396                     add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE,
397                              an(rolenamebuf), MENU_UNSELECTED);
398                     lastch = thisch;
399                 }
400             }
401             any.a_int = pick_role(flags.initrace, flags.initgend,
402                                   flags.initalign, PICK_RANDOM) + 1;
403             if (any.a_int == 0) /* must be non-zero */
404                 any.a_int = randrole() + 1;
405             add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
406                      MENU_UNSELECTED);
407             any.a_int = i + 1; /* must be non-zero */
408             add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
409                      MENU_UNSELECTED);
410             Sprintf(pbuf, "Pick a role for your %s", plbuf);
411             end_menu(win, pbuf);
412             n = select_menu(win, PICK_ONE, &selected);
413             destroy_nhwindow(win);
414
415             /* Process the choice */
416             if (n != 1 || selected[0].item.a_int == any.a_int)
417                 goto give_up; /* Selected quit */
418
419             flags.initrole = selected[0].item.a_int - 1;
420             free((genericptr_t) selected), selected = 0;
421         }
422         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
423                                        flags.initrace, flags.initgend,
424                                        flags.initalign);
425     }
426
427     /* Select a race, if necessary */
428     /* force compatibility with role, try for compatibility with
429      * pre-selected gender/alignment */
430     if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
431         /* pre-selected race not valid */
432         if (pick4u == 'y' || flags.initrace == ROLE_RANDOM
433             || flags.randomall) {
434             flags.initrace = pick_race(flags.initrole, flags.initgend,
435                                        flags.initalign, PICK_RANDOM);
436             if (flags.initrace < 0) {
437                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible race!"); */
438                 flags.initrace = randrace(flags.initrole);
439             }
440         } else { /* pick4u == 'n' */
441             /* Count the number of valid races */
442             n = 0; /* number valid */
443             k = 0; /* valid race */
444             for (i = 0; races[i].noun; i++) {
445                 if (ok_race(flags.initrole, i, flags.initgend,
446                             flags.initalign)) {
447                     n++;
448                     k = i;
449                 }
450             }
451             if (n == 0) {
452                 for (i = 0; races[i].noun; i++) {
453                     if (validrace(flags.initrole, i)) {
454                         n++;
455                         k = i;
456                     }
457                 }
458             }
459
460             /* Permit the user to pick, if there is more than one */
461             if (n > 1) {
462                 /* tty_clear_nhwindow(BASE_WINDOW); */
463                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Race"); */
464                 win = create_nhwindow(NHW_MENU);
465                 start_menu(win);
466                 any.a_void = 0; /* zero out all bits */
467                 for (i = 0; races[i].noun; i++)
468                     if (ok_race(flags.initrole, i, flags.initgend,
469                                 flags.initalign)) {
470                         any.a_int = i + 1; /* must be non-zero */
471                         add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0,
472                                  ATR_NONE, races[i].noun, MENU_UNSELECTED);
473                     }
474                 any.a_int = pick_race(flags.initrole, flags.initgend,
475                                       flags.initalign, PICK_RANDOM) + 1;
476                 if (any.a_int == 0) /* must be non-zero */
477                     any.a_int = randrace(flags.initrole) + 1;
478                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
479                          MENU_UNSELECTED);
480                 any.a_int = i + 1; /* must be non-zero */
481                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
482                          MENU_UNSELECTED);
483                 Sprintf(pbuf, "Pick the race of your %s", plbuf);
484                 end_menu(win, pbuf);
485                 n = select_menu(win, PICK_ONE, &selected);
486                 destroy_nhwindow(win);
487                 if (n != 1 || selected[0].item.a_int == any.a_int)
488                     goto give_up; /* Selected quit */
489
490                 k = selected[0].item.a_int - 1;
491                 free((genericptr_t) selected), selected = 0;
492             }
493             flags.initrace = k;
494         }
495         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
496                                        flags.initrace, flags.initgend,
497                                        flags.initalign);
498     }
499
500     /* Select a gender, if necessary */
501     /* force compatibility with role/race, try for compatibility with
502      * pre-selected alignment */
503     if (flags.initgend < 0
504         || !validgend(flags.initrole, flags.initrace, flags.initgend)) {
505         /* pre-selected gender not valid */
506         if (pick4u == 'y' || flags.initgend == ROLE_RANDOM
507             || flags.randomall) {
508             flags.initgend = pick_gend(flags.initrole, flags.initrace,
509                                        flags.initalign, PICK_RANDOM);
510             if (flags.initgend < 0) {
511                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible gender!"); */
512                 flags.initgend = randgend(flags.initrole, flags.initrace);
513             }
514         } else { /* pick4u == 'n' */
515             /* Count the number of valid genders */
516             n = 0; /* number valid */
517             k = 0; /* valid gender */
518             for (i = 0; i < ROLE_GENDERS; i++) {
519                 if (ok_gend(flags.initrole, flags.initrace, i,
520                             flags.initalign)) {
521                     n++;
522                     k = i;
523                 }
524             }
525             if (n == 0) {
526                 for (i = 0; i < ROLE_GENDERS; i++) {
527                     if (validgend(flags.initrole, flags.initrace, i)) {
528                         n++;
529                         k = i;
530                     }
531                 }
532             }
533
534             /* Permit the user to pick, if there is more than one */
535             if (n > 1) {
536                 /* tty_clear_nhwindow(BASE_WINDOW); */
537                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Gender"); */
538                 win = create_nhwindow(NHW_MENU);
539                 start_menu(win);
540                 any.a_void = 0; /* zero out all bits */
541                 for (i = 0; i < ROLE_GENDERS; i++)
542                     if (ok_gend(flags.initrole, flags.initrace, i,
543                                 flags.initalign)) {
544                         any.a_int = i + 1;
545                         add_menu(win, NO_GLYPH, &any, genders[i].adj[0], 0,
546                                  ATR_NONE, genders[i].adj, MENU_UNSELECTED);
547                     }
548                 any.a_int = pick_gend(flags.initrole, flags.initrace,
549                                       flags.initalign, PICK_RANDOM) + 1;
550                 if (any.a_int == 0) /* must be non-zero */
551                     any.a_int = randgend(flags.initrole, flags.initrace) + 1;
552                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
553                          MENU_UNSELECTED);
554                 any.a_int = i + 1; /* must be non-zero */
555                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
556                          MENU_UNSELECTED);
557                 Sprintf(pbuf, "Pick the gender of your %s", plbuf);
558                 end_menu(win, pbuf);
559                 n = select_menu(win, PICK_ONE, &selected);
560                 destroy_nhwindow(win);
561                 if (n != 1 || selected[0].item.a_int == any.a_int)
562                     goto give_up; /* Selected quit */
563
564                 k = selected[0].item.a_int - 1;
565                 free((genericptr_t) selected), selected = 0;
566             }
567             flags.initgend = k;
568         }
569         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
570                                        flags.initrace, flags.initgend,
571                                        flags.initalign);
572     }
573
574     /* Select an alignment, if necessary */
575     /* force compatibility with role/race/gender */
576     if (flags.initalign < 0
577         || !validalign(flags.initrole, flags.initrace, flags.initalign)) {
578         /* pre-selected alignment not valid */
579         if (pick4u == 'y' || flags.initalign == ROLE_RANDOM
580             || flags.randomall) {
581             flags.initalign = pick_align(flags.initrole, flags.initrace,
582                                          flags.initgend, PICK_RANDOM);
583             if (flags.initalign < 0) {
584                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); */
585                 flags.initalign = randalign(flags.initrole, flags.initrace);
586             }
587         } else { /* pick4u == 'n' */
588             /* Count the number of valid alignments */
589             n = 0; /* number valid */
590             k = 0; /* valid alignment */
591             for (i = 0; i < ROLE_ALIGNS; i++) {
592                 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
593                              i)) {
594                     n++;
595                     k = i;
596                 }
597             }
598             if (n == 0) {
599                 for (i = 0; i < ROLE_ALIGNS; i++) {
600                     if (validalign(flags.initrole, flags.initrace, i)) {
601                         n++;
602                         k = i;
603                     }
604                 }
605             }
606
607             /* Permit the user to pick, if there is more than one */
608             if (n > 1) {
609                 /* tty_clear_nhwindow(BASE_WINDOW); */
610                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Alignment"); */
611                 win = create_nhwindow(NHW_MENU);
612                 start_menu(win);
613                 any.a_void = 0; /* zero out all bits */
614                 for (i = 0; i < ROLE_ALIGNS; i++)
615                     if (ok_align(flags.initrole, flags.initrace,
616                                  flags.initgend, i)) {
617                         any.a_int = i + 1;
618                         add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0,
619                                  ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
620                     }
621                 any.a_int = pick_align(flags.initrole, flags.initrace,
622                                        flags.initgend, PICK_RANDOM) + 1;
623                 if (any.a_int == 0) /* must be non-zero */
624                     any.a_int = randalign(flags.initrole, flags.initrace) + 1;
625                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
626                          MENU_UNSELECTED);
627                 any.a_int = i + 1; /* must be non-zero */
628                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
629                          MENU_UNSELECTED);
630                 Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
631                 end_menu(win, pbuf);
632                 n = select_menu(win, PICK_ONE, &selected);
633                 destroy_nhwindow(win);
634                 if (n != 1 || selected[0].item.a_int == any.a_int)
635                     goto give_up; /* Selected quit */
636
637                 k = selected[0].item.a_int - 1;
638                 free((genericptr_t) selected), selected = 0;
639             }
640             flags.initalign = k;
641         }
642     }
643     /* Success! */
644     /* tty_display_nhwindow(BASE_WINDOW, FALSE); */
645 }
646
647 /* Ask the user for a player name. */
648 void
649 mswin_askname(void)
650 {
651     logDebug("mswin_askname()\n");
652
653     if (mswin_getlin_window("who are you?", plname, PL_NSIZ) == IDCANCEL) {
654         bail("bye-bye");
655         /* not reached */
656     }
657 }
658
659 /* Does window event processing (e.g. exposure events).
660    A noop for the tty and X window-ports.
661 */
662 void
663 mswin_get_nh_event(void)
664 {
665     logDebug("mswin_get_nh_event()\n");
666     return;
667 }
668
669 /* Exits the window system.  This should dismiss all windows,
670    except the "window" used for raw_print().  str is printed if possible.
671 */
672 void
673 mswin_exit_nhwindows(const char *str)
674 {
675     logDebug("mswin_exit_nhwindows(%s)\n", str);
676
677     /* Write Window settings to the registry */
678     mswin_write_reg();
679
680     // Don't do any of this (?) - exit_nhwindows does not terminate
681     // the application
682     // DestroyWindow(GetNHApp()->hMainWnd);
683     // terminate(EXIT_SUCCESS);
684 }
685
686 /* Prepare the window to be suspended. */
687 void
688 mswin_suspend_nhwindows(const char *str)
689 {
690     logDebug("mswin_suspend_nhwindows(%s)\n", str);
691     return;
692 }
693
694 /* Restore the windows after being suspended. */
695 void
696 mswin_resume_nhwindows()
697 {
698     logDebug("mswin_resume_nhwindows()\n");
699     return;
700 }
701
702 /*  Create a window of type "type" which can be
703         NHW_MESSAGE     (top line)
704         NHW_STATUS      (bottom lines)
705         NHW_MAP         (main dungeon)
706         NHW_MENU        (inventory or other "corner" windows)
707         NHW_TEXT        (help/text, full screen paged window)
708 */
709 winid
710 mswin_create_nhwindow(int type)
711 {
712     winid i = 0;
713     MSNHMsgAddWnd data;
714
715     logDebug("mswin_create_nhwindow(%d)\n", type);
716
717     /* Return the next available winid
718      */
719
720     for (i = 1; i < MAXWINDOWS; i++)
721         if (GetNHApp()->windowlist[i].win == NULL
722             && !GetNHApp()->windowlist[i].dead)
723             break;
724     if (i == MAXWINDOWS)
725         panic("ERROR:  No windows available...\n");
726
727     switch (type) {
728     case NHW_MAP: {
729         GetNHApp()->windowlist[i].win = mswin_init_map_window();
730         GetNHApp()->windowlist[i].type = type;
731         GetNHApp()->windowlist[i].dead = 0;
732         break;
733     }
734     case NHW_MESSAGE: {
735         GetNHApp()->windowlist[i].win = mswin_init_message_window();
736         GetNHApp()->windowlist[i].type = type;
737         GetNHApp()->windowlist[i].dead = 0;
738         break;
739     }
740     case NHW_STATUS: {
741         GetNHApp()->windowlist[i].win = mswin_init_status_window();
742         GetNHApp()->windowlist[i].type = type;
743         GetNHApp()->windowlist[i].dead = 0;
744         break;
745     }
746     case NHW_MENU: {
747         GetNHApp()->windowlist[i].win = NULL; // will create later
748         GetNHApp()->windowlist[i].type = type;
749         GetNHApp()->windowlist[i].dead = 1;
750         break;
751     }
752     case NHW_TEXT: {
753         GetNHApp()->windowlist[i].win = mswin_init_text_window();
754         GetNHApp()->windowlist[i].type = type;
755         GetNHApp()->windowlist[i].dead = 0;
756         break;
757     }
758     }
759
760     ZeroMemory(&data, sizeof(data));
761     data.wid = i;
762     SendMessage(GetNHApp()->hMainWnd, WM_MSNH_COMMAND,
763                 (WPARAM) MSNH_MSG_ADDWND, (LPARAM) &data);
764     return i;
765 }
766
767 /* Clear the given window, when asked to. */
768 void
769 mswin_clear_nhwindow(winid wid)
770 {
771     logDebug("mswin_clear_nhwindow(%d)\n", wid);
772
773     if ((wid >= 0) && (wid < MAXWINDOWS)
774         && (GetNHApp()->windowlist[wid].win != NULL)) {
775         if (GetNHApp()->windowlist[wid].type == NHW_MAP) {
776             if (Is_rogue_level(&u.uz))
777                 mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
778                                ROGUE_LEVEL_MAP_MODE);
779             else
780                 mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
781                                iflags.wc_map_mode);
782         }
783
784         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
785                     (WPARAM) MSNH_MSG_CLEAR_WINDOW, (LPARAM) NULL);
786     }
787 }
788
789 /* -- Display the window on the screen.  If there is data
790                    pending for output in that window, it should be sent.
791                    If blocking is TRUE, display_nhwindow() will not
792                    return until the data has been displayed on the screen,
793                    and acknowledged by the user where appropriate.
794                 -- All calls are blocking in the tty window-port.
795                 -- Calling display_nhwindow(WIN_MESSAGE,???) will do a
796                    --more--, if necessary, in the tty window-port.
797 */
798 void
799 mswin_display_nhwindow(winid wid, BOOLEAN_P block)
800 {
801     logDebug("mswin_display_nhwindow(%d, %d)\n", wid, block);
802     if (GetNHApp()->windowlist[wid].win != NULL) {
803         if (GetNHApp()->windowlist[wid].type == NHW_MENU) {
804             MENU_ITEM_P *p;
805             mswin_menu_window_select_menu(GetNHApp()->windowlist[wid].win,
806                                           PICK_NONE, &p);
807         }
808         if (GetNHApp()->windowlist[wid].type == NHW_TEXT) {
809             mswin_display_text_window(GetNHApp()->windowlist[wid].win);
810         }
811         if (GetNHApp()->windowlist[wid].type == NHW_RIP) {
812             mswin_display_RIP_window(GetNHApp()->windowlist[wid].win);
813         } else {
814             if (!block) {
815                 UpdateWindow(GetNHApp()->windowlist[wid].win);
816             } else {
817                 if (GetNHApp()->windowlist[wid].type == NHW_MAP) {
818                     (void) mswin_nhgetch();
819                 }
820             }
821         }
822         SetFocus(GetNHApp()->hMainWnd);
823     }
824 }
825
826 HWND
827 mswin_hwnd_from_winid(winid wid)
828 {
829     if (wid >= 0 && wid < MAXWINDOWS) {
830         return GetNHApp()->windowlist[wid].win;
831     } else {
832         return NULL;
833     }
834 }
835
836 winid
837 mswin_winid_from_handle(HWND hWnd)
838 {
839     winid i = 0;
840
841     for (i = 1; i < MAXWINDOWS; i++)
842         if (GetNHApp()->windowlist[i].win == hWnd)
843             return i;
844     return -1;
845 }
846
847 winid
848 mswin_winid_from_type(int type)
849 {
850     winid i = 0;
851
852     for (i = 1; i < MAXWINDOWS; i++)
853         if (GetNHApp()->windowlist[i].type == type)
854             return i;
855     return -1;
856 }
857
858 void
859 mswin_window_mark_dead(winid wid)
860 {
861     if (wid >= 0 && wid < MAXWINDOWS) {
862         GetNHApp()->windowlist[wid].win = NULL;
863         GetNHApp()->windowlist[wid].dead = 1;
864     }
865 }
866
867 /* Destroy will dismiss the window if the window has not
868  * already been dismissed.
869 */
870 void
871 mswin_destroy_nhwindow(winid wid)
872 {
873     logDebug("mswin_destroy_nhwindow(%d)\n", wid);
874
875     if ((GetNHApp()->windowlist[wid].type == NHW_MAP)
876         || (GetNHApp()->windowlist[wid].type == NHW_MESSAGE)
877         || (GetNHApp()->windowlist[wid].type == NHW_STATUS)) {
878         /* main windows is going to take care of those */
879         return;
880     }
881
882     if (wid != -1) {
883         if (!GetNHApp()->windowlist[wid].dead
884             && GetNHApp()->windowlist[wid].win != NULL)
885             DestroyWindow(GetNHApp()->windowlist[wid].win);
886         GetNHApp()->windowlist[wid].win = NULL;
887         GetNHApp()->windowlist[wid].type = 0;
888         GetNHApp()->windowlist[wid].dead = 0;
889     }
890 }
891
892 /* Next output to window will start at (x,y), also moves
893  displayable cursor to (x,y).  For backward compatibility,
894  1 <= x < cols, 0 <= y < rows, where cols and rows are
895  the size of window.
896 */
897 void
898 mswin_curs(winid wid, int x, int y)
899 {
900     logDebug("mswin_curs(%d, %d, %d)\n", wid, x, y);
901
902     if ((wid >= 0) && (wid < MAXWINDOWS)
903         && (GetNHApp()->windowlist[wid].win != NULL)) {
904         MSNHMsgCursor data;
905         data.x = x;
906         data.y = y;
907         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
908                     (WPARAM) MSNH_MSG_CURSOR, (LPARAM) &data);
909     }
910 }
911
912 /*
913 putstr(window, attr, str)
914                 -- Print str on the window with the given attribute.  Only
915                    printable ASCII characters (040-0126) must be supported.
916                    Multiple putstr()s are output on separate lines.
917 Attributes
918                    can be one of
919                         ATR_NONE (or 0)
920                         ATR_ULINE
921                         ATR_BOLD
922                         ATR_BLINK
923                         ATR_INVERSE
924                    If a window-port does not support all of these, it may map
925                    unsupported attributes to a supported one (e.g. map them
926                    all to ATR_INVERSE).  putstr() may compress spaces out of
927                    str, break str, or truncate str, if necessary for the
928                    display.  Where putstr() breaks a line, it has to clear
929                    to end-of-line.
930                 -- putstr should be implemented such that if two putstr()s
931                    are done consecutively the user will see the first and
932                    then the second.  In the tty port, pline() achieves this
933                    by calling more() or displaying both on the same line.
934 */
935 void
936 mswin_putstr(winid wid, int attr, const char *text)
937 {
938     logDebug("mswin_putstr(%d, %d, %s)\n", wid, attr, text);
939
940     mswin_putstr_ex(wid, attr, text, 0);
941 }
942
943 void
944 mswin_putstr_ex(winid wid, int attr, const char *text, boolean app)
945 {
946     if ((wid >= 0) && (wid < MAXWINDOWS)) {
947         if (GetNHApp()->windowlist[wid].win == NULL
948             && GetNHApp()->windowlist[wid].type == NHW_MENU) {
949             GetNHApp()->windowlist[wid].win =
950                 mswin_init_menu_window(MENU_TYPE_TEXT);
951             GetNHApp()->windowlist[wid].dead = 0;
952         }
953
954         if (GetNHApp()->windowlist[wid].win != NULL) {
955             MSNHMsgPutstr data;
956             ZeroMemory(&data, sizeof(data));
957             data.attr = attr;
958             data.text = text;
959             data.append = app;
960             SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
961                         (WPARAM) MSNH_MSG_PUTSTR, (LPARAM) &data);
962         }
963     }
964 }
965
966 /* Display the file named str.  Complain about missing files
967                    iff complain is TRUE.
968 */
969 void
970 mswin_display_file(const char *filename, BOOLEAN_P must_exist)
971 {
972     dlb *f;
973     TCHAR wbuf[BUFSZ];
974
975     logDebug("mswin_display_file(%s, %d)\n", filename, must_exist);
976
977     f = dlb_fopen(filename, RDTMODE);
978     if (!f) {
979         if (must_exist) {
980             TCHAR message[90];
981             _stprintf(message, TEXT("Warning! Could not find file: %s\n"),
982                       NH_A2W(filename, wbuf, sizeof(wbuf)));
983             MessageBox(GetNHApp()->hMainWnd, message, TEXT("ERROR"),
984                        MB_OK | MB_ICONERROR);
985         }
986     } else {
987         winid text;
988         char line[LLEN];
989
990         text = mswin_create_nhwindow(NHW_TEXT);
991
992         while (dlb_fgets(line, LLEN, f)) {
993             size_t len;
994             len = strlen(line);
995             if (line[len - 1] == '\n')
996                 line[len - 1] = '\x0';
997             mswin_putstr(text, ATR_NONE, line);
998         }
999         (void) dlb_fclose(f);
1000
1001         mswin_display_nhwindow(text, 1);
1002         mswin_destroy_nhwindow(text);
1003     }
1004 }
1005
1006 /* Start using window as a menu.  You must call start_menu()
1007    before add_menu().  After calling start_menu() you may not
1008    putstr() to the window.  Only windows of type NHW_MENU may
1009    be used for menus.
1010 */
1011 void
1012 mswin_start_menu(winid wid)
1013 {
1014     logDebug("mswin_start_menu(%d)\n", wid);
1015     if ((wid >= 0) && (wid < MAXWINDOWS)) {
1016         if (GetNHApp()->windowlist[wid].win == NULL
1017             && GetNHApp()->windowlist[wid].type == NHW_MENU) {
1018             GetNHApp()->windowlist[wid].win =
1019                 mswin_init_menu_window(MENU_TYPE_MENU);
1020             GetNHApp()->windowlist[wid].dead = 0;
1021         }
1022
1023         if (GetNHApp()->windowlist[wid].win != NULL) {
1024             SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1025                         (WPARAM) MSNH_MSG_STARTMENU, (LPARAM) NULL);
1026         }
1027     }
1028 }
1029
1030 /*
1031 add_menu(windid window, int glyph, const anything identifier,
1032                                 char accelerator, char groupacc,
1033                                 int attr, char *str, boolean preselected)
1034                 -- Add a text line str to the given menu window.  If
1035 identifier
1036                    is 0, then the line cannot be selected (e.g. a title).
1037                    Otherwise, identifier is the value returned if the line is
1038                    selected.  Accelerator is a keyboard key that can be used
1039                    to select the line.  If the accelerator of a selectable
1040                    item is 0, the window system is free to select its own
1041                    accelerator.  It is up to the window-port to make the
1042                    accelerator visible to the user (e.g. put "a - " in front
1043                    of str).  The value attr is the same as in putstr().
1044                    Glyph is an optional glyph to accompany the line.  If
1045                    window port cannot or does not want to display it, this
1046                    is OK.  If there is no glyph applicable, then this
1047                    value will be NO_GLYPH.
1048                 -- All accelerators should be in the range [A-Za-z].
1049                 -- It is expected that callers do not mix accelerator
1050                    choices.  Either all selectable items have an accelerator
1051                    or let the window system pick them.  Don't do both.
1052                 -- Groupacc is a group accelerator.  It may be any character
1053                    outside of the standard accelerator (see above) or a
1054                    number.  If 0, the item is unaffected by any group
1055                    accelerator.  If this accelerator conflicts with
1056                    the menu command (or their user defined alises), it loses.
1057                    The menu commands and aliases take care not to interfere
1058                    with the default object class symbols.
1059                 -- If you want this choice to be preselected when the
1060                    menu is displayed, set preselected to TRUE.
1061 */
1062 void
1063 mswin_add_menu(winid wid, int glyph, const ANY_P *identifier,
1064                CHAR_P accelerator, CHAR_P group_accel, int attr,
1065                const char *str, BOOLEAN_P presel)
1066 {
1067     logDebug("mswin_add_menu(%d, %d, %p, %c, %c, %d, %s, %d)\n", wid, glyph,
1068              identifier, (char) accelerator, (char) group_accel, attr, str,
1069              presel);
1070     if ((wid >= 0) && (wid < MAXWINDOWS)
1071         && (GetNHApp()->windowlist[wid].win != NULL)) {
1072         MSNHMsgAddMenu data;
1073         ZeroMemory(&data, sizeof(data));
1074         data.glyph = glyph;
1075         data.identifier = identifier;
1076         data.accelerator = accelerator;
1077         data.group_accel = group_accel;
1078         data.attr = attr;
1079         data.str = str;
1080         data.presel = presel;
1081
1082         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1083                     (WPARAM) MSNH_MSG_ADDMENU, (LPARAM) &data);
1084     }
1085 }
1086
1087 /*
1088 end_menu(window, prompt)
1089                 -- Stop adding entries to the menu and flushes the window
1090                    to the screen (brings to front?).  Prompt is a prompt
1091                    to give the user.  If prompt is NULL, no prompt will
1092                    be printed.
1093                 ** This probably shouldn't flush the window any more (if
1094                 ** it ever did).  That should be select_menu's job.  -dean
1095 */
1096 void
1097 mswin_end_menu(winid wid, const char *prompt)
1098 {
1099     logDebug("mswin_end_menu(%d, %s)\n", wid, prompt);
1100     if ((wid >= 0) && (wid < MAXWINDOWS)
1101         && (GetNHApp()->windowlist[wid].win != NULL)) {
1102         MSNHMsgEndMenu data;
1103         ZeroMemory(&data, sizeof(data));
1104         data.text = prompt;
1105
1106         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1107                     (WPARAM) MSNH_MSG_ENDMENU, (LPARAM) &data);
1108     }
1109 }
1110
1111 /*
1112 int select_menu(windid window, int how, menu_item **selected)
1113                 -- Return the number of items selected; 0 if none were chosen,
1114                    -1 when explicitly cancelled.  If items were selected, then
1115                    selected is filled in with an allocated array of menu_item
1116                    structures, one for each selected line.  The caller must
1117                    free this array when done with it.  The "count" field
1118                    of selected is a user supplied count.  If the user did
1119                    not supply a count, then the count field is filled with
1120                    -1 (meaning all).  A count of zero is equivalent to not
1121                    being selected and should not be in the list.  If no items
1122                    were selected, then selected is NULL'ed out.  How is the
1123                    mode of the menu.  Three valid values are PICK_NONE,
1124                    PICK_ONE, and PICK_N, meaning: nothing is selectable,
1125                    only one thing is selectable, and any number valid items
1126                    may selected.  If how is PICK_NONE, this function should
1127                    never return anything but 0 or -1.
1128                 -- You may call select_menu() on a window multiple times --
1129                    the menu is saved until start_menu() or destroy_nhwindow()
1130                    is called on the window.
1131                 -- Note that NHW_MENU windows need not have select_menu()
1132                    called for them. There is no way of knowing whether
1133                    select_menu() will be called for the window at
1134                    create_nhwindow() time.
1135 */
1136 int
1137 mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected)
1138 {
1139     int nReturned = -1;
1140
1141     logDebug("mswin_select_menu(%d, %d)\n", wid, how);
1142
1143     if ((wid >= 0) && (wid < MAXWINDOWS)
1144         && (GetNHApp()->windowlist[wid].win != NULL)) {
1145         nReturned = mswin_menu_window_select_menu(
1146             GetNHApp()->windowlist[wid].win, how, selected);
1147     }
1148     return nReturned;
1149 }
1150
1151 /*
1152     -- Indicate to the window port that the inventory has been changed.
1153     -- Merely calls display_inventory() for window-ports that leave the
1154         window up, otherwise empty.
1155 */
1156 void
1157 mswin_update_inventory()
1158 {
1159     logDebug("mswin_update_inventory()\n");
1160 }
1161
1162 /*
1163 mark_synch()    -- Don't go beyond this point in I/O on any channel until
1164                    all channels are caught up to here.  Can be an empty call
1165                    for the moment
1166 */
1167 void
1168 mswin_mark_synch()
1169 {
1170     logDebug("mswin_mark_synch()\n");
1171 }
1172
1173 /*
1174 wait_synch()    -- Wait until all pending output is complete (*flush*() for
1175                    streams goes here).
1176                 -- May also deal with exposure events etc. so that the
1177                    display is OK when return from wait_synch().
1178 */
1179 void
1180 mswin_wait_synch()
1181 {
1182     logDebug("mswin_wait_synch()\n");
1183 }
1184
1185 /*
1186 cliparound(x, y)-- Make sure that the user is more-or-less centered on the
1187                    screen if the playing area is larger than the screen.
1188                 -- This function is only defined if CLIPPING is defined.
1189 */
1190 void
1191 mswin_cliparound(int x, int y)
1192 {
1193     winid wid = WIN_MAP;
1194
1195     logDebug("mswin_cliparound(%d, %d)\n", x, y);
1196
1197     if ((wid >= 0) && (wid < MAXWINDOWS)
1198         && (GetNHApp()->windowlist[wid].win != NULL)) {
1199         MSNHMsgClipAround data;
1200         data.x = x;
1201         data.y = y;
1202         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1203                     (WPARAM) MSNH_MSG_CLIPAROUND, (LPARAM) &data);
1204     }
1205 }
1206
1207 /*
1208 print_glyph(window, x, y, glyph, bkglyph)
1209                 -- Print the glyph at (x,y) on the given window.  Glyphs are
1210                    integers at the interface, mapped to whatever the window-
1211                    port wants (symbol, font, color, attributes, ...there's
1212                    a 1-1 map between glyphs and distinct things on the map).
1213 */
1214 void
1215 mswin_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)
1216 {
1217     logDebug("mswin_print_glyph(%d, %d, %d, %d, %d)\n", wid, x, y, glyph, bkglyph);
1218
1219     if ((wid >= 0) && (wid < MAXWINDOWS)
1220         && (GetNHApp()->windowlist[wid].win != NULL)) {
1221         MSNHMsgPrintGlyph data;
1222
1223         ZeroMemory(&data, sizeof(data));
1224         data.x = x;
1225         data.y = y;
1226         data.glyph = glyph;
1227         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1228                     (WPARAM) MSNH_MSG_PRINT_GLYPH, (LPARAM) &data);
1229     }
1230 }
1231
1232 /*
1233 raw_print(str)  -- Print directly to a screen, or otherwise guarantee that
1234                    the user sees str.  raw_print() appends a newline to str.
1235                    It need not recognize ASCII control characters.  This is
1236                    used during startup (before windowing system initialization
1237                    -- maybe this means only error startup messages are raw),
1238                    for error messages, and maybe other "msg" uses.  E.g.
1239                    updating status for micros (i.e, "saving").
1240 */
1241 void
1242 mswin_raw_print(const char *str)
1243 {
1244     TCHAR wbuf[255];
1245     logDebug("mswin_raw_print(%s)\n", str);
1246     if (str && *str)
1247         MessageBox(GetNHApp()->hMainWnd, NH_A2W(str, wbuf, sizeof(wbuf)),
1248                    TEXT("NetHack"), MB_OK);
1249 }
1250
1251 /*
1252 raw_print_bold(str)
1253                 -- Like raw_print(), but prints in bold/standout (if
1254 possible).
1255 */
1256 void
1257 mswin_raw_print_bold(const char *str)
1258 {
1259     TCHAR wbuf[255];
1260     logDebug("mswin_raw_print_bold(%s)\n", str);
1261     if (str && *str)
1262         MessageBox(GetNHApp()->hMainWnd, NH_A2W(str, wbuf, sizeof(wbuf)),
1263                    TEXT("NetHack"), MB_OK);
1264 }
1265
1266 /*
1267 int nhgetch()   -- Returns a single character input from the user.
1268                 -- In the tty window-port, nhgetch() assumes that tgetch()
1269                    will be the routine the OS provides to read a character.
1270                    Returned character _must_ be non-zero.
1271 */
1272 int
1273 mswin_nhgetch()
1274 {
1275     PMSNHEvent event;
1276     int key = 0;
1277
1278     logDebug("mswin_nhgetch()\n");
1279
1280     while ((event = mswin_input_pop()) == NULL || event->type != NHEVENT_CHAR)
1281         mswin_main_loop();
1282
1283     key = event->kbd.ch;
1284     return (key);
1285 }
1286
1287 /*
1288 int nh_poskey(int *x, int *y, int *mod)
1289                 -- Returns a single character input from the user or a
1290                    a positioning event (perhaps from a mouse).  If the
1291                    return value is non-zero, a character was typed, else,
1292                    a position in the MAP window is returned in x, y and mod.
1293                    mod may be one of
1294
1295                         CLICK_1         -- mouse click type 1
1296                         CLICK_2         -- mouse click type 2
1297
1298                    The different click types can map to whatever the
1299                    hardware supports.  If no mouse is supported, this
1300                    routine always returns a non-zero character.
1301 */
1302 int
1303 mswin_nh_poskey(int *x, int *y, int *mod)
1304 {
1305     PMSNHEvent event;
1306     int key;
1307
1308     logDebug("mswin_nh_poskey()\n");
1309
1310     while ((event = mswin_input_pop()) == NULL)
1311         mswin_main_loop();
1312
1313     if (event->type == NHEVENT_MOUSE) {
1314         *mod = event->ms.mod;
1315         *x = event->ms.x;
1316         *y = event->ms.y;
1317         key = 0;
1318     } else {
1319         key = event->kbd.ch;
1320     }
1321     return (key);
1322 }
1323
1324 /*
1325 nhbell()        -- Beep at user.  [This will exist at least until sounds are
1326                    redone, since sounds aren't attributable to windows
1327 anyway.]
1328 */
1329 void
1330 mswin_nhbell()
1331 {
1332     logDebug("mswin_nhbell()\n");
1333 }
1334
1335 /*
1336 doprev_message()
1337                 -- Display previous messages.  Used by the ^P command.
1338                 -- On the tty-port this scrolls WIN_MESSAGE back one line.
1339 */
1340 int
1341 mswin_doprev_message()
1342 {
1343     logDebug("mswin_doprev_message()\n");
1344     SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_VSCROLL,
1345                 MAKEWPARAM(SB_LINEUP, 0), (LPARAM) NULL);
1346     return 0;
1347 }
1348
1349 /*
1350 char yn_function(const char *ques, const char *choices, char default)
1351                 -- Print a prompt made up of ques, choices and default.
1352                    Read a single character response that is contained in
1353                    choices or default.  If choices is NULL, all possible
1354                    inputs are accepted and returned.  This overrides
1355                    everything else.  The choices are expected to be in
1356                    lower case.  Entering ESC always maps to 'q', or 'n',
1357                    in that order, if present in choices, otherwise it maps
1358                    to default.  Entering any other quit character (SPACE,
1359                    RETURN, NEWLINE) maps to default.
1360                 -- If the choices string contains ESC, then anything after
1361                    it is an acceptable response, but the ESC and whatever
1362                    follows is not included in the prompt.
1363                 -- If the choices string contains a '#' then accept a count.
1364                    Place this value in the global "yn_number" and return '#'.
1365                 -- This uses the top line in the tty window-port, other
1366                    ports might use a popup.
1367 */
1368 char
1369 mswin_yn_function(const char *question, const char *choices, CHAR_P def)
1370 {
1371     int result = -1;
1372     char ch;
1373     char yn_esc_map = '\033';
1374     char message[BUFSZ];
1375     char res_ch[2];
1376
1377     logDebug("mswin_yn_function(%s, %s, %d)\n", question, choices, def);
1378
1379     if (choices) {
1380         char *cb, choicebuf[QBUFSZ];
1381         Strcpy(choicebuf, choices);
1382         if ((cb = index(choicebuf, '\033')) != 0) {
1383             /* anything beyond <esc> is hidden */
1384             *cb = '\0';
1385         }
1386         (void) strncpy(message, question, QBUFSZ - 1);
1387         message[QBUFSZ - 1] = '\0';
1388         sprintf(eos(message), " [%s]", choicebuf);
1389         if (def)
1390             sprintf(eos(message), " (%c)", def);
1391         Strcat(message, " ");
1392         /* escape maps to 'q' or 'n' or default, in that order */
1393         yn_esc_map =
1394             (index(choices, 'q') ? 'q' : (index(choices, 'n') ? 'n' : def));
1395     } else {
1396         Strcpy(message, question);
1397     }
1398
1399 #if defined(WIN_CE_SMARTPHONE)
1400     {
1401         char buf[BUFSZ];
1402         ZeroMemory(buf, sizeof(buf));
1403         if (choices) {
1404             if (!index(choices, '\033'))
1405                 buf[0] = '\033'; /* make sure ESC is always available */
1406             strncat(buf, choices, sizeof(buf) - 2);
1407             NHSPhoneSetKeypadFromString(buf);
1408         } else {
1409             /* sometimes choices are included in the message itself, e.g.
1410              * "what? [abcd]" */
1411             char *p1, *p2;
1412             p1 = strchr(question, '[');
1413             p2 = strrchr(question, ']');
1414             if (p1 && p2 && p1 < p2) {
1415                 buf[0] = '\033'; /* make sure ESC is always available */
1416                 strncat(buf, p1 + 1, p2 - p1 - 1);
1417                 NHSPhoneSetKeypadFromString(buf);
1418             } else if (strstr(question, "direction")) {
1419                 /* asking for direction here */
1420                 NHSPhoneSetKeypadDirection();
1421             } else {
1422                 /* anything goes */
1423                 NHSPhoneSetKeypadFromString("\0330-9a-zA-Z");
1424             }
1425         }
1426     }
1427 #endif /* defined(WIN_CE_SMARTPHONE) */
1428
1429     mswin_putstr(WIN_MESSAGE, ATR_BOLD, message);
1430
1431     /* Only here if main window is not present */
1432     while (result < 0) {
1433         ch = mswin_nhgetch();
1434         if (ch == '\033') {
1435             result = yn_esc_map;
1436         } else if (choices && !index(choices, ch)) {
1437             /* FYI: ch==-115 is for KP_ENTER */
1438             if (def
1439                 && (ch == ' ' || ch == '\r' || ch == '\n' || ch == -115)) {
1440                 result = def;
1441             } else {
1442                 mswin_nhbell();
1443                 /* and try again... */
1444             }
1445         } else {
1446             result = ch;
1447         }
1448     }
1449
1450     /* display selection in the message window */
1451     if (isprint(ch)) {
1452         res_ch[0] = ch;
1453         res_ch[1] = '\x0';
1454         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, res_ch, 1);
1455     }
1456
1457     /* prevent "--more--" prompt from appearing when several
1458        questions being asked in the same loop (like selling
1459        something in the shop)
1460        It does not really clears the window - mhmsgwnd.c */
1461     mswin_clear_nhwindow(WIN_MESSAGE);
1462
1463 #if defined(WIN_CE_SMARTPHONE)
1464     NHSPhoneSetKeypadDefault();
1465 #endif
1466     return result;
1467 }
1468
1469 /*
1470 getlin(const char *ques, char *input)
1471             -- Prints ques as a prompt and reads a single line of text,
1472                up to a newline.  The string entered is returned without the
1473                newline.  ESC is used to cancel, in which case the string
1474                "\033\000" is returned.
1475             -- getlin() must call flush_screen(1) before doing anything.
1476             -- This uses the top line in the tty window-port, other
1477                ports might use a popup.
1478 */
1479 void
1480 mswin_getlin(const char *question, char *input)
1481 {
1482     logDebug("mswin_getlin(%s, %p)\n", question, input);
1483     if (mswin_getlin_window(question, input, BUFSZ) == IDCANCEL) {
1484         strcpy(input, "\033");
1485     }
1486 }
1487
1488 /*
1489 int get_ext_cmd(void)
1490             -- Get an extended command in a window-port specific way.
1491                An index into extcmdlist[] is returned on a successful
1492                selection, -1 otherwise.
1493 */
1494 int
1495 mswin_get_ext_cmd()
1496 {
1497     int ret;
1498     logDebug("mswin_get_ext_cmd()\n");
1499
1500     if (mswin_ext_cmd_window(&ret) == IDCANCEL)
1501         return -1;
1502     else
1503         return ret;
1504 }
1505
1506 /*
1507 number_pad(state)
1508             -- Initialize the number pad to the given state.
1509 */
1510 void
1511 mswin_number_pad(int state)
1512 {
1513     /* Do Nothing */
1514     logDebug("mswin_number_pad(%d)\n", state);
1515 }
1516
1517 /*
1518 delay_output()  -- Causes a visible delay of 50ms in the output.
1519                Conceptually, this is similar to wait_synch() followed
1520                by a nap(50ms), but allows asynchronous operation.
1521 */
1522 void
1523 mswin_delay_output()
1524 {
1525     logDebug("mswin_delay_output()\n");
1526     Sleep(50);
1527 }
1528
1529 void
1530 mswin_change_color()
1531 {
1532     logDebug("mswin_change_color()\n");
1533 }
1534
1535 char *
1536 mswin_get_color_string()
1537 {
1538     logDebug("mswin_get_color_string()\n");
1539     return ("");
1540 }
1541
1542 /*
1543 start_screen()  -- Only used on Unix tty ports, but must be declared for
1544                completeness.  Sets up the tty to work in full-screen
1545                graphics mode.  Look at win/tty/termcap.c for an
1546                example.  If your window-port does not need this function
1547                just declare an empty function.
1548 */
1549 void
1550 mswin_start_screen()
1551 {
1552     /* Do Nothing */
1553     logDebug("mswin_start_screen()\n");
1554 }
1555
1556 /*
1557 end_screen()    -- Only used on Unix tty ports, but must be declared for
1558                completeness.  The complement of start_screen().
1559 */
1560 void
1561 mswin_end_screen()
1562 {
1563     /* Do Nothing */
1564     logDebug("mswin_end_screen()\n");
1565 }
1566
1567 /*
1568 outrip(winid, int, when)
1569             -- The tombstone code.  If you want the traditional code use
1570                genl_outrip for the value and check the #if in rip.c.
1571 */
1572 void
1573 mswin_outrip(winid wid, int how, time_t when)
1574 {
1575     logDebug("mswin_outrip(%d, %d, %ld)\n", wid, how, (long) when);
1576     if ((wid >= 0) && (wid < MAXWINDOWS)) {
1577         DestroyWindow(GetNHApp()->windowlist[wid].win);
1578         GetNHApp()->windowlist[wid].win = mswin_init_RIP_window();
1579         GetNHApp()->windowlist[wid].type = NHW_RIP;
1580         GetNHApp()->windowlist[wid].dead = 0;
1581     }
1582     genl_outrip(wid, how, when);
1583 }
1584
1585 /* handle options updates here */
1586 void
1587 mswin_preference_update(const char *pref)
1588 {
1589     HDC hdc;
1590
1591     if (_stricmp(pref, "font_menu") == 0
1592         || _stricmp(pref, "font_size_menu") == 0) {
1593         if (iflags.wc_fontsiz_menu < NHFONT_SIZE_MIN
1594             || iflags.wc_fontsiz_menu > NHFONT_SIZE_MAX)
1595             iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE;
1596
1597         hdc = GetDC(GetNHApp()->hMainWnd);
1598         mswin_get_font(NHW_MENU, ATR_NONE, hdc, TRUE);
1599         mswin_get_font(NHW_MENU, ATR_BOLD, hdc, TRUE);
1600         mswin_get_font(NHW_MENU, ATR_DIM, hdc, TRUE);
1601         mswin_get_font(NHW_MENU, ATR_ULINE, hdc, TRUE);
1602         mswin_get_font(NHW_MENU, ATR_BLINK, hdc, TRUE);
1603         mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, TRUE);
1604         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1605
1606         mswin_layout_main_window(NULL);
1607         return;
1608     }
1609
1610     if (_stricmp(pref, "font_status") == 0
1611         || _stricmp(pref, "font_size_status") == 0) {
1612         if (iflags.wc_fontsiz_status < NHFONT_SIZE_MIN
1613             || iflags.wc_fontsiz_status > NHFONT_SIZE_MAX)
1614             iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE;
1615
1616         hdc = GetDC(GetNHApp()->hMainWnd);
1617         mswin_get_font(NHW_STATUS, ATR_NONE, hdc, TRUE);
1618         mswin_get_font(NHW_STATUS, ATR_BOLD, hdc, TRUE);
1619         mswin_get_font(NHW_STATUS, ATR_DIM, hdc, TRUE);
1620         mswin_get_font(NHW_STATUS, ATR_ULINE, hdc, TRUE);
1621         mswin_get_font(NHW_STATUS, ATR_BLINK, hdc, TRUE);
1622         mswin_get_font(NHW_STATUS, ATR_INVERSE, hdc, TRUE);
1623         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1624
1625         mswin_layout_main_window(NULL);
1626         return;
1627     }
1628
1629     if (_stricmp(pref, "font_message") == 0
1630         || _stricmp(pref, "font_size_message") == 0) {
1631         if (iflags.wc_fontsiz_message < NHFONT_SIZE_MIN
1632             || iflags.wc_fontsiz_message > NHFONT_SIZE_MAX)
1633             iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE;
1634
1635         hdc = GetDC(GetNHApp()->hMainWnd);
1636         mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, TRUE);
1637         mswin_get_font(NHW_MESSAGE, ATR_BOLD, hdc, TRUE);
1638         mswin_get_font(NHW_MESSAGE, ATR_DIM, hdc, TRUE);
1639         mswin_get_font(NHW_MESSAGE, ATR_ULINE, hdc, TRUE);
1640         mswin_get_font(NHW_MESSAGE, ATR_BLINK, hdc, TRUE);
1641         mswin_get_font(NHW_MESSAGE, ATR_INVERSE, hdc, TRUE);
1642         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1643
1644         mswin_layout_main_window(NULL);
1645         return;
1646     }
1647
1648     if (_stricmp(pref, "font_text") == 0
1649         || _stricmp(pref, "font_size_text") == 0) {
1650         if (iflags.wc_fontsiz_text < NHFONT_SIZE_MIN
1651             || iflags.wc_fontsiz_text > NHFONT_SIZE_MAX)
1652             iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE;
1653
1654         hdc = GetDC(GetNHApp()->hMainWnd);
1655         mswin_get_font(NHW_TEXT, ATR_NONE, hdc, TRUE);
1656         mswin_get_font(NHW_TEXT, ATR_BOLD, hdc, TRUE);
1657         mswin_get_font(NHW_TEXT, ATR_DIM, hdc, TRUE);
1658         mswin_get_font(NHW_TEXT, ATR_ULINE, hdc, TRUE);
1659         mswin_get_font(NHW_TEXT, ATR_BLINK, hdc, TRUE);
1660         mswin_get_font(NHW_TEXT, ATR_INVERSE, hdc, TRUE);
1661         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1662
1663         mswin_layout_main_window(NULL);
1664         return;
1665     }
1666
1667     if (_stricmp(pref, "scroll_margin") == 0) {
1668         mswin_cliparound(u.ux, u.uy);
1669         return;
1670     }
1671
1672     if (_stricmp(pref, "map_mode") == 0) {
1673         mswin_select_map_mode(iflags.wc_map_mode);
1674         return;
1675     }
1676
1677     if (_stricmp(pref, "hilite_pet") == 0) {
1678         InvalidateRect(mswin_hwnd_from_winid(WIN_MAP), NULL, TRUE);
1679         return;
1680     }
1681
1682     if (_stricmp(pref, "align_message") == 0
1683         || _stricmp(pref, "align_status") == 0) {
1684         mswin_layout_main_window(NULL);
1685         return;
1686     }
1687
1688     if (_stricmp(pref, "vary_msgcount") == 0) {
1689         InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE);
1690         mswin_layout_main_window(NULL);
1691         return;
1692     }
1693
1694     if (_stricmp(pref, "fullscreen") == 0) {
1695         mswin_set_fullscreen(iflags.wc2_fullscreen);
1696         return;
1697     }
1698
1699     if (_stricmp(pref, "softkeyboard") == 0) {
1700         GetNHApp()->bUseSIP = iflags.wc2_softkeyboard;
1701         return;
1702     }
1703
1704     if (_stricmp(pref, "wraptext") == 0) {
1705         GetNHApp()->bWrapText = iflags.wc2_wraptext;
1706         return;
1707     }
1708 }
1709
1710 void
1711 mswin_main_loop()
1712 {
1713     MSG msg;
1714
1715     while (!mswin_have_input() && GetMessage(&msg, NULL, 0, 0) != 0) {
1716         if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) {
1717             TranslateMessage(&msg);
1718             DispatchMessage(&msg);
1719         }
1720     }
1721 }
1722
1723 /* clean up and quit */
1724 void
1725 bail(const char *mesg)
1726 {
1727     clearlocks();
1728     mswin_exit_nhwindows(mesg);
1729     terminate(EXIT_SUCCESS);
1730     /*NOTREACHED*/
1731 }
1732
1733 BOOL
1734 initMapTiles(void)
1735 {
1736     HBITMAP hBmp;
1737     BITMAP bm;
1738     TCHAR wbuf[MAX_PATH];
1739     int tl_num;
1740     SIZE map_size;
1741     extern int total_tiles_used;
1742
1743     /* no file - no tile */
1744     if (!(iflags.wc_tile_file && *iflags.wc_tile_file))
1745         return TRUE;
1746
1747     /* load bitmap */
1748     hBmp = SHLoadDIBitmap(NH_A2W(iflags.wc_tile_file, wbuf, MAX_PATH));
1749     if (hBmp == NULL) {
1750         raw_print(
1751             "Cannot load tiles from the file. Reverting back to default.");
1752         return FALSE;
1753     }
1754
1755     /* calculate tile dimensions */
1756     GetObject(hBmp, sizeof(BITMAP), (LPVOID) &bm);
1757     if (bm.bmWidth % iflags.wc_tile_width
1758         || bm.bmHeight % iflags.wc_tile_height) {
1759         DeleteObject(hBmp);
1760         raw_print("Tiles bitmap does not match tile_width and tile_height "
1761                   "options. Reverting back to default.");
1762         return FALSE;
1763     }
1764
1765     tl_num = (bm.bmWidth / iflags.wc_tile_width)
1766              * (bm.bmHeight / iflags.wc_tile_height);
1767     if (tl_num < total_tiles_used) {
1768         DeleteObject(hBmp);
1769         raw_print("Number of tiles in the bitmap is less than required by "
1770                   "the game. Reverting back to default.");
1771         return FALSE;
1772     }
1773
1774     /* set the tile information */
1775     if (GetNHApp()->bmpMapTiles != GetNHApp()->bmpTiles) {
1776         DeleteObject(GetNHApp()->bmpMapTiles);
1777     }
1778
1779     GetNHApp()->bmpMapTiles = hBmp;
1780     GetNHApp()->mapTile_X = iflags.wc_tile_width;
1781     GetNHApp()->mapTile_Y = iflags.wc_tile_height;
1782     GetNHApp()->mapTilesPerLine = bm.bmWidth / iflags.wc_tile_width;
1783
1784     map_size.cx = GetNHApp()->mapTile_X * COLNO;
1785     map_size.cy = GetNHApp()->mapTile_Y * ROWNO;
1786     mswin_map_stretch(mswin_hwnd_from_winid(WIN_MAP), &map_size, TRUE);
1787     return TRUE;
1788 }
1789
1790 void
1791 mswin_popup_display(HWND hWnd, int *done_indicator)
1792 {
1793     MSG msg;
1794     HWND hChild;
1795
1796     /* activate the menu window */
1797     GetNHApp()->hPopupWnd = hWnd;
1798
1799     mswin_layout_main_window(hWnd);
1800
1801     /* disable game windows */
1802     for (hChild = GetWindow(GetNHApp()->hMainWnd, GW_CHILD); hChild;
1803          hChild = GetWindow(hChild, GW_HWNDNEXT)) {
1804         if (hChild != hWnd)
1805             EnableWindow(hChild, FALSE);
1806     }
1807 #if defined(WIN_CE_SMARTPHONE)
1808     ShowWindow(GetNHApp()->hMenuBar, SW_HIDE);
1809     ShowWindow(SHFindMenuBar(hWnd), SW_SHOW);
1810 #else
1811     EnableWindow(GetNHApp()->hMenuBar, FALSE);
1812 #endif
1813
1814     /* bring menu window on top */
1815     SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,
1816                  SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1817
1818     /* go into message loop */
1819     if (done_indicator)
1820         *done_indicator = 0;
1821     while (IsWindow(hWnd) && (done_indicator == NULL || !*done_indicator)
1822            && GetMessage(&msg, NULL, 0, 0) != 0) {
1823         if (!IsDialogMessage(hWnd, &msg)) {
1824             if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable,
1825                                       &msg)) {
1826                 TranslateMessage(&msg);
1827                 DispatchMessage(&msg);
1828             }
1829         }
1830     }
1831 }
1832
1833 void
1834 mswin_popup_destroy(HWND hWnd)
1835 {
1836     HWND hChild;
1837
1838     /* enable game windows */
1839     for (hChild = GetWindow(GetNHApp()->hMainWnd, GW_CHILD); hChild;
1840          hChild = GetWindow(hChild, GW_HWNDNEXT)) {
1841         if (hChild != hWnd) {
1842             EnableWindow(hChild, TRUE);
1843         }
1844     }
1845 #if defined(WIN_CE_SMARTPHONE)
1846     ShowWindow(SHFindMenuBar(hWnd), SW_HIDE);
1847     ShowWindow(GetNHApp()->hMenuBar, SW_SHOW);
1848 #else
1849     EnableWindow(GetNHApp()->hMenuBar, TRUE);
1850 #endif
1851
1852     SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0,
1853                  SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW);
1854     GetNHApp()->hPopupWnd = NULL;
1855     mswin_window_mark_dead(mswin_winid_from_handle(hWnd));
1856     DestroyWindow(hWnd);
1857
1858     mswin_layout_main_window(hWnd);
1859
1860     SetFocus(GetNHApp()->hMainWnd);
1861 }
1862
1863 void
1864 mswin_set_fullscreen(BOOL is_fullscreen)
1865 {
1866 #if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE)
1867     SetForegroundWindow(GetNHApp()->hMainWnd);
1868     if (is_fullscreen) {
1869         SHFullScreen(GetNHApp()->hMainWnd,
1870                      SHFS_HIDETASKBAR | SHFS_HIDESTARTICON);
1871         MoveWindow(GetNHApp()->hMainWnd, 0, 0, GetSystemMetrics(SM_CXSCREEN),
1872                    GetSystemMetrics(SM_CYSCREEN), FALSE);
1873     } else {
1874         RECT rc;
1875         SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0);
1876         SHFullScreen(GetNHApp()->hMainWnd,
1877                      SHFS_SHOWTASKBAR | SHFS_SHOWSTARTICON);
1878         MoveWindow(GetNHApp()->hMainWnd, rc.left, rc.top, rc.right - rc.left,
1879                    rc.bottom - rc.top, FALSE);
1880     }
1881     GetNHApp()->bFullScreen = is_fullscreen;
1882 #else
1883     GetNHApp()->bFullScreen = FALSE;
1884 #endif
1885 }
1886
1887 #if defined(WIN_CE_SMARTPHONE)
1888 void
1889 NHSPhoneDialogSetup(HWND hDlg, UINT nToolBarId, BOOL is_edit,
1890                     BOOL is_fullscreen)
1891 {
1892     SHMENUBARINFO mbi;
1893     HWND hOK, hCancel;
1894     RECT rtOK, rtDlg;
1895
1896     // Create our MenuBar
1897     ZeroMemory(&mbi, sizeof(SHMENUBARINFO));
1898     mbi.cbSize = sizeof(mbi);
1899     mbi.hwndParent = hDlg;
1900     mbi.nToolBarId = nToolBarId;
1901     mbi.hInstRes = GetNHApp()->hApp;
1902     if (!SHCreateMenuBar(&mbi)) {
1903         error("cannot create dialog menu");
1904     }
1905
1906     if (is_fullscreen) {
1907         SHINITDLGINFO shidi;
1908         RECT main_wnd_rect;
1909         shidi.dwMask = SHIDIM_FLAGS;
1910         shidi.dwFlags = SHIDIF_SIZEDLGFULLSCREEN;
1911         shidi.hDlg = hDlg;
1912         SHInitDialog(&shidi);
1913
1914         GetWindowRect(GetNHApp()->hMainWnd, &main_wnd_rect);
1915         MoveWindow(hDlg, main_wnd_rect.left, main_wnd_rect.top,
1916                    main_wnd_rect.right - main_wnd_rect.left,
1917                    main_wnd_rect.bottom - main_wnd_rect.top, FALSE);
1918     }
1919
1920     /* hide OK and CANCEL buttons */
1921     hOK = GetDlgItem(hDlg, IDOK);
1922     hCancel = GetDlgItem(hDlg, IDCANCEL);
1923
1924     if (IsWindow(hCancel))
1925         ShowWindow(hCancel, SW_HIDE);
1926     if (IsWindow(hOK)) {
1927         GetWindowRect(hOK, &rtOK);
1928         GetWindowRect(hDlg, &rtDlg);
1929
1930         rtDlg.bottom -= rtOK.bottom - rtOK.top;
1931         ShowWindow(hOK, SW_HIDE);
1932         SetWindowPos(hDlg, HWND_TOP, 0, 0, rtDlg.right - rtDlg.left,
1933                      rtDlg.bottom - rtDlg.top,
1934                      SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOZORDER);
1935     }
1936
1937     /* override "Back" button for edit box dialogs */
1938     if (is_edit)
1939         SendMessage(mbi.hwndMB, SHCMBM_OVERRIDEKEY, VK_TBACK,
1940                     MAKELPARAM(SHMBOF_NODEFAULT | SHMBOF_NOTIFY,
1941                                SHMBOF_NODEFAULT | SHMBOF_NOTIFY));
1942 }
1943 #endif /* defined(WIN_CE_SMARTPHONE) */
1944
1945 void
1946 mswin_read_reg(void)
1947 {
1948 }
1949
1950 void
1951 mswin_destroy_reg(void)
1952 {
1953 }
1954
1955 void
1956 mswin_write_reg(void)
1957 {
1958 }
1959
1960 /* check HKCU\Software\\Microsoft\\Shell\HasKeyboard for keyboard presence,
1961   if the key is not there assume older device and no keyboard */
1962 BOOL
1963 mswin_has_keyboard(void)
1964 {
1965     DWORD dwHasKB = 0;
1966 #if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE)
1967     HKEY hKey;
1968     DWORD dwType;
1969     DWORD dwSize = sizeof(dwHasKB);
1970     if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Shell"), 0,
1971                      0, &hKey) == ERROR_SUCCESS) {
1972         if (RegQueryValueEx(hKey, _T("HasKeyboard"), NULL, &dwType,
1973                             (LPBYTE) &dwHasKB, &dwSize) != ERROR_SUCCESS) {
1974             dwHasKB = 0;
1975         }
1976         RegCloseKey(hKey);
1977     }
1978 #endif
1979     return (dwHasKB == 1);
1980 }
1981
1982 #ifdef _DEBUG
1983 #include <stdarg.h>
1984
1985 void
1986 logDebug(const char *fmt, ...)
1987 {
1988     FILE *dfp = fopen("nhtrace.log", "a");
1989
1990     if (dfp) {
1991         va_list args;
1992
1993         va_start(args, fmt);
1994         vfprintf(dfp, fmt, args);
1995         va_end(args);
1996         fclose(dfp);
1997     }
1998 }
1999
2000 #endif