1 /***************************************
2   XBomb - 'Minesweeper' game - Version 2.2b.
3 
4   All X Window functions.
5   ******************/ /******************
6   Written by Andrew M. Bishop
7 
8   This file Copyright 1994-2014 Andrew M. Bishop
9   It may be distributed under the GNU Public License, version 2, or
10   any higher version.  See section COPYING of the GNU Public license
11   for conditions under which this file may be redistributed.
12   ***************************************/
13 
14 
15 #include <stdio.h>
16 
17 #include <X11/X.h>
18 #include <X11/Xlib.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #include <X11/Xaw/Command.h>
22 #include <X11/Xaw/Toggle.h>
23 #include <X11/Xaw/Form.h>
24 #include <X11/Xaw/Label.h>
25 #include <X11/Xaw/MenuButton.h>
26 #include <X11/Xaw/SimpleMenu.h>
27 #include <X11/Xaw/SmeBSB.h>
28 #include <X11/keysym.h>
29 
30 #include <sys/time.h>
31 #include <unistd.h>
32 
33 #include "xbomb.h"
34 #include "icon.h"
35 
36 #if defined(__sun__) && !defined(__svr4__)
37 int gettimeofday(struct timeval *tv, struct timezone *tz);
38 #endif
39 
40 /*+ The current information about the grid +*/
41 extern int grid_width,          /*+ width. +*/
42            grid_height,         /*+ height. +*/
43            grid_bombs,          /*+ number of bombs. +*/
44            grid_type,           /*+ type. +*/
45            grid_level;          /*+ level. +*/
46 
47 /*+ The names of the different game +*/
48 extern char *levels[NLEVELS],   /*+ levels (difficulty). +*/
49             *types[NTYPES];     /*+ types (grid shapes). +*/
50 
51 static int xoffset,yoffset;
52 static int status=0;
53 static int unsigned scalex=10,scaley=10;
54 static int unsigned pixw,pixh;
55 static XtAppContext app_context;
56 static Display* display;
57 static Window play_window;
58 static Widget play_area,clock_w,uxb_w,hiscore,toplevel;
59 static Widget highscore,highscoresform,highscores[12][4];
60 static Pixmap icon_p,bombpix[2]={0,0};
61 static XPoint outline[8];
62 
63 static void size_expose_proc(Widget w,XtPointer va,XEvent* e,Boolean* vb);
64 static void mouse_press_proc(Widget w,XtPointer va,XEvent* e,Boolean* vb);
65 static void key_press_proc(Widget w,XtPointer va,XEvent* e,Boolean* vb);
66 static void change_status_proc(Widget widget,XtPointer clientData,XtPointer callData);
67 static void hiscore_proc(Widget widget,XtPointer clientData,XtPointer callData);
68 static void close_proc(Widget w,XtPointer va,XEvent* e,Boolean* vb);
69 static void display_clock(XtPointer p,XtIntervalId i);
70 static int msecs(void);
71 
72 
73 #define GC_BOMB        0        /*+ For drawing the bomb and flag foreground. +*/
74 #define GC_ACTUAL_BACK 1        /*+ For drawing the bomb background. +*/
75 #define GC_THINK_BACK  2        /*+ For drawing the flag background. +*/
76 #define GC_NUMBER1     3        /*+ For drawing the number 1 on the grid. +*/
77 #define GC_NUMBER2     4        /*+ For drawing the number 2 +*/
78 #define GC_NUMBER3     5        /*+ For drawing the number 3 +*/
79 #define GC_NUMBER4     6        /*+ For drawing the number 4 +*/
80 #define GC_NUMBER5     7        /*+ For drawing the number 5 +*/
81 #define GC_NUMBER6     8        /*+ For drawing the number 6 +*/
82 #define GC_NUMBER7     9        /*+ For drawing the number 7 +*/
83 #define GC_NUMBER8     10       /*+ For drawing the number 8 +*/
84 #define GC_NUMBER9     11       /*+ For drawing the number 9 +*/
85 #define GC_NUMBER10    12       /*+ For drawing the number 10 (A) +*/
86 #define GC_NUMBER11    13       /*+ For drawing the number 11 (B) +*/
87 #define GC_NUMBER12    14       /*+ For drawing the number 12 (C) +*/
88 #define GC_UNSEEN      15       /*+ For unseen (hidden) tiles. +*/
89 #define GC_CORRECT     16       /*+ For correctly guessed tiles at the end.+*/
90 #define GC_BLANK       17       /*+ For blank squares. +*/
91 #define NUM_GC         18
92 
93 /*+ A structure to hold the colour and font resource data in. +*/
94 struct temp
95 {
96  XFontStruct* fontstruct;       /*+ The font. +*/
97  Pixel colours[NUM_GC-1];       /*+ The colours. +*/
98 }
99 resources;
100 
101 /*+ An array to hold the GCs in. +*/
102 static GC gc[NUM_GC];
103 
104 /*+ The colour resources for the map. +*/
105 static XtResource xresources[]=
106 {
107  {"font"    , "XBomb", XtRFontStruct, sizeof(XFontStruct*), 0               , XtRString, "lucidasans-24"},
108  {"bomb"    , "XBomb", XtRPixel     , sizeof(String)      ,   sizeof(String), XtRString, "black" },
109  {"bombreal", "XBomb", XtRPixel     , sizeof(String)      , 2*sizeof(String), XtRString, "red" },
110  {"bombmark", "XBomb", XtRPixel     , sizeof(String)      , 3*sizeof(String), XtRString, "orange" },
111  {"number"  , "XBomb", XtRPixel     , sizeof(String)      , 4*sizeof(String), XtRString, "navyblue" },
112  {"number2" , "XBomb", XtRPixel     , sizeof(String)      , 5*sizeof(String), XtRString, "darkgreen" },
113  {"number3" , "XBomb", XtRPixel     , sizeof(String)      , 6*sizeof(String), XtRString, "darkred" },
114  {"number4" , "XBomb", XtRPixel     , sizeof(String)      , 7*sizeof(String), XtRString, "black" },
115  {"number5" , "XBomb", XtRPixel     , sizeof(String)      , 8*sizeof(String), XtRString, "turquoise" },
116  {"number6" , "XBomb", XtRPixel     , sizeof(String)      , 9*sizeof(String), XtRString, "orange" },
117  {"number7" , "XBomb", XtRPixel     , sizeof(String)      ,10*sizeof(String), XtRString, "violet" },
118  {"number8" , "XBomb", XtRPixel     , sizeof(String)      ,11*sizeof(String), XtRString, "lightgreen" },
119  {"number9" , "XBomb", XtRPixel     , sizeof(String)      ,12*sizeof(String), XtRString, "blue" },
120  {"number10", "XBomb", XtRPixel     , sizeof(String)      ,13*sizeof(String), XtRString, "blue" },
121  {"number11", "XBomb", XtRPixel     , sizeof(String)      ,14*sizeof(String), XtRString, "blue" },
122  {"number12", "XBomb", XtRPixel     , sizeof(String)      ,15*sizeof(String), XtRString, "blue" },
123  {"hidden"  , "XBomb", XtRPixel     , sizeof(String)      ,16*sizeof(String), XtRString, "grey50" },
124  {"correct" , "XBomb", XtRPixel     , sizeof(String)      ,17*sizeof(String), XtRString, "green" }
125 };
126 
127 
128 /*++++++++++++++++++++++++++++++++++++++
129   Open the X connection.
130 
131   int *argc A pointer to the command line parameters.
132 
133   char **argv The pointer to the command line parameters.
134   ++++++++++++++++++++++++++++++++++++++*/
135 
InitialiseX(int * argc,char ** argv)136 void InitialiseX(int *argc,char **argv)
137 {
138  Atom close_atom;
139  XFontStruct *fontstruct;
140  XGCValues values;
141  Widget form,quit,start,menu,menushell;
142  int i;
143 
144  /* Initialise the display */
145 
146  toplevel=XtVaAppInitialize(&app_context,"XBomb",
147                             NULL, (Cardinal)0,argc,argv,NULL,
148                             XtNtitle,"XBomb V2.2b",
149                             XtNiconName,"XBomb",
150                             NULL);
151 
152  display=XtDisplay(toplevel);
153 
154  /* Create the widgets */
155 
156  form=XtVaCreateManagedWidget("form",formWidgetClass,toplevel,
157                               XtNwidth,100,XtNheight,100,
158                               NULL);
159 
160  XtAddEventHandler(form,KeyReleaseMask,False,(XtEventHandler)key_press_proc,NULL);
161 
162  start=XtVaCreateManagedWidget("start",commandWidgetClass,form,
163                                XtNlabel,"Restart",
164                                XtNfromHoriz,NULL,XtNfromVert,NULL,
165                                XtNleft,XtChainLeft,XtNtop,XtChainTop,XtNright,XtChainLeft,XtNbottom,XtChainTop,
166                                NULL);
167  XtAddCallback(start,XtNcallback,change_status_proc,(XtPointer)GAME_START);
168 
169  menu=XtVaCreateManagedWidget("menu",menuButtonWidgetClass,form,
170                               XtNlabel,"Level",
171                               XtNmenuName,"levelmenushell",
172                               XtNfromHoriz,start,XtNfromVert,NULL,
173                               XtNleft,XtChainLeft,XtNtop,XtChainTop,XtNright,XtChainLeft,XtNbottom,XtChainTop,
174                               NULL);
175 
176  menushell=XtCreatePopupShell("levelmenushell",simpleMenuWidgetClass,menu,NULL,0);
177 
178  for(i=0;i<NLEVELS;i++)
179    {
180     Widget item=XtVaCreateManagedWidget(levels[i],smeBSBObjectClass,menushell,
181                                         XtNlabel,levels[i],
182                                         NULL);
183     XtAddCallback(item,XtNcallback,change_status_proc,(XtPointer)(GAME_LEVEL+i));
184    }
185 
186  menu=XtVaCreateManagedWidget("menu",menuButtonWidgetClass,form,
187                               XtNlabel,"Game Type",
188                               XtNmenuName,"typemenushell",
189                               XtNfromHoriz,menu,XtNfromVert,NULL,
190                               XtNleft,XtChainLeft,XtNtop,XtChainTop,XtNright,XtChainLeft,XtNbottom,XtChainTop,
191                               NULL);
192 
193  menushell=XtCreatePopupShell("typemenushell",simpleMenuWidgetClass,menu,NULL,0);
194 
195  for(i=0;i<NTYPES;i++)
196    {
197     Widget item=XtVaCreateManagedWidget(types[i],smeBSBObjectClass,menushell,
198                                         XtNlabel,types[i],
199                                         NULL);
200     XtAddCallback(item,XtNcallback,change_status_proc,(XtPointer)(GAME_TYPE+i));
201    }
202 
203  hiscore=XtVaCreateManagedWidget("hiscore",toggleWidgetClass,form,
204                                  XtNlabel,"Hi-Scores",
205                                  XtNfromHoriz,menu,XtNfromVert,NULL,
206                                  XtNleft,XtChainLeft,XtNtop,XtChainTop,XtNright,XtChainLeft,XtNbottom,XtChainTop,
207                                  NULL);
208  XtAddCallback(hiscore,XtNcallback,hiscore_proc,NULL);
209 
210  quit=XtVaCreateManagedWidget("quit",commandWidgetClass,form,
211                               XtNlabel,"Quit",
212                               XtNfromHoriz,hiscore,XtNfromVert,NULL,
213                               XtNleft,XtChainLeft,XtNtop,XtChainTop,XtNright,XtChainLeft,XtNbottom,XtChainTop,
214                               NULL);
215  XtAddCallback(quit,XtNcallback,change_status_proc,(XtPointer)GAME_QUIT);
216 
217  play_area=XtVaCreateManagedWidget("grid",coreWidgetClass,form,
218                                    XtNwidth,400,XtNheight,400,
219                                    XtNtop,XtChainTop,XtNright,XtChainRight,XtNleft,XtChainLeft,XtNbottom,XtChainBottom,
220                                    XtNfromHoriz,NULL,XtNfromVert,start,
221                                    XtNresizable,True,
222                                    NULL);
223 
224  XtAddEventHandler(play_area,ButtonPressMask,False,(XtEventHandler)mouse_press_proc,NULL);
225  XtAddEventHandler(play_area,ExposureMask|StructureNotifyMask,False,(XtEventHandler)size_expose_proc,NULL);
226 
227  clock_w=XtVaCreateManagedWidget("clock",labelWidgetClass,form,
228                                  XtNlabel,"Time : MMMM.MMM",
229                                  XtNfromHoriz,NULL,XtNfromVert,play_area,
230                                  XtNtop,XtChainBottom,XtNright,XtChainLeft,XtNleft,XtChainLeft,XtNbottom,XtChainBottom,
231                                  NULL);
232 
233  uxb_w=XtVaCreateManagedWidget("uxb",labelWidgetClass,form,
234                                XtNlabel,"UXB : MMM",
235                                XtNfromHoriz,clock_w,XtNfromVert,play_area,
236                                XtNtop,XtChainBottom,XtNright,XtChainLeft,XtNleft,XtChainLeft,XtNbottom,XtChainBottom,
237                                NULL);
238 
239  /* Create graphics contexts */
240 
241  XtVaGetSubresources(play_area,(XtPointer)&resources,"grid","XBomb",xresources,XtNumber(xresources),NULL);
242 
243  values.font=resources.fontstruct->fid;
244  for(i=0;i<NUM_GC-1;i++)
245    {
246     values.foreground=resources.colours[i];
247     if(i==GC_UNSEEN)
248       {
249        GC tempgc;
250        XGCValues gcxval;
251        Dimension w,h;
252        Pixmap stipple=XCreatePixmap(display,RootWindowOfScreen(XtScreen(toplevel)),8,8,1);
253 
254        gcxval.foreground=0;
255        gcxval.background=0;
256        tempgc=XCreateGC(display,stipple,GCForeground|GCBackground,&gcxval);
257 
258        XFillRectangle(display,stipple,tempgc,0,0,8,8);
259 
260        XSetForeground(display,tempgc,(unsigned long)~0);
261 
262        for(w=0;w<8;w++)
263           for(h=0;h<8;h++)
264              if((w+h)%2)
265                 XDrawPoint(display,stipple,tempgc,w,h);
266 
267        XtVaGetValues(play_area,XtNbackground,&values.background,NULL);
268        values.stipple=stipple;
269        values.fill_style=FillOpaqueStippled;
270        gc[i]=XCreateGC(display,RootWindowOfScreen(XtScreen(toplevel)),GCForeground|GCBackground|GCFont|GCStipple|GCFillStyle,&values);
271 
272        XFreeGC(display,tempgc);
273        XFreePixmap(display,stipple);
274       }
275     else
276        gc[i]=XCreateGC(display,RootWindowOfScreen(XtScreen(toplevel)),GCForeground|GCFont,&values);
277    }
278 
279  XtVaGetValues(play_area,XtNbackground,&values.foreground,NULL);
280  gc[GC_BLANK]=XCreateGC(display,RootWindowOfScreen(XtScreen(toplevel)),GCForeground,&values);
281 
282  /* The highscore table. */
283 
284  highscore=XtVaCreatePopupShell("hiscores",topLevelShellWidgetClass,toplevel,
285                                 XtNtitle,"XBomb V2.2b High Scores",XtNiconName,"XBomb Hi-scores",
286                                 XtNmappedWhenManaged,False,
287                                 NULL);
288 
289  highscoresform=XtVaCreateManagedWidget("form",formWidgetClass,highscore,
290                                         XtNwidth,200,XtNheight,100,
291                                         NULL);
292 
293  XtAddEventHandler(highscoresform,KeyReleaseMask,False,(XtEventHandler)key_press_proc,NULL);
294 
295  for(i=0;i<12;i++)
296    {
297     char name[4][6];
298 
299     sprintf(name[0],"hs%da",i);
300     sprintf(name[1],"hs%db",i);
301     sprintf(name[2],"hs%ds",i);
302     sprintf(name[3],"hs%dd",i);
303 
304     highscores[i][0]=XtVaCreateManagedWidget(name[0],labelWidgetClass,highscoresform,
305                                              XtNborderWidth,0,XtNlabel,"MMM",
306                                              XtNfromHoriz,NULL,XtNfromVert,i?highscores[i-1][0]:NULL,
307                                              NULL);
308     highscores[i][1]=XtVaCreateManagedWidget(name[1],labelWidgetClass,highscoresform,
309                                              XtNborderWidth,0,XtNlabel,"MMMMMMM",
310                                              XtNfromHoriz,highscores[i][0],XtNfromVert,i?highscores[i-1][0]:NULL,
311                                              NULL);
312     highscores[i][2]=XtVaCreateManagedWidget(name[2],labelWidgetClass,highscoresform,
313                                              XtNborderWidth,0,XtNlabel,"MMMM",
314                                              XtNfromHoriz,highscores[i][1],XtNfromVert,i?highscores[i-1][0]:NULL,
315                                              NULL);
316     highscores[i][3]=XtVaCreateManagedWidget(name[3],labelWidgetClass,highscoresform,
317                                              XtNborderWidth,0,XtNlabel,"MMMMMMMMMMMMMM",
318                                              XtNjustify,XtJustifyLeft,
319                                              XtNfromHoriz,highscores[i][2],XtNfromVert,i?highscores[i-1][0]:NULL,
320                                              NULL);
321    }
322 
323  /* Add the icon */
324 
325  icon_p=XCreateBitmapFromData(display,RootWindowOfScreen(XtScreen(toplevel)),
326                               icon_bits,icon_width,icon_height);
327 
328  XtVaSetValues(toplevel,XtNiconPixmap,icon_p,NULL);
329  XtVaSetValues(highscore,XtNiconPixmap,icon_p,NULL);
330 
331  /* Show the widgets */
332 
333  XtRealizeWidget(toplevel);
334  XtManageChild(highscore);
335  XFlush(display);
336 
337  play_window=XtWindow(play_area);
338 
339  /* Centre the text */
340 
341  fontstruct=XQueryFont(display,XGContextFromGC(gc[GC_NUMBER1]));
342 
343  xoffset=XTextWidth(fontstruct,"0",1)/2;
344  yoffset=fontstruct->ascent/2;
345 
346  /* Put an action on the close button */
347 
348  close_atom=XInternAtom(display,"WM_DELETE_WINDOW",False);
349 
350  XSetWMProtocols(display,XtWindow(toplevel),&close_atom,1);
351  XtAddEventHandler(toplevel,0,True,close_proc,NULL);
352 
353  XSetWMProtocols(display,XtWindow(highscore),&close_atom,1);
354  XtAddEventHandler(highscore,0,True,close_proc,NULL);
355 }
356 
357 
358 /*++++++++++++++++++++++++++++++++++++++
359   Close the X connection.
360   ++++++++++++++++++++++++++++++++++++++*/
361 
FinishUpX(void)362 void FinishUpX(void)
363 {
364  int i;
365 
366  /* Free the graphics contexts. */
367 
368  for(i=0;i<NUM_GC;i++)
369     XFreeGC(display,gc[i]);
370 
371  /* Free the pixmaps. */
372 
373  for(i=0;i<2;i++)
374     XFreePixmap(display,bombpix[i]);
375 
376  XFreePixmap(display,icon_p);
377 
378  /* Close the display.. */
379 
380  XCloseDisplay(display);
381 }
382 
383 
384 /*++++++++++++++++++++++++++++++++++++++
385   Process events.
386 
387   int ProcessXEvents Returns the game status.
388   ++++++++++++++++++++++++++++++++++++++*/
389 
ProcessXEvents(void)390 int ProcessXEvents(void)
391 {
392  XEvent event;
393 
394  status=0;
395 
396  XtAppNextEvent(app_context,&event);
397  XtDispatchEvent(&event);
398 
399  return(status);
400 }
401 
402 
403 /*++++++++++++++++++++++++++++++++++++++
404   Draw a value in the specified square.
405 
406   int x The X position.
407 
408   int y The y position.
409 
410   unsigned char value The value of the square.
411   ++++++++++++++++++++++++++++++++++++++*/
412 
DrawSquare(int x,int y,unsigned char value)413 void DrawSquare(int x,int y,unsigned char value)
414 {
415  int xpos=0,ypos=0;
416  static char grid_numbers[13][2]={" ","1","2","3","4","5","6","7","8","9","A","B","C"};
417  int which_gc;
418 
419  /* Find the position. */
420 
421  switch(grid_type)
422    {
423    case GAME_HEXAGON:
424     xpos=x*scalex+(y%2)*scalex/2;
425     ypos=y*scaley+scaley/3;
426     outline[0].x=xpos; outline[0].y=ypos;
427     break;
428    case GAME_SQUARE:
429     xpos=x*scalex;
430     ypos=y*scaley;
431     break;
432    case GAME_TRIANGLE:
433     xpos=x*scalex/2+scalex/4;
434     ypos=y*scaley-((x+y)%2)*scaley/2+scaley/2;
435     if((x+y)%2)
436        outline[4].x=xpos+scalex/4, outline[4].y=ypos+scaley;
437     else
438        outline[0].x=xpos+scalex/4, outline[0].y=ypos-scaley/2;
439     break;
440    }
441 
442  /* Choose the context. */
443 
444  if(value&UNSEEN)
445    {
446     if(value&THINK_BOMB)
447        which_gc=GC_THINK_BACK;
448     else
449        which_gc=GC_UNSEEN;
450    }
451  else
452    {
453     if(value&ACTUAL_BOMB)
454        which_gc=GC_ACTUAL_BACK;
455     else
456        which_gc=GC_BLANK;
457    }
458 
459  /* Fill the background. */
460 
461  switch(grid_type)
462    {
463    case GAME_HEXAGON:
464     XFillPolygon(display,play_window,gc[which_gc],outline,7,Convex,CoordModePrevious);
465     break;
466    case GAME_SQUARE:
467     XFillRectangle(display,play_window,gc[which_gc],xpos,ypos,scalex,scaley);
468     break;
469    case GAME_TRIANGLE:
470     if((x+y)%2)
471        XFillPolygon(display,play_window,gc[which_gc],&outline[4],4,Convex,CoordModePrevious);
472     else
473        XFillPolygon(display,play_window,gc[which_gc],&outline[0],4,Convex,CoordModePrevious);
474     break;
475    }
476 
477  /* Draw the bomb or text. */
478 
479  if(value&UNSEEN)
480    {
481     if(value&THINK_BOMB)
482        XCopyArea(display,bombpix[1],play_window,gc[GC_THINK_BACK],0,0,pixw,pixh,xpos,ypos);
483    }
484  else if(value&ACTUAL_BOMB)
485     XCopyArea(display,bombpix[0],play_window,gc[GC_ACTUAL_BACK],0,0,pixw,pixh,xpos,ypos);
486  else if((value&~CORRECT)>0 && (value&~CORRECT)<=12)
487    {
488     int text_xpos=xpos+pixw/2-xoffset;
489     int text_ypos=ypos+pixh/2+yoffset;
490     int gc_number=(value&~CORRECT)+GC_NUMBER1-1;  /* Calculate GC_NUMBERx offset. */
491     XDrawString(display,play_window,gc[gc_number],text_xpos,text_ypos,grid_numbers[(value&~CORRECT)],1);
492    }
493 
494  /* Draw the correct squares at the end. */
495 
496  if(value&CORRECT)
497     which_gc=GC_CORRECT;
498  else
499     which_gc=GC_BOMB;
500 
501  switch(grid_type)
502    {
503    case GAME_HEXAGON:
504     XDrawLines(display,play_window,gc[which_gc],outline,7,CoordModePrevious);
505     break;
506    case GAME_SQUARE:
507     XDrawRectangle(display,play_window,gc[which_gc],xpos,ypos,scalex-1,scaley-1);
508     break;
509    case GAME_TRIANGLE:
510     if((x+y)%2)
511        XDrawLines(display,play_window,gc[which_gc],&outline[4],4,CoordModePrevious);
512     else
513        XDrawLines(display,play_window,gc[which_gc],&outline[0],4,CoordModePrevious);
514     break;
515    }
516 
517 }
518 
519 
520 /*++++++++++++++++++++++++++++++++++++++
521   Display the high scores.
522 
523   char *scores[11][4] The score information.
524 
525   int which_score Which score was just completed.
526   ++++++++++++++++++++++++++++++++++++++*/
527 
DisplayHighScores(char * scores[11][4],int which_score)528 void DisplayHighScores(char *scores[11][4],int which_score)
529 {
530  int i,j;
531  Pixel fore,back;
532 
533  XtVaSetValues(highscores[0][0],XtNlabel,"Pos",NULL);
534  XtVaSetValues(highscores[0][1],XtNlabel,"Name",NULL);
535  XtVaSetValues(highscores[0][2],XtNlabel,"Time",NULL);
536  XtVaSetValues(highscores[0][3],XtNlabel,"Date",NULL);
537 
538  XtVaGetValues(highscores[0][0],
539                XtNbackground,&back,XtNforeground,&fore,
540                NULL);
541 
542  for(j=0;j<4;j++)
543     XtVaSetValues(highscores[0][j],
544                   XtNforeground,fore,
545                   XtNbackground,back,
546                   NULL);
547 
548  for(i=0;i<11;i++)
549     for(j=0;j<4;j++)
550        XtVaSetValues(highscores[i+1][j],
551                      XtNforeground,i==which_score?back:fore,
552                      XtNbackground,i==which_score?fore:back,
553                      XtNlabel,scores[i][j],
554                      NULL);
555 }
556 
557 
558 /*++++++++++++++++++++++++++++++++++++++
559   A callback that is activated by an expose event on the play area window.
560 
561   Widget w The widget that caused the callback.
562 
563   XtPointer va Not used.
564 
565   XEvent* e The event that requires action.
566 
567   Boolean* vb Not used.
568 
569   This function is only ever called from the Xt Intrinsics routines.
570   ++++++++++++++++++++++++++++++++++++++*/
571 
size_expose_proc(Widget w,XtPointer va,XEvent * e,Boolean * vb)572 static void size_expose_proc(Widget w,XtPointer va,XEvent* e,Boolean* vb)
573 {
574  if(e->type==ConfigureNotify)
575     ScaleWindow();
576 
577  if(e->type==MapNotify || (e->type==Expose && !e->xexpose.count))
578     DrawGrid();
579 }
580 
581 
582 /*++++++++++++++++++++++++++++++++++++++
583   Scales the window.
584   ++++++++++++++++++++++++++++++++++++++*/
585 
ScaleWindow(void)586 void ScaleWindow(void)
587 {
588  unsigned short old_width,old_height;
589  unsigned short new_width=0,new_height=0;
590 
591  /* Get the old size. */
592 
593  XtVaGetValues(play_area,XtNwidth,&old_width,XtNheight,&old_height,NULL);
594 
595  /* Choose a new size */
596 
597  switch(grid_type)
598    {
599    case GAME_HEXAGON:
600     scalex=(int)old_width*2/(7*(2*grid_width+1)/6);
601     scaley=(int)old_height*3/(3*grid_height+1);
602     scaley=6*((scalex+scaley)/12);
603     if(scaley<30) scaley=18;
604     scalex=(scaley*7)/6;
605     new_width =scalex*(2*grid_width+1)/2;
606     new_height=scaley*(3*grid_height+1)/3;
607     break;
608    case GAME_SQUARE:
609     scalex=(int)old_width /grid_width;
610     scaley=(int)old_height/grid_height;
611     scalex=scaley=(scalex+scaley)/2;
612     if(scalex<30) scalex=scaley=20;
613     new_width =scalex*grid_width;
614     new_height=scaley*grid_height;
615     break;
616    case GAME_TRIANGLE:
617     scalex=(int)old_width*2/(grid_width+1);
618     scaley=(int)old_height/(7*grid_height/8);
619     scalex=4*((scalex+scaley)/8);
620     if(scalex<16) scalex=16;
621     scaley=(scalex*7)/8;
622     new_width =scalex/2*(grid_width+1);
623     new_height=scaley*grid_height;
624     break;
625    }
626 
627  /* If changed then re-size the window. */
628 
629  if(new_width!=old_width || new_height!=old_height)
630    {
631     unsigned short width,height;
632     new_width-=old_width;
633     new_height-=old_height;
634     XtVaGetValues(XtParent(XtParent(play_area)),XtNwidth,&width,XtNheight,&height,NULL);
635     XResizeWindow(display,XtWindow(XtParent(XtParent(play_area))),(unsigned)(width+new_width),(unsigned)(height+new_height));
636    }
637  else /* Must be here due to a correct sizing. */
638    {
639     int i;
640 
641     /* Get the outline of a funny shape tile. */
642 
643     if(grid_type==GAME_HEXAGON)
644       {
645        /* outline[0] is filled at the time of drawing */
646        outline[1].x= (int)scalex/2;    outline[1].y=-(int)scaley/3;
647        outline[2].x= (int)scalex/2;    outline[2].y= (int)scaley/3;
648        outline[3].x= 0;                outline[3].y=-(int)scaley/3+scaley;
649        outline[4].x=-(int)scalex/2;    outline[4].y= (int)scaley/3;
650        outline[5].x=-(int)scalex/2;    outline[5].y=-(int)scaley/3;
651        outline[6].x= 0;                outline[6].y= (int)scaley/3-scaley;
652       }
653     else if(grid_type==GAME_TRIANGLE)
654       {
655        /* outline[0] is filled at the time of drawing */
656        outline[1].x=-(int)scalex/2+1;    outline[1].y= (int)scaley-1;
657        outline[2].x= (int)scalex-2;      outline[2].y=0;
658        outline[3].x=-(int)scalex/2+1;    outline[3].y=-(int)scaley+1;
659        /* outline[4] is filled at the time of drawing */
660        outline[5].x=-(int)scalex/2+1;    outline[5].y=-(int)scaley;
661        outline[6].x= (int)scalex-2;      outline[6].y=0;
662        outline[7].x=-(int)scalex/2+1;    outline[7].y= (int)scaley;
663       }
664 
665     /* Draw the bomb and flag pixmaps. */
666 
667     for(i=0;i<2;i++)
668       {
669        if(grid_type==GAME_SQUARE)
670           pixw=scalex,pixh=scaley;
671        else if(grid_type==GAME_HEXAGON)
672           pixw=scalex,pixh=scaley*2/3;
673        else
674           pixw=scalex/2,pixh=scaley/2;
675 
676        if(bombpix[i])
677           XFreePixmap(display,bombpix[i]);
678 
679        bombpix[i]=XCreatePixmap(display,XtWindow(play_area),pixw,pixh,(unsigned)DefaultDepthOfScreen(XtScreen(play_area)));
680 
681        XFillRectangle(display,bombpix[i],gc[i+1],0,0,pixw,pixh);
682 
683        if(i==0)
684          {
685           XFillArc(display,bombpix[i],gc[GC_BOMB],(signed)pixw/4,(signed)pixh/2-2,pixw/2,pixh/2,0,360*64);
686           XFillRectangle(display,bombpix[i],gc[GC_BOMB],3*(signed)pixw/8,3*(signed)pixh/8,pixw/4,pixh/4);
687           XDrawLine(display,bombpix[i],gc[GC_BOMB],(signed)pixw/2,(signed)pixh/2,(signed)pixw/2,(signed)pixh/4);
688           XDrawArc(display,bombpix[i],gc[GC_BOMB],(signed)pixw/2,2,pixw/2,pixh/2,70*64,110*64);
689          }
690        else
691          {
692           XPoint points[3];
693           XFillArc(display,bombpix[i],gc[GC_BOMB],(signed)pixw/4,(signed)pixh*3/4-2,pixw/2,pixh/2,0,180*64);
694           XDrawLine(display,bombpix[i],gc[GC_BOMB],(signed)pixw/2,(signed)pixh*3/4,(signed)pixw/2,(signed)pixh/4);
695           points[0].x=pixw/2;   points[0].y=pixh/8;
696           points[1].x=pixw*4/5; points[1].y=pixh/4;
697           points[2].x=pixw/2;   points[2].y=pixh*3/8;
698           XFillPolygon(display,bombpix[i],gc[GC_BOMB],points,3,Convex,CoordModeOrigin);
699          }
700       }
701 
702     /* Redraw the whole grid. */
703 
704     XClearWindow(display,play_window);
705     DrawGrid();
706    }
707 }
708 
709 
710 /*++++++++++++++++++++++++++++++++++++++
711   A callback that is activated by a mouse press in the play area window.
712 
713   Widget w The widget that caused the callback.
714 
715   XtPointer va Not used.
716 
717   XEvent* e The event that requires action.
718 
719   Boolean* vb Not used.
720 
721   This function is only ever called from the Xt Intrinsics routines.
722   ++++++++++++++++++++++++++++++++++++++*/
723 
mouse_press_proc(Widget w,XtPointer va,XEvent * e,Boolean * vb)724 static void mouse_press_proc(Widget w,XtPointer va,XEvent* e,Boolean* vb)
725 {
726  int x=-1,y=-1;
727  int x_guess,y_guess,dx,dy;
728  int i,j,d,dmin=1e6;
729 
730  /* Work out which tile mouse pressed in. */
731 
732  switch(grid_type)
733    {
734    case GAME_HEXAGON:
735     y_guess=e->xbutton.y/scaley;
736     x_guess=(e->xbutton.x-(y_guess%2)*scalex/2)/scalex;
737     for(i=x_guess-1;i<=(x_guess+1);i++)
738        for(j=y_guess-1;j<=(y_guess+1);j++)
739          {
740           if(i<0 || i>=grid_width || j<0 || j>=grid_height)
741              continue;
742           dx=e->xbutton.x-(i*scalex+(1+(j%2))*scalex/2);
743           dy=e->xbutton.y-(j*scaley+2*scaley/3);
744           d=dx*dx+dy*dy;
745           if(d<dmin)
746             {x=i;y=j;dmin=d;}
747       }
748     break;
749    case GAME_SQUARE:
750     x=e->xbutton.x/scalex;
751     y=e->xbutton.y/scaley;
752     break;
753    case GAME_TRIANGLE:
754     x_guess=e->xbutton.x*2/scalex;
755     y=e->xbutton.y/scaley;
756     for(i=x_guess-1;i<=(x_guess+1);i++)
757       {
758        if(i<0 || i>=grid_width)
759           continue;
760        dx=e->xbutton.x-(i*scalex/2+scalex/2);
761        dy=e->xbutton.y-(y*scaley+(1+(1+i+y)%2)*scaley/3);
762        d=dx*dx+dy*dy;
763        if(d<dmin)
764          {x=i;dmin=d;}
765       }
766     break;
767    }
768 
769  /* Throw out errors. */
770 
771  if(x<0 || x>=grid_width || y<0 || y>=grid_height)
772     {XBell(display,0);return;}
773 
774  /* Do something based on button number. */
775 
776  switch(e->xbutton.button)
777    {
778    case 1: SelectSquareOrAdjacent(x,y); break;
779    case 2: SelectAdjacent(x,y); break;
780    case 3: MarkBomb(x,y); break;
781    }
782 }
783 
784 
785 /*++++++++++++++++++++++++++++++++++++++
786   A callback that is activated by a key press in the play area window.
787 
788   Widget w The widget that caused the callback.
789 
790   XtPointer va Not used.
791 
792   XEvent* e The event that requires action.
793 
794   Boolean* vb Not used.
795 
796   This function is only ever called from the Xt Intrinsics routines.
797   ++++++++++++++++++++++++++++++++++++++*/
798 
key_press_proc(Widget w,XtPointer va,XEvent * e,Boolean * vb)799 static void key_press_proc(Widget w,XtPointer va,XEvent* e,Boolean* vb)
800 {
801  KeySym k=XLookupKeysym((XKeyEvent*)e,0);
802  Boolean shift=((XKeyEvent*)e)->state==ShiftMask || ((XKeyEvent*)e)->state==ControlMask;
803 
804  if(k==NoSymbol)return;
805 
806  /* Do an action based on which key is pressed. */
807 
808  switch(k)
809    {
810    case XK_1:
811     status=GAME_EASY;
812     break;
813    case XK_2:
814     status=GAME_MEDIUM;
815     break;
816    case XK_3:
817     status=GAME_DIFFICULT;
818     break;
819 
820    case XK_s:
821     if(shift)
822        status=GAME_SQUARE;
823     else
824        status=GAME_START;
825     break;
826 
827    case XK_h:
828     if(shift)
829        status=GAME_HEXAGON;
830     else
831        hiscore_proc(NULL,NULL,NULL);
832     break;
833 
834    case XK_q:
835     do
836       {
837        if(w==toplevel)
838          {status=GAME_QUIT;break;}
839        if(w==highscore)
840          {hiscore_proc(NULL,NULL,NULL);break;}
841       }
842     while((w=XtParent(w)));
843     break;
844 
845    case XK_t:
846     if(shift)
847        status=GAME_TRIANGLE;
848     break;
849 
850    default: ;
851    }
852 }
853 
854 
855 /*++++++++++++++++++++++++++++++++++++++
856   This function changes the state of the status flag.
857 
858   Widget widget Not used.
859 
860   XtPointer clientData The new status.
861 
862   XtPointer callData Not used.
863 
864   This function is only ever called from the Xt Intrinsics routines.
865   ++++++++++++++++++++++++++++++++++++++*/
866 
change_status_proc(Widget widget,XtPointer clientData,XtPointer callData)867 static void change_status_proc(Widget widget,XtPointer clientData,XtPointer callData)
868 {
869  status=(int)clientData;
870 }
871 
872 
873 /*++++++++++++++++++++++++++++++++++++++
874   This function displays the hi-scores.
875 
876   Widget widget Not used.
877 
878   XtPointer clientData Not used.
879 
880   XtPointer callData Not used.
881 
882   This function is only ever called from the Xt Intrinsics routines.
883   ++++++++++++++++++++++++++++++++++++++*/
884 
hiscore_proc(Widget widget,XtPointer clientData,XtPointer callData)885 static void hiscore_proc(Widget widget,XtPointer clientData,XtPointer callData)
886 {
887  static Boolean state=False;
888 
889  state=!state;
890 
891  if(!widget)
892     XtVaSetValues(hiscore,XtNstate,state,NULL);
893 
894  if(state)
895     XtPopup(highscore,XtGrabNone);
896  else
897     XtPopdown(highscore);
898 }
899 
900 
901 /*++++++++++++++++++++++++++++++++++++++
902   A callback that is activated by a close window event on the toplevel window.
903 
904   Widget w The widget that caused the callback.
905 
906   XtPointer va Not used.
907 
908   XEvent* e The event that requires action.
909 
910   Boolean* vb Not used.
911 
912   This function is only ever called from the Xt Intrinsics routines.
913   ++++++++++++++++++++++++++++++++++++++*/
914 
close_proc(Widget w,XtPointer va,XEvent * e,Boolean * vb)915 static void close_proc(Widget w,XtPointer va,XEvent* e,Boolean* vb)
916 {
917  XClientMessageEvent *cev=(XClientMessageEvent*)e;
918  Atom atom_type=XInternAtom(display,"WM_PROTOCOLS",False);
919 
920  if(atom_type==cev->message_type)
921    {
922     Atom atom_proto=XInternAtom(display,"WM_DELETE_WINDOW",False);
923 
924     if(cev->format==32 && atom_proto==(Atom)cev->data.l[0])
925       {
926        if(w==toplevel)
927           status=GAME_QUIT;
928        else
929           hiscore_proc(NULL,NULL,NULL);
930       }
931    }
932 }
933 
934 
935 /*++++++++++++++++++++++++++++++++++++++
936   Sets the number of UXB in the grid.
937 
938   int n The number of UXB.
939   ++++++++++++++++++++++++++++++++++++++*/
940 
SetUXB(int n)941 void SetUXB(int n)
942 {
943  char string[16];
944 
945  sprintf(string,"UXB : %3d",n);
946  XtVaSetValues(uxb_w,XtNlabel,string,NULL);
947 }
948 
949 
950 /*------------ The clock -------------*/
951 
952 static int ticks;
953 static XtIntervalId id;
954 static struct timeval starttimeval;
955 
956 /*++++++++++++++++++++++++++++++++++++++
957   The function called for each clock tick.
958 
959   XtPointer p Not used.
960 
961   XtIntervalId i Not used.
962 
963   This function is only called from the Intrinsics.
964   ++++++++++++++++++++++++++++++++++++++*/
965 
display_clock(XtPointer p,XtIntervalId i)966 static void display_clock(XtPointer p,XtIntervalId i)
967 {
968  char string[16];
969  int ticks1000=msecs();
970 
971  ticks=ticks1000/1000;
972  ticks1000-=1000*ticks;
973 
974  id=XtAppAddTimeOut(app_context,(unsigned)(1000-ticks1000),(XtTimerCallbackProc)display_clock,NULL);
975 
976  sprintf(string,"Time : %4d",ticks);
977  XtVaSetValues(clock_w,XtNlabel,string,NULL);
978 }
979 
980 
981 /*++++++++++++++++++++++++++++++++++++++
982   Starts the clock
983 
984   int reset If 0 then resets the clock else if 1 then starts the clock.
985   ++++++++++++++++++++++++++++++++++++++*/
986 
StartClock(int reset)987 void StartClock(int reset)
988 {
989  if(reset)
990    {
991     gettimeofday(&starttimeval,NULL);
992     display_clock(NULL,0);
993    }
994  else
995    {
996     char string[16];
997     sprintf(string,"Time :    0");
998     XtVaSetValues(clock_w,XtNlabel,string,NULL);
999    }
1000 }
1001 
1002 
1003 /*++++++++++++++++++++++++++++++++++++++
1004   Stops the clock and returns the time in seconds.
1005 
1006   int StopClock Returns the time in seconds.
1007   ++++++++++++++++++++++++++++++++++++++*/
1008 
StopClock(void)1009 int StopClock(void)
1010 {
1011  char string[16];
1012 
1013  XtRemoveTimeOut(id);
1014  ticks=msecs();
1015 
1016  sprintf(string,"Time : %8.3f",(double)ticks/1000.0);
1017  XtVaSetValues(clock_w,XtNlabel,string,NULL);
1018 
1019  return(ticks);
1020 }
1021 
1022 
1023 /*++++++++++++++++++++++++++++++++++++++
1024   Returns the number of milliseconds since starting the game.
1025 
1026   int msecs The number of milli-seconds.
1027   ++++++++++++++++++++++++++++++++++++++*/
1028 
msecs(void)1029 static int msecs(void)
1030 {
1031  struct timeval nowtimeval;
1032 
1033  gettimeofday(&nowtimeval,NULL);
1034 
1035  return(1000*(nowtimeval.tv_sec-starttimeval.tv_sec)+(nowtimeval.tv_usec-starttimeval.tv_usec)/1000);
1036 }
1037