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