OSDN Git Service

a01b5e5e142ceb55b3d252b0282d2854c8ddcef4
[jnethack/source.git] / win / win32 / mhmain.c
1 /* NetHack 3.6  mhmain.c        $NHDT-Date: 1432512811 2015/05/25 00:13:31 $  $NHDT-Branch: master $:$NHDT-Revision: 1.62 $ */
2 /* Copyright (C) 2001 by Alex Kompel     */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
8 /* JNetHack may be freely redistributed.  See license for details. */
9
10 #include "winMS.h"
11 #include <commdlg.h>
12 #include "date.h"
13 #include "patchlevel.h"
14 #include "resource.h"
15 #include "mhmsg.h"
16 #include "mhinput.h"
17 #include "mhmain.h"
18 #include "mhmenu.h"
19 #include "mhstatus.h"
20 #include "mhmsgwnd.h"
21 #include "mhmap.h"
22
23 typedef struct mswin_nethack_main_window {
24     int mapAcsiiModeSave;
25 } NHMainWindow, *PNHMainWindow;
26
27 extern winid WIN_STATUS;
28
29 static TCHAR szMainWindowClass[] = TEXT("MSNHMainWndClass");
30 static TCHAR szTitle[MAX_LOADSTRING];
31 extern void mswin_display_splash_window(BOOL);
32
33 LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
34 LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
35 static LRESULT onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
36 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
37 static void register_main_window_class(void);
38 static int menuid2mapmode(int menuid);
39 static int mapmode2menuid(int map_mode);
40 static void nhlock_windows(BOOL lock);
41 static char *nh_compose_ascii_screenshot();
42 static void mswin_apply_window_style_all();
43 // returns strdup() created pointer - callee assumes the ownership
44
45 HWND
46 mswin_init_main_window()
47 {
48     static int run_once = 0;
49     HWND ret;
50     WINDOWPLACEMENT wp;
51
52     /* register window class */
53     if (!run_once) {
54         LoadString(GetNHApp()->hApp, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
55         register_main_window_class();
56         run_once = 1;
57     }
58
59     /* create the main window */
60     ret =
61         CreateWindow(szMainWindowClass, /* registered class name */
62                      szTitle,           /* window name */
63                      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, /* window style */
64                      CW_USEDEFAULT,    /* horizontal position of window */
65                      CW_USEDEFAULT,    /* vertical position of window */
66                      CW_USEDEFAULT,    /* window width */
67                      CW_USEDEFAULT,    /* window height */
68                      NULL,             /* handle to parent or owner window */
69                      NULL,             /* menu handle or child identifier */
70                      GetNHApp()->hApp, /* handle to application instance */
71                      NULL              /* window-creation data */
72                      );
73
74     if (!ret)
75         panic("Cannot create main window");
76
77     if (GetNHApp()->regMainMinX != CW_USEDEFAULT) {
78         wp.length = sizeof(wp);
79         wp.showCmd = GetNHApp()->regMainShowState;
80
81         wp.ptMinPosition.x = GetNHApp()->regMainMinX;
82         wp.ptMinPosition.y = GetNHApp()->regMainMinY;
83
84         wp.ptMaxPosition.x = GetNHApp()->regMainMaxX;
85         wp.ptMaxPosition.y = GetNHApp()->regMainMaxY;
86
87         wp.rcNormalPosition.left = GetNHApp()->regMainLeft;
88         wp.rcNormalPosition.top = GetNHApp()->regMainTop;
89         wp.rcNormalPosition.right = GetNHApp()->regMainRight;
90         wp.rcNormalPosition.bottom = GetNHApp()->regMainBottom;
91         SetWindowPlacement(ret, &wp);
92     } else
93         ShowWindow(ret, SW_SHOWDEFAULT);
94
95     UpdateWindow(ret);
96     return ret;
97 }
98
99 void
100 register_main_window_class()
101 {
102     WNDCLASS wcex;
103
104     ZeroMemory(&wcex, sizeof(wcex));
105     wcex.style = CS_HREDRAW | CS_VREDRAW;
106     wcex.lpfnWndProc = (WNDPROC) MainWndProc;
107     wcex.cbClsExtra = 0;
108     wcex.cbWndExtra = 0;
109     wcex.hInstance = GetNHApp()->hApp;
110     wcex.hIcon = LoadIcon(GetNHApp()->hApp, (LPCTSTR) IDI_NETHACKW);
111     wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
112     wcex.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
113     wcex.lpszMenuName = (TCHAR *) IDC_NETHACKW;
114     wcex.lpszClassName = szMainWindowClass;
115
116     RegisterClass(&wcex);
117 }
118
119 /*
120  * Keypad keys are translated to the normal values below.
121  * Shifted keypad keys are translated to the
122  *    shift values below.
123  */
124
125 enum KEY_INDEXES {
126     KEY_NW,
127     KEY_N,
128     KEY_NE,
129     KEY_MINUS,
130     KEY_W,
131     KEY_GOINTERESTING,
132     KEY_E,
133     KEY_PLUS,
134     KEY_SW,
135     KEY_S,
136     KEY_SE,
137     KEY_INV,
138     KEY_WAITLOOK,
139     KEY_LAST
140 };
141
142 static const unsigned char
143     /* normal, shift, control */
144     keypad[KEY_LAST][3] =
145         {
146           { 'y', 'Y', C('y') },    /* 7 */
147           { 'k', 'K', C('k') },    /* 8 */
148           { 'u', 'U', C('u') },    /* 9 */
149           { 'm', C('p'), C('p') }, /* - */
150           { 'h', 'H', C('h') },    /* 4 */
151           { 'g', 'G', 'g' },       /* 5 */
152           { 'l', 'L', C('l') },    /* 6 */
153           { '+', 'P', C('p') },    /* + */
154           { 'b', 'B', C('b') },    /* 1 */
155           { 'j', 'J', C('j') },    /* 2 */
156           { 'n', 'N', C('n') },    /* 3 */
157           { 'i', 'I', C('i') },    /* Ins */
158           { '.', ':', ':' }        /* Del */
159         },
160     numpad[KEY_LAST][3] = {
161         { '7', M('7'), '7' },    /* 7 */
162         { '8', M('8'), '8' },    /* 8 */
163         { '9', M('9'), '9' },    /* 9 */
164         { 'm', C('p'), C('p') }, /* - */
165         { '4', M('4'), '4' },    /* 4 */
166         { '5', M('5'), '5' },    /* 5 */
167         { '6', M('6'), '6' },    /* 6 */
168         { '+', 'P', C('p') },    /* + */
169         { '1', M('1'), '1' },    /* 1 */
170         { '2', M('2'), '2' },    /* 2 */
171         { '3', M('3'), '3' },    /* 3 */
172         { '0', M('0'), '0' },    /* Ins */
173         { '.', ':', ':' }        /* Del */
174     };
175
176 #define STATEON(x) ((GetKeyState(x) & 0xFFFE) != 0)
177 #define KEYTABLE_REGULAR(x) ((iflags.num_pad ? numpad : keypad)[x][0])
178 #define KEYTABLE_SHIFT(x) ((iflags.num_pad ? numpad : keypad)[x][1])
179 #define KEYTABLE(x) \
180     (STATEON(VK_SHIFT) ? KEYTABLE_SHIFT(x) : KEYTABLE_REGULAR(x))
181
182 static const char *extendedlist = "acdefijlmnopqrstuvw?2";
183
184 #define SCANLO 0x02
185 static const char scanmap[] = {
186     /* ... */
187     '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0, 0, 0, 0, 'q', 'w',
188     'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd',
189     'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
190     'b', 'n', 'm', ',', '.', '?' /* ... */
191 };
192
193 #define IDT_FUZZ_TIMER 100 
194
195 /*
196 //  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
197 //
198 //  PURPOSE:  Processes messages for the main window.
199 */
200 LRESULT CALLBACK
201 MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
202 {
203     PNHMainWindow data = (PNHMainWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
204 #if 1 /*JP*/
205     static int doublebyte = 0;
206 #endif
207
208     switch (message) {
209     case WM_CREATE:
210         /* set window data */
211         data = (PNHMainWindow) malloc(sizeof(NHMainWindow));
212         if (!data)
213             panic("out of memory");
214         ZeroMemory(data, sizeof(NHMainWindow));
215         data->mapAcsiiModeSave = MAP_MODE_ASCII12x16;
216         SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
217
218         /* update menu items */
219         CheckMenuItem(
220             GetMenu(hWnd), IDM_SETTING_LOCKWINDOWS,
221             MF_BYCOMMAND
222                 | (GetNHApp()->bWindowsLocked ? MF_CHECKED : MF_UNCHECKED));
223
224         CheckMenuItem(GetMenu(hWnd), IDM_SETTING_AUTOLAYOUT,
225                       GetNHApp()->bAutoLayout ? MF_CHECKED : MF_UNCHECKED);
226
227         /* store handle to the mane menu in the application record */
228         GetNHApp()->hMainWnd = hWnd;
229         break;
230
231     case WM_MSNH_COMMAND:
232         onMSNHCommand(hWnd, wParam, lParam);
233         break;
234
235     case WM_KEYDOWN: {
236
237         /* translate arrow keys into nethack commands */
238         switch (wParam) {
239         case VK_LEFT:
240             if (STATEON(VK_CONTROL)) {
241                 /* scroll map window one line left */
242                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_HSCROLL,
243                             MAKEWPARAM(SB_LINEUP, 0), (LPARAM) NULL);
244             } else {
245                 NHEVENT_KBD(KEYTABLE(KEY_W));
246             }
247             return 0;
248
249         case VK_RIGHT:
250             if (STATEON(VK_CONTROL)) {
251                 /* scroll map window one line right */
252                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_HSCROLL,
253                             MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM) NULL);
254             } else {
255                 NHEVENT_KBD(KEYTABLE(KEY_E));
256             }
257             return 0;
258
259         case VK_UP:
260             if (STATEON(VK_CONTROL)) {
261                 /* scroll map window one line up */
262                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
263                             MAKEWPARAM(SB_LINEUP, 0), (LPARAM) NULL);
264             } else {
265                 NHEVENT_KBD(KEYTABLE(KEY_N));
266             }
267             return 0;
268
269         case VK_DOWN:
270             if (STATEON(VK_CONTROL)) {
271                 /* scroll map window one line down */
272                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
273                             MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM) NULL);
274             } else {
275                 NHEVENT_KBD(KEYTABLE(KEY_S));
276             }
277             return 0;
278
279         case VK_HOME:
280             if (STATEON(VK_CONTROL)) {
281                 /* scroll map window to upper left corner */
282                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
283                             MAKEWPARAM(SB_THUMBTRACK, 0), (LPARAM) NULL);
284
285                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_HSCROLL,
286                             MAKEWPARAM(SB_THUMBTRACK, 0), (LPARAM) NULL);
287             } else {
288                 NHEVENT_KBD(KEYTABLE(KEY_NW));
289             }
290             return 0;
291
292         case VK_END:
293             if (STATEON(VK_CONTROL)) {
294                 /* scroll map window to lower right corner */
295                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
296                             MAKEWPARAM(SB_THUMBTRACK, ROWNO), (LPARAM) NULL);
297
298                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_HSCROLL,
299                             MAKEWPARAM(SB_THUMBTRACK, COLNO), (LPARAM) NULL);
300             } else {
301                 NHEVENT_KBD(KEYTABLE(KEY_SW));
302             }
303             return 0;
304
305         case VK_PRIOR:
306             if (STATEON(VK_CONTROL)) {
307                 /* scroll map window one page up */
308                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
309                             MAKEWPARAM(SB_PAGEUP, 0), (LPARAM) NULL);
310             } else {
311                 NHEVENT_KBD(KEYTABLE(KEY_NE));
312             }
313             return 0;
314
315         case VK_NEXT:
316             if (STATEON(VK_CONTROL)) {
317                 /* scroll map window one page down */
318                 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
319                             MAKEWPARAM(SB_PAGEDOWN, 0), (LPARAM) NULL);
320             } else {
321                 NHEVENT_KBD(KEYTABLE(KEY_SE));
322             }
323             return 0;
324
325         case VK_DECIMAL:
326         case VK_DELETE:
327             NHEVENT_KBD(KEYTABLE(KEY_WAITLOOK));
328             return 0;
329
330         case VK_INSERT:
331             NHEVENT_KBD(KEYTABLE(KEY_INV));
332             return 0;
333
334         case VK_SUBTRACT:
335             NHEVENT_KBD(KEYTABLE(KEY_MINUS));
336             return 0;
337
338         case VK_ADD:
339             NHEVENT_KBD(KEYTABLE(KEY_PLUS));
340             return 0;
341
342 #if defined(DEBUG) && defined(_MSC_VER)
343         case VK_PAUSE:
344             if (IsDebuggerPresent()) {
345                 iflags.debug_fuzzer = !iflags.debug_fuzzer;
346                 return 0;
347             }
348             break;
349 #endif
350
351         case VK_CLEAR: /* This is the '5' key */
352             NHEVENT_KBD(KEYTABLE(KEY_GOINTERESTING));
353             return 0;
354
355         case VK_F4:
356             if (IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
357                 mswin_select_map_mode(IS_MAP_ASCII(iflags.wc_map_mode)
358                                           ? data->mapAcsiiModeSave
359                                           : MAP_MODE_TILES);
360             } else {
361                 mswin_select_map_mode(IS_MAP_ASCII(iflags.wc_map_mode)
362                                           ? MAP_MODE_ASCII_FIT_TO_SCREEN
363                                           : MAP_MODE_TILES_FIT_TO_SCREEN);
364             }
365             return 0;
366
367         case VK_F5:
368             if (IS_MAP_ASCII(iflags.wc_map_mode)) {
369                 if (IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
370                     mswin_select_map_mode(MAP_MODE_TILES_FIT_TO_SCREEN);
371                 } else {
372                     mswin_select_map_mode(MAP_MODE_TILES);
373                 }
374             } else {
375                 if (IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
376                     mswin_select_map_mode(MAP_MODE_ASCII_FIT_TO_SCREEN);
377                 } else {
378                     mswin_select_map_mode(data->mapAcsiiModeSave);
379                 }
380             }
381             return 0;
382
383         default: {
384             WORD c;
385             BYTE kbd_state[256];
386
387             c = 0;
388             ZeroMemory(kbd_state, sizeof(kbd_state));
389             GetKeyboardState(kbd_state);
390
391             if (ToAscii((UINT) wParam, (lParam >> 16) & 0xFF, kbd_state, &c, 0)) {
392                 NHEVENT_KBD(c & 0xFF);
393                 return 0;
394             } else {
395                 return 1;
396             }
397         }
398
399         } /* end switch */
400     } break;
401
402 #if 1 /*JP*//*\91S\8ap\95¶\8e\9a\91ÃŽ\89\9e*/
403     case WM_CHAR:
404     {
405         if (doublebyte == 1) {
406             NHEVENT_KBD(wParam & 0xFF);
407             doublebyte = 0;
408             return 0;
409         } else if (is_kanji(wParam)) {
410             NHEVENT_KBD(wParam & 0xFF);
411             doublebyte = 1;
412             return 0;
413         }
414     } break;
415
416 #endif
417     case WM_SYSCHAR: /* Alt-char pressed */
418     {
419         /*
420           If not nethackmode, don't handle Alt-keys here.
421           If no Alt-key pressed it can never be an extended command
422         */
423         if (GetNHApp()->regNetHackMode && ((lParam & 1 << 29) != 0)) {
424             unsigned char c = (unsigned char) (wParam & 0xFF);
425             unsigned char scancode = (lParam >> 16) & 0xFF;
426             if (index(extendedlist, tolower(c)) != 0) {
427                 NHEVENT_KBD(M(tolower(c)));
428             } else if (scancode == (SCANLO + SIZE(scanmap)) - 1) {
429                 NHEVENT_KBD(M('?'));
430             }
431             return 0;
432         }
433         return DefWindowProc(hWnd, message, wParam, lParam);
434     } break;
435
436     case WM_COMMAND:
437         /* process commands - menu commands mostly */
438         if (onWMCommand(hWnd, wParam, lParam))
439             return DefWindowProc(hWnd, message, wParam, lParam);
440         else
441             return 0;
442
443     case WM_MOVE:
444     case WM_SIZE: {
445         WINDOWPLACEMENT wp;
446
447         mswin_layout_main_window(NULL);
448
449         wp.length = sizeof(wp);
450         if (GetWindowPlacement(hWnd, &wp)) {
451             GetNHApp()->regMainShowState =
452                 (wp.showCmd == SW_SHOWMAXIMIZED ? SW_SHOWMAXIMIZED
453                                                 : SW_SHOWNORMAL);
454
455             GetNHApp()->regMainMinX = wp.ptMinPosition.x;
456             GetNHApp()->regMainMinY = wp.ptMinPosition.y;
457
458             GetNHApp()->regMainMaxX = wp.ptMaxPosition.x;
459             GetNHApp()->regMainMaxY = wp.ptMaxPosition.y;
460
461             GetNHApp()->regMainLeft = wp.rcNormalPosition.left;
462             GetNHApp()->regMainTop = wp.rcNormalPosition.top;
463             GetNHApp()->regMainRight = wp.rcNormalPosition.right;
464             GetNHApp()->regMainBottom = wp.rcNormalPosition.bottom;
465         }
466         break;
467     }
468     case WM_SETFOCUS:
469         /* if there is a menu window out there -
470            transfer input focus to it */
471         if (IsWindow(GetNHApp()->hPopupWnd)) {
472             SetFocus(GetNHApp()->hPopupWnd);
473         }
474         break;
475
476     case WM_CLOSE: {
477         /* exit gracefully */
478         if (program_state.gameover) {
479             /* assume the user really meant this, as the game is already
480              * over... */
481             /* to make sure we still save bones, just set stop printing flag
482              */
483             program_state.stopprint++;
484             NHEVENT_KBD(
485                 '\033'); /* and send keyboard input as if user pressed ESC */
486             /* additional code for this is done in menu and rip windows */
487         } else if (!program_state.something_worth_saving) {
488             /* User exited before the game started, e.g. during splash display
489              */
490             /* Just get out. */
491             bail((char *) 0);
492         } else {
493             /* prompt user for action */
494 #if 0 /*JP*/
495             switch (NHMessageBox(hWnd, TEXT("Save?"),
496 #else
497             switch (NHMessageBox(hWnd, TEXT("\95Û\91¶\82µ\82Ä\8fI\97¹\82µ\82Ãœ\82·\82©\81H"),
498 #endif
499                                  MB_YESNOCANCEL | MB_ICONQUESTION)) {
500             case IDYES:
501 #ifdef SAFERHANGUP
502                 /* destroy popup window - it has its own loop and we need to
503                 return control to NetHack core at this point */
504                 if (IsWindow(GetNHApp()->hPopupWnd))
505                     SendMessage(GetNHApp()->hPopupWnd, WM_COMMAND, IDCANCEL,
506                                 0);
507
508                 /* tell NetHack core that "hangup" is requested */
509                 hangup(1);
510 #else
511                 NHEVENT_KBD('y');
512                 dosave();
513 #endif
514                 break;
515             case IDNO:
516                 NHEVENT_KBD('q');
517                 done(QUIT);
518                 break;
519             case IDCANCEL:
520                 break;
521             }
522         }
523     }
524         return 0;
525
526     case WM_DESTROY:
527         /* apparently we never get here
528            TODO: work on exit routines - need to send
529            WM_QUIT somehow */
530
531         /* clean up */
532         free((PNHMainWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA));
533         SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
534
535         // PostQuitMessage(0);
536         exit(1);
537         break;
538
539     case WM_DPICHANGED: {
540         mswin_layout_main_window(NULL);
541     } break;
542
543     default:
544         return DefWindowProc(hWnd, message, wParam, lParam);
545     }
546     return 0;
547 }
548
549 void
550 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
551 {
552     UNREFERENCED_PARAMETER(hWnd);
553     UNREFERENCED_PARAMETER(wParam);
554     UNREFERENCED_PARAMETER(lParam);
555
556     switch (wParam) {
557     /* new window was just added */
558     case MSNH_MSG_ADDWND: {
559         PMSNHMsgAddWnd msg_param = (PMSNHMsgAddWnd) lParam;
560         HWND child;
561
562         if (GetNHApp()->windowlist[msg_param->wid].type == NHW_MAP)
563             mswin_select_map_mode(iflags.wc_map_mode);
564
565         child = GetNHApp()->windowlist[msg_param->wid].win;
566     } break;
567
568     case MSNH_MSG_RANDOM_INPUT: {
569         nhassert(iflags.debug_fuzzer);
570         NHEVENT_KBD(randomkey());
571     } break;
572
573     }
574 }
575
576 /* adjust windows to fit main window layout
577    ---------------------------
578    |        Status           |
579    +-------------------------+
580    |                         |
581    |                         |
582    |          MAP            |
583    |                         |
584    |                         |
585    +-------------------------+
586    |       Messages          |
587    ---------------------------
588 */
589 void
590 mswin_layout_main_window(HWND changed_child)
591 {
592     winid i;
593     RECT client_rt, wnd_rect;
594     POINT status_org;
595     SIZE status_size;
596     POINT msg_org;
597     SIZE msg_size;
598     POINT map_org;
599     SIZE map_size;
600     SIZE menu_size;
601     HWND wnd_status, wnd_msg;
602     PNHMainWindow data;
603
604     if (GetNHApp()->bAutoLayout) {
605         GetClientRect(GetNHApp()->hMainWnd, &client_rt);
606         data = (PNHMainWindow) GetWindowLongPtr(GetNHApp()->hMainWnd,
607                                                 GWLP_USERDATA);
608
609         /* get sizes of child windows */
610         wnd_status = mswin_hwnd_from_winid(WIN_STATUS);
611         if (IsWindow(wnd_status)) {
612             mswin_status_window_size(wnd_status, &status_size);
613         } else {
614             status_size.cx = status_size.cy = 0;
615         }
616
617         wnd_msg = mswin_hwnd_from_winid(WIN_MESSAGE);
618         if (IsWindow(wnd_msg)) {
619             mswin_message_window_size(wnd_msg, &msg_size);
620         } else {
621             msg_size.cx = msg_size.cy = 0;
622         }
623
624         /* find all menu windows and calculate the size */
625         menu_size.cx = menu_size.cy = 0;
626         for (i = 0; i < MAXWINDOWS; i++) {
627             SIZE tmp_size;
628             if (GetNHApp()->windowlist[i].win
629                 && !GetNHApp()->windowlist[i].dead
630                 && GetNHApp()->windowlist[i].type == NHW_MENU) {
631                 mswin_menu_window_size(GetNHApp()->windowlist[i].win,
632                                        &tmp_size);
633                 menu_size.cx = max(menu_size.cx, tmp_size.cx);
634                 menu_size.cy = max(menu_size.cy, tmp_size.cy);
635             }
636         }
637
638         /* set window positions */
639         SetRect(&wnd_rect, client_rt.left, client_rt.top, client_rt.right,
640                 client_rt.bottom);
641         switch (iflags.wc_align_status) {
642         case ALIGN_LEFT:
643             status_size.cx = (wnd_rect.right - wnd_rect.left) / 4;
644             status_size.cy =
645                 (wnd_rect.bottom - wnd_rect.top); // that won't look good
646             status_org.x = wnd_rect.left;
647             status_org.y = wnd_rect.top;
648             wnd_rect.left += status_size.cx;
649             break;
650
651         case ALIGN_RIGHT:
652             status_size.cx = (wnd_rect.right - wnd_rect.left) / 4;
653             status_size.cy =
654                 (wnd_rect.bottom - wnd_rect.top); // that won't look good
655             status_org.x = wnd_rect.right - status_size.cx;
656             status_org.y = wnd_rect.top;
657             wnd_rect.right -= status_size.cx;
658             break;
659
660         case ALIGN_TOP:
661             status_size.cx = (wnd_rect.right - wnd_rect.left);
662             status_org.x = wnd_rect.left;
663             status_org.y = wnd_rect.top;
664             wnd_rect.top += status_size.cy;
665             break;
666
667         case ALIGN_BOTTOM:
668         default:
669             status_size.cx = (wnd_rect.right - wnd_rect.left);
670             status_org.x = wnd_rect.left;
671             status_org.y = wnd_rect.bottom - status_size.cy;
672             wnd_rect.bottom -= status_size.cy;
673             break;
674         }
675
676         switch (iflags.wc_align_message) {
677         case ALIGN_LEFT:
678             msg_size.cx = (wnd_rect.right - wnd_rect.left) / 4;
679             msg_size.cy = (wnd_rect.bottom - wnd_rect.top);
680             msg_org.x = wnd_rect.left;
681             msg_org.y = wnd_rect.top;
682             wnd_rect.left += msg_size.cx;
683             break;
684
685         case ALIGN_RIGHT:
686             msg_size.cx = (wnd_rect.right - wnd_rect.left) / 4;
687             msg_size.cy = (wnd_rect.bottom - wnd_rect.top);
688             msg_org.x = wnd_rect.right - msg_size.cx;
689             msg_org.y = wnd_rect.top;
690             wnd_rect.right -= msg_size.cx;
691             break;
692
693         case ALIGN_TOP:
694             msg_size.cx = (wnd_rect.right - wnd_rect.left);
695             msg_org.x = wnd_rect.left;
696             msg_org.y = wnd_rect.top;
697             wnd_rect.top += msg_size.cy;
698             break;
699
700         case ALIGN_BOTTOM:
701         default:
702             msg_size.cx = (wnd_rect.right - wnd_rect.left);
703             msg_org.x = wnd_rect.left;
704             msg_org.y = wnd_rect.bottom - msg_size.cy;
705             wnd_rect.bottom -= msg_size.cy;
706             break;
707         }
708
709         /* map window */
710         map_org.x = wnd_rect.left;
711         map_org.y = wnd_rect.top;
712         map_size.cx = wnd_rect.right - wnd_rect.left;
713         map_size.cy = wnd_rect.bottom - wnd_rect.top;
714
715         GetNHApp()->rtStatusWindow.left = status_org.x;
716         GetNHApp()->rtStatusWindow.top = status_org.y;
717         GetNHApp()->rtStatusWindow.right = status_org.x + status_size.cx;
718         GetNHApp()->rtStatusWindow.bottom = status_org.y + status_size.cy;
719
720         GetNHApp()->rtTextWindow.left = map_org.x;
721         GetNHApp()->rtTextWindow.top = map_org.y;
722         GetNHApp()->rtTextWindow.right =
723             map_org.x + (wnd_rect.right - wnd_rect.left);
724         GetNHApp()->rtTextWindow.bottom = map_org.y + map_size.cy;
725
726         GetNHApp()->rtMapWindow.left = map_org.x;
727         GetNHApp()->rtMapWindow.top = map_org.y;
728         GetNHApp()->rtMapWindow.right = map_org.x + map_size.cx;
729         GetNHApp()->rtMapWindow.bottom = map_org.y + map_size.cy;
730
731         GetNHApp()->rtMsgWindow.left = msg_org.x;
732         GetNHApp()->rtMsgWindow.top = msg_org.y;
733         GetNHApp()->rtMsgWindow.right = msg_org.x + msg_size.cx;
734         GetNHApp()->rtMsgWindow.bottom = msg_org.y + msg_size.cy;
735
736         /*  map_width/4 < menu_width < map_width*2/3 */
737         GetNHApp()->rtMenuWindow.left =
738             GetNHApp()->rtMapWindow.right
739             - min(map_size.cx * 2 / 3, max(map_size.cx / 4, menu_size.cx));
740         GetNHApp()->rtMenuWindow.top = GetNHApp()->rtMapWindow.top;
741         GetNHApp()->rtMenuWindow.right = GetNHApp()->rtMapWindow.right;
742         GetNHApp()->rtMenuWindow.bottom = GetNHApp()->rtMapWindow.bottom;
743
744         GetNHApp()->rtInvenWindow.left = GetNHApp()->rtMenuWindow.left;
745         GetNHApp()->rtInvenWindow.top = GetNHApp()->rtMenuWindow.top;
746         GetNHApp()->rtInvenWindow.right = GetNHApp()->rtMenuWindow.right;
747         GetNHApp()->rtInvenWindow.bottom = GetNHApp()->rtMenuWindow.bottom;
748
749         /* adjust map window size only if perm_invent is set */
750         if (iflags.perm_invent)
751             GetNHApp()->rtMapWindow.right = GetNHApp()->rtMenuWindow.left;
752     }
753
754     /* go through the windows list and adjust sizes */
755     for (i = 0; i < MAXWINDOWS; i++) {
756         if (GetNHApp()->windowlist[i].win
757             && !GetNHApp()->windowlist[i].dead) {
758             RECT rt;
759             /* kludge - inventory window should have its own type (same as
760                menu-text
761                as a matter of fact) */
762             if (iflags.perm_invent && i == WIN_INVEN) {
763                 mswin_get_window_placement(NHW_INVEN, &rt);
764             } else {
765                 mswin_get_window_placement(GetNHApp()->windowlist[i].type,
766                                            &rt);
767             }
768
769             MoveWindow(GetNHApp()->windowlist[i].win, rt.left, rt.top,
770                        rt.right - rt.left, rt.bottom - rt.top, TRUE);
771         }
772     }
773     if (IsWindow(changed_child))
774         SetForegroundWindow(changed_child);
775 }
776
777 VOID CALLBACK FuzzTimerProc(
778         _In_ HWND     hwnd,
779         _In_ UINT     uMsg,
780         _In_ UINT_PTR idEvent,
781         _In_ DWORD    dwTime
782         )
783 {
784         INPUT input[16];
785         int i_pos = 0;
786         int c = randomkey();
787         SHORT k = VkKeyScanA(c);
788         BOOL gen_alt = (rn2(50) == 0) && isalpha(c);
789
790         if (!iflags.debug_fuzzer) {
791                 KillTimer(hwnd, IDT_FUZZ_TIMER);
792                 return;
793         }
794
795         if (!GetFocus())
796             return;
797
798         ZeroMemory(input, sizeof(input));
799         if (gen_alt) {
800                 input[i_pos].type = INPUT_KEYBOARD;
801                 input[i_pos].ki.dwFlags = KEYEVENTF_SCANCODE;
802                 input[i_pos].ki.wScan = MapVirtualKey(VK_MENU, 0);
803                 i_pos++;
804         }
805
806         if (HIBYTE(k) & 1) {
807                 input[i_pos].type = INPUT_KEYBOARD;
808                 input[i_pos].ki.dwFlags = KEYEVENTF_SCANCODE;
809                 input[i_pos].ki.wScan = MapVirtualKey(VK_LSHIFT, 0);
810                 i_pos++;
811         }
812
813         input[i_pos].type = INPUT_KEYBOARD;
814         input[i_pos].ki.dwFlags = KEYEVENTF_SCANCODE;
815         input[i_pos].ki.wScan = MapVirtualKey(LOBYTE(k), 0);
816         i_pos++;
817
818         if (HIBYTE(k) & 1) {
819                 input[i_pos].type = INPUT_KEYBOARD;
820                 input[i_pos].ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
821                 input[i_pos].ki.wScan = MapVirtualKey(VK_LSHIFT, 0);
822                 i_pos++;
823         }
824         if (gen_alt) {
825                 input[i_pos].type = INPUT_KEYBOARD;
826                 input[i_pos].ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
827                 input[i_pos].ki.wScan = MapVirtualKey(VK_MENU, 0);
828                 i_pos++;
829         }
830         SendInput(i_pos, input, sizeof(input[0]));
831 }
832
833 LRESULT
834 onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
835 {
836     int wmId, wmEvent;
837     PNHMainWindow data;
838
839     UNREFERENCED_PARAMETER(lParam);
840
841     data = (PNHMainWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
842     wmId = LOWORD(wParam);
843     wmEvent = HIWORD(wParam);
844
845     // Parse the menu selections:
846     switch (wmId) {
847     case IDM_ABOUT:
848         mswin_display_splash_window(TRUE);
849         break;
850
851     case IDM_FUZZ:
852         if (iflags.debug_fuzzer)
853             KillTimer(hWnd, IDT_FUZZ_TIMER);
854         else
855             SetTimer(hWnd, IDT_FUZZ_TIMER, 10, FuzzTimerProc);
856         iflags.debug_fuzzer = !iflags.debug_fuzzer;
857         break;
858     case IDM_EXIT:
859         if (iflags.debug_fuzzer)
860             break;
861         done2();
862         break;
863
864     case IDM_SAVE:
865         if (iflags.debug_fuzzer)
866             break;
867         if (!program_state.gameover && !program_state.done_hup)
868             dosave();
869         else
870             MessageBeep(0);
871         break;
872
873     case IDM_MAP_TILES:
874     case IDM_MAP_ASCII4X6:
875     case IDM_MAP_ASCII6X8:
876     case IDM_MAP_ASCII8X8:
877     case IDM_MAP_ASCII16X8:
878     case IDM_MAP_ASCII7X12:
879     case IDM_MAP_ASCII8X12:
880     case IDM_MAP_ASCII12X16:
881     case IDM_MAP_ASCII16X12:
882     case IDM_MAP_ASCII10X18:
883         mswin_select_map_mode(menuid2mapmode(wmId));
884         break;
885
886     case IDM_MAP_FIT_TO_SCREEN:
887         if (IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
888             mswin_select_map_mode(IS_MAP_ASCII(iflags.wc_map_mode)
889                                       ? data->mapAcsiiModeSave
890                                       : MAP_MODE_TILES);
891         } else {
892             mswin_select_map_mode(IS_MAP_ASCII(iflags.wc_map_mode)
893                                       ? MAP_MODE_ASCII_FIT_TO_SCREEN
894                                       : MAP_MODE_TILES_FIT_TO_SCREEN);
895         }
896         break;
897
898     case IDM_SETTING_SCREEN_TO_CLIPBOARD: {
899         char *p;
900         size_t len;
901         HANDLE hglbCopy;
902         char *p_copy;
903
904         p = nh_compose_ascii_screenshot();
905         if (!p)
906             return 0;
907         len = strlen(p);
908
909         if (!OpenClipboard(hWnd)) {
910             NHMessageBox(hWnd, TEXT("Cannot open clipboard"),
911                          MB_OK | MB_ICONERROR);
912             free(p);
913             return 0;
914         }
915
916         EmptyClipboard();
917
918         hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (len + 1) * sizeof(char));
919         if (hglbCopy == NULL) {
920             CloseClipboard();
921             free(p);
922             return FALSE;
923         }
924
925         p_copy = (char *) GlobalLock(hglbCopy);
926         strncpy(p_copy, p, len);
927         p_copy[len] = 0; // null character
928         GlobalUnlock(hglbCopy);
929
930         SetClipboardData(SYMHANDLING(H_IBM) ? CF_OEMTEXT : CF_TEXT, hglbCopy);
931
932         CloseClipboard();
933
934         free(p);
935     } break;
936
937     case IDM_SETTING_SCREEN_TO_FILE: {
938         OPENFILENAME ofn;
939         TCHAR filename[1024];
940         TCHAR whackdir[MAX_PATH];
941         FILE *pFile;
942         char *text;
943         wchar_t *wtext;
944         int tlen = 0;
945
946         if (iflags.debug_fuzzer)
947             break;
948
949         ZeroMemory(filename, sizeof(filename));
950         ZeroMemory(&ofn, sizeof(ofn));
951         ofn.lStructSize = sizeof(OPENFILENAME);
952         ofn.hwndOwner = hWnd;
953         ofn.hInstance = GetNHApp()->hApp;
954         ofn.lpstrFilter = TEXT("Text Files (*.txt)\x0*.txt\x0")
955             TEXT("All Files (*.*)\x0*.*\x0") TEXT("\x0\x0");
956         ofn.lpstrCustomFilter = NULL;
957         ofn.nMaxCustFilter = 0;
958         ofn.nFilterIndex = 1;
959         ofn.lpstrFile = filename;
960         ofn.nMaxFile = SIZE(filename);
961         ofn.lpstrFileTitle = NULL;
962         ofn.nMaxFileTitle = 0;
963         ofn.lpstrInitialDir = NH_A2W(hackdir, whackdir, MAX_PATH);
964         ofn.lpstrTitle = NULL;
965         ofn.Flags = OFN_LONGNAMES | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
966         ofn.nFileOffset = 0;
967         ofn.nFileExtension = 0;
968         ofn.lpstrDefExt = TEXT("txt");
969         ofn.lCustData = 0;
970         ofn.lpfnHook = 0;
971         ofn.lpTemplateName = 0;
972
973         if (!GetSaveFileName(&ofn))
974             return FALSE;
975
976         text = nh_compose_ascii_screenshot();
977         if (!text)
978             return FALSE;
979
980         pFile = _tfopen(filename, TEXT("wt+,ccs=UTF-8"));
981         if (!pFile) {
982             TCHAR buf[4096];
983             _stprintf(buf, TEXT("Cannot open %s for writing!"), filename);
984             NHMessageBox(hWnd, buf, MB_OK | MB_ICONERROR);
985             free(text);
986             return FALSE;
987         }
988
989         tlen = strlen(text);
990         wtext = (wchar_t *) malloc(tlen * sizeof(wchar_t));
991         if (!wtext)
992             panic("out of memory");
993         MultiByteToWideChar(NH_CODEPAGE, 0, text, -1, wtext, tlen);
994         fwrite(wtext, tlen * sizeof(wchar_t), 1, pFile);
995         fclose(pFile);
996         free(text);
997         free(wtext);
998     } break;
999
1000     case IDM_NHMODE: {
1001         GetNHApp()->regNetHackMode = GetNHApp()->regNetHackMode ? 0 : 1;
1002         mswin_menu_check_intf_mode();
1003         mswin_apply_window_style_all();
1004         break;
1005     }
1006     case IDM_CLEARSETTINGS: {
1007         mswin_destroy_reg();
1008         /* Notify the user that windows settings will not be saved this time.
1009          */
1010         NHMessageBox(GetNHApp()->hMainWnd,
1011                      TEXT("Your Windows Settings will not be stored when you "
1012                           "exit this time."),
1013                      MB_OK | MB_ICONINFORMATION);
1014         break;
1015     }
1016
1017     case IDM_SETTING_AUTOLAYOUT:
1018         GetNHApp()->bAutoLayout = !GetNHApp()->bAutoLayout;
1019         mswin_layout_main_window(NULL);
1020
1021         /* Update menu item check-mark */
1022         CheckMenuItem(GetMenu(GetNHApp()->hMainWnd), IDM_SETTING_AUTOLAYOUT,
1023                       GetNHApp()->bAutoLayout ? MF_CHECKED : MF_UNCHECKED);
1024         break;
1025
1026     case IDM_SETTING_LOCKWINDOWS:
1027         nhlock_windows(!GetNHApp()->bWindowsLocked);
1028         break;
1029
1030     case IDM_HELP_LONG:
1031         display_file(HELP, TRUE);
1032         break;
1033
1034     case IDM_HELP_COMMANDS:
1035         display_file(SHELP, TRUE);
1036         break;
1037
1038     case IDM_HELP_HISTORY:
1039         (void) dohistory();
1040         break;
1041
1042     case IDM_HELP_INFO_CHAR:
1043         (void) dowhatis();
1044         break;
1045
1046     case IDM_HELP_INFO_KEY:
1047         (void) dowhatdoes();
1048         break;
1049
1050     case IDM_HELP_OPTIONS:
1051         option_help();
1052         break;
1053
1054     case IDM_HELP_OPTIONS_LONG:
1055         display_file(OPTIONFILE, TRUE);
1056         break;
1057
1058     case IDM_HELP_EXTCMD:
1059         (void) doextlist();
1060         break;
1061
1062     case IDM_HELP_LICENSE:
1063         display_file(LICENSE, TRUE);
1064         break;
1065
1066     case IDM_HELP_PORTHELP:
1067         display_file(PORT_HELP, TRUE);
1068         break;
1069
1070     default:
1071         return 1;
1072     }
1073     return 0;
1074 }
1075
1076 // Mesage handler for about box.
1077 LRESULT CALLBACK
1078 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
1079 {
1080     char buf[BUFSZ];
1081     TCHAR wbuf[BUFSZ];
1082     RECT main_rt, dlg_rt;
1083     SIZE dlg_sz;
1084
1085     UNREFERENCED_PARAMETER(lParam);
1086
1087     switch (message) {
1088     case WM_INITDIALOG:
1089         getversionstring(buf);
1090         SetDlgItemText(hDlg, IDC_ABOUT_VERSION,
1091                        NH_A2W(buf, wbuf, sizeof(wbuf)));
1092
1093         SetDlgItemText(hDlg, IDC_ABOUT_COPYRIGHT,
1094                        NH_A2W(COPYRIGHT_BANNER_A "\n" COPYRIGHT_BANNER_B
1095                                                  "\n" COPYRIGHT_BANNER_C
1096                                                  "\n" COPYRIGHT_BANNER_D,
1097                               wbuf, BUFSZ));
1098
1099         /* center dialog in the main window */
1100         GetWindowRect(GetNHApp()->hMainWnd, &main_rt);
1101         GetWindowRect(hDlg, &dlg_rt);
1102         dlg_sz.cx = dlg_rt.right - dlg_rt.left;
1103         dlg_sz.cy = dlg_rt.bottom - dlg_rt.top;
1104
1105         dlg_rt.left = (main_rt.left + main_rt.right - dlg_sz.cx) / 2;
1106         dlg_rt.right = dlg_rt.left + dlg_sz.cx;
1107         dlg_rt.top = (main_rt.top + main_rt.bottom - dlg_sz.cy) / 2;
1108         dlg_rt.bottom = dlg_rt.top + dlg_sz.cy;
1109         MoveWindow(hDlg, (main_rt.left + main_rt.right - dlg_sz.cx) / 2,
1110                    (main_rt.top + main_rt.bottom - dlg_sz.cy) / 2, dlg_sz.cx,
1111                    dlg_sz.cy, TRUE);
1112
1113         return TRUE;
1114
1115     case WM_COMMAND:
1116         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
1117             EndDialog(hDlg, LOWORD(wParam));
1118             return TRUE;
1119         }
1120         break;
1121     }
1122     return FALSE;
1123 }
1124
1125 void
1126 mswin_menu_check_intf_mode()
1127 {
1128     HMENU hMenu = GetMenu(GetNHApp()->hMainWnd);
1129
1130     if (GetNHApp()->regNetHackMode)
1131         CheckMenuItem(hMenu, IDM_NHMODE, MF_CHECKED);
1132     else
1133         CheckMenuItem(hMenu, IDM_NHMODE, MF_UNCHECKED);
1134 }
1135
1136 void
1137 mswin_select_map_mode(int mode)
1138 {
1139     PNHMainWindow data;
1140     winid map_id;
1141
1142     map_id = WIN_MAP;
1143     data =
1144         (PNHMainWindow) GetWindowLongPtr(GetNHApp()->hMainWnd, GWLP_USERDATA);
1145
1146     /* override for Rogue level */
1147     if (Is_rogue_level(&u.uz) && !IS_MAP_ASCII(mode))
1148         return;
1149
1150     /* set map mode menu mark */
1151     if (IS_MAP_ASCII(mode)) {
1152         CheckMenuRadioItem(
1153             GetMenu(GetNHApp()->hMainWnd), IDM_MAP_TILES, IDM_MAP_ASCII10X18,
1154             mapmode2menuid(IS_MAP_FIT_TO_SCREEN(mode) ? data->mapAcsiiModeSave
1155                                                       : mode),
1156             MF_BYCOMMAND);
1157     } else {
1158         CheckMenuRadioItem(GetMenu(GetNHApp()->hMainWnd), IDM_MAP_TILES,
1159                            IDM_MAP_ASCII10X18, mapmode2menuid(MAP_MODE_TILES),
1160                            MF_BYCOMMAND);
1161     }
1162
1163     /* set fit-to-screen mode mark */
1164     CheckMenuItem(GetMenu(GetNHApp()->hMainWnd), IDM_MAP_FIT_TO_SCREEN,
1165                   MF_BYCOMMAND | (IS_MAP_FIT_TO_SCREEN(mode) ? MF_CHECKED
1166                                                              : MF_UNCHECKED));
1167
1168     if (IS_MAP_ASCII(iflags.wc_map_mode)
1169         && !IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
1170         data->mapAcsiiModeSave = iflags.wc_map_mode;
1171     }
1172
1173     iflags.wc_map_mode = mode;
1174
1175     /*
1176     ** first, check if WIN_MAP has been inialized.
1177     ** If not - attempt to retrieve it by type, then check it again
1178     */
1179     if (map_id == WIN_ERR)
1180         map_id = mswin_winid_from_type(NHW_MAP);
1181     if (map_id != WIN_ERR)
1182         mswin_map_mode(mswin_hwnd_from_winid(map_id), mode);
1183 }
1184
1185 static struct t_menu2mapmode {
1186     int menuID;
1187     int mapMode;
1188 } _menu2mapmode[] = { { IDM_MAP_TILES, MAP_MODE_TILES },
1189                       { IDM_MAP_ASCII4X6, MAP_MODE_ASCII4x6 },
1190                       { IDM_MAP_ASCII6X8, MAP_MODE_ASCII6x8 },
1191                       { IDM_MAP_ASCII8X8, MAP_MODE_ASCII8x8 },
1192                       { IDM_MAP_ASCII16X8, MAP_MODE_ASCII16x8 },
1193                       { IDM_MAP_ASCII7X12, MAP_MODE_ASCII7x12 },
1194                       { IDM_MAP_ASCII8X12, MAP_MODE_ASCII8x12 },
1195                       { IDM_MAP_ASCII12X16, MAP_MODE_ASCII12x16 },
1196                       { IDM_MAP_ASCII16X12, MAP_MODE_ASCII16x12 },
1197                       { IDM_MAP_ASCII10X18, MAP_MODE_ASCII10x18 },
1198                       { IDM_MAP_FIT_TO_SCREEN, MAP_MODE_ASCII_FIT_TO_SCREEN },
1199                       { -1, -1 } };
1200
1201 int
1202 menuid2mapmode(int menuid)
1203 {
1204     struct t_menu2mapmode *p;
1205     for (p = _menu2mapmode; p->mapMode != -1; p++)
1206         if (p->menuID == menuid)
1207             return p->mapMode;
1208     return -1;
1209 }
1210
1211 int
1212 mapmode2menuid(int map_mode)
1213 {
1214     struct t_menu2mapmode *p;
1215     for (p = _menu2mapmode; p->mapMode != -1; p++)
1216         if (p->mapMode == map_mode)
1217             return p->menuID;
1218     return -1;
1219 }
1220
1221 void
1222 nhlock_windows(BOOL lock)
1223 {
1224     /* update menu */
1225     GetNHApp()->bWindowsLocked = lock;
1226     CheckMenuItem(GetMenu(GetNHApp()->hMainWnd), IDM_SETTING_LOCKWINDOWS,
1227                   MF_BYCOMMAND | (lock ? MF_CHECKED : MF_UNCHECKED));
1228
1229     /* restyle windows */
1230     mswin_apply_window_style_all();
1231 }
1232
1233 void
1234 mswin_apply_window_style(HWND hwnd) {
1235     DWORD style = 0, exstyle = 0;
1236
1237     style = GetWindowLong(hwnd, GWL_STYLE);
1238     exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
1239
1240     if( !GetNHApp()->bWindowsLocked ) {
1241         style = WS_CHILD|WS_CLIPSIBLINGS|WS_CAPTION|WS_SIZEBOX|(style & (WS_VISIBLE|WS_VSCROLL|WS_HSCROLL));
1242         exstyle = WS_EX_WINDOWEDGE;
1243     } else if (GetNHApp()->regNetHackMode) {
1244         /* do away borders */
1245         style = WS_CHILD|WS_CLIPSIBLINGS|(style & (WS_VISIBLE|WS_VSCROLL|WS_HSCROLL));
1246         exstyle = 0;
1247     } else {
1248         style = WS_CHILD|WS_CLIPSIBLINGS|WS_THICKFRAME|(style & (WS_VISIBLE|WS_VSCROLL|WS_HSCROLL));
1249         exstyle = WS_EX_WINDOWEDGE;
1250     }
1251
1252     SetWindowLong(hwnd, GWL_STYLE, style);
1253     SetWindowLong(hwnd, GWL_EXSTYLE, exstyle);
1254     SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1255                  SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOCOPYBITS);
1256 }
1257
1258 void
1259 mswin_apply_window_style_all() {
1260     int i;
1261     for (i = 0; i < MAXWINDOWS; i++) {
1262         if (IsWindow(GetNHApp()->windowlist[i].win)
1263             && !GetNHApp()->windowlist[i].dead) {
1264             mswin_apply_window_style(GetNHApp()->windowlist[i].win);
1265         }
1266     }
1267     mswin_layout_main_window(NULL);
1268 }
1269
1270 // returns strdup() created pointer - callee assumes the ownership
1271 #define TEXT_BUFFER_SIZE 4096
1272 char *
1273 nh_compose_ascii_screenshot()
1274 {
1275     char *retval;
1276     PMSNHMsgGetText text;
1277
1278     retval = (char *) malloc(3 * TEXT_BUFFER_SIZE);
1279
1280     text =
1281         (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText) + TEXT_BUFFER_SIZE);
1282     text->max_size =
1283         TEXT_BUFFER_SIZE
1284         - 1; /* make sure we always have 0 at the end of the buffer */
1285
1286     ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
1287     SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1288                 (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
1289     strcpy(retval, text->buffer);
1290
1291     ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
1292     SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_MSNH_COMMAND,
1293                 (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
1294     strcat(retval, text->buffer);
1295
1296     ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
1297     SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND,
1298                 (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
1299     strcat(retval, text->buffer);
1300
1301     free(text);
1302     return retval;
1303 }