720a8fb7 |
1 | /* |
2 | * windows.c: Windows front end for my puzzle collection. |
3 | */ |
4e7ef6e6 |
4 | |
5 | #include <windows.h> |
6 | |
7 | #include <stdio.h> |
4efb3868 |
8 | #include <assert.h> |
4e7ef6e6 |
9 | #include <stdarg.h> |
10 | #include <stdlib.h> |
11 | #include <time.h> |
12 | |
13 | #include "puzzles.h" |
14 | |
eb2ad6f1 |
15 | #define IDM_NEW 0x0010 |
16 | #define IDM_RESTART 0x0020 |
17 | #define IDM_UNDO 0x0030 |
18 | #define IDM_REDO 0x0040 |
19 | #define IDM_QUIT 0x0050 |
20 | #define IDM_PRESETS 0x0100 |
21 | |
c71454c0 |
22 | #ifdef DEBUG |
23 | static FILE *debug_fp = NULL; |
24 | static HANDLE debug_hdl = INVALID_HANDLE_VALUE; |
25 | static int debug_got_console = 0; |
26 | |
27 | void dputs(char *buf) |
28 | { |
29 | DWORD dw; |
30 | |
31 | if (!debug_got_console) { |
32 | if (AllocConsole()) { |
33 | debug_got_console = 1; |
34 | debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE); |
35 | } |
36 | } |
37 | if (!debug_fp) { |
38 | debug_fp = fopen("debug.log", "w"); |
39 | } |
40 | |
41 | if (debug_hdl != INVALID_HANDLE_VALUE) { |
42 | WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL); |
43 | } |
44 | fputs(buf, debug_fp); |
45 | fflush(debug_fp); |
46 | } |
47 | |
48 | void debug_printf(char *fmt, ...) |
49 | { |
50 | char buf[4096]; |
51 | va_list ap; |
52 | |
53 | va_start(ap, fmt); |
54 | vsprintf(buf, fmt, ap); |
55 | dputs(buf); |
56 | va_end(ap); |
57 | } |
58 | |
59 | #define debug(x) (debug_printf x) |
60 | |
61 | #else |
62 | |
63 | #define debug(x) |
64 | |
65 | #endif |
66 | |
4efb3868 |
67 | struct font { |
68 | HFONT font; |
69 | int type; |
70 | int size; |
71 | }; |
72 | |
4e7ef6e6 |
73 | struct frontend { |
74 | midend_data *me; |
75 | HWND hwnd; |
76 | HBITMAP bitmap, prevbm; |
77 | HDC hdc_bm; |
78 | COLORREF *colours; |
79 | HBRUSH *brushes; |
80 | HPEN *pens; |
4efb3868 |
81 | HRGN clip; |
4e7ef6e6 |
82 | UINT timer; |
eb2ad6f1 |
83 | int npresets; |
84 | game_params **presets; |
4efb3868 |
85 | struct font *fonts; |
86 | int nfonts, fontsize; |
4e7ef6e6 |
87 | }; |
88 | |
89 | void fatal(char *fmt, ...) |
90 | { |
91 | char buf[2048]; |
92 | va_list ap; |
93 | |
94 | va_start(ap, fmt); |
95 | vsprintf(buf, fmt, ap); |
96 | va_end(ap); |
97 | |
98 | MessageBox(NULL, buf, "Fatal error", MB_ICONEXCLAMATION | MB_OK); |
99 | |
100 | exit(1); |
101 | } |
102 | |
103 | void frontend_default_colour(frontend *fe, float *output) |
104 | { |
105 | DWORD c = GetSysColor(COLOR_MENU); /* ick */ |
106 | |
107 | output[0] = (float)(GetRValue(c) / 255.0); |
108 | output[1] = (float)(GetGValue(c) / 255.0); |
109 | output[2] = (float)(GetBValue(c) / 255.0); |
110 | } |
111 | |
4efb3868 |
112 | void clip(frontend *fe, int x, int y, int w, int h) |
113 | { |
114 | if (!fe->clip) { |
115 | fe->clip = CreateRectRgn(0, 0, 1, 1); |
116 | GetClipRgn(fe->hdc_bm, fe->clip); |
117 | } |
118 | |
119 | IntersectClipRect(fe->hdc_bm, x, y, x+w, y+h); |
120 | } |
121 | |
122 | void unclip(frontend *fe) |
123 | { |
124 | assert(fe->clip); |
125 | SelectClipRgn(fe->hdc_bm, fe->clip); |
126 | } |
127 | |
128 | void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, |
129 | int align, int colour, char *text) |
130 | { |
131 | int i; |
132 | |
133 | /* |
134 | * Find or create the font. |
135 | */ |
136 | for (i = 0; i < fe->nfonts; i++) |
137 | if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize) |
138 | break; |
139 | |
140 | if (i == fe->nfonts) { |
141 | if (fe->fontsize <= fe->nfonts) { |
142 | fe->fontsize = fe->nfonts + 10; |
143 | fe->fonts = sresize(fe->fonts, fe->fontsize, struct font); |
144 | } |
145 | |
146 | fe->nfonts++; |
147 | |
148 | fe->fonts[i].type = fonttype; |
149 | fe->fonts[i].size = fontsize; |
150 | |
151 | /* |
152 | * FIXME: Really I should make at least _some_ effort to |
153 | * pick the correct font. |
154 | */ |
155 | fe->fonts[i].font = CreateFont(-fontsize, 0, 0, 0, 0, |
156 | FALSE, FALSE, FALSE, DEFAULT_CHARSET, |
157 | OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, |
158 | DEFAULT_QUALITY, |
159 | (fonttype == FONT_FIXED ? |
160 | FIXED_PITCH | FF_DONTCARE : |
161 | VARIABLE_PITCH | FF_SWISS), |
162 | NULL); |
163 | } |
164 | |
165 | /* |
166 | * Position and draw the text. |
167 | */ |
168 | { |
169 | HFONT oldfont; |
170 | TEXTMETRIC tm; |
171 | SIZE size; |
172 | |
173 | oldfont = SelectObject(fe->hdc_bm, fe->fonts[i].font); |
174 | if (GetTextMetrics(fe->hdc_bm, &tm)) { |
175 | if (align & ALIGN_VCENTRE) |
176 | y -= (tm.tmAscent+tm.tmDescent)/2; |
177 | else |
178 | y -= tm.tmAscent; |
179 | } |
180 | if (GetTextExtentPoint32(fe->hdc_bm, text, strlen(text), &size)) { |
181 | if (align & ALIGN_HCENTRE) |
182 | x -= size.cx / 2; |
183 | else if (align & ALIGN_HRIGHT) |
184 | x -= size.cx; |
185 | } |
186 | SetBkMode(fe->hdc_bm, TRANSPARENT); |
187 | TextOut(fe->hdc_bm, x, y, text, strlen(text)); |
188 | SelectObject(fe->hdc_bm, oldfont); |
189 | } |
190 | } |
191 | |
4e7ef6e6 |
192 | void draw_rect(frontend *fe, int x, int y, int w, int h, int colour) |
193 | { |
fbdd7610 |
194 | if (w == 1 && h == 1) { |
195 | /* |
196 | * Rectangle() appears to get uppity if asked to draw a 1x1 |
197 | * rectangle, presumably on the grounds that that's beneath |
198 | * its dignity and you ought to be using SetPixel instead. |
199 | * So I will. |
200 | */ |
201 | SetPixel(fe->hdc_bm, x, y, fe->colours[colour]); |
202 | } else { |
203 | HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]); |
204 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
205 | Rectangle(fe->hdc_bm, x, y, x+w, y+h); |
206 | SelectObject(fe->hdc_bm, oldbrush); |
207 | SelectObject(fe->hdc_bm, oldpen); |
208 | } |
4e7ef6e6 |
209 | } |
210 | |
211 | void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) |
212 | { |
213 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
214 | MoveToEx(fe->hdc_bm, x1, y1, NULL); |
215 | LineTo(fe->hdc_bm, x2, y2); |
216 | SetPixel(fe->hdc_bm, x2, y2, fe->colours[colour]); |
217 | SelectObject(fe->hdc_bm, oldpen); |
218 | } |
219 | |
220 | void draw_polygon(frontend *fe, int *coords, int npoints, |
221 | int fill, int colour) |
222 | { |
223 | POINT *pts = snewn(npoints+1, POINT); |
224 | int i; |
225 | |
226 | for (i = 0; i <= npoints; i++) { |
227 | int j = (i < npoints ? i : 0); |
228 | pts[i].x = coords[j*2]; |
229 | pts[i].y = coords[j*2+1]; |
230 | } |
231 | |
232 | if (fill) { |
233 | HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]); |
234 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
235 | Polygon(fe->hdc_bm, pts, npoints); |
236 | SelectObject(fe->hdc_bm, oldbrush); |
237 | SelectObject(fe->hdc_bm, oldpen); |
238 | } else { |
239 | HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); |
240 | Polyline(fe->hdc_bm, pts, npoints+1); |
241 | SelectObject(fe->hdc_bm, oldpen); |
242 | } |
243 | |
244 | sfree(pts); |
245 | } |
246 | |
247 | void start_draw(frontend *fe) |
248 | { |
249 | HDC hdc_win; |
250 | hdc_win = GetDC(fe->hwnd); |
251 | fe->hdc_bm = CreateCompatibleDC(hdc_win); |
252 | fe->prevbm = SelectObject(fe->hdc_bm, fe->bitmap); |
253 | ReleaseDC(fe->hwnd, hdc_win); |
4efb3868 |
254 | fe->clip = NULL; |
4e7ef6e6 |
255 | } |
256 | |
257 | void draw_update(frontend *fe, int x, int y, int w, int h) |
258 | { |
259 | RECT r; |
260 | |
261 | r.left = x; |
262 | r.top = y; |
263 | r.right = x + w; |
264 | r.bottom = y + h; |
265 | |
266 | InvalidateRect(fe->hwnd, &r, FALSE); |
267 | } |
268 | |
269 | void end_draw(frontend *fe) |
270 | { |
271 | SelectObject(fe->hdc_bm, fe->prevbm); |
272 | DeleteDC(fe->hdc_bm); |
4efb3868 |
273 | if (fe->clip) { |
274 | DeleteObject(fe->clip); |
275 | fe->clip = NULL; |
276 | } |
4e7ef6e6 |
277 | } |
278 | |
279 | void deactivate_timer(frontend *fe) |
280 | { |
281 | KillTimer(fe->hwnd, fe->timer); |
282 | fe->timer = 0; |
283 | } |
284 | |
285 | void activate_timer(frontend *fe) |
286 | { |
287 | fe->timer = SetTimer(fe->hwnd, fe->timer, 20, NULL); |
288 | } |
289 | |
290 | static frontend *new_window(HINSTANCE inst) |
291 | { |
292 | frontend *fe; |
293 | int x, y; |
294 | RECT r; |
295 | HDC hdc; |
296 | |
297 | fe = snew(frontend); |
298 | fe->me = midend_new(fe); |
299 | midend_new_game(fe->me, NULL); |
300 | midend_size(fe->me, &x, &y); |
301 | |
302 | fe->timer = 0; |
303 | |
304 | { |
305 | int i, ncolours; |
306 | float *colours; |
307 | |
308 | colours = midend_colours(fe->me, &ncolours); |
309 | |
310 | fe->colours = snewn(ncolours, COLORREF); |
311 | fe->brushes = snewn(ncolours, HBRUSH); |
312 | fe->pens = snewn(ncolours, HPEN); |
313 | |
314 | for (i = 0; i < ncolours; i++) { |
315 | fe->colours[i] = RGB(255 * colours[i*3+0], |
316 | 255 * colours[i*3+1], |
317 | 255 * colours[i*3+2]); |
318 | fe->brushes[i] = CreateSolidBrush(fe->colours[i]); |
319 | if (!fe->brushes[i]) |
320 | MessageBox(fe->hwnd, "ooh", "eck", MB_OK); |
321 | fe->pens[i] = CreatePen(PS_SOLID, 1, fe->colours[i]); |
322 | } |
323 | } |
324 | |
325 | r.left = r.top = 0; |
326 | r.right = x; |
327 | r.bottom = y; |
328 | AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ |
329 | (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED), |
eb2ad6f1 |
330 | TRUE, 0); |
4e7ef6e6 |
331 | |
0c490335 |
332 | fe->hwnd = CreateWindowEx(0, game_name, game_name, |
4e7ef6e6 |
333 | WS_OVERLAPPEDWINDOW &~ |
334 | (WS_THICKFRAME | WS_MAXIMIZEBOX), |
335 | CW_USEDEFAULT, CW_USEDEFAULT, |
336 | r.right - r.left, r.bottom - r.top, |
337 | NULL, NULL, inst, NULL); |
338 | |
eb2ad6f1 |
339 | { |
340 | HMENU bar = CreateMenu(); |
341 | HMENU menu = CreateMenu(); |
342 | |
343 | AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Game"); |
344 | AppendMenu(menu, MF_ENABLED, IDM_NEW, "New"); |
345 | AppendMenu(menu, MF_ENABLED, IDM_RESTART, "Restart"); |
346 | |
347 | if ((fe->npresets = midend_num_presets(fe->me)) > 0) { |
348 | HMENU sub = CreateMenu(); |
349 | int i; |
350 | |
351 | AppendMenu(menu, MF_ENABLED|MF_POPUP, (UINT)sub, "Type"); |
352 | |
353 | fe->presets = snewn(fe->npresets, game_params *); |
354 | |
355 | for (i = 0; i < fe->npresets; i++) { |
356 | char *name; |
357 | |
358 | midend_fetch_preset(fe->me, i, &name, &fe->presets[i]); |
359 | |
360 | /* |
361 | * FIXME: we ought to go through and do something |
362 | * with ampersands here. |
363 | */ |
364 | |
365 | AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name); |
366 | } |
367 | } |
368 | |
369 | AppendMenu(menu, MF_SEPARATOR, 0, 0); |
370 | AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo"); |
371 | AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo"); |
372 | AppendMenu(menu, MF_SEPARATOR, 0, 0); |
373 | AppendMenu(menu, MF_ENABLED, IDM_QUIT, "Exit"); |
374 | SetMenu(fe->hwnd, bar); |
375 | } |
376 | |
4e7ef6e6 |
377 | hdc = GetDC(fe->hwnd); |
378 | fe->bitmap = CreateCompatibleBitmap(hdc, x, y); |
379 | ReleaseDC(fe->hwnd, hdc); |
380 | |
381 | SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe); |
382 | |
383 | ShowWindow(fe->hwnd, SW_NORMAL); |
384 | SetForegroundWindow(fe->hwnd); |
385 | |
386 | midend_redraw(fe->me); |
387 | |
388 | return fe; |
389 | } |
390 | |
391 | static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, |
392 | WPARAM wParam, LPARAM lParam) |
393 | { |
394 | frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA); |
395 | |
396 | switch (message) { |
397 | case WM_CLOSE: |
398 | DestroyWindow(hwnd); |
399 | return 0; |
eb2ad6f1 |
400 | case WM_COMMAND: |
401 | switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ |
402 | case IDM_NEW: |
403 | if (!midend_process_key(fe->me, 0, 0, 'n')) |
404 | PostQuitMessage(0); |
405 | break; |
406 | case IDM_RESTART: |
407 | if (!midend_process_key(fe->me, 0, 0, 'r')) |
408 | PostQuitMessage(0); |
409 | break; |
410 | case IDM_UNDO: |
411 | if (!midend_process_key(fe->me, 0, 0, 'u')) |
412 | PostQuitMessage(0); |
413 | break; |
414 | case IDM_REDO: |
415 | if (!midend_process_key(fe->me, 0, 0, '\x12')) |
416 | PostQuitMessage(0); |
417 | break; |
418 | case IDM_QUIT: |
419 | if (!midend_process_key(fe->me, 0, 0, 'q')) |
420 | PostQuitMessage(0); |
421 | break; |
422 | default: |
423 | { |
424 | int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10; |
425 | |
426 | if (p >= 0 && p < fe->npresets) { |
427 | RECT r; |
428 | HDC hdc; |
429 | int x, y; |
430 | |
431 | midend_set_params(fe->me, fe->presets[p]); |
432 | midend_new_game(fe->me, NULL); |
433 | midend_size(fe->me, &x, &y); |
434 | |
435 | r.left = r.top = 0; |
436 | r.right = x; |
437 | r.bottom = y; |
438 | AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ |
439 | (WS_THICKFRAME | WS_MAXIMIZEBOX | |
440 | WS_OVERLAPPED), |
441 | TRUE, 0); |
442 | |
443 | SetWindowPos(fe->hwnd, NULL, 0, 0, |
444 | r.right - r.left, r.bottom - r.top, |
445 | SWP_NOMOVE | SWP_NOZORDER); |
446 | |
447 | DeleteObject(fe->bitmap); |
448 | |
449 | hdc = GetDC(fe->hwnd); |
450 | fe->bitmap = CreateCompatibleBitmap(hdc, x, y); |
451 | ReleaseDC(fe->hwnd, hdc); |
452 | |
453 | midend_redraw(fe->me); |
454 | } |
455 | } |
456 | break; |
457 | } |
458 | break; |
4e7ef6e6 |
459 | case WM_DESTROY: |
460 | PostQuitMessage(0); |
461 | return 0; |
462 | case WM_PAINT: |
463 | { |
464 | PAINTSTRUCT p; |
465 | HDC hdc, hdc2; |
466 | HBITMAP prevbm; |
467 | |
468 | hdc = BeginPaint(hwnd, &p); |
469 | hdc2 = CreateCompatibleDC(hdc); |
470 | prevbm = SelectObject(hdc2, fe->bitmap); |
471 | BitBlt(hdc, |
472 | p.rcPaint.left, p.rcPaint.top, |
473 | p.rcPaint.right - p.rcPaint.left, |
474 | p.rcPaint.bottom - p.rcPaint.top, |
475 | hdc2, |
476 | p.rcPaint.left, p.rcPaint.top, |
477 | SRCCOPY); |
478 | SelectObject(hdc2, prevbm); |
479 | DeleteDC(hdc2); |
480 | EndPaint(hwnd, &p); |
481 | } |
482 | return 0; |
483 | case WM_KEYDOWN: |
484 | { |
485 | int key = -1; |
486 | |
487 | switch (wParam) { |
488 | case VK_LEFT: key = CURSOR_LEFT; break; |
489 | case VK_RIGHT: key = CURSOR_RIGHT; break; |
490 | case VK_UP: key = CURSOR_UP; break; |
491 | case VK_DOWN: key = CURSOR_DOWN; break; |
c71454c0 |
492 | /* |
493 | * Diagonal keys on the numeric keypad. |
494 | */ |
495 | case VK_PRIOR: |
496 | if (!(lParam & 0x01000000)) key = CURSOR_UP_RIGHT; |
497 | break; |
498 | case VK_NEXT: |
499 | if (!(lParam & 0x01000000)) key = CURSOR_DOWN_RIGHT; |
500 | break; |
501 | case VK_HOME: |
502 | if (!(lParam & 0x01000000)) key = CURSOR_UP_LEFT; |
503 | break; |
504 | case VK_END: |
505 | if (!(lParam & 0x01000000)) key = CURSOR_DOWN_LEFT; |
506 | break; |
507 | /* |
508 | * Numeric keypad keys with Num Lock on. |
509 | */ |
510 | case VK_NUMPAD4: key = CURSOR_LEFT; break; |
511 | case VK_NUMPAD6: key = CURSOR_RIGHT; break; |
512 | case VK_NUMPAD8: key = CURSOR_UP; break; |
513 | case VK_NUMPAD2: key = CURSOR_DOWN; break; |
514 | case VK_NUMPAD9: key = CURSOR_UP_RIGHT; break; |
515 | case VK_NUMPAD3: key = CURSOR_DOWN_RIGHT; break; |
516 | case VK_NUMPAD7: key = CURSOR_UP_LEFT; break; |
517 | case VK_NUMPAD1: key = CURSOR_DOWN_LEFT; break; |
4e7ef6e6 |
518 | } |
519 | |
520 | if (key != -1) { |
eb2ad6f1 |
521 | if (!midend_process_key(fe->me, 0, 0, key)) |
4e7ef6e6 |
522 | PostQuitMessage(0); |
c71454c0 |
523 | } else { |
524 | MSG m; |
525 | m.hwnd = hwnd; |
526 | m.message = WM_KEYDOWN; |
527 | m.wParam = wParam; |
528 | m.lParam = lParam & 0xdfff; |
529 | TranslateMessage(&m); |
4e7ef6e6 |
530 | } |
531 | } |
532 | break; |
533 | case WM_LBUTTONDOWN: |
534 | case WM_RBUTTONDOWN: |
535 | case WM_MBUTTONDOWN: |
b0f06719 |
536 | { |
537 | int button; |
538 | |
539 | /* |
540 | * Shift-clicks count as middle-clicks, since otherwise |
541 | * two-button Windows users won't have any kind of |
542 | * middle click to use. |
543 | */ |
544 | if (message == WM_MBUTTONDOWN || (wParam & MK_SHIFT)) |
545 | button = MIDDLE_BUTTON; |
546 | else if (message == WM_LBUTTONDOWN) |
547 | button = LEFT_BUTTON; |
548 | else |
549 | button = RIGHT_BUTTON; |
550 | |
551 | if (!midend_process_key(fe->me, LOWORD(lParam), |
552 | HIWORD(lParam), button)) |
553 | PostQuitMessage(0); |
554 | } |
4e7ef6e6 |
555 | break; |
556 | case WM_CHAR: |
eb2ad6f1 |
557 | if (!midend_process_key(fe->me, 0, 0, (unsigned char)wParam)) |
4e7ef6e6 |
558 | PostQuitMessage(0); |
559 | return 0; |
560 | case WM_TIMER: |
561 | if (fe->timer) |
562 | midend_timer(fe->me, (float)0.02); |
563 | return 0; |
564 | } |
565 | |
566 | return DefWindowProc(hwnd, message, wParam, lParam); |
567 | } |
568 | |
569 | int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) |
570 | { |
571 | MSG msg; |
572 | |
573 | srand(time(NULL)); |
574 | |
575 | if (!prev) { |
576 | WNDCLASS wndclass; |
577 | |
578 | wndclass.style = 0; |
579 | wndclass.lpfnWndProc = WndProc; |
580 | wndclass.cbClsExtra = 0; |
581 | wndclass.cbWndExtra = 0; |
582 | wndclass.hInstance = inst; |
583 | wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION); |
584 | wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); |
585 | wndclass.hbrBackground = NULL; |
586 | wndclass.lpszMenuName = NULL; |
0c490335 |
587 | wndclass.lpszClassName = game_name; |
4e7ef6e6 |
588 | |
589 | RegisterClass(&wndclass); |
590 | } |
591 | |
592 | new_window(inst); |
593 | |
594 | while (GetMessage(&msg, NULL, 0, 0)) { |
4e7ef6e6 |
595 | DispatchMessage(&msg); |
596 | } |
597 | |
598 | return msg.wParam; |
599 | } |