720a8fb7 |
1 | /* |
2 | * windows.c: Windows front end for my puzzle collection. |
3 | */ |
4e7ef6e6 |
4 | |
5 | #include <windows.h> |
6 | |
7 | #include <stdio.h> |
8 | #include <stdarg.h> |
9 | #include <stdlib.h> |
10 | #include <time.h> |
11 | |
12 | #include "puzzles.h" |
13 | |
14 | struct frontend { |
15 | midend_data *me; |
16 | HWND hwnd; |
17 | HBITMAP bitmap, prevbm; |
18 | HDC hdc_bm; |
19 | COLORREF *colours; |
20 | HBRUSH *brushes; |
21 | HPEN *pens; |
22 | UINT timer; |
23 | }; |
24 | |
25 | void fatal(char *fmt, ...) |
26 | { |
27 | char buf[2048]; |
28 | va_list ap; |
29 | |
30 | va_start(ap, fmt); |
31 | vsprintf(buf, fmt, ap); |
32 | va_end(ap); |
33 | |
34 | MessageBox(NULL, buf, "Fatal error", MB_ICONEXCLAMATION | MB_OK); |
35 | |
36 | exit(1); |
37 | } |
38 | |
39 | void frontend_default_colour(frontend *fe, float *output) |
40 | { |
41 | DWORD c = GetSysColor(COLOR_MENU); /* ick */ |
42 | |
43 | output[0] = (float)(GetRValue(c) / 255.0); |
44 | output[1] = (float)(GetGValue(c) / 255.0); |
45 | output[2] = (float)(GetBValue(c) / 255.0); |
46 | } |
47 | |
48 | void draw_rect(frontend *fe, int x, int y, int w, int h, int colour) |
49 | { |
fbdd7610 |
50 | if (w == 1 && h == 1) { |
51 | /* |
52 | * Rectangle() appears to get uppity if asked to draw a 1x1 |
53 | * rectangle, presumably on the grounds that that's beneath |
54 | * its dignity and you ought to be using SetPixel instead. |
55 | * So I will. |
56 | */ |
57 | SetPixel(fe->hdc_bm, x, y, fe->colours[colour]); |
58 | } else { |
59 | HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]); |
60 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
61 | Rectangle(fe->hdc_bm, x, y, x+w, y+h); |
62 | SelectObject(fe->hdc_bm, oldbrush); |
63 | SelectObject(fe->hdc_bm, oldpen); |
64 | } |
4e7ef6e6 |
65 | } |
66 | |
67 | void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) |
68 | { |
69 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
70 | MoveToEx(fe->hdc_bm, x1, y1, NULL); |
71 | LineTo(fe->hdc_bm, x2, y2); |
72 | SetPixel(fe->hdc_bm, x2, y2, fe->colours[colour]); |
73 | SelectObject(fe->hdc_bm, oldpen); |
74 | } |
75 | |
76 | void draw_polygon(frontend *fe, int *coords, int npoints, |
77 | int fill, int colour) |
78 | { |
79 | POINT *pts = snewn(npoints+1, POINT); |
80 | int i; |
81 | |
82 | for (i = 0; i <= npoints; i++) { |
83 | int j = (i < npoints ? i : 0); |
84 | pts[i].x = coords[j*2]; |
85 | pts[i].y = coords[j*2+1]; |
86 | } |
87 | |
88 | if (fill) { |
89 | HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]); |
90 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
91 | Polygon(fe->hdc_bm, pts, npoints); |
92 | SelectObject(fe->hdc_bm, oldbrush); |
93 | SelectObject(fe->hdc_bm, oldpen); |
94 | } else { |
95 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
96 | Polyline(fe->hdc_bm, pts, npoints+1); |
97 | SelectObject(fe->hdc_bm, oldpen); |
98 | } |
99 | |
100 | sfree(pts); |
101 | } |
102 | |
103 | void start_draw(frontend *fe) |
104 | { |
105 | HDC hdc_win; |
106 | hdc_win = GetDC(fe->hwnd); |
107 | fe->hdc_bm = CreateCompatibleDC(hdc_win); |
108 | fe->prevbm = SelectObject(fe->hdc_bm, fe->bitmap); |
109 | ReleaseDC(fe->hwnd, hdc_win); |
110 | } |
111 | |
112 | void draw_update(frontend *fe, int x, int y, int w, int h) |
113 | { |
114 | RECT r; |
115 | |
116 | r.left = x; |
117 | r.top = y; |
118 | r.right = x + w; |
119 | r.bottom = y + h; |
120 | |
121 | InvalidateRect(fe->hwnd, &r, FALSE); |
122 | } |
123 | |
124 | void end_draw(frontend *fe) |
125 | { |
126 | SelectObject(fe->hdc_bm, fe->prevbm); |
127 | DeleteDC(fe->hdc_bm); |
128 | } |
129 | |
130 | void deactivate_timer(frontend *fe) |
131 | { |
132 | KillTimer(fe->hwnd, fe->timer); |
133 | fe->timer = 0; |
134 | } |
135 | |
136 | void activate_timer(frontend *fe) |
137 | { |
138 | fe->timer = SetTimer(fe->hwnd, fe->timer, 20, NULL); |
139 | } |
140 | |
141 | static frontend *new_window(HINSTANCE inst) |
142 | { |
143 | frontend *fe; |
144 | int x, y; |
145 | RECT r; |
146 | HDC hdc; |
147 | |
148 | fe = snew(frontend); |
149 | fe->me = midend_new(fe); |
150 | midend_new_game(fe->me, NULL); |
151 | midend_size(fe->me, &x, &y); |
152 | |
153 | fe->timer = 0; |
154 | |
155 | { |
156 | int i, ncolours; |
157 | float *colours; |
158 | |
159 | colours = midend_colours(fe->me, &ncolours); |
160 | |
161 | fe->colours = snewn(ncolours, COLORREF); |
162 | fe->brushes = snewn(ncolours, HBRUSH); |
163 | fe->pens = snewn(ncolours, HPEN); |
164 | |
165 | for (i = 0; i < ncolours; i++) { |
166 | fe->colours[i] = RGB(255 * colours[i*3+0], |
167 | 255 * colours[i*3+1], |
168 | 255 * colours[i*3+2]); |
169 | fe->brushes[i] = CreateSolidBrush(fe->colours[i]); |
170 | if (!fe->brushes[i]) |
171 | MessageBox(fe->hwnd, "ooh", "eck", MB_OK); |
172 | fe->pens[i] = CreatePen(PS_SOLID, 1, fe->colours[i]); |
173 | } |
174 | } |
175 | |
176 | r.left = r.top = 0; |
177 | r.right = x; |
178 | r.bottom = y; |
179 | AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ |
180 | (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED), |
181 | FALSE, 0); |
182 | |
183 | fe->hwnd = CreateWindowEx(0, "puzzle", "puzzle", |
184 | WS_OVERLAPPEDWINDOW &~ |
185 | (WS_THICKFRAME | WS_MAXIMIZEBOX), |
186 | CW_USEDEFAULT, CW_USEDEFAULT, |
187 | r.right - r.left, r.bottom - r.top, |
188 | NULL, NULL, inst, NULL); |
189 | |
190 | hdc = GetDC(fe->hwnd); |
191 | fe->bitmap = CreateCompatibleBitmap(hdc, x, y); |
192 | ReleaseDC(fe->hwnd, hdc); |
193 | |
194 | SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe); |
195 | |
196 | ShowWindow(fe->hwnd, SW_NORMAL); |
197 | SetForegroundWindow(fe->hwnd); |
198 | |
199 | midend_redraw(fe->me); |
200 | |
201 | return fe; |
202 | } |
203 | |
204 | static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, |
205 | WPARAM wParam, LPARAM lParam) |
206 | { |
207 | frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA); |
208 | |
209 | switch (message) { |
210 | case WM_CLOSE: |
211 | DestroyWindow(hwnd); |
212 | return 0; |
213 | case WM_DESTROY: |
214 | PostQuitMessage(0); |
215 | return 0; |
216 | case WM_PAINT: |
217 | { |
218 | PAINTSTRUCT p; |
219 | HDC hdc, hdc2; |
220 | HBITMAP prevbm; |
221 | |
222 | hdc = BeginPaint(hwnd, &p); |
223 | hdc2 = CreateCompatibleDC(hdc); |
224 | prevbm = SelectObject(hdc2, fe->bitmap); |
225 | BitBlt(hdc, |
226 | p.rcPaint.left, p.rcPaint.top, |
227 | p.rcPaint.right - p.rcPaint.left, |
228 | p.rcPaint.bottom - p.rcPaint.top, |
229 | hdc2, |
230 | p.rcPaint.left, p.rcPaint.top, |
231 | SRCCOPY); |
232 | SelectObject(hdc2, prevbm); |
233 | DeleteDC(hdc2); |
234 | EndPaint(hwnd, &p); |
235 | } |
236 | return 0; |
237 | case WM_KEYDOWN: |
238 | { |
239 | int key = -1; |
240 | |
241 | switch (wParam) { |
242 | case VK_LEFT: key = CURSOR_LEFT; break; |
243 | case VK_RIGHT: key = CURSOR_RIGHT; break; |
244 | case VK_UP: key = CURSOR_UP; break; |
245 | case VK_DOWN: key = CURSOR_DOWN; break; |
246 | } |
247 | |
248 | if (key != -1) { |
249 | if (!midend_process_key(fe->me, -1, -1, key)) |
250 | PostQuitMessage(0); |
251 | } |
252 | } |
253 | break; |
254 | case WM_LBUTTONDOWN: |
255 | case WM_RBUTTONDOWN: |
256 | case WM_MBUTTONDOWN: |
257 | if (!midend_process_key(fe->me, LOWORD(lParam), HIWORD(lParam), |
258 | (message == WM_LBUTTONDOWN ? LEFT_BUTTON : |
259 | message == WM_RBUTTONDOWN ? RIGHT_BUTTON : |
260 | MIDDLE_BUTTON))) |
261 | PostQuitMessage(0); |
262 | |
263 | break; |
264 | case WM_CHAR: |
265 | if (!midend_process_key(fe->me, -1, -1, (unsigned char)wParam)) |
266 | PostQuitMessage(0); |
267 | return 0; |
268 | case WM_TIMER: |
269 | if (fe->timer) |
270 | midend_timer(fe->me, (float)0.02); |
271 | return 0; |
272 | } |
273 | |
274 | return DefWindowProc(hwnd, message, wParam, lParam); |
275 | } |
276 | |
277 | int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) |
278 | { |
279 | MSG msg; |
280 | |
281 | srand(time(NULL)); |
282 | |
283 | if (!prev) { |
284 | WNDCLASS wndclass; |
285 | |
286 | wndclass.style = 0; |
287 | wndclass.lpfnWndProc = WndProc; |
288 | wndclass.cbClsExtra = 0; |
289 | wndclass.cbWndExtra = 0; |
290 | wndclass.hInstance = inst; |
291 | wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION); |
292 | wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); |
293 | wndclass.hbrBackground = NULL; |
294 | wndclass.lpszMenuName = NULL; |
295 | wndclass.lpszClassName = "puzzle"; |
296 | |
297 | RegisterClass(&wndclass); |
298 | } |
299 | |
300 | new_window(inst); |
301 | |
302 | while (GetMessage(&msg, NULL, 0, 0)) { |
303 | TranslateMessage(&msg); |
304 | DispatchMessage(&msg); |
305 | } |
306 | |
307 | return msg.wParam; |
308 | } |