1 /*
2 * Copyright � 1993-1999 Marc Baudoin <babafou@babafou.eu.org>
3 *
4 * Permission to use, copy, modify, and distribute this software and
5 * its documentation for any purpose and without fee is hereby
6 * granted, provided that the above copyright notice appear in all
7 * copies and that both that copyright notice and this permission
8 * notice appear in supporting documentation. The author makes no
9 * representations about the suitability of this software for any
10 * purpose. It is provided "as is" without express or implied
11 * warranty.
12 *
13 */
14
15 static char *const cvsid = "$Id: xdemineur.c,v 1.3.2.2 1999/07/29 21:25:33 babafou Exp $" ;
16
17 #include <sys/types.h>
18
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sysexits.h>
23 #include <unistd.h>
24
25 #include <sys/time.h>
26
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/keysym.h>
30
31 #include <X11/xpm.h>
32
33 #include "demineur.h"
34 #include "xdemineur.h"
35
36 #include "xdemineur.xbm"
37
38 #include "pixmaps/face_normal.xpm"
39 #include "pixmaps/face_click.xpm"
40 #include "pixmaps/face_play.xpm"
41 #include "pixmaps/face_happy.xpm"
42 #include "pixmaps/face_sad.xpm"
43
44 #include "pixmaps/digit_0.xpm"
45 #include "pixmaps/digit_1.xpm"
46 #include "pixmaps/digit_2.xpm"
47 #include "pixmaps/digit_3.xpm"
48 #include "pixmaps/digit_4.xpm"
49 #include "pixmaps/digit_5.xpm"
50 #include "pixmaps/digit_6.xpm"
51 #include "pixmaps/digit_7.xpm"
52 #include "pixmaps/digit_8.xpm"
53 #include "pixmaps/digit_9.xpm"
54
55 #include "pixmaps/square_0.xpm"
56 #include "pixmaps/square_1.xpm"
57 #include "pixmaps/square_2.xpm"
58 #include "pixmaps/square_3.xpm"
59 #include "pixmaps/square_4.xpm"
60 #include "pixmaps/square_5.xpm"
61 #include "pixmaps/square_6.xpm"
62 #include "pixmaps/square_7.xpm"
63 #include "pixmaps/square_8.xpm"
64
65 #include "pixmaps/relief.xpm"
66 #include "pixmaps/flag.xpm"
67 #include "pixmaps/question.xpm"
68 #include "pixmaps/mine.xpm"
69 #include "pixmaps/mine_lost.xpm"
70 #include "pixmaps/mine_false.xpm"
71
72 /* ------------------------------------------------------------------------- */
73
74 typedef enum
75 {
76 ITEM_NOTHING , ITEM_SQUARE , ITEM_FACE
77 }
78 item_t ;
79
80 typedef struct
81 {
82 int row , column ;
83 enum
84 {
85 STATE_NONE , STATE_UNCOVER , STATE_CLEAR , STATE_FLAG_QUESTION ,
86 STATE_FACE
87 }
88 state ;
89 }
90 previous_t ;
91
92 typedef enum
93 {
94 RAISED , SUNKEN
95 }
96 relief_t ;
97
98 typedef enum
99 {
100 INSIDE , OUTSIDE
101 }
102 inout_t ;
103
104 /* ------------------------------------------------------------------------- */
105
106 void xdemineur_colors ( ) ;
107
108 void xdemineur_pixmaps ( ) ;
109
110 void xdemineur_xpm ( char **data , Pixmap *pixmap_return ) ;
111
112 item_t mouse ( int x , int y , int *row , int *column ) ;
113
114
115 void xdemineur_frames ( ) ;
116
117 void xdemineur_frame ( int x1 , int y1 , int x2 , int y2 , int width ,
118 relief_t relief , inout_t inoutside ) ;
119
120 void xdemineur_face_click ( ) ;
121
122 void xdemineur_face_play ( ) ;
123
124 void xdemineur_face_display ( Pixmap face ) ;
125
126 void xdemineur_mines ( ) ;
127
128 void xdemineur_digits ( int number , int digits , int x , int y ) ;
129
130 void xdemineur_grid ( ) ;
131
132 void xdemineur_square_play ( int row , int column ) ;
133
134 void xdemineur_squares_clear ( int row , int column ) ;
135
136 void xdemineur_squares ( int row , int column ) ;
137
138 void xdemineur_end ( ) ;
139
140 /* ------------------------------------------------------------------------- */
141
142 extern board_t board ;
143 extern int mines ;
144 extern volatile time_t elapsed ;
145 extern state_t state ;
146
147 static Display *display ;
148 static int screen ;
149 static Window window ;
150 static Pixmap icon_bitmap ;
151 static Atom protocol[1] ;
152 static GC gc ;
153
154 static unsigned long black , white , gray , light_gray ;
155
156 static Pixmap face_normal , face_click , face_play , face_happy , face_sad ,
157 digit[10] , square[9] ,
158 relief , flag , question , mine , mine_lost , mine_false ;
159
160 /* ------------------------------------------------------------------------- */
161
xdemineur_initialize(int argc,char ** argv,char * display_name,char * geometry)162 void xdemineur_initialize ( int argc , char **argv ,
163 char *display_name , char *geometry )
164 {
165 XSizeHints size_hints ;
166 int x_pos = 0 , y_pos = 0 ,
167 width = MIN_WIDTH , height = MIN_HEIGHT ;
168 char *window_title = "xd�mineur" , *icon_title = "xd�mineur" ;
169 XTextProperty window_name , icon_name ;
170 XWMHints wm_hints ;
171 XClassHint class_hints ;
172 XGCValues values ;
173
174 display = XOpenDisplay ( display_name ) ;
175 if ( display == NULL )
176 {
177 fprintf ( stderr , "Error: Can't open display: %s\n" ,
178 XDisplayName ( display_name ) ) ;
179 exit ( EX_OSERR ) ;
180 }
181
182 screen = DefaultScreen ( display ) ;
183
184 board.columns = COLUMNS_MIN ;
185 board.rows = ROWS_MIN ;
186
187 size_hints.flags = 0 ;
188 if ( geometry != NULL )
189 {
190 int flags , x , y ;
191 unsigned int w , h ;
192
193 flags = XParseGeometry ( geometry , &x , &y , &w , &h ) ;
194
195 if ( WidthValue & flags )
196 {
197 if ( w < COLUMNS_MIN )
198 {
199 board.columns = COLUMNS_MIN ;
200 fprintf ( stderr ,
201 "%d columns is too small! Using %d columns instead.\n" ,
202 w , COLUMNS_MIN ) ;
203 }
204 else
205 {
206 board.columns = w ;
207 width = BASE_WIDTH + board.columns * WIDTH_INC ;
208 }
209 }
210
211 if ( HeightValue & flags )
212 {
213 if ( h < ROWS_MIN )
214 {
215 board.rows = ROWS_MIN ;
216 fprintf ( stderr ,
217 "%d rows is too small! Using %d rows instead.\n" ,
218 h , ROWS_MIN ) ;
219 }
220 else
221 {
222 board.rows = h ;
223 height = BASE_HEIGHT + board.rows * HEIGHT_INC ;
224 }
225 }
226
227 if ( XValue & flags )
228 {
229 size_hints.flags = USPosition ;
230 if ( XNegative & flags )
231 {
232 x_pos = DisplayWidth ( display , screen ) - width + x ;
233 }
234 else
235 {
236 x_pos = x ;
237 }
238 }
239
240 if ( YValue & flags )
241 {
242 size_hints.flags = USPosition ;
243 if ( YNegative & flags )
244 {
245 y_pos = DisplayHeight ( display , screen ) - height + y ;
246 }
247 else
248 {
249 y_pos = y ;
250 }
251 }
252 }
253
254 window = XCreateSimpleWindow ( display , RootWindow ( display , screen ) ,
255 x_pos , y_pos , width , height , 1 ,
256 BlackPixel ( display , screen ) ,
257 WhitePixel ( display , screen ) ) ;
258
259 XStringListToTextProperty ( &window_title , 1 , &window_name ) ;
260 XStringListToTextProperty ( &icon_title , 1 , &icon_name ) ;
261
262 size_hints.flags |= USSize | PMinSize | PMaxSize |
263 PResizeInc | PBaseSize ;
264 size_hints.min_width = size_hints.max_width = width ;
265 size_hints.min_height = size_hints.max_height = height ;
266 size_hints.width_inc = WIDTH_INC ;
267 size_hints.height_inc = HEIGHT_INC ;
268 size_hints.base_width = BASE_WIDTH ;
269 size_hints.base_height = BASE_HEIGHT ;
270
271 icon_bitmap = XCreateBitmapFromData ( display ,
272 RootWindow ( display , screen ) ,
273 xdemineur_bits ,
274 xdemineur_width , xdemineur_height ) ;
275
276 wm_hints.flags = InputHint | StateHint | IconPixmapHint | WindowGroupHint;
277 wm_hints.input = True ;
278 wm_hints.initial_state = NormalState ;
279 wm_hints.icon_pixmap = icon_bitmap ;
280 wm_hints.window_group = window ;
281
282 class_hints.res_name = argv[0] ;
283 class_hints.res_class = "XD�mineur" ;
284
285 XSetWMProperties ( display , window ,
286 &window_name , &icon_name ,
287 argv , argc ,
288 &size_hints , &wm_hints , &class_hints ) ;
289
290 XSetCommand ( display , window , argv , argc ) ;
291
292 protocol[0] = XInternAtom ( display , "WM_DELETE_WINDOW" , False ) ;
293 XSetWMProtocols ( display , window , protocol , 1 ) ;
294
295 values.foreground = BlackPixel ( display , screen ) ;
296 values.background = WhitePixel ( display , screen ) ;
297 values.graphics_exposures = False ;
298 gc = XCreateGC ( display , window ,
299 GCForeground | GCBackground | GCGraphicsExposures ,
300 &values ) ;
301
302 XSelectInput ( display , window ,
303 KeyPressMask |
304 ButtonPressMask |
305 ButtonReleaseMask |
306 ExposureMask |
307 VisibilityChangeMask |
308 StructureNotifyMask ) ;
309
310 xdemineur_colors ( ) ;
311 xdemineur_pixmaps ( ) ;
312
313 XMapWindow ( display , window ) ;
314 }
315
316 /* ------------------------------------------------------------------------- */
317
xdemineur_colors()318 void xdemineur_colors ( )
319 {
320 Colormap default_colormap ;
321 XColor color ;
322
323 default_colormap = DefaultColormap ( display , screen ) ;
324 XParseColor ( display , default_colormap , "black" , &color ) ;
325 XAllocColor ( display , default_colormap , &color ) ;
326 black = color.pixel ;
327 XParseColor ( display , default_colormap , "white" , &color ) ;
328 XAllocColor ( display , default_colormap , &color ) ;
329 white = color.pixel ;
330 XParseColor ( display , default_colormap , "gray50" , &color ) ;
331 XAllocColor ( display , default_colormap , &color ) ;
332 gray = color.pixel ;
333 XParseColor ( display , default_colormap , "gray70" , &color ) ;
334 XAllocColor ( display , default_colormap , &color ) ;
335 light_gray = color.pixel ;
336
337 XSetWindowBackground ( display , window , light_gray ) ;
338 }
339
340 /* ------------------------------------------------------------------------- */
341
xdemineur_pixmaps()342 void xdemineur_pixmaps ( )
343 {
344 xdemineur_xpm ( xpm_face_normal , &face_normal ) ;
345 xdemineur_xpm ( xpm_face_click , &face_click ) ;
346 xdemineur_xpm ( xpm_face_play , &face_play ) ;
347 xdemineur_xpm ( xpm_face_happy , &face_happy ) ;
348 xdemineur_xpm ( xpm_face_sad , &face_sad ) ;
349
350 xdemineur_xpm ( xpm_digit_0 , &digit[0] ) ;
351 xdemineur_xpm ( xpm_digit_1 , &digit[1] ) ;
352 xdemineur_xpm ( xpm_digit_2 , &digit[2] ) ;
353 xdemineur_xpm ( xpm_digit_3 , &digit[3] ) ;
354 xdemineur_xpm ( xpm_digit_4 , &digit[4] ) ;
355 xdemineur_xpm ( xpm_digit_5 , &digit[5] ) ;
356 xdemineur_xpm ( xpm_digit_6 , &digit[6] ) ;
357 xdemineur_xpm ( xpm_digit_7 , &digit[7] ) ;
358 xdemineur_xpm ( xpm_digit_8 , &digit[8] ) ;
359 xdemineur_xpm ( xpm_digit_9 , &digit[9] ) ;
360
361 xdemineur_xpm ( xpm_square_0 , &square[0] ) ;
362 xdemineur_xpm ( xpm_square_1 , &square[1] ) ;
363 xdemineur_xpm ( xpm_square_2 , &square[2] ) ;
364 xdemineur_xpm ( xpm_square_3 , &square[3] ) ;
365 xdemineur_xpm ( xpm_square_4 , &square[4] ) ;
366 xdemineur_xpm ( xpm_square_5 , &square[5] ) ;
367 xdemineur_xpm ( xpm_square_6 , &square[6] ) ;
368 xdemineur_xpm ( xpm_square_7 , &square[7] ) ;
369 xdemineur_xpm ( xpm_square_8 , &square[8] ) ;
370
371 xdemineur_xpm ( xpm_relief , &relief ) ;
372 xdemineur_xpm ( xpm_flag , &flag ) ;
373 xdemineur_xpm ( xpm_question , &question ) ;
374 xdemineur_xpm ( xpm_mine , &mine ) ;
375 xdemineur_xpm ( xpm_mine_lost , &mine_lost ) ;
376 xdemineur_xpm ( xpm_mine_false , &mine_false ) ;
377 }
378
379 /* ------------------------------------------------------------------------- */
380
xdemineur_xpm(char ** data,Pixmap * pixmap_return)381 void xdemineur_xpm ( char **data , Pixmap *pixmap_return )
382 {
383 int xpm_status ;
384
385 xpm_status = XpmCreatePixmapFromData ( display , window ,
386 data , pixmap_return ,
387 NULL , NULL ) ;
388 if ( xpm_status != XpmSuccess )
389 {
390 fprintf ( stderr ,
391 "XpmError: %s\n" , XpmGetErrorString ( xpm_status ) ) ;
392 exit ( EX_OSERR ) ;
393 }
394 }
395
396 /* ------------------------------------------------------------------------- */
397
xdemineur_event_loop()398 void xdemineur_event_loop ( )
399 {
400 fd_set readfds ;
401 XEvent event ;
402 Region region ;
403 XRectangle rectangle ;
404 item_t item ;
405 previous_t previous ;
406
407 FD_ZERO ( &readfds ) ;
408 region = XCreateRegion ( ) ;
409 for ( ; ; )
410 {
411 int row , column ;
412
413 if ( XPending ( display ) == 0 ) /* no more events to handle */
414 {
415 FD_SET ( ConnectionNumber ( display ) , &readfds ) ;
416 if ( select ( ConnectionNumber ( display ) + 1 , &readfds , NULL ,
417 NULL , NULL ) == -1 ) /* wait for events or signal */
418 {
419 if ( errno == EINTR ) /* interrupted by signal */
420 {
421 xdemineur_timer ( ) ;
422 XFlush ( display ) ;
423 continue ;
424 }
425 }
426 /* an event occurred, proceed to XNextEvent() */
427 }
428
429 XNextEvent ( display , &event ) ;
430 switch ( event.type )
431 {
432 case KeyPress :
433 switch ( XLookupKeysym ( &event.xkey , event.xkey.state ) )
434 {
435 case XK_Escape :
436 case XK_Q :
437 case XK_q :
438 XDestroyRegion ( region ) ;
439 xdemineur_end ( ) ;
440 return ;
441 }
442 break ;
443 case ButtonPress :
444 switch ( mouse ( event.xbutton.x , event.xbutton.y ,
445 &row , &column ) )
446 {
447 case ITEM_SQUARE :
448 if ( state != PLAYING )
449 {
450 previous.state = STATE_NONE ;
451 break ;
452 }
453
454 switch ( event.xbutton.button )
455 {
456 case Button1 : /* uncover square */
457 if ( board.board[row][column].state == HIDDEN )
458 {
459 xdemineur_face_play ( ) ;
460 xdemineur_square_play ( row , column ) ;
461 previous.row = row ;
462 previous.column = column ;
463 previous.state = STATE_UNCOVER ;
464 }
465 else
466 {
467 previous.state = STATE_NONE ;
468 }
469 break ;
470 case Button2 : /* uncover squares around */
471 if ( board.board[row][column].state == UNCOVERED )
472 {
473 if ( demineur_hidden ( row , column ) != 0 &&
474 board.board[row][column].around
475 == demineur_flags ( row , column ) )
476 {
477 xdemineur_face_play ( ) ;
478 xdemineur_squares_clear ( row , column ) ;
479 previous.row = row ;
480 previous.column = column ;
481 previous.state = STATE_CLEAR ;
482 }
483 else
484 {
485 previous.state = STATE_NONE ;
486 }
487 }
488 break ;
489 case Button3 : /* put flag or question mark */
490 if ( board.board[row][column].state == UNCOVERED )
491 {
492 previous.state = STATE_NONE;
493 break ;
494 }
495
496 xdemineur_face_play ( ) ;
497 demineur_flag_question ( row , column ) ;
498 xdemineur_mines ( ) ;
499 xdemineur_square ( row , column ) ;
500 previous.state = STATE_FLAG_QUESTION ;
501 break ;
502 }
503 break ;
504 case ITEM_FACE :
505 xdemineur_face_click ( ) ;
506 previous.state = STATE_FACE ;
507 break ;
508 case ITEM_NOTHING :
509 previous.state = STATE_NONE ;
510 break ;
511 }
512 break ;
513 case ButtonRelease :
514 item = mouse ( event.xbutton.x , event.xbutton.y , &row , &column ) ;
515 switch ( previous.state )
516 {
517 case STATE_NONE :
518 break ;
519 case STATE_UNCOVER :
520 if ( row == previous.row && column == previous.column )
521 {
522 demineur_play ( row , column ) ;
523 }
524 else
525 {
526 xdemineur_square ( previous.row , previous.column ) ;
527 }
528 xdemineur_face ( ) ;
529 break ;
530 case STATE_CLEAR :
531 if ( row == previous.row && column == previous.column )
532 {
533 demineur_clear ( row , column ) ;
534 }
535 else
536 {
537 xdemineur_squares ( previous.row , previous.column ) ;
538 }
539 xdemineur_face ( ) ;
540 break ;
541 case STATE_FLAG_QUESTION :
542 xdemineur_face ( ) ;
543 break ;
544 case STATE_FACE :
545 if ( item == ITEM_FACE ) /* new game */
546 {
547 demineur_end ( ) ;
548 demineur_initialize ( 0 ) ;
549 xdemineur_display ( ) ;
550 demineur_start_timer ( ) ;
551 }
552 else
553 {
554 xdemineur_face ( ) ;
555 }
556 break ;
557 }
558 previous.state = STATE_NONE ;
559 break ;
560 case Expose :
561 rectangle.x = ( short ) event.xexpose.x ;
562 rectangle.y = ( short ) event.xexpose.y ;
563 rectangle.width = ( unsigned short ) event.xexpose.width ;
564 rectangle.height = ( unsigned short ) event.xexpose.height ;
565 XUnionRectWithRegion ( &rectangle , region , region ) ;
566 if ( event.xexpose.count == 0 )
567 {
568 XSetRegion ( display , gc , region ) ;
569 xdemineur_display ( ) ;
570 XSetClipMask ( display , gc , None ) ;
571 XDestroyRegion ( region ) ;
572 region = XCreateRegion ( ) ;
573 }
574 break ;
575 case VisibilityNotify :
576 switch ( event.xvisibility.state )
577 {
578 case VisibilityUnobscured :
579 if ( state == PLAYING )
580 {
581 demineur_start_timer ( ) ;
582 }
583 break ;
584 case VisibilityPartiallyObscured :
585 break ;
586 case VisibilityFullyObscured :
587 if ( state == PLAYING )
588 {
589 demineur_stop_timer ( ) ;
590 }
591 break ;
592 }
593 break ;
594 case UnmapNotify : /* the window has been iconified */
595 if ( state == PLAYING )
596 {
597 demineur_stop_timer ( ) ;
598 }
599 break ;
600 case MapNotify : /* the window has been deiconified */
601 if ( state == PLAYING )
602 {
603 demineur_start_timer ( ) ;
604 }
605 break ;
606 case ClientMessage :
607 if ( event.xclient.data.l[0] == protocol[0] )
608 {
609 XDestroyRegion ( region ) ;
610 xdemineur_end ( ) ;
611 return ;
612 }
613 break ;
614 }
615 }
616 }
617
618 /* ------------------------------------------------------------------------- */
619
mouse(int x,int y,int * row,int * column)620 item_t mouse ( int x , int y , int *row , int *column )
621 {
622 int board_width = board.columns * WIDTH_INC ,
623 board_height = board.rows * HEIGHT_INC ,
624 x_face = ( BASE_WIDTH + board.columns * WIDTH_INC - FACE_WIDTH ) / 2 ;
625
626 *row = *column = 0 ;
627
628 if ( x > X_BOARD &&
629 x < X_BOARD + board_width &&
630 y > Y_BOARD &&
631 y < Y_BOARD + board_height )
632 {
633 if ( ( x - X_BOARD ) % WIDTH_INC == 0 ||
634 ( y - Y_BOARD ) % HEIGHT_INC == 0 )
635 {
636 return ITEM_NOTHING ;
637 }
638 else
639 {
640 *column = 1 + ( x - X_BOARD ) / WIDTH_INC ;
641 *row = 1 + ( y - Y_BOARD ) / HEIGHT_INC ;
642 return ITEM_SQUARE ;
643 }
644 }
645 else if ( x >= x_face &&
646 x <= x_face + FACE_WIDTH &&
647 y >= Y_FACE &&
648 y <= Y_FACE + FACE_HEIGHT )
649 {
650 return ITEM_FACE ;
651 }
652 else
653 {
654 return ITEM_NOTHING ;
655 }
656 }
657
658 /* ------------------------------------------------------------------------- */
659
xdemineur_display()660 void xdemineur_display ( )
661 {
662 int row , column ;
663
664 xdemineur_frames ( ) ;
665 xdemineur_face ( ) ;
666 xdemineur_mines ( ) ;
667 xdemineur_timer ( ) ;
668 xdemineur_grid ( ) ;
669 for ( row = 1 ; row <= board.rows ; row ++ )
670 {
671 for ( column = 1 ; column <= board.columns ; column ++ )
672 {
673 xdemineur_square ( row , column ) ;
674 }
675 }
676 }
677
678 /* ------------------------------------------------------------------------- */
679
xdemineur_frames()680 void xdemineur_frames ( )
681 {
682 int board_width = board.columns * WIDTH_INC ;
683 int board_height = board.rows * HEIGHT_INC ;
684 int window_width = BASE_WIDTH + board_width ;
685 int window_height = BASE_HEIGHT + board_height ;
686
687 xdemineur_frame ( 0 , 0 , window_width - 1 , window_height - 1 ,
688 RELIEF_WIDTH , RAISED , INSIDE) ;
689 xdemineur_frame ( X_BOARD , Y_BOARD ,
690 X_BOARD + board_width , Y_BOARD + board_height ,
691 RELIEF_WIDTH , SUNKEN , OUTSIDE) ;
692 xdemineur_frame ( EDGE , EDGE , window_width - 1 - EDGE , Y_BOARD - EDGE ,
693 RELIEF_WIDTH , SUNKEN , OUTSIDE ) ;
694 }
695
696 /* ------------------------------------------------------------------------- */
697
xdemineur_frame(int x1,int y1,int x2,int y2,int width,relief_t relief,inout_t inoutside)698 void xdemineur_frame ( int x1 , int y1 , int x2 , int y2 , int width ,
699 relief_t relief , inout_t inoutside )
700 {
701 int coord ;
702
703 if ( inoutside == OUTSIDE )
704 {
705 x1 -= width ; x2 += width ;
706 y1 -= width ; y2 += width ;
707 }
708
709 XSetForeground ( display , gc , ( relief == RAISED ) ? white : gray ) ;
710 for ( coord = 0 ; coord < width ; coord ++ )
711 {
712 XDrawLine ( display , window , gc ,
713 x1 , y1 + coord , x2 - coord , y1 + coord ) ;
714 XDrawLine ( display , window , gc ,
715 x1 + coord , y1 , x1 + coord , y2 - coord ) ;
716 }
717 XSetForeground ( display , gc , ( relief == RAISED ) ? gray : white ) ;
718 for ( coord = 0 ; coord < width ; coord ++ )
719 {
720 XDrawLine ( display , window , gc ,
721 x1 + 1 + coord , y2 - coord , x2 , y2 - coord ) ;
722 XDrawLine ( display , window , gc ,
723 x2 - coord , y1 + 1 + coord , x2 - coord , y2 ) ;
724 }
725 }
726
727 /* ------------------------------------------------------------------------- */
728
xdemineur_face()729 void xdemineur_face ( )
730 {
731 switch ( state )
732 {
733 case PLAYING :
734 xdemineur_face_display ( face_normal ) ;
735 break ;
736 case WON :
737 xdemineur_face_display ( face_happy ) ;
738 break ;
739 case LOST :
740 xdemineur_face_display ( face_sad ) ;
741 break ;
742 }
743 }
744
745 /* ------------------------------------------------------------------------- */
746
xdemineur_face_click()747 void xdemineur_face_click ( )
748 {
749 xdemineur_face_display ( face_click ) ;
750 }
751
752 /* ------------------------------------------------------------------------- */
753
xdemineur_face_play()754 void xdemineur_face_play ( )
755 {
756 xdemineur_face_display ( face_play ) ;
757 }
758
759 /* ------------------------------------------------------------------------- */
760
xdemineur_face_display(Pixmap face)761 void xdemineur_face_display ( Pixmap face )
762 {
763 XCopyArea ( display , face , window , gc ,
764 0 , 0 , FACE_WIDTH , FACE_HEIGHT ,
765 ( BASE_WIDTH + board.columns * WIDTH_INC - FACE_WIDTH ) / 2 ,
766 Y_FACE ) ;
767 }
768
769 /* ------------------------------------------------------------------------- */
770
xdemineur_mines()771 void xdemineur_mines ( )
772 {
773 xdemineur_digits ( mines , 4 , X_DIGITS , Y_DIGITS ) ;
774 }
775
776 /* ------------------------------------------------------------------------- */
777
xdemineur_timer()778 void xdemineur_timer ( )
779 {
780 int x = BASE_WIDTH + board.columns * WIDTH_INC
781 - X_DIGITS - 4 * DIGIT_WIDTH ;
782
783 xdemineur_digits ( elapsed , 4 , x , Y_DIGITS ) ;
784 }
785
786 /* ------------------------------------------------------------------------- */
787
xdemineur_digits(int number,int digits,int x,int y)788 void xdemineur_digits ( int number , int digits , int x , int y )
789 {
790 int i , remainder = number ;
791
792 for ( i = digits - 1 ; i >= 0 ; i -- , remainder /= 10 )
793 {
794 XCopyArea ( display , digit[remainder % 10] , window , gc ,
795 0 , 0 , DIGIT_WIDTH , DIGIT_HEIGHT ,
796 x + i * DIGIT_WIDTH , y ) ;
797 }
798
799 xdemineur_frame ( x , y ,
800 x + digits * DIGIT_WIDTH - 1 , y + DIGIT_HEIGHT - 1 ,
801 1 , SUNKEN , OUTSIDE ) ;
802 }
803
804 /* ------------------------------------------------------------------------- */
805
xdemineur_grid()806 void xdemineur_grid ( )
807 {
808 int coord ,
809 board_width = board.columns * WIDTH_INC + 1 ,
810 board_height = board.rows * HEIGHT_INC + 1 ;
811
812 XSetForeground ( display , gc , black ) ;
813
814 for ( coord = X_BOARD ;
815 coord < X_BOARD + board_width ;
816 coord += WIDTH_INC )
817 {
818 XDrawLine ( display , window , gc ,
819 coord , Y_BOARD , coord , Y_BOARD + board_height - 1 ) ;
820 }
821
822 for ( coord = Y_BOARD ;
823 coord < Y_BOARD + board_height ;
824 coord += HEIGHT_INC )
825 {
826 XDrawLine ( display , window , gc ,
827 X_BOARD , coord , X_BOARD + board_width - 1 , coord ) ;
828 }
829 }
830
831 /* ------------------------------------------------------------------------- */
832
xdemineur_square(int row,int column)833 void xdemineur_square ( int row , int column )
834 {
835 int x = X_BOARD + 1 + ( column - 1 ) * ( SQUARE_WIDTH + 1 ) ,
836 y = Y_BOARD + 1 + ( row - 1 ) * ( SQUARE_HEIGHT + 1 ) ;
837
838 if ( row < 1 || row > board.rows || column < 1 || column > board.columns )
839 {
840 return ;
841 }
842
843 switch ( board.board[row][column].state )
844 {
845 case HIDDEN :
846 if ( state == LOST && board.board[row][column].mine )
847 {
848 XCopyArea ( display , mine , window , gc ,
849 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
850 }
851 else
852 {
853 XCopyArea ( display , relief , window , gc ,
854 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
855 }
856 break ;
857 case FLAGGED :
858 if ( state == LOST && ! board.board[row][column].mine )
859 {
860 XCopyArea ( display , mine_false , window , gc ,
861 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
862 }
863 else
864 {
865 XCopyArea ( display , flag , window , gc ,
866 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
867 }
868 break ;
869 case QUESTION :
870 if ( state == LOST && board.board[row][column].mine )
871 {
872 XCopyArea ( display , mine , window , gc ,
873 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
874 }
875 else
876 {
877 XCopyArea ( display , question , window , gc ,
878 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
879 }
880 break ;
881 case UNCOVERED :
882 if ( ! board.board[row][column].mine )
883 {
884 XCopyArea ( display , square[board.board[row][column].around] ,
885 window , gc ,
886 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
887 }
888 else
889 {
890 XCopyArea ( display , mine_lost , window , gc ,
891 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
892 }
893 break ;
894 }
895 }
896
897 /* ------------------------------------------------------------------------- */
898
xdemineur_square_play(int row,int column)899 void xdemineur_square_play ( int row , int column )
900 {
901 int x = X_BOARD + 1 + ( column - 1 ) * ( SQUARE_WIDTH + 1 ) ,
902 y = Y_BOARD + 1 + ( row - 1 ) * ( SQUARE_HEIGHT + 1 ) ;
903
904 if ( row < 1 || row > board.rows || column < 1 || column > board.columns )
905 {
906 return ;
907 }
908
909 XCopyArea ( display , square[0] , window , gc ,
910 0 , 0 , SQUARE_WIDTH , SQUARE_HEIGHT , x , y ) ;
911 }
912
913 /* ------------------------------------------------------------------------- */
914
xdemineur_squares_clear(int row,int column)915 void xdemineur_squares_clear ( int row , int column )
916 {
917 int r , c ;
918
919 for ( r = row - 1 ; r <= row + 1 ; r ++ )
920 {
921 for ( c = column - 1 ; c <= column + 1 ; c ++ )
922 {
923 if ( board.board[r][c].state == HIDDEN )
924 {
925 xdemineur_square_play ( r , c ) ;
926 }
927 }
928 }
929 }
930
931 /* ------------------------------------------------------------------------- */
932
xdemineur_squares(int row,int column)933 void xdemineur_squares ( int row , int column )
934 {
935 int r , c ;
936
937 for ( r = row - 1 ; r <= row + 1 ; r ++ )
938 {
939 for ( c = column - 1 ; c <= column + 1 ; c ++ )
940 {
941 xdemineur_square ( r , c ) ;
942 }
943 }
944 }
945
946 /* ------------------------------------------------------------------------- */
947
xdemineur_end()948 void xdemineur_end ( )
949 {
950 int i ;
951
952 XFreePixmap ( display , face_normal ) ;
953 XFreePixmap ( display , face_click ) ;
954 XFreePixmap ( display , face_play ) ;
955 XFreePixmap ( display , face_happy ) ;
956 XFreePixmap ( display , face_sad ) ;
957 for ( i = 0 ; i <= 9 ; i ++ )
958 {
959 XFreePixmap ( display , digit[i] ) ;
960 }
961 for ( i = 0 ; i <= 8 ; i ++ )
962 {
963 XFreePixmap ( display , square[i] ) ;
964 }
965 XFreePixmap ( display , relief ) ;
966 XFreePixmap ( display , flag ) ;
967 XFreePixmap ( display , question ) ;
968 XFreePixmap ( display , mine ) ;
969 XFreePixmap ( display , mine_lost ) ;
970 XFreePixmap ( display , mine_false ) ;
971
972 XFreeGC ( display , gc ) ;
973 XFreePixmap ( display , icon_bitmap ) ;
974 XDestroyWindow ( display , window ) ;
975 XCloseDisplay ( display ) ;
976 }
977