Remove Get/SetBeepActive from USER driver and manage it locally inside
[wine] / programs / winemine / main.c
1 /*
2  * WineMine (main.c)
3  * 
4  * Copyright 2000 Joshua Thielen <jt85296@ltu.edu>
5  * To be distributed under the Wine License
6  */
7
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 #include <windows.h>
13 #include "main.h"
14 #include "dialog.h"
15 #include "resource.h"
16
17 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
18 #ifdef WINE_STRICT
19 #define NULL_HANDLE NULL
20 #else
21 #define NULL_HANDLE 0
22 #endif
23
24 #ifdef DUMB_DEBUG
25 #include <stdio.h>
26 #define DEBUG(x) fprintf(stderr,x)
27 #else
28 #define DEBUG(x) 
29 #endif
30
31 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
32
33     MSG msg;
34     WNDCLASS wc;
35     HWND hWnd;
36     HACCEL haccel;        
37     char appname[9];
38
39     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
40
41     wc.style = 0;
42     wc.lpfnWndProc = MainProc;
43     wc.cbClsExtra = 0;
44     wc.cbWndExtra = 0;
45     wc.hInstance = hInst;
46     wc.hIcon = LoadIcon( hInst, appname );
47     wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
48     wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH );
49     wc.lpszMenuName = "MENU_WINEMINE";
50     wc.lpszClassName = appname;
51     
52     if (!RegisterClass(&wc)) exit(1);
53     hWnd = CreateWindow( appname, appname, 
54         WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, 
55         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
56         NULL_HANDLE, NULL_HANDLE, hInst, NULL );
57     
58     if (!hWnd) exit(1);
59     
60     ShowWindow( hWnd, cmdshow );
61     UpdateWindow( hWnd );
62
63     haccel = LoadAccelerators( hInst, appname );
64     SetTimer( hWnd, ID_TIMER, 1000, NULL );
65
66     while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
67         if (!TranslateAccelerator( hWnd, haccel, &msg ))
68             TranslateMessage( &msg );
69         
70         DispatchMessage( &msg );
71     }
72     return msg.wParam;
73 }
74
75 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
76 {
77     HDC hdc;
78     PAINTSTRUCT ps;
79     HMENU hMenu;        
80     static BOARD board;     
81
82     switch( msg ) {
83     case WM_CREATE:         
84         board.hInst = ((LPCREATESTRUCT) lParam)->hInstance;
85         board.hWnd = hWnd;
86         InitBoard( &board );
87         CreateBoard( &board );
88         return 0;
89
90     case WM_PAINT:
91       {
92         HDC hMemDC;
93
94         DEBUG("WM_PAINT\n");
95         hdc = BeginPaint( hWnd, &ps );
96         hMemDC = CreateCompatibleDC( hdc );
97
98         DrawBoard( hdc, hMemDC, &ps, &board );
99
100         DeleteDC( hMemDC );
101         EndPaint( hWnd, &ps );
102
103         return 0;
104       }
105
106     case WM_MOVE:
107         DEBUG("WM_MOVE\n");
108         board.pos.x = (unsigned) LOWORD(lParam);
109         board.pos.y = (unsigned) HIWORD(lParam);    
110         return 0;
111
112     case WM_DESTROY:
113         SaveBoard( &board );
114         DestroyBoard( &board ); 
115         PostQuitMessage( 0 );
116         return 0;
117     
118     case WM_TIMER:
119         if( board.status == PLAYING ) {
120             board.time++;
121                   RedrawWindow( hWnd, &board.timer_rect, NULL_HANDLE, 
122                     RDW_INVALIDATE | RDW_UPDATENOW );      
123         }
124         return 0;
125
126     case WM_LBUTTONDOWN:
127         DEBUG("WM_LBUTTONDOWN\n");
128         if( wParam & MK_RBUTTON )
129             msg = WM_MBUTTONDOWN;        
130         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
131         SetCapture( hWnd );
132         return 0;
133
134     case WM_LBUTTONUP:
135         DEBUG("WM_LBUTTONUP\n");
136         if( wParam & MK_RBUTTON )
137             msg = WM_MBUTTONUP;    
138         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
139         ReleaseCapture();
140         return 0;
141
142     case WM_RBUTTONDOWN:
143         DEBUG("WM_RBUTTONDOWN\n");
144         if( wParam & MK_LBUTTON ) {    
145             board.press.x = 0;
146             board.press.y = 0;    
147             msg = WM_MBUTTONDOWN;
148         }    
149         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
150         return 0;
151
152     case WM_RBUTTONUP:
153         DEBUG("WM_RBUTTONUP\n");
154         if( wParam & MK_LBUTTON ) 
155             msg = WM_MBUTTONUP;    
156         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
157         return 0;
158
159     case WM_MBUTTONDOWN:
160         DEBUG("WM_MBUTTONDOWN\n");
161         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
162         return 0;
163
164     case WM_MBUTTONUP:
165         DEBUG("WM_MBUTTONUP\n");
166         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
167         return 0;
168
169     case WM_MOUSEMOVE:
170     {
171         if( (wParam & MK_LBUTTON) && (wParam & MK_RBUTTON) ) {
172             msg = WM_MBUTTONDOWN;
173         }
174         else if( wParam & MK_LBUTTON ) {
175             msg = WM_LBUTTONDOWN;
176         }
177         else {
178             return 0;
179         }
180
181         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam),  msg );
182
183         return 0;    
184     } 
185
186     case WM_COMMAND:
187         switch(LOWORD(wParam)) {
188         case IDM_NEW:
189             CreateBoard( &board );
190             return 0;
191
192         case IDM_MARKQ:
193             hMenu = GetMenu( hWnd );     
194             board.IsMarkQ = !board.IsMarkQ;
195             if( board.IsMarkQ )
196                 CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED );
197             else 
198                 CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED );
199             return 0;
200
201         case IDM_BEGINNER:
202             SetDifficulty( &board, BEGINNER );    
203             CreateBoard( &board );
204             return 0;
205
206         case IDM_ADVANCED:
207             SetDifficulty( &board, ADVANCED );    
208             CreateBoard( &board );
209             return 0;
210
211         case IDM_EXPERT:
212             SetDifficulty( &board, EXPERT );    
213             CreateBoard( &board );
214             return 0;
215
216         case IDM_CUSTOM:
217             SetDifficulty( &board, CUSTOM );
218             CreateBoard( &board );
219             return 0;
220
221         case IDM_EXIT:
222             SendMessage( hWnd, WM_CLOSE, 0, 0);
223             return 0;
224
225         case IDM_TIMES:
226             DialogBoxParam( board.hInst, "DLG_TIMES", hWnd, 
227                     TimesDlgProc, (LPARAM) &board);
228             return 0;
229
230         case IDM_ABOUT:
231             DialogBox( board.hInst, "DLG_ABOUT", hWnd, AboutDlgProc );
232             return 0;
233         default:
234             DEBUG("Unknown WM_COMMAND command message received\n");
235             break;
236         }
237     } 
238     return( DefWindowProc( hWnd, msg, wParam, lParam ));
239 }
240
241 void InitBoard( BOARD *p_board )
242 {
243     HMENU hMenu;
244
245     p_board->hMinesBMP = LoadBitmap( p_board->hInst, "mines");
246     p_board->hFacesBMP = LoadBitmap( p_board->hInst, "faces");
247     p_board->hLedsBMP = LoadBitmap( p_board->hInst, "leds");     
248     
249     LoadBoard( p_board );    
250     
251     if( p_board->pos.x < (unsigned) GetSystemMetrics( SM_CXFIXEDFRAME ))
252         p_board->pos.x = GetSystemMetrics( SM_CXFIXEDFRAME );
253
254     if( p_board->pos.x > (unsigned) (GetSystemMetrics( SM_CXSCREEN ) 
255     - GetSystemMetrics( SM_CXFIXEDFRAME ))) {
256         p_board->pos.x = GetSystemMetrics( SM_CXSCREEN ) 
257         - GetSystemMetrics( SM_CXFIXEDFRAME );
258     }
259
260     if( p_board->pos.y < (unsigned) (GetSystemMetrics( SM_CYMENU )
261     + GetSystemMetrics( SM_CYCAPTION )
262     + GetSystemMetrics( SM_CYFIXEDFRAME ))) {
263         p_board->pos.y = GetSystemMetrics( SM_CYMENU ) +
264         GetSystemMetrics( SM_CYCAPTION ) +
265         GetSystemMetrics( SM_CYFIXEDFRAME );
266     }
267
268     if( p_board->pos.y > (unsigned) (GetSystemMetrics( SM_CYSCREEN ) 
269     - GetSystemMetrics( SM_CYFIXEDFRAME ))) {
270         p_board->pos.y = GetSystemMetrics( SM_CYSCREEN )
271         - GetSystemMetrics( SM_CYFIXEDFRAME );
272     }
273     
274     hMenu = GetMenu( p_board->hWnd );
275     CheckMenuItem( hMenu, IDM_BEGINNER + (unsigned) p_board->difficulty, 
276             MF_CHECKED );
277     if( p_board->IsMarkQ )
278         CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED );
279     else 
280         CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED );
281     CheckLevel( p_board );
282 }
283
284 void LoadBoard( BOARD *p_board ) 
285 {
286     DWORD size;
287     DWORD type;
288     HKEY hkey;
289     char data[16];
290     char key_name[8];
291     unsigned i;
292
293
294     RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Wine\\WineMine", 
295             0, KEY_QUERY_VALUE, &hkey );
296     
297     size = sizeof( data );
298     if( RegQueryValueEx( hkey, "Xpos", NULL, (LPDWORD) &type, 
299             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS ) {
300         p_board->pos.x = atoi( data );
301     }
302     else 
303         p_board->pos.x = GetSystemMetrics( SM_CXFIXEDFRAME );
304
305     size = sizeof( data );
306     if( RegQueryValueEx( hkey, "Ypos", NULL, (LPDWORD) &type, 
307             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
308         p_board->pos.y = atoi( data );
309     else 
310         p_board->pos.y = GetSystemMetrics( SM_CYMENU )
311         + GetSystemMetrics( SM_CYCAPTION ) 
312         + GetSystemMetrics( SM_CYFIXEDFRAME );
313
314     size = sizeof( data );
315     if( RegQueryValueEx( hkey, "Rows", NULL, (LPDWORD) &type, 
316             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
317         p_board->rows = atoi( data );
318     else
319         p_board->rows = BEGINNER_ROWS;
320
321     size = sizeof( data );
322     if( RegQueryValueEx( hkey, "Cols", NULL, (LPDWORD) &type, 
323             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
324         p_board->cols = atoi( data );
325     else
326         p_board->cols = BEGINNER_COLS;    
327     
328     size = sizeof( data );
329     if( RegQueryValueEx( hkey, "Mines", NULL, (LPDWORD) &type, 
330             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
331         p_board->mines = atoi( data );
332     else 
333         p_board->rows = BEGINNER_ROWS;
334
335     size = sizeof( data );
336     if( RegQueryValueEx( hkey, "Difficulty", NULL, (LPDWORD) &type, 
337             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
338         p_board->difficulty = (DIFFICULTY) atoi( data );
339     else 
340         p_board->difficulty = BEGINNER;
341
342     size = sizeof( data );
343     if( RegQueryValueEx( hkey, "MarkQ", NULL, (LPDWORD) &type, 
344             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
345         p_board->IsMarkQ = atoi( data );
346     else 
347         p_board->IsMarkQ = TRUE;
348         
349     for( i = 0; i < 3; i++ ) {
350         wsprintf( key_name, "Name%d", i );
351         size = sizeof( data );
352         if( RegQueryValueEx( hkey, key_name, NULL, (LPDWORD) &type, 
353                 (LPBYTE) data, 
354                 (LPDWORD) &size ) == ERROR_SUCCESS )
355                     strncpy( p_board->best_name[i], data, sizeof( data ) );
356         else 
357             wsprintf( p_board->best_name[i], "Nobody");
358     }
359     
360     for( i = 0; i < 3; i++ ) {
361         wsprintf( key_name, "Time%d", i );
362         size = sizeof( data );
363         if( RegQueryValueEx( hkey, key_name, NULL, (LPDWORD) &type, 
364                 (LPBYTE) data, 
365                 (LPDWORD) &size ) == ERROR_SUCCESS )
366             p_board->best_time[i] = atoi( data );
367         else 
368             p_board->best_time[i] = 999;
369     }
370     RegCloseKey( hkey );
371 }
372
373 void SaveBoard( BOARD *p_board )
374 {
375     DWORD disp;
376     HKEY hkey;
377     SECURITY_ATTRIBUTES sa;
378     unsigned i;
379     char data[16];
380     char key_name[8];
381     
382     if( RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
383                 "Software\\Wine\\WineMine", 0, NULL,
384                 REG_OPTION_NON_VOLATILE, KEY_WRITE, &sa, 
385                 &hkey, &disp ) != ERROR_SUCCESS)
386         return;    
387
388     wsprintf( data, "%d", p_board->pos.x );
389     RegSetValueEx( hkey, "Xpos", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
390
391     wsprintf( data, "%d", p_board->pos.x );
392     RegSetValueEx( hkey, "Ypos", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
393
394     wsprintf( data, "%d", (int) p_board->difficulty );
395     RegSetValueEx( hkey, "Difficulty", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
396
397     wsprintf( data, "%d", p_board->rows );
398     RegSetValueEx( hkey, "Rows", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
399
400     wsprintf( data, "%d", p_board->cols );
401     RegSetValueEx( hkey, "Cols", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
402
403     wsprintf( data, "%d", p_board->mines );
404     RegSetValueEx( hkey, "Mines", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
405
406     wsprintf( data, "%d", (int) p_board->IsMarkQ );
407     RegSetValueEx( hkey, "MarkQ", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
408
409     for( i = 0; i < 3; i++ ) {
410         wsprintf( key_name, "Name%u", i );
411         strncpy( data, p_board->best_name[i], sizeof( data ) );
412         RegSetValueEx( hkey, key_name, 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
413     }
414
415     for( i = 0; i < 3; i++ ) {
416         wsprintf( key_name, "Time%u", i );
417         wsprintf( data, "%d", p_board->best_time[i] );
418         RegSetValueEx( hkey, key_name, 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
419     }
420     RegCloseKey( hkey );
421 }
422
423 void DestroyBoard( BOARD *p_board )
424 {
425     DeleteObject( p_board->hFacesBMP );
426     DeleteObject( p_board->hLedsBMP );
427     DeleteObject( p_board->hMinesBMP );
428 }
429
430 void SetDifficulty( BOARD *p_board, DIFFICULTY difficulty )
431 {
432     HMENU hMenu = GetMenu( p_board->hWnd );
433
434     CheckMenuItem( hMenu, IDM_BEGINNER + p_board->difficulty, MF_UNCHECKED );
435     p_board->difficulty = difficulty; 
436     CheckMenuItem( hMenu, IDM_BEGINNER + difficulty, MF_CHECKED );
437     
438     switch( difficulty ) {
439     case BEGINNER:    
440         p_board->cols = BEGINNER_COLS;
441         p_board->rows = BEGINNER_ROWS;
442         p_board->mines = BEGINNER_MINES;
443         break;
444
445     case ADVANCED:    
446         p_board->cols = ADVANCED_COLS;
447         p_board->rows = ADVANCED_ROWS;
448         p_board->mines = ADVANCED_MINES;
449         break;
450
451     case EXPERT:
452         p_board->cols = EXPERT_COLS;
453         p_board->rows = EXPERT_ROWS;
454         p_board->mines = EXPERT_MINES;        
455         break;    
456
457     case CUSTOM:
458         DialogBoxParam( p_board->hInst, "DLG_CUSTOM", p_board->hWnd, 
459                 CustomDlgProc, (LPARAM) p_board);
460         break;    
461     }
462 }
463
464 void CreateBoard( BOARD *p_board )
465 {
466     int left, top, bottom, right, wnd_x, wnd_y, wnd_width, wnd_height;
467
468     p_board->mb = MB_NONE;
469     p_board->boxes_left = p_board->cols * p_board->rows - p_board->mines;
470     p_board->num_flags = 0;
471     
472     CreateBoxes( p_board );
473
474     p_board->width = p_board->cols * MINE_WIDTH + BOARD_WMARGIN * 2;
475     
476     p_board->height = p_board->rows * MINE_HEIGHT + LED_HEIGHT 
477         + BOARD_HMARGIN * 3;
478
479     wnd_x = p_board->pos.x - GetSystemMetrics( SM_CXFIXEDFRAME );
480     wnd_y = p_board->pos.y - GetSystemMetrics( SM_CYMENU ) 
481         - GetSystemMetrics( SM_CYCAPTION ) 
482         - GetSystemMetrics( SM_CYFIXEDFRAME );
483     wnd_width = p_board->width 
484         + GetSystemMetrics( SM_CXFIXEDFRAME ) * 2;
485     wnd_height = p_board->height 
486         + GetSystemMetrics( SM_CYMENU ) 
487         + GetSystemMetrics( SM_CYCAPTION ) 
488         + GetSystemMetrics( SM_CYFIXEDFRAME ) * 2;
489
490     /* setting the mines rectangle boundary */    
491     left = BOARD_WMARGIN; 
492     top = BOARD_HMARGIN * 2 + LED_HEIGHT; 
493     right = left + p_board->cols * MINE_WIDTH;
494     bottom = top + p_board->rows * MINE_HEIGHT;
495     SetRect( &p_board->mines_rect, left, top, right, bottom );
496
497     /* setting the face rectangle boundary */    
498     left = p_board->width / 2 - FACE_WIDTH / 2;
499     top = BOARD_HMARGIN;
500     right = left + FACE_WIDTH;
501     bottom = top + FACE_HEIGHT; 
502     SetRect( &p_board->face_rect, left, top, right, bottom );
503     
504     /* setting the timer rectangle boundary */    
505     left = BOARD_WMARGIN;
506     top = BOARD_HMARGIN;
507     right = left + LED_WIDTH * 3;
508     bottom = top + LED_HEIGHT; 
509     SetRect( &p_board->timer_rect, left, top, right, bottom );
510         
511     /* setting the counter rectangle boundary */    
512     left =  p_board->width - BOARD_WMARGIN - LED_WIDTH * 3;
513     top = BOARD_HMARGIN;
514     right = p_board->width - BOARD_WMARGIN;
515     bottom = top + LED_HEIGHT; 
516     SetRect( &p_board->counter_rect, left, top, right, bottom );
517         
518     p_board->status = WAITING;
519     p_board->face_bmp = SMILE_BMP;     
520     p_board->time = 0;
521
522     MoveWindow( p_board->hWnd, wnd_x, wnd_y, wnd_width, wnd_height, TRUE );
523     RedrawWindow( p_board->hWnd, NULL, NULL_HANDLE, 
524             RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE );
525 }
526
527
528 void CheckLevel( BOARD *p_board )
529 {
530     if( p_board->rows < BEGINNER_ROWS )
531         p_board->rows = BEGINNER_ROWS;
532
533     if( p_board->rows > MAX_ROWS )
534         p_board->rows = MAX_ROWS;
535         
536     if( p_board->cols < BEGINNER_COLS )
537         p_board->cols = BEGINNER_COLS;
538
539     if( p_board->cols > MAX_COLS )
540         p_board->cols = MAX_COLS;
541
542     if( p_board->mines < BEGINNER_MINES )
543         p_board->mines = BEGINNER_MINES;               
544
545     if( p_board->mines > p_board->cols * p_board->rows - 1 ) 
546         p_board->mines = p_board->cols * p_board->rows - 1; 
547 }
548
549
550 void CreateBoxes( BOARD *p_board )
551 {
552     int i, j;
553     unsigned col, row;
554
555     srand( (unsigned) time( NULL ) );
556
557     /* Create the boxes...  
558      * We actually create them with an empty border,
559      * so special care doesn't have to be taken on the edges
560      */
561     
562     for( col = 0; col <= p_board->cols + 1; col++ )
563       for( row = 0; row <= p_board->rows + 1; row++ ) {
564         p_board->box[col][row].IsPressed = FALSE;  
565         p_board->box[col][row].IsMine = FALSE;
566         p_board->box[col][row].FlagType = NORMAL;
567         p_board->box[col][row].NumMines = 0;
568       }
569
570     /* create mines */
571     i = 0;
572      while( (unsigned) i < p_board->mines ) {
573           col = (int) (p_board->cols * (float) rand() / RAND_MAX + 1);
574           row = (int) (p_board->rows * (float) rand() / RAND_MAX + 1);
575
576         if( !p_board->box[col][row].IsMine ) {
577             i++;
578             p_board->box[col][row].IsMine = TRUE;
579         }
580     }
581
582     /*
583      * Now we label the remaining boxes with the
584      * number of mines surrounding them.
585      */
586
587     for( col = 1; col < p_board->cols + 1; col++ )
588     for( row = 1; row < p_board->rows + 1; row++ ) {
589         for( i = -1; i <= 1; i++ )
590         for( j = -1; j <= 1; j++ ) {
591             if( p_board->box[col + i][row + j].IsMine ) {
592                 p_board->box[col][row].NumMines++ ;
593             }
594         }
595     }
596 }
597
598 void DrawMines ( HDC hdc, HDC hMemDC, BOARD *p_board )
599 {
600     HGDIOBJ hOldObj;
601     unsigned col, row;
602     hOldObj = SelectObject (hMemDC, p_board->hMinesBMP);
603
604     for( row = 1; row <= p_board->rows; row++ ) { 
605       for( col = 1; col <= p_board->cols; col++ ) {
606         DrawMine( hdc, hMemDC, p_board, col, row, FALSE );    
607       }
608     }
609     SelectObject( hMemDC, hOldObj );
610 }
611
612 void DrawMine( HDC hdc, HDC hMemDC, BOARD *p_board, unsigned col, unsigned row, BOOL IsPressed )
613 {
614     MINEBMP_OFFSET offset = BOX_BMP;
615
616     if( col == 0 || col > p_board->cols || row == 0 || row > p_board->rows )
617            return;    
618
619     if( p_board->status == GAMEOVER ) {
620         if( p_board->box[col][row].IsMine ) {
621             switch( p_board->box[col][row].FlagType ) {
622             case FLAG:
623                 offset = FLAG_BMP;
624                 break;
625             case COMPLETE:
626                 offset = EXPLODE_BMP;                        
627                 break;
628             case NORMAL:
629                 offset = MINE_BMP;
630             }
631         } else {
632             switch( p_board->box[col][row].FlagType ) {    
633             case QUESTION:
634                 offset = QUESTION_BMP;     
635                 break;
636             case FLAG:
637                 offset = WRONG_BMP;
638                 break;
639             case NORMAL:    
640                 offset = BOX_BMP;
641                 break;
642             case COMPLETE:
643                 /* Do nothing */
644                 break;
645             default:
646                 DEBUG("Unknown FlagType during game over in DrawMine\n"); 
647                 break;
648             }
649         } 
650     } else {    /* WAITING or PLAYING */
651         switch( p_board->box[col][row].FlagType ) {
652         case QUESTION:
653             if( !IsPressed )    
654                 offset = QUESTION_BMP;     
655             else
656                 offset = QPRESS_BMP;    
657             break;
658         case FLAG:
659             offset = FLAG_BMP;
660             break;
661         case NORMAL:    
662             if( !IsPressed )
663                 offset = BOX_BMP;
664             else 
665                 offset = MPRESS_BMP;
666             break;
667         case COMPLETE:
668             /* Do nothing */
669             break;
670         default:
671             DEBUG("Unknown FlagType while playing in DrawMine\n"); 
672             break;
673         }
674     } 
675
676     if( p_board->box[col][row].FlagType == COMPLETE  
677         && !p_board->box[col][row].IsMine )
678           offset = (MINEBMP_OFFSET) p_board->box[col][row].NumMines;
679     
680     BitBlt( hdc,
681             (col - 1) * MINE_WIDTH + p_board->mines_rect.left,
682             (row - 1) * MINE_HEIGHT + p_board->mines_rect.top,
683             MINE_WIDTH, MINE_HEIGHT,
684             hMemDC, 0, offset * MINE_HEIGHT, SRCCOPY );
685 }
686
687 void DrawLeds( HDC hdc, HDC hMemDC, BOARD *p_board, int number, int x, int y )
688 {
689     HGDIOBJ hOldObj;
690     unsigned led[3], i;
691     int count;     
692
693     count = number;
694     if( count < 1000 ) {
695         if( count >= 0 ) {
696             led[0] = count / 100 ;
697             count -= led[0] * 100;
698         }
699         else {
700             led[0] = 10; /* negative sign */
701             count = -count;
702         }
703         led[1] = count / 10;
704         count -= led[1] * 10;
705         led[2] = count;
706     }
707     else {
708         for( i = 0; i < 3; i++ )
709             led[i] = 10;
710     }
711
712     /* use unlit led if not playing */
713     if( p_board->status == WAITING )
714         for( i = 0; i < 3; i++ )
715             led[i] = 11;  
716     
717     hOldObj = SelectObject (hMemDC, p_board->hLedsBMP);
718
719     for( i = 0; i < 3; i++ ) {
720         BitBlt( hdc,
721             i * LED_WIDTH + x,
722             y,
723             LED_WIDTH,
724             LED_HEIGHT,
725             hMemDC, 
726             0, 
727             led[i] * LED_HEIGHT, 
728             SRCCOPY);
729     }
730     
731     SelectObject( hMemDC, hOldObj );
732 }
733
734
735 void DrawFace( HDC hdc, HDC hMemDC, BOARD *p_board )
736 {
737     HGDIOBJ hOldObj; 
738
739     hOldObj = SelectObject (hMemDC, p_board->hFacesBMP);
740
741     BitBlt( hdc,
742         p_board->face_rect.left,
743         p_board->face_rect.top,
744         FACE_WIDTH,
745         FACE_HEIGHT,
746         hMemDC, 0, p_board->face_bmp * FACE_HEIGHT, SRCCOPY);
747     
748     SelectObject( hMemDC, hOldObj );
749 }
750
751
752 void DrawBoard( HDC hdc, HDC hMemDC, PAINTSTRUCT *ps, BOARD *p_board )
753 {
754     RECT tmp_rect;    
755     
756     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->counter_rect ) )    
757         DrawLeds( hdc, hMemDC, p_board, p_board->mines - p_board->num_flags,
758                   p_board->counter_rect.left,
759                   p_board->counter_rect.top );
760
761     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->timer_rect ) )    
762         DrawLeds( hdc, hMemDC, p_board, p_board->time,
763                   p_board->timer_rect.left, 
764                   p_board->timer_rect.top );
765     
766     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->face_rect ) )    
767         DrawFace( hdc, hMemDC, p_board );
768     
769     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->mines_rect ) )    
770         DrawMines( hdc, hMemDC, p_board );
771 }    
772
773
774 void TestBoard( HWND hWnd, BOARD *p_board, unsigned x, unsigned y, int msg )
775 {
776     POINT pt;
777     
778     pt.x = x;
779     pt.y = y;
780
781     if( PtInRect( &p_board->mines_rect, pt ) && p_board->status != GAMEOVER
782     && p_board->status != WON ) 
783         TestMines( p_board, pt, msg );
784     else {
785         UnpressBoxes( p_board, 
786             p_board->press.x, 
787             p_board->press.y );    
788         p_board->press.x = 0;
789         p_board->press.y = 0;
790     }
791
792     if( p_board->boxes_left == 0 ) {
793         p_board->status = WON;
794        
795         if( p_board->difficulty != CUSTOM &&
796                     p_board->time < p_board->best_time[p_board->difficulty] ) {
797             p_board->best_time[p_board->difficulty] = p_board->time;
798         
799             DialogBoxParam( p_board->hInst, "DLG_CONGRATS", hWnd, 
800                     CongratsDlgProc, (LPARAM) p_board);
801     
802             DialogBoxParam( p_board->hInst, "DLG_TIMES", hWnd, 
803                     TimesDlgProc, (LPARAM) p_board);
804         }
805     }
806     TestFace( p_board, pt, msg );    
807 }
808
809 void TestMines( BOARD *p_board, POINT pt, int msg )
810 {
811     BOOL draw = TRUE;
812     unsigned col, row;    
813
814     col = (pt.x - p_board->mines_rect.left) / MINE_WIDTH + 1;
815     row = (pt.y - p_board->mines_rect.top ) / MINE_HEIGHT + 1;
816     
817     switch ( msg ) {
818     case WM_LBUTTONDOWN:
819         if( p_board->press.x != col || p_board->press.y != row ) {
820             UnpressBox( p_board, 
821                     p_board->press.x, p_board->press.y );
822             p_board->press.x = col;
823             p_board->press.y = row;
824             PressBox( p_board, col, row );    
825         }    
826         draw = FALSE;    
827         break;
828
829     case WM_LBUTTONUP:
830         if( p_board->press.x != col || p_board->press.y != row )
831             UnpressBox( p_board, 
832                     p_board->press.x, p_board->press.y );
833         p_board->press.x = 0;
834         p_board->press.y = 0;
835         if( p_board->box[col][row].FlagType != FLAG )
836             p_board->status = PLAYING;    
837         CompleteBox( p_board, col, row );
838         break;
839
840     case WM_MBUTTONDOWN:
841         PressBoxes( p_board, col, row );
842         draw = FALSE;
843         break;
844
845     case WM_MBUTTONUP:
846         if( p_board->press.x != col || p_board->press.y != row )
847             UnpressBoxes( p_board, 
848                     p_board->press.x, p_board->press.y );
849         p_board->press.x = 0;
850         p_board->press.y = 0;
851         CompleteBoxes( p_board, col, row );
852         break;
853
854     case WM_RBUTTONDOWN:
855         AddFlag( p_board, col, row );
856         p_board->status = PLAYING;    
857         break;
858     default:
859         DEBUG("Unknown message type received in TestMines\n");
860         break;
861     }
862
863     if( draw )
864     {
865         RedrawWindow( p_board->hWnd, NULL, NULL_HANDLE, 
866             RDW_INVALIDATE | RDW_UPDATENOW );
867     }
868 }    
869
870
871 void TestFace( BOARD *p_board, POINT pt, int msg )
872 {
873     if( p_board->status == PLAYING || p_board->status == WAITING ) {
874         if( msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN )
875             p_board->face_bmp = OOH_BMP;
876         else p_board->face_bmp = SMILE_BMP;
877     }
878     else if( p_board->status == GAMEOVER ) 
879         p_board->face_bmp = DEAD_BMP;
880     else if( p_board->status == WON ) 
881             p_board->face_bmp = COOL_BMP;
882
883     if( PtInRect( &p_board->face_rect, pt ) ) {        
884         if( msg == WM_LBUTTONDOWN )     
885             p_board->face_bmp = SPRESS_BMP;
886         
887         if( msg == WM_LBUTTONUP )     
888             CreateBoard( p_board );    
889     }
890
891     RedrawWindow( p_board->hWnd, &p_board->face_rect, NULL_HANDLE, 
892         RDW_INVALIDATE | RDW_UPDATENOW );
893 }
894
895
896 void CompleteBox( BOARD *p_board, unsigned col, unsigned row )
897 {
898     int i, j;
899
900     if( p_board->box[col][row].FlagType != COMPLETE && 
901             p_board->box[col][row].FlagType != FLAG && 
902             col > 0 && col < p_board->cols + 1 && 
903             row > 0 && row < p_board->rows + 1 ) {
904         p_board->box[col][row].FlagType = COMPLETE;
905     
906         if( p_board->box[col][row].IsMine ) {
907             p_board->face_bmp = DEAD_BMP; 
908             p_board->status = GAMEOVER;
909         }
910         else if( p_board->status != GAMEOVER ) 
911             p_board->boxes_left--;
912
913         if( p_board->box[col][row].NumMines == 0 ) 
914         {
915             for( i = -1; i <= 1; i++ )
916             for( j = -1; j <= 1; j++ )
917                 CompleteBox( p_board, col + i, row + j  );
918         }
919     } 
920 }
921
922
923 void CompleteBoxes( BOARD *p_board, unsigned col, unsigned row )
924 {
925     unsigned numFlags = 0;
926     int i, j;
927
928     if( p_board->box[col][row].FlagType == COMPLETE ) {
929         for( i = -1; i <= 1; i++ )
930           for( j = -1; j <= 1; j++ ) {
931             if( p_board->box[col+i][row+j].FlagType == FLAG ) 
932                 numFlags++;
933           }
934     
935         if( numFlags == p_board->box[col][row].NumMines ) {
936             for( i = -1; i <= 1; i++ )
937               for( j = -1; j <= 1; j++ ) {
938                 if( p_board->box[col+i][row+j].FlagType != FLAG ) 
939                     CompleteBox( p_board, col+i, row+j );
940               }
941         }
942     }
943 }
944
945
946 void AddFlag( BOARD *p_board, unsigned col, unsigned row )
947 {
948     if( p_board->box[col][row].FlagType != COMPLETE ) {
949         switch( p_board->box[col][row].FlagType ) {
950         case FLAG:
951             if( p_board->IsMarkQ )
952                 p_board->box[col][row].FlagType = QUESTION;
953             else 
954                 p_board->box[col][row].FlagType = NORMAL;
955             p_board->num_flags--;
956             break;
957
958         case QUESTION:
959             p_board->box[col][row].FlagType = NORMAL;
960             break;
961         
962         default:
963             p_board->box[col][row].FlagType = FLAG;
964             p_board->num_flags++;
965         }    
966     }    
967 }
968
969
970 void PressBox( BOARD *p_board, unsigned col, unsigned row )
971 {
972     HDC hdc;      
973     HGDIOBJ hOldObj; 
974     HDC hMemDC;
975
976     hdc = GetDC( p_board->hWnd );
977     hMemDC = CreateCompatibleDC( hdc );
978     hOldObj = SelectObject (hMemDC, p_board->hMinesBMP);
979
980     DrawMine( hdc, hMemDC, p_board, col, row, TRUE );
981
982     SelectObject( hMemDC, hOldObj ); 
983     DeleteDC( hMemDC );
984     ReleaseDC( p_board->hWnd, hdc );
985 }
986
987
988 void PressBoxes( BOARD *p_board, unsigned col, unsigned row )
989 {
990     int i, j;
991
992     for( i = -1; i <= 1; i++ )
993       for( j = -1; j <= 1; j++ ) {
994         p_board->box[col + i][row + j].IsPressed = TRUE;    
995         PressBox( p_board, col + i, row + j );
996     }
997
998     for( i = -1; i <= 1; i++ )
999       for( j = -1; j <= 1; j++ ) {
1000         if( !p_board->box[p_board->press.x + i][p_board->press.y + j].IsPressed )    
1001             UnpressBox( p_board, p_board->press.x + i, p_board->press.y + j );
1002     }
1003
1004     for( i = -1; i <= 1; i++ )
1005       for( j = -1; j <= 1; j++ ) {
1006         p_board->box[col + i][row + j].IsPressed = FALSE;    
1007         PressBox( p_board, col + i, row + j );
1008     }
1009
1010     p_board->press.x = col;
1011     p_board->press.y = row;    
1012 }
1013
1014
1015 void UnpressBox( BOARD *p_board, unsigned col, unsigned row )
1016 {
1017     HDC hdc; 
1018     HGDIOBJ hOldObj; 
1019     HDC hMemDC;
1020
1021     hdc = GetDC( p_board->hWnd );
1022     hMemDC = CreateCompatibleDC( hdc );
1023     hOldObj = SelectObject( hMemDC, p_board->hMinesBMP );
1024
1025     DrawMine( hdc, hMemDC, p_board, col, row, FALSE );
1026
1027     SelectObject( hMemDC, hOldObj ); 
1028     DeleteDC( hMemDC );
1029     ReleaseDC( p_board->hWnd, hdc );
1030 }
1031
1032
1033 void UnpressBoxes( BOARD *p_board, unsigned col, unsigned row )
1034 {
1035     int i, j;    
1036     
1037     for( i = -1; i <= 1; i++ )
1038       for( j = -1; j <= 1; j++ ) {
1039         UnpressBox( p_board, col + i, row + j );
1040       }
1041 }