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