2 * windows.c: Windows front end for my puzzle collection.
17 #define IDM_NEW 0x0010
18 #define IDM_RESTART 0x0020
19 #define IDM_UNDO 0x0030
20 #define IDM_REDO 0x0040
21 #define IDM_QUIT 0x0050
22 #define IDM_CONFIG 0x0060
23 #define IDM_SEED 0x0070
24 #define IDM_PRESETS 0x0100
27 static FILE *debug_fp
= NULL
;
28 static HANDLE debug_hdl
= INVALID_HANDLE_VALUE
;
29 static int debug_got_console
= 0;
35 if (!debug_got_console
) {
37 debug_got_console
= 1;
38 debug_hdl
= GetStdHandle(STD_OUTPUT_HANDLE
);
42 debug_fp
= fopen("debug.log", "w");
45 if (debug_hdl
!= INVALID_HANDLE_VALUE
) {
46 WriteFile(debug_hdl
, buf
, strlen(buf
), &dw
, NULL
);
52 void debug_printf(char *fmt
, ...)
58 vsprintf(buf
, fmt
, ap
);
63 #define debug(x) (debug_printf x)
83 HWND hwnd
, statusbar
, cfgbox
;
85 HBITMAP bitmap
, prevbm
;
92 DWORD timer_last_tickcount
;
94 game_params
**presets
;
98 struct cfg_aux
*cfgaux
;
99 int cfg_which
, cfg_done
;
103 void fatal(char *fmt
, ...)
109 vsprintf(buf
, fmt
, ap
);
112 MessageBox(NULL
, buf
, "Fatal error", MB_ICONEXCLAMATION
| MB_OK
);
117 void status_bar(frontend
*fe
, char *text
)
119 SetWindowText(fe
->statusbar
, text
);
122 void frontend_default_colour(frontend
*fe
, float *output
)
124 DWORD c
= GetSysColor(COLOR_MENU
); /* ick */
126 output
[0] = (float)(GetRValue(c
) / 255.0);
127 output
[1] = (float)(GetGValue(c
) / 255.0);
128 output
[2] = (float)(GetBValue(c
) / 255.0);
131 void clip(frontend
*fe
, int x
, int y
, int w
, int h
)
134 fe
->clip
= CreateRectRgn(0, 0, 1, 1);
135 GetClipRgn(fe
->hdc_bm
, fe
->clip
);
138 IntersectClipRect(fe
->hdc_bm
, x
, y
, x
+w
, y
+h
);
141 void unclip(frontend
*fe
)
144 SelectClipRgn(fe
->hdc_bm
, fe
->clip
);
147 void draw_text(frontend
*fe
, int x
, int y
, int fonttype
, int fontsize
,
148 int align
, int colour
, char *text
)
153 * Find or create the font.
155 for (i
= 0; i
< fe
->nfonts
; i
++)
156 if (fe
->fonts
[i
].type
== fonttype
&& fe
->fonts
[i
].size
== fontsize
)
159 if (i
== fe
->nfonts
) {
160 if (fe
->fontsize
<= fe
->nfonts
) {
161 fe
->fontsize
= fe
->nfonts
+ 10;
162 fe
->fonts
= sresize(fe
->fonts
, fe
->fontsize
, struct font
);
167 fe
->fonts
[i
].type
= fonttype
;
168 fe
->fonts
[i
].size
= fontsize
;
171 * FIXME: Really I should make at least _some_ effort to
172 * pick the correct font.
174 fe
->fonts
[i
].font
= CreateFont(-fontsize
, 0, 0, 0, 0,
175 FALSE
, FALSE
, FALSE
, DEFAULT_CHARSET
,
176 OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
,
178 (fonttype
== FONT_FIXED ?
179 FIXED_PITCH
| FF_DONTCARE
:
180 VARIABLE_PITCH
| FF_SWISS
),
185 * Position and draw the text.
192 oldfont
= SelectObject(fe
->hdc_bm
, fe
->fonts
[i
].font
);
193 if (GetTextMetrics(fe
->hdc_bm
, &tm
)) {
194 if (align
& ALIGN_VCENTRE
)
195 y
-= (tm
.tmAscent
+tm
.tmDescent
)/2;
199 if (GetTextExtentPoint32(fe
->hdc_bm
, text
, strlen(text
), &size
)) {
200 if (align
& ALIGN_HCENTRE
)
202 else if (align
& ALIGN_HRIGHT
)
205 SetBkMode(fe
->hdc_bm
, TRANSPARENT
);
206 TextOut(fe
->hdc_bm
, x
, y
, text
, strlen(text
));
207 SelectObject(fe
->hdc_bm
, oldfont
);
211 void draw_rect(frontend
*fe
, int x
, int y
, int w
, int h
, int colour
)
213 if (w
== 1 && h
== 1) {
215 * Rectangle() appears to get uppity if asked to draw a 1x1
216 * rectangle, presumably on the grounds that that's beneath
217 * its dignity and you ought to be using SetPixel instead.
220 SetPixel(fe
->hdc_bm
, x
, y
, fe
->colours
[colour
]);
222 HBRUSH oldbrush
= SelectObject(fe
->hdc_bm
, fe
->brushes
[colour
]);
223 HPEN oldpen
= SelectObject(fe
->hdc_bm
, fe
->pens
[colour
]);
224 Rectangle(fe
->hdc_bm
, x
, y
, x
+w
, y
+h
);
225 SelectObject(fe
->hdc_bm
, oldbrush
);
226 SelectObject(fe
->hdc_bm
, oldpen
);
230 void draw_line(frontend
*fe
, int x1
, int y1
, int x2
, int y2
, int colour
)
232 HPEN oldpen
= SelectObject(fe
->hdc_bm
, fe
->pens
[colour
]);
233 MoveToEx(fe
->hdc_bm
, x1
, y1
, NULL
);
234 LineTo(fe
->hdc_bm
, x2
, y2
);
235 SetPixel(fe
->hdc_bm
, x2
, y2
, fe
->colours
[colour
]);
236 SelectObject(fe
->hdc_bm
, oldpen
);
239 void draw_polygon(frontend
*fe
, int *coords
, int npoints
,
240 int fill
, int colour
)
242 POINT
*pts
= snewn(npoints
+1, POINT
);
245 for (i
= 0; i
<= npoints
; i
++) {
246 int j
= (i
< npoints ? i
: 0);
247 pts
[i
].x
= coords
[j
*2];
248 pts
[i
].y
= coords
[j
*2+1];
252 HBRUSH oldbrush
= SelectObject(fe
->hdc_bm
, fe
->brushes
[colour
]);
253 HPEN oldpen
= SelectObject(fe
->hdc_bm
, fe
->pens
[colour
]);
254 Polygon(fe
->hdc_bm
, pts
, npoints
);
255 SelectObject(fe
->hdc_bm
, oldbrush
);
256 SelectObject(fe
->hdc_bm
, oldpen
);
258 HPEN oldpen
= SelectObject(fe
->hdc_bm
, fe
->pens
[colour
]);
259 Polyline(fe
->hdc_bm
, pts
, npoints
+1);
260 SelectObject(fe
->hdc_bm
, oldpen
);
266 void start_draw(frontend
*fe
)
269 hdc_win
= GetDC(fe
->hwnd
);
270 fe
->hdc_bm
= CreateCompatibleDC(hdc_win
);
271 fe
->prevbm
= SelectObject(fe
->hdc_bm
, fe
->bitmap
);
272 ReleaseDC(fe
->hwnd
, hdc_win
);
274 SetMapMode(fe
->hdc_bm
, MM_TEXT
);
277 void draw_update(frontend
*fe
, int x
, int y
, int w
, int h
)
286 InvalidateRect(fe
->hwnd
, &r
, FALSE
);
289 void end_draw(frontend
*fe
)
291 SelectObject(fe
->hdc_bm
, fe
->prevbm
);
292 DeleteDC(fe
->hdc_bm
);
294 DeleteObject(fe
->clip
);
299 void deactivate_timer(frontend
*fe
)
301 KillTimer(fe
->hwnd
, fe
->timer
);
305 void activate_timer(frontend
*fe
)
308 fe
->timer
= SetTimer(fe
->hwnd
, fe
->timer
, 20, NULL
);
309 fe
->timer_last_tickcount
= GetTickCount();
313 static frontend
*new_window(HINSTANCE inst
, char *game_id
, char **error
)
324 fe
->me
= midend_new(fe
, &t
, sizeof(t
));
327 *error
= midend_game_id(fe
->me
, game_id
, FALSE
);
336 midend_new_game(fe
->me
);
337 midend_size(fe
->me
, &x
, &y
);
342 fe
->nfonts
= fe
->fontsize
= 0;
348 colours
= midend_colours(fe
->me
, &ncolours
);
350 fe
->colours
= snewn(ncolours
, COLORREF
);
351 fe
->brushes
= snewn(ncolours
, HBRUSH
);
352 fe
->pens
= snewn(ncolours
, HPEN
);
354 for (i
= 0; i
< ncolours
; i
++) {
355 fe
->colours
[i
] = RGB(255 * colours
[i
*3+0],
356 255 * colours
[i
*3+1],
357 255 * colours
[i
*3+2]);
358 fe
->brushes
[i
] = CreateSolidBrush(fe
->colours
[i
]);
359 fe
->pens
[i
] = CreatePen(PS_SOLID
, 1, fe
->colours
[i
]);
366 AdjustWindowRectEx(&r
, WS_OVERLAPPEDWINDOW
&~
367 (WS_THICKFRAME
| WS_MAXIMIZEBOX
| WS_OVERLAPPED
),
370 fe
->hwnd
= CreateWindowEx(0, game_name
, game_name
,
371 WS_OVERLAPPEDWINDOW
&~
372 (WS_THICKFRAME
| WS_MAXIMIZEBOX
),
373 CW_USEDEFAULT
, CW_USEDEFAULT
,
374 r
.right
- r
.left
, r
.bottom
- r
.top
,
375 NULL
, NULL
, inst
, NULL
);
378 HMENU bar
= CreateMenu();
379 HMENU menu
= CreateMenu();
381 AppendMenu(bar
, MF_ENABLED
|MF_POPUP
, (UINT
)menu
, "Game");
382 AppendMenu(menu
, MF_ENABLED
, IDM_NEW
, "New");
383 AppendMenu(menu
, MF_ENABLED
, IDM_RESTART
, "Restart");
384 AppendMenu(menu
, MF_ENABLED
, IDM_SEED
, "Specific...");
386 if ((fe
->npresets
= midend_num_presets(fe
->me
)) > 0 ||
387 game_can_configure
) {
388 HMENU sub
= CreateMenu();
391 AppendMenu(bar
, MF_ENABLED
|MF_POPUP
, (UINT
)sub
, "Type");
393 fe
->presets
= snewn(fe
->npresets
, game_params
*);
395 for (i
= 0; i
< fe
->npresets
; i
++) {
398 midend_fetch_preset(fe
->me
, i
, &name
, &fe
->presets
[i
]);
401 * FIXME: we ought to go through and do something
402 * with ampersands here.
405 AppendMenu(sub
, MF_ENABLED
, IDM_PRESETS
+ 0x10 * i
, name
);
408 if (game_can_configure
) {
409 AppendMenu(sub
, MF_ENABLED
, IDM_CONFIG
, "Custom...");
413 AppendMenu(menu
, MF_SEPARATOR
, 0, 0);
414 AppendMenu(menu
, MF_ENABLED
, IDM_UNDO
, "Undo");
415 AppendMenu(menu
, MF_ENABLED
, IDM_REDO
, "Redo");
416 AppendMenu(menu
, MF_SEPARATOR
, 0, 0);
417 AppendMenu(menu
, MF_ENABLED
, IDM_QUIT
, "Exit");
418 SetMenu(fe
->hwnd
, bar
);
421 if (midend_wants_statusbar(fe
->me
)) {
422 fe
->statusbar
= CreateWindowEx(0, STATUSCLASSNAME
, "ooh",
423 WS_CHILD
| WS_VISIBLE
,
424 0, 0, 0, 0, /* status bar does these */
425 fe
->hwnd
, NULL
, inst
, NULL
);
426 GetWindowRect(fe
->statusbar
, &sr
);
427 SetWindowPos(fe
->hwnd
, NULL
, 0, 0,
428 r
.right
- r
.left
, r
.bottom
- r
.top
+ sr
.bottom
- sr
.top
,
429 SWP_NOMOVE
| SWP_NOZORDER
);
430 SetWindowPos(fe
->statusbar
, NULL
, 0, y
, x
, sr
.bottom
- sr
.top
,
433 fe
->statusbar
= NULL
;
436 hdc
= GetDC(fe
->hwnd
);
437 fe
->bitmap
= CreateCompatibleBitmap(hdc
, x
, y
);
438 ReleaseDC(fe
->hwnd
, hdc
);
440 SetWindowLong(fe
->hwnd
, GWL_USERDATA
, (LONG
)fe
);
442 ShowWindow(fe
->hwnd
, SW_NORMAL
);
443 SetForegroundWindow(fe
->hwnd
);
445 midend_redraw(fe
->me
);
450 static int CALLBACK
ConfigDlgProc(HWND hwnd
, UINT msg
,
451 WPARAM wParam
, LPARAM lParam
)
453 frontend
*fe
= (frontend
*)GetWindowLong(hwnd
, GWL_USERDATA
);
463 * OK and Cancel are special cases.
465 if ((HIWORD(wParam
) == BN_CLICKED
||
466 HIWORD(wParam
) == BN_DOUBLECLICKED
) &&
467 (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
)) {
468 if (LOWORD(wParam
) == IDOK
) {
469 char *err
= midend_set_config(fe
->me
, fe
->cfg_which
, fe
->cfg
);
472 MessageBox(hwnd
, err
, "Validation error",
473 MB_ICONERROR
| MB_OK
);
484 * First find the control whose id this is.
486 for (i
= fe
->cfg
, j
= fe
->cfgaux
; i
->type
!= C_END
; i
++, j
++) {
487 if (j
->ctlid
== LOWORD(wParam
))
490 if (i
->type
== C_END
)
491 return 0; /* not our problem */
493 if (i
->type
== C_STRING
&& HIWORD(wParam
) == EN_CHANGE
) {
495 GetDlgItemText(fe
->cfgbox
, j
->ctlid
, buffer
, lenof(buffer
));
496 buffer
[lenof(buffer
)-1] = '\0';
498 i
->sval
= dupstr(buffer
);
499 } else if (i
->type
== C_BOOLEAN
&&
500 (HIWORD(wParam
) == BN_CLICKED
||
501 HIWORD(wParam
) == BN_DOUBLECLICKED
)) {
502 i
->ival
= IsDlgButtonChecked(fe
->cfgbox
, j
->ctlid
);
503 } else if (i
->type
== C_CHOICES
&&
504 HIWORD(wParam
) == CBN_SELCHANGE
) {
505 i
->ival
= SendDlgItemMessage(fe
->cfgbox
, j
->ctlid
,
519 HWND
mkctrl(frontend
*fe
, int x1
, int x2
, int y1
, int y2
,
520 char *wclass
, int wstyle
,
521 int exstyle
, char *wtext
, int wid
)
524 ret
= CreateWindowEx(exstyle
, wclass
, wtext
,
525 wstyle
| WS_CHILD
| WS_VISIBLE
, x1
, y1
, x2
-x1
, y2
-y1
,
526 fe
->cfgbox
, (HMENU
) wid
, fe
->inst
, NULL
);
527 SendMessage(ret
, WM_SETFONT
, (WPARAM
)fe
->cfgfont
, MAKELPARAM(TRUE
, 0));
531 static int get_config(frontend
*fe
, int which
)
544 int winwidth
, winheight
, col1l
, col1r
, col2l
, col2r
, y
;
545 int height
, width
, maxlabel
, maxcheckbox
;
547 wc
.style
= CS_DBLCLKS
| CS_SAVEBITS
| CS_BYTEALIGNWINDOW
;
548 wc
.lpfnWndProc
= DefDlgProc
;
550 wc
.cbWndExtra
= DLGWINDOWEXTRA
+ 8;
551 wc
.hInstance
= fe
->inst
;
553 wc
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
554 wc
.hbrBackground
= (HBRUSH
) (COLOR_BACKGROUND
+1);
555 wc
.lpszMenuName
= NULL
;
556 wc
.lpszClassName
= "GameConfigBox";
559 hdc
= GetDC(fe
->hwnd
);
560 SetMapMode(hdc
, MM_TEXT
);
562 fe
->cfg_done
= FALSE
;
564 fe
->cfgfont
= CreateFont(-MulDiv(8, GetDeviceCaps(hdc
, LOGPIXELSY
), 72),
566 FALSE
, FALSE
, FALSE
, DEFAULT_CHARSET
,
567 OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
,
572 oldfont
= SelectObject(hdc
, fe
->cfgfont
);
573 if (GetTextMetrics(hdc
, &tm
)) {
574 height
= tm
.tmAscent
+ tm
.tmDescent
;
575 width
= tm
.tmAveCharWidth
;
580 fe
->cfg
= midend_get_config(fe
->me
, which
, &title
);
581 fe
->cfg_which
= which
;
584 * Figure out the layout of the config box by measuring the
585 * length of each piece of text.
587 maxlabel
= maxcheckbox
= 0;
588 winheight
= height
/2;
590 for (i
= fe
->cfg
; i
->type
!= C_END
; i
++) {
595 * Both these control types have a label filling only
596 * the left-hand column of the box.
598 if (GetTextExtentPoint32(hdc
, i
->name
, strlen(i
->name
), &size
) &&
601 winheight
+= height
* 3 / 2 + (height
/ 2);
606 * Checkboxes take up the whole of the box width.
608 if (GetTextExtentPoint32(hdc
, i
->name
, strlen(i
->name
), &size
) &&
609 maxcheckbox
< size
.cx
)
610 maxcheckbox
= size
.cx
;
611 winheight
+= height
+ (height
/ 2);
616 winheight
+= height
+ height
* 7 / 4; /* OK / Cancel buttons */
619 col1r
= col1l
+ maxlabel
;
620 col2l
= col1r
+ 2*width
;
621 col2r
= col2l
+ 30*width
;
622 if (col2r
< col1l
+2*height
+maxcheckbox
)
623 col2r
= col1l
+2*height
+maxcheckbox
;
624 winwidth
= col2r
+ 2*width
;
626 ReleaseDC(fe
->hwnd
, hdc
);
629 * Create the dialog, now that we know its size.
636 r
.bottom
= winheight
;
638 AdjustWindowRectEx(&r
, (WS_OVERLAPPEDWINDOW
/*|
639 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
640 WS_CAPTION | WS_SYSMENU*/) &~
641 (WS_MAXIMIZEBOX
| WS_OVERLAPPED
),
645 * Centre the dialog on its parent window.
649 GetWindowRect(fe
->hwnd
, &r2
);
650 r
.left
= (r2
.left
+ r2
.right
- r
.right
) / 2;
651 r
.top
= (r2
.top
+ r2
.bottom
- r
.bottom
) / 2;
655 fe
->cfgbox
= CreateWindowEx(0, wc
.lpszClassName
, title
,
656 DS_MODALFRAME
| WS_POPUP
| WS_VISIBLE
|
657 WS_CAPTION
| WS_SYSMENU
,
659 r
.right
-r
.left
, r
.bottom
-r
.top
,
660 fe
->hwnd
, NULL
, fe
->inst
, NULL
);
664 SendMessage(fe
->cfgbox
, WM_SETFONT
, (WPARAM
)fe
->cfgfont
, FALSE
);
666 SetWindowLong(fe
->cfgbox
, GWL_USERDATA
, (LONG
)fe
);
667 SetWindowLong(fe
->cfgbox
, DWL_DLGPROC
, (LONG
)ConfigDlgProc
);
670 * Count the controls so we can allocate cfgaux.
672 for (nctrls
= 0, i
= fe
->cfg
; i
->type
!= C_END
; i
++)
674 fe
->cfgaux
= snewn(nctrls
, struct cfg_aux
);
678 for (i
= fe
->cfg
, j
= fe
->cfgaux
; i
->type
!= C_END
; i
++, j
++) {
682 * Edit box with a label beside it.
684 mkctrl(fe
, col1l
, col1r
, y
+height
*1/8, y
+height
*9/8,
685 "Static", 0, 0, i
->name
, id
++);
686 ctl
= mkctrl(fe
, col2l
, col2r
, y
, y
+height
*3/2,
687 "EDIT", WS_TABSTOP
| ES_AUTOHSCROLL
,
688 WS_EX_CLIENTEDGE
, "", (j
->ctlid
= id
++));
689 SetWindowText(ctl
, i
->sval
);
697 mkctrl(fe
, col1l
, col2r
, y
, y
+height
, "BUTTON",
698 BS_NOTIFY
| BS_AUTOCHECKBOX
| WS_TABSTOP
,
699 0, i
->name
, (j
->ctlid
= id
++));
700 CheckDlgButton(fe
->cfgbox
, j
->ctlid
, (i
->ival
!= 0));
706 * Drop-down list with a label beside it.
708 mkctrl(fe
, col1l
, col1r
, y
+height
*1/8, y
+height
*9/8,
709 "STATIC", 0, 0, i
->name
, id
++);
710 ctl
= mkctrl(fe
, col2l
, col2r
, y
, y
+height
*41/2,
711 "COMBOBOX", WS_TABSTOP
|
712 CBS_DROPDOWNLIST
| CBS_HASSTRINGS
,
713 WS_EX_CLIENTEDGE
, "", (j
->ctlid
= id
++));
715 char c
, *p
, *q
, *str
;
717 SendMessage(ctl
, CB_RESETCONTENT
, 0, 0);
722 while (*q
&& *q
!= c
) q
++;
723 str
= snewn(q
-p
+1, char);
724 strncpy(str
, p
, q
-p
);
726 SendMessage(ctl
, CB_ADDSTRING
, 0, (LPARAM
)str
);
733 SendMessage(ctl
, CB_SETCURSEL
, i
->ival
, 0);
739 assert(y
< winheight
);
743 y
+= height
/2; /* extra space before OK and Cancel */
744 mkctrl(fe
, col1l
, (col1l
+col2r
)/2-width
, y
, y
+height
*7/4, "BUTTON",
745 BS_PUSHBUTTON
| BS_NOTIFY
| WS_TABSTOP
| BS_DEFPUSHBUTTON
, 0,
747 mkctrl(fe
, (col1l
+col2r
)/2+width
, col2r
, y
, y
+height
*7/4, "BUTTON",
748 BS_PUSHBUTTON
| BS_NOTIFY
| WS_TABSTOP
, 0, "Cancel", IDCANCEL
);
750 SendMessage(fe
->cfgbox
, WM_INITDIALOG
, 0, 0);
752 EnableWindow(fe
->hwnd
, FALSE
);
753 ShowWindow(fe
->cfgbox
, SW_NORMAL
);
754 while ((gm
=GetMessage(&msg
, NULL
, 0, 0)) > 0) {
755 if (!IsDialogMessage(fe
->cfgbox
, &msg
))
756 DispatchMessage(&msg
);
760 EnableWindow(fe
->hwnd
, TRUE
);
761 SetForegroundWindow(fe
->hwnd
);
762 DestroyWindow(fe
->cfgbox
);
763 DeleteObject(fe
->cfgfont
);
768 return (fe
->cfg_done
== 2);
771 static void new_game_type(frontend
*fe
)
777 midend_new_game(fe
->me
);
778 midend_size(fe
->me
, &x
, &y
);
783 AdjustWindowRectEx(&r
, WS_OVERLAPPEDWINDOW
&~
784 (WS_THICKFRAME
| WS_MAXIMIZEBOX
|
788 if (fe
->statusbar
!= NULL
) {
789 GetWindowRect(fe
->statusbar
, &sr
);
791 sr
.left
= sr
.right
= sr
.top
= sr
.bottom
= 0;
793 SetWindowPos(fe
->hwnd
, NULL
, 0, 0,
795 r
.bottom
- r
.top
+ sr
.bottom
- sr
.top
,
796 SWP_NOMOVE
| SWP_NOZORDER
);
797 if (fe
->statusbar
!= NULL
)
798 SetWindowPos(fe
->statusbar
, NULL
, 0, y
, x
,
799 sr
.bottom
- sr
.top
, SWP_NOZORDER
);
801 DeleteObject(fe
->bitmap
);
803 hdc
= GetDC(fe
->hwnd
);
804 fe
->bitmap
= CreateCompatibleBitmap(hdc
, x
, y
);
805 ReleaseDC(fe
->hwnd
, hdc
);
807 midend_redraw(fe
->me
);
810 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
811 WPARAM wParam
, LPARAM lParam
)
813 frontend
*fe
= (frontend
*)GetWindowLong(hwnd
, GWL_USERDATA
);
820 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
822 if (!midend_process_key(fe
->me
, 0, 0, 'n'))
826 if (!midend_process_key(fe
->me
, 0, 0, 'r'))
830 if (!midend_process_key(fe
->me
, 0, 0, 'u'))
834 if (!midend_process_key(fe
->me
, 0, 0, '\x12'))
838 if (!midend_process_key(fe
->me
, 0, 0, 'q'))
842 if (get_config(fe
, CFG_SETTINGS
))
846 if (get_config(fe
, CFG_SEED
))
851 int p
= ((wParam
&~ 0xF) - IDM_PRESETS
) / 0x10;
853 if (p
>= 0 && p
< fe
->npresets
) {
854 midend_set_params(fe
->me
, fe
->presets
[p
]);
870 hdc
= BeginPaint(hwnd
, &p
);
871 hdc2
= CreateCompatibleDC(hdc
);
872 prevbm
= SelectObject(hdc2
, fe
->bitmap
);
874 p
.rcPaint
.left
, p
.rcPaint
.top
,
875 p
.rcPaint
.right
- p
.rcPaint
.left
,
876 p
.rcPaint
.bottom
- p
.rcPaint
.top
,
878 p
.rcPaint
.left
, p
.rcPaint
.top
,
880 SelectObject(hdc2
, prevbm
);
890 case VK_LEFT
: key
= CURSOR_LEFT
; break;
891 case VK_RIGHT
: key
= CURSOR_RIGHT
; break;
892 case VK_UP
: key
= CURSOR_UP
; break;
893 case VK_DOWN
: key
= CURSOR_DOWN
; break;
895 * Diagonal keys on the numeric keypad.
898 if (!(lParam
& 0x01000000)) key
= CURSOR_UP_RIGHT
;
901 if (!(lParam
& 0x01000000)) key
= CURSOR_DOWN_RIGHT
;
904 if (!(lParam
& 0x01000000)) key
= CURSOR_UP_LEFT
;
907 if (!(lParam
& 0x01000000)) key
= CURSOR_DOWN_LEFT
;
910 * Numeric keypad keys with Num Lock on.
912 case VK_NUMPAD4
: key
= CURSOR_LEFT
; break;
913 case VK_NUMPAD6
: key
= CURSOR_RIGHT
; break;
914 case VK_NUMPAD8
: key
= CURSOR_UP
; break;
915 case VK_NUMPAD2
: key
= CURSOR_DOWN
; break;
916 case VK_NUMPAD9
: key
= CURSOR_UP_RIGHT
; break;
917 case VK_NUMPAD3
: key
= CURSOR_DOWN_RIGHT
; break;
918 case VK_NUMPAD7
: key
= CURSOR_UP_LEFT
; break;
919 case VK_NUMPAD1
: key
= CURSOR_DOWN_LEFT
; break;
923 if (!midend_process_key(fe
->me
, 0, 0, key
))
928 m
.message
= WM_KEYDOWN
;
930 m
.lParam
= lParam
& 0xdfff;
931 TranslateMessage(&m
);
942 * Shift-clicks count as middle-clicks, since otherwise
943 * two-button Windows users won't have any kind of
944 * middle click to use.
946 if (message
== WM_MBUTTONDOWN
|| (wParam
& MK_SHIFT
))
947 button
= MIDDLE_BUTTON
;
948 else if (message
== WM_LBUTTONDOWN
)
949 button
= LEFT_BUTTON
;
951 button
= RIGHT_BUTTON
;
953 if (!midend_process_key(fe
->me
, LOWORD(lParam
),
954 HIWORD(lParam
), button
))
967 * Shift-clicks count as middle-clicks, since otherwise
968 * two-button Windows users won't have any kind of
969 * middle click to use.
971 if (message
== WM_MBUTTONUP
|| (wParam
& MK_SHIFT
))
972 button
= MIDDLE_RELEASE
;
973 else if (message
== WM_LBUTTONUP
)
974 button
= LEFT_RELEASE
;
976 button
= RIGHT_RELEASE
;
978 if (!midend_process_key(fe
->me
, LOWORD(lParam
),
979 HIWORD(lParam
), button
))
989 if (wParam
& (MK_MBUTTON
| MK_SHIFT
))
990 button
= MIDDLE_DRAG
;
991 else if (wParam
& MK_LBUTTON
)
996 if (!midend_process_key(fe
->me
, LOWORD(lParam
),
997 HIWORD(lParam
), button
))
1002 if (!midend_process_key(fe
->me
, 0, 0, (unsigned char)wParam
))
1007 DWORD now
= GetTickCount();
1008 float elapsed
= (float) (now
- fe
->timer_last_tickcount
) * 0.001F
;
1009 midend_timer(fe
->me
, elapsed
);
1010 fe
->timer_last_tickcount
= now
;
1015 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1018 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1023 InitCommonControls();
1029 wndclass
.lpfnWndProc
= WndProc
;
1030 wndclass
.cbClsExtra
= 0;
1031 wndclass
.cbWndExtra
= 0;
1032 wndclass
.hInstance
= inst
;
1033 wndclass
.hIcon
= LoadIcon(inst
, IDI_APPLICATION
);
1034 wndclass
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
1035 wndclass
.hbrBackground
= NULL
;
1036 wndclass
.lpszMenuName
= NULL
;
1037 wndclass
.lpszClassName
= game_name
;
1039 RegisterClass(&wndclass
);
1042 while (*cmdline
&& isspace(*cmdline
))
1045 if (!new_window(inst
, *cmdline ? cmdline
: NULL
, &error
)) {
1047 sprintf(buf
, "%.100s Error", game_name
);
1048 MessageBox(NULL
, error
, buf
, MB_OK
|MB_ICONERROR
);
1052 while (GetMessage(&msg
, NULL
, 0, 0)) {
1053 DispatchMessage(&msg
);