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 | { |
50 | HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]); |
51 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
52 | Rectangle(fe->hdc_bm, x, y, x+w, y+h); |
53 | SelectObject(fe->hdc_bm, oldbrush); |
54 | SelectObject(fe->hdc_bm, oldpen); |
55 | } |
56 | |
57 | void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) |
58 | { |
59 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
60 | MoveToEx(fe->hdc_bm, x1, y1, NULL); |
61 | LineTo(fe->hdc_bm, x2, y2); |
62 | SetPixel(fe->hdc_bm, x2, y2, fe->colours[colour]); |
63 | SelectObject(fe->hdc_bm, oldpen); |
64 | } |
65 | |
66 | void draw_polygon(frontend *fe, int *coords, int npoints, |
67 | int fill, int colour) |
68 | { |
69 | POINT *pts = snewn(npoints+1, POINT); |
70 | int i; |
71 | |
72 | for (i = 0; i <= npoints; i++) { |
73 | int j = (i < npoints ? i : 0); |
74 | pts[i].x = coords[j*2]; |
75 | pts[i].y = coords[j*2+1]; |
76 | } |
77 | |
78 | if (fill) { |
79 | HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]); |
80 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
81 | Polygon(fe->hdc_bm, pts, npoints); |
82 | SelectObject(fe->hdc_bm, oldbrush); |
83 | SelectObject(fe->hdc_bm, oldpen); |
84 | } else { |
85 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
86 | Polyline(fe->hdc_bm, pts, npoints+1); |
87 | SelectObject(fe->hdc_bm, oldpen); |
88 | } |
89 | |
90 | sfree(pts); |
91 | } |
92 | |
93 | void start_draw(frontend *fe) |
94 | { |
95 | HDC hdc_win; |
96 | hdc_win = GetDC(fe->hwnd); |
97 | fe->hdc_bm = CreateCompatibleDC(hdc_win); |
98 | fe->prevbm = SelectObject(fe->hdc_bm, fe->bitmap); |
99 | ReleaseDC(fe->hwnd, hdc_win); |
100 | } |
101 | |
102 | void draw_update(frontend *fe, int x, int y, int w, int h) |
103 | { |
104 | RECT r; |
105 | |
106 | r.left = x; |
107 | r.top = y; |
108 | r.right = x + w; |
109 | r.bottom = y + h; |
110 | |
111 | InvalidateRect(fe->hwnd, &r, FALSE); |
112 | } |
113 | |
114 | void end_draw(frontend *fe) |
115 | { |
116 | SelectObject(fe->hdc_bm, fe->prevbm); |
117 | DeleteDC(fe->hdc_bm); |
118 | } |
119 | |
120 | void deactivate_timer(frontend *fe) |
121 | { |
122 | KillTimer(fe->hwnd, fe->timer); |
123 | fe->timer = 0; |
124 | } |
125 | |
126 | void activate_timer(frontend *fe) |
127 | { |
128 | fe->timer = SetTimer(fe->hwnd, fe->timer, 20, NULL); |
129 | } |
130 | |
131 | static frontend *new_window(HINSTANCE inst) |
132 | { |
133 | frontend *fe; |
134 | int x, y; |
135 | RECT r; |
136 | HDC hdc; |
137 | |
138 | fe = snew(frontend); |
139 | fe->me = midend_new(fe); |
140 | midend_new_game(fe->me, NULL); |
141 | midend_size(fe->me, &x, &y); |
142 | |
143 | fe->timer = 0; |
144 | |
145 | { |
146 | int i, ncolours; |
147 | float *colours; |
148 | |
149 | colours = midend_colours(fe->me, &ncolours); |
150 | |
151 | fe->colours = snewn(ncolours, COLORREF); |
152 | fe->brushes = snewn(ncolours, HBRUSH); |
153 | fe->pens = snewn(ncolours, HPEN); |
154 | |
155 | for (i = 0; i < ncolours; i++) { |
156 | fe->colours[i] = RGB(255 * colours[i*3+0], |
157 | 255 * colours[i*3+1], |
158 | 255 * colours[i*3+2]); |
159 | fe->brushes[i] = CreateSolidBrush(fe->colours[i]); |
160 | if (!fe->brushes[i]) |
161 | MessageBox(fe->hwnd, "ooh", "eck", MB_OK); |
162 | fe->pens[i] = CreatePen(PS_SOLID, 1, fe->colours[i]); |
163 | } |
164 | } |
165 | |
166 | r.left = r.top = 0; |
167 | r.right = x; |
168 | r.bottom = y; |
169 | AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ |
170 | (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED), |
171 | FALSE, 0); |
172 | |
173 | fe->hwnd = CreateWindowEx(0, "puzzle", "puzzle", |
174 | WS_OVERLAPPEDWINDOW &~ |
175 | (WS_THICKFRAME | WS_MAXIMIZEBOX), |
176 | CW_USEDEFAULT, CW_USEDEFAULT, |
177 | r.right - r.left, r.bottom - r.top, |
178 | NULL, NULL, inst, NULL); |
179 | |
180 | hdc = GetDC(fe->hwnd); |
181 | fe->bitmap = CreateCompatibleBitmap(hdc, x, y); |
182 | ReleaseDC(fe->hwnd, hdc); |
183 | |
184 | SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe); |
185 | |
186 | ShowWindow(fe->hwnd, SW_NORMAL); |
187 | SetForegroundWindow(fe->hwnd); |
188 | |
189 | midend_redraw(fe->me); |
190 | |
191 | return fe; |
192 | } |
193 | |
194 | static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, |
195 | WPARAM wParam, LPARAM lParam) |
196 | { |
197 | frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA); |
198 | |
199 | switch (message) { |
200 | case WM_CLOSE: |
201 | DestroyWindow(hwnd); |
202 | return 0; |
203 | case WM_DESTROY: |
204 | PostQuitMessage(0); |
205 | return 0; |
206 | case WM_PAINT: |
207 | { |
208 | PAINTSTRUCT p; |
209 | HDC hdc, hdc2; |
210 | HBITMAP prevbm; |
211 | |
212 | hdc = BeginPaint(hwnd, &p); |
213 | hdc2 = CreateCompatibleDC(hdc); |
214 | prevbm = SelectObject(hdc2, fe->bitmap); |
215 | BitBlt(hdc, |
216 | p.rcPaint.left, p.rcPaint.top, |
217 | p.rcPaint.right - p.rcPaint.left, |
218 | p.rcPaint.bottom - p.rcPaint.top, |
219 | hdc2, |
220 | p.rcPaint.left, p.rcPaint.top, |
221 | SRCCOPY); |
222 | SelectObject(hdc2, prevbm); |
223 | DeleteDC(hdc2); |
224 | EndPaint(hwnd, &p); |
225 | } |
226 | return 0; |
227 | case WM_KEYDOWN: |
228 | { |
229 | int key = -1; |
230 | |
231 | switch (wParam) { |
232 | case VK_LEFT: key = CURSOR_LEFT; break; |
233 | case VK_RIGHT: key = CURSOR_RIGHT; break; |
234 | case VK_UP: key = CURSOR_UP; break; |
235 | case VK_DOWN: key = CURSOR_DOWN; break; |
236 | } |
237 | |
238 | if (key != -1) { |
239 | if (!midend_process_key(fe->me, -1, -1, key)) |
240 | PostQuitMessage(0); |
241 | } |
242 | } |
243 | break; |
244 | case WM_LBUTTONDOWN: |
245 | case WM_RBUTTONDOWN: |
246 | case WM_MBUTTONDOWN: |
247 | if (!midend_process_key(fe->me, LOWORD(lParam), HIWORD(lParam), |
248 | (message == WM_LBUTTONDOWN ? LEFT_BUTTON : |
249 | message == WM_RBUTTONDOWN ? RIGHT_BUTTON : |
250 | MIDDLE_BUTTON))) |
251 | PostQuitMessage(0); |
252 | |
253 | break; |
254 | case WM_CHAR: |
255 | if (!midend_process_key(fe->me, -1, -1, (unsigned char)wParam)) |
256 | PostQuitMessage(0); |
257 | return 0; |
258 | case WM_TIMER: |
259 | if (fe->timer) |
260 | midend_timer(fe->me, (float)0.02); |
261 | return 0; |
262 | } |
263 | |
264 | return DefWindowProc(hwnd, message, wParam, lParam); |
265 | } |
266 | |
267 | int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) |
268 | { |
269 | MSG msg; |
270 | |
271 | srand(time(NULL)); |
272 | |
273 | if (!prev) { |
274 | WNDCLASS wndclass; |
275 | |
276 | wndclass.style = 0; |
277 | wndclass.lpfnWndProc = WndProc; |
278 | wndclass.cbClsExtra = 0; |
279 | wndclass.cbWndExtra = 0; |
280 | wndclass.hInstance = inst; |
281 | wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION); |
282 | wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); |
283 | wndclass.hbrBackground = NULL; |
284 | wndclass.lpszMenuName = NULL; |
285 | wndclass.lpszClassName = "puzzle"; |
286 | |
287 | RegisterClass(&wndclass); |
288 | } |
289 | |
290 | new_window(inst); |
291 | |
292 | while (GetMessage(&msg, NULL, 0, 0)) { |
293 | TranslateMessage(&msg); |
294 | DispatchMessage(&msg); |
295 | } |
296 | |
297 | return msg.wParam; |
298 | } |