chiark / gitweb /
Most of a Windows front end. Something's not _quite_ right in the
[sgt-puzzles.git] / windows.c
1 /*
2  * windows.c: Windows front end for my puzzle collection.
3  */
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 }