2 * windows.c: Windows front end for my puzzle collection.
14 #define IDM_NEW 0x0010
15 #define IDM_RESTART 0x0020
16 #define IDM_UNDO 0x0030
17 #define IDM_REDO 0x0040
18 #define IDM_QUIT 0x0050
19 #define IDM_PRESETS 0x0100
22 static FILE *debug_fp
= NULL
;
23 static HANDLE debug_hdl
= INVALID_HANDLE_VALUE
;
24 static int debug_got_console
= 0;
30 if (!debug_got_console
) {
32 debug_got_console
= 1;
33 debug_hdl
= GetStdHandle(STD_OUTPUT_HANDLE
);
37 debug_fp
= fopen("debug.log", "w");
40 if (debug_hdl
!= INVALID_HANDLE_VALUE
) {
41 WriteFile(debug_hdl
, buf
, strlen(buf
), &dw
, NULL
);
47 void debug_printf(char *fmt
, ...)
53 vsprintf(buf
, fmt
, ap
);
58 #define debug(x) (debug_printf x)
69 HBITMAP bitmap
, prevbm
;
76 game_params
**presets
;
79 void fatal(char *fmt
, ...)
85 vsprintf(buf
, fmt
, ap
);
88 MessageBox(NULL
, buf
, "Fatal error", MB_ICONEXCLAMATION
| MB_OK
);
93 void frontend_default_colour(frontend
*fe
, float *output
)
95 DWORD c
= GetSysColor(COLOR_MENU
); /* ick */
97 output
[0] = (float)(GetRValue(c
) / 255.0);
98 output
[1] = (float)(GetGValue(c
) / 255.0);
99 output
[2] = (float)(GetBValue(c
) / 255.0);
102 void draw_rect(frontend
*fe
, int x
, int y
, int w
, int h
, int colour
)
104 if (w
== 1 && h
== 1) {
106 * Rectangle() appears to get uppity if asked to draw a 1x1
107 * rectangle, presumably on the grounds that that's beneath
108 * its dignity and you ought to be using SetPixel instead.
111 SetPixel(fe
->hdc_bm
, x
, y
, fe
->colours
[colour
]);
113 HBRUSH oldbrush
= SelectObject(fe
->hdc_bm
, fe
->brushes
[colour
]);
114 HPEN oldpen
= SelectObject(fe
->hdc_bm
, fe
->pens
[colour
]);
115 Rectangle(fe
->hdc_bm
, x
, y
, x
+w
, y
+h
);
116 SelectObject(fe
->hdc_bm
, oldbrush
);
117 SelectObject(fe
->hdc_bm
, oldpen
);
121 void draw_line(frontend
*fe
, int x1
, int y1
, int x2
, int y2
, int colour
)
123 HPEN oldpen
= SelectObject(fe
->hdc_bm
, fe
->pens
[colour
]);
124 MoveToEx(fe
->hdc_bm
, x1
, y1
, NULL
);
125 LineTo(fe
->hdc_bm
, x2
, y2
);
126 SetPixel(fe
->hdc_bm
, x2
, y2
, fe
->colours
[colour
]);
127 SelectObject(fe
->hdc_bm
, oldpen
);
130 void draw_polygon(frontend
*fe
, int *coords
, int npoints
,
131 int fill
, int colour
)
133 POINT
*pts
= snewn(npoints
+1, POINT
);
136 for (i
= 0; i
<= npoints
; i
++) {
137 int j
= (i
< npoints ? i
: 0);
138 pts
[i
].x
= coords
[j
*2];
139 pts
[i
].y
= coords
[j
*2+1];
143 HBRUSH oldbrush
= SelectObject(fe
->hdc_bm
, fe
->brushes
[colour
]);
144 HPEN oldpen
= SelectObject(fe
->hdc_bm
, fe
->pens
[colour
]);
145 Polygon(fe
->hdc_bm
, pts
, npoints
);
146 SelectObject(fe
->hdc_bm
, oldbrush
);
147 SelectObject(fe
->hdc_bm
, oldpen
);
149 HPEN oldpen
= SelectObject(fe
->hdc_bm
, fe
->pens
[colour
]);
150 Polyline(fe
->hdc_bm
, pts
, npoints
+1);
151 SelectObject(fe
->hdc_bm
, oldpen
);
157 void start_draw(frontend
*fe
)
160 hdc_win
= GetDC(fe
->hwnd
);
161 fe
->hdc_bm
= CreateCompatibleDC(hdc_win
);
162 fe
->prevbm
= SelectObject(fe
->hdc_bm
, fe
->bitmap
);
163 ReleaseDC(fe
->hwnd
, hdc_win
);
166 void draw_update(frontend
*fe
, int x
, int y
, int w
, int h
)
175 InvalidateRect(fe
->hwnd
, &r
, FALSE
);
178 void end_draw(frontend
*fe
)
180 SelectObject(fe
->hdc_bm
, fe
->prevbm
);
181 DeleteDC(fe
->hdc_bm
);
184 void deactivate_timer(frontend
*fe
)
186 KillTimer(fe
->hwnd
, fe
->timer
);
190 void activate_timer(frontend
*fe
)
192 fe
->timer
= SetTimer(fe
->hwnd
, fe
->timer
, 20, NULL
);
195 static frontend
*new_window(HINSTANCE inst
)
203 fe
->me
= midend_new(fe
);
204 midend_new_game(fe
->me
, NULL
);
205 midend_size(fe
->me
, &x
, &y
);
213 colours
= midend_colours(fe
->me
, &ncolours
);
215 fe
->colours
= snewn(ncolours
, COLORREF
);
216 fe
->brushes
= snewn(ncolours
, HBRUSH
);
217 fe
->pens
= snewn(ncolours
, HPEN
);
219 for (i
= 0; i
< ncolours
; i
++) {
220 fe
->colours
[i
] = RGB(255 * colours
[i
*3+0],
221 255 * colours
[i
*3+1],
222 255 * colours
[i
*3+2]);
223 fe
->brushes
[i
] = CreateSolidBrush(fe
->colours
[i
]);
225 MessageBox(fe
->hwnd
, "ooh", "eck", MB_OK
);
226 fe
->pens
[i
] = CreatePen(PS_SOLID
, 1, fe
->colours
[i
]);
233 AdjustWindowRectEx(&r
, WS_OVERLAPPEDWINDOW
&~
234 (WS_THICKFRAME
| WS_MAXIMIZEBOX
| WS_OVERLAPPED
),
237 fe
->hwnd
= CreateWindowEx(0, "puzzle", "puzzle",
238 WS_OVERLAPPEDWINDOW
&~
239 (WS_THICKFRAME
| WS_MAXIMIZEBOX
),
240 CW_USEDEFAULT
, CW_USEDEFAULT
,
241 r
.right
- r
.left
, r
.bottom
- r
.top
,
242 NULL
, NULL
, inst
, NULL
);
245 HMENU bar
= CreateMenu();
246 HMENU menu
= CreateMenu();
248 AppendMenu(bar
, MF_ENABLED
|MF_POPUP
, (UINT
)menu
, "Game");
249 AppendMenu(menu
, MF_ENABLED
, IDM_NEW
, "New");
250 AppendMenu(menu
, MF_ENABLED
, IDM_RESTART
, "Restart");
252 if ((fe
->npresets
= midend_num_presets(fe
->me
)) > 0) {
253 HMENU sub
= CreateMenu();
256 AppendMenu(menu
, MF_ENABLED
|MF_POPUP
, (UINT
)sub
, "Type");
258 fe
->presets
= snewn(fe
->npresets
, game_params
*);
260 for (i
= 0; i
< fe
->npresets
; i
++) {
263 midend_fetch_preset(fe
->me
, i
, &name
, &fe
->presets
[i
]);
266 * FIXME: we ought to go through and do something
267 * with ampersands here.
270 AppendMenu(sub
, MF_ENABLED
, IDM_PRESETS
+ 0x10 * i
, name
);
274 AppendMenu(menu
, MF_SEPARATOR
, 0, 0);
275 AppendMenu(menu
, MF_ENABLED
, IDM_UNDO
, "Undo");
276 AppendMenu(menu
, MF_ENABLED
, IDM_REDO
, "Redo");
277 AppendMenu(menu
, MF_SEPARATOR
, 0, 0);
278 AppendMenu(menu
, MF_ENABLED
, IDM_QUIT
, "Exit");
279 SetMenu(fe
->hwnd
, bar
);
282 hdc
= GetDC(fe
->hwnd
);
283 fe
->bitmap
= CreateCompatibleBitmap(hdc
, x
, y
);
284 ReleaseDC(fe
->hwnd
, hdc
);
286 SetWindowLong(fe
->hwnd
, GWL_USERDATA
, (LONG
)fe
);
288 ShowWindow(fe
->hwnd
, SW_NORMAL
);
289 SetForegroundWindow(fe
->hwnd
);
291 midend_redraw(fe
->me
);
296 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
297 WPARAM wParam
, LPARAM lParam
)
299 frontend
*fe
= (frontend
*)GetWindowLong(hwnd
, GWL_USERDATA
);
306 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
308 if (!midend_process_key(fe
->me
, 0, 0, 'n'))
312 if (!midend_process_key(fe
->me
, 0, 0, 'r'))
316 if (!midend_process_key(fe
->me
, 0, 0, 'u'))
320 if (!midend_process_key(fe
->me
, 0, 0, '\x12'))
324 if (!midend_process_key(fe
->me
, 0, 0, 'q'))
329 int p
= ((wParam
&~ 0xF) - IDM_PRESETS
) / 0x10;
331 if (p
>= 0 && p
< fe
->npresets
) {
336 midend_set_params(fe
->me
, fe
->presets
[p
]);
337 midend_new_game(fe
->me
, NULL
);
338 midend_size(fe
->me
, &x
, &y
);
343 AdjustWindowRectEx(&r
, WS_OVERLAPPEDWINDOW
&~
344 (WS_THICKFRAME
| WS_MAXIMIZEBOX
|
348 SetWindowPos(fe
->hwnd
, NULL
, 0, 0,
349 r
.right
- r
.left
, r
.bottom
- r
.top
,
350 SWP_NOMOVE
| SWP_NOZORDER
);
352 DeleteObject(fe
->bitmap
);
354 hdc
= GetDC(fe
->hwnd
);
355 fe
->bitmap
= CreateCompatibleBitmap(hdc
, x
, y
);
356 ReleaseDC(fe
->hwnd
, hdc
);
358 midend_redraw(fe
->me
);
373 hdc
= BeginPaint(hwnd
, &p
);
374 hdc2
= CreateCompatibleDC(hdc
);
375 prevbm
= SelectObject(hdc2
, fe
->bitmap
);
377 p
.rcPaint
.left
, p
.rcPaint
.top
,
378 p
.rcPaint
.right
- p
.rcPaint
.left
,
379 p
.rcPaint
.bottom
- p
.rcPaint
.top
,
381 p
.rcPaint
.left
, p
.rcPaint
.top
,
383 SelectObject(hdc2
, prevbm
);
393 case VK_LEFT
: key
= CURSOR_LEFT
; break;
394 case VK_RIGHT
: key
= CURSOR_RIGHT
; break;
395 case VK_UP
: key
= CURSOR_UP
; break;
396 case VK_DOWN
: key
= CURSOR_DOWN
; break;
398 * Diagonal keys on the numeric keypad.
401 if (!(lParam
& 0x01000000)) key
= CURSOR_UP_RIGHT
;
404 if (!(lParam
& 0x01000000)) key
= CURSOR_DOWN_RIGHT
;
407 if (!(lParam
& 0x01000000)) key
= CURSOR_UP_LEFT
;
410 if (!(lParam
& 0x01000000)) key
= CURSOR_DOWN_LEFT
;
413 * Numeric keypad keys with Num Lock on.
415 case VK_NUMPAD4
: key
= CURSOR_LEFT
; break;
416 case VK_NUMPAD6
: key
= CURSOR_RIGHT
; break;
417 case VK_NUMPAD8
: key
= CURSOR_UP
; break;
418 case VK_NUMPAD2
: key
= CURSOR_DOWN
; break;
419 case VK_NUMPAD9
: key
= CURSOR_UP_RIGHT
; break;
420 case VK_NUMPAD3
: key
= CURSOR_DOWN_RIGHT
; break;
421 case VK_NUMPAD7
: key
= CURSOR_UP_LEFT
; break;
422 case VK_NUMPAD1
: key
= CURSOR_DOWN_LEFT
; break;
426 if (!midend_process_key(fe
->me
, 0, 0, key
))
431 m
.message
= WM_KEYDOWN
;
433 m
.lParam
= lParam
& 0xdfff;
434 TranslateMessage(&m
);
445 * Shift-clicks count as middle-clicks, since otherwise
446 * two-button Windows users won't have any kind of
447 * middle click to use.
449 if (message
== WM_MBUTTONDOWN
|| (wParam
& MK_SHIFT
))
450 button
= MIDDLE_BUTTON
;
451 else if (message
== WM_LBUTTONDOWN
)
452 button
= LEFT_BUTTON
;
454 button
= RIGHT_BUTTON
;
456 if (!midend_process_key(fe
->me
, LOWORD(lParam
),
457 HIWORD(lParam
), button
))
462 if (!midend_process_key(fe
->me
, 0, 0, (unsigned char)wParam
))
467 midend_timer(fe
->me
, (float)0.02);
471 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
474 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
484 wndclass
.lpfnWndProc
= WndProc
;
485 wndclass
.cbClsExtra
= 0;
486 wndclass
.cbWndExtra
= 0;
487 wndclass
.hInstance
= inst
;
488 wndclass
.hIcon
= LoadIcon(inst
, IDI_APPLICATION
);
489 wndclass
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
490 wndclass
.hbrBackground
= NULL
;
491 wndclass
.lpszMenuName
= NULL
;
492 wndclass
.lpszClassName
= "puzzle";
494 RegisterClass(&wndclass
);
499 while (GetMessage(&msg
, NULL
, 0, 0)) {
500 DispatchMessage(&msg
);