1
2 /*-
3 # MOTIF-BASED HEXAGONS
4 #
5 # xmhexagons.c
6 #
7 ###
8 #
9 # Copyright (c) 1993 - 99 David Albert Bagley, bagleyd@tux.org
10 #
11 # All Rights Reserved
12 #
13 # Permission to use, copy, modify, and distribute this software and
14 # its documentation for any purpose and without fee is hereby granted,
15 # provided that the above copyright notice appear in all copies and
16 # that both that copyright notice and this permission notice appear in
17 # supporting documentation, and that the name of the author not be
18 # used in advertising or publicity pertaining to distribution of the
19 # software without specific, written prior permission.
20 #
21 # This program is distributed in the hope that it will be "useful",
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 #
25 */
26
27 /*-
28 Version 5: 95/10/01 Xt/Motif
29 Version 4: 94/06/07 Xt
30 Version 3: 93/04/01 Motif
31 Version 2: 92/01/07 XView
32 Version 1: 91/09/05 SunView
33 */
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #ifdef VMS
39 #include <unixlib.h>
40 #define getlogin() cuserid(NULL)
41 #else
42 #if HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 #endif
46 #if HAVE_FCNTL_H
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #endif
51 #include <X11/Intrinsic.h>
52 #include <X11/StringDefs.h>
53 #include <X11/Shell.h>
54 #include <X11/cursorfont.h>
55 #include <Xm/PanedW.h>
56 #include <Xm/RowColumn.h>
57 #include <Xm/Label.h>
58 #include <Xm/LabelG.h>
59 #include <Xm/Scale.h>
60 #include <Xm/ToggleB.h>
61 #include "Hexagons.h"
62 #include "hexagons.xbm"
63 #include "mouse-l.xbm"
64 #include "mouse-r.xbm"
65
66 #ifndef SCOREFILE
67 #define SCOREFILE "/usr/games/lib/hexagons.scores"
68 #endif
69
70 /* The following are in HexagonsP.h also */
71 #define MINHEXAGONS 1
72 #define MAXORIENT 2
73 #define NOCORN 0
74 #define CORNERS 1
75
76 #define MAXHEXAGONS 12
77 #define MAXRECORD 32767
78 #define FILENAMELEN 1024
79 #define USERNAMELEN 128
80 #define NOACCESS "noaccess"
81 #define NOBODY "nobody"
82
83 typedef struct {
84 int score;
85 char name[USERNAMELEN];
86 } GameRecord;
87
88 static void Initialize(Widget w);
89 static void CallbackHexagons(Widget w, caddr_t clientData,
90 hexagonsCallbackStruct * callData);
91
92 static void PrintRecord(int size, Boolean corners);
93 static Boolean HandleSolved(int counter, int size, int corners);
94 static void InitRecords(void);
95 static void ReadRecords(void);
96 static void WriteRecords(void);
97
98 static void motif_print(Widget w, char *text);
99 static void HexSlider(Widget w, XtPointer clientData,
100 XmScaleCallbackStruct * cbs);
101 static void CornersToggle(Widget w, XtPointer clientData,
102 XmToggleButtonCallbackStruct * cbs);
103
104 static Arg arg[2];
105 static Widget moves, record, message, hexagons, cornersSwitch, hex;
106 static GameRecord hexagonsRecord[MAXORIENT][MAXHEXAGONS - MINHEXAGONS + 1];
107 static int movesDsp = 0;
108 static char messageDsp[128] = "Welcome";
109 static char usernameDsp[USERNAMELEN] = "";
110 static char buff[256];
111
112 static void
Usage(void)113 Usage(void)
114 {
115 (void) fprintf(stderr, "usage: xmhexagons\n");
116 (void) fprintf(stderr,
117 "\t[-geometry [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]]\n");
118 (void) fprintf(stderr,
119 "\t[-display [{host}]:[{vs}]] [-[no]mono] [-[no]{reverse|rv}]\n");
120 (void) fprintf(stderr,
121 "\t[-{foreground|fg} {color}] [-{background|bg} {color}]\n");
122 (void) fprintf(stderr,
123 "\t[-{border|bd} {color}] [-tile {color}] [-size {int}]\n");
124 (void) fprintf(stderr,
125 "\t[-[no]corners] [-base {int}] [-username {string}]\n");
126 exit(1);
127 }
128
129 static XrmOptionDescRec options[] =
130 {
131 {"-mono", "*hexagons.mono", XrmoptionNoArg, "TRUE"},
132 {"-nomono", "*hexagons.mono", XrmoptionNoArg, "FALSE"},
133 {"-rv", "*hexagons.reverse", XrmoptionNoArg, "TRUE"},
134 {"-reverse", "*hexagons.reverse", XrmoptionNoArg, "TRUE"},
135 {"-norv", "*hexagons.reverse", XrmoptionNoArg, "FALSE"},
136 {"-noreverse", "*hexagons.reverse", XrmoptionNoArg, "FALSE"},
137 {"-fg", "*hexagons.Foreground", XrmoptionSepArg, NULL},
138 {"-foreground", "*hexagons.Foreground", XrmoptionSepArg, NULL},
139 {"-bg", "*Background", XrmoptionSepArg, NULL},
140 {"-background", "*Background", XrmoptionSepArg, NULL},
141 {"-bd", "*hexagons.tileBorder", XrmoptionSepArg, NULL},
142 {"-border", "*hexagons.tileBorder", XrmoptionSepArg, NULL},
143 {"-tile", "*hexagons.tileColor", XrmoptionSepArg, NULL},
144 {"-border", "*hexagons.tileBorder", XrmoptionSepArg, NULL},
145 {"-size", "*hexagons.size", XrmoptionSepArg, NULL},
146 {"-corners", "*hexagons.corners", XrmoptionNoArg, "TRUE"},
147 {"-nocorners", "*hexagons.corners", XrmoptionNoArg, "FALSE"},
148 {"-base", "*hexagons.base", XrmoptionSepArg, NULL},
149 {"-username", "*hexagons.userName", XrmoptionSepArg, NULL}
150 };
151
152 int
main(int argc,char ** argv)153 main(int argc, char **argv)
154 {
155 Widget toplevel;
156 Widget panel, panel2, rowcol, rowcol2;
157 Pixmap mouseLeftCursor, mouseRightCursor;
158 Pixel fg, bg;
159
160 toplevel = XtInitialize(argv[0], "Hexagons",
161 options, XtNumber(options), &argc, argv);
162 if (argc != 1)
163 Usage();
164
165 XtSetArg(arg[0],
166 XtNiconPixmap, XCreateBitmapFromData(XtDisplay(toplevel),
167 RootWindowOfScreen(XtScreen(toplevel)),
168 (char *) hexagons_bits, hexagons_width, hexagons_height));
169 XtSetArg(arg[1], XmNkeyboardFocusPolicy, XmPOINTER); /* not XmEXPLICIT */
170 XtSetValues(toplevel, arg, 2);
171 panel = XtCreateManagedWidget("panel",
172 xmPanedWindowWidgetClass, toplevel, NULL, 0);
173 panel2 = XtVaCreateManagedWidget("panel2", xmPanedWindowWidgetClass, panel,
174 XmNseparatorOn, False,
175 XmNsashWidth, 1,
176 XmNsashHeight, 1, NULL);
177
178 rowcol = XtVaCreateManagedWidget("Rowcol", xmRowColumnWidgetClass, panel2,
179 XmNnumColumns, 2,
180 XmNorientation, XmHORIZONTAL,
181 XmNpacking, XmPACK_COLUMN, NULL);
182 XtVaGetValues(rowcol,
183 XmNforeground, &fg,
184 XmNbackground, &bg, NULL);
185 mouseLeftCursor = XCreatePixmapFromBitmapData(XtDisplay(rowcol),
186 RootWindowOfScreen(XtScreen(rowcol)), (char *) mouse_left_bits,
187 mouse_left_width, mouse_left_height, fg, bg,
188 DefaultDepthOfScreen(XtScreen(rowcol)));
189 mouseRightCursor = XCreatePixmapFromBitmapData(XtDisplay(rowcol),
190 RootWindowOfScreen(XtScreen(rowcol)), (char *) mouse_right_bits,
191 mouse_right_width, mouse_right_height, fg, bg,
192 DefaultDepthOfScreen(XtScreen(rowcol)));
193 XtVaCreateManagedWidget("mouseLeftText",
194 xmLabelGadgetClass, rowcol,
195 XtVaTypedArg, XmNlabelString, XmRString, "Move tile", 10, NULL);
196 XtVaCreateManagedWidget("mouseLeft",
197 xmLabelGadgetClass, rowcol,
198 XmNlabelType, XmPIXMAP, XmNlabelPixmap, mouseLeftCursor, NULL);
199 XtVaCreateManagedWidget("mouseRightText",
200 xmLabelGadgetClass, rowcol,
201 XtVaTypedArg, XmNlabelString, XmRString, "Randomize", 10, NULL);
202 XtVaCreateManagedWidget("mouseRight",
203 xmLabelGadgetClass, rowcol,
204 XmNlabelType, XmPIXMAP, XmNlabelPixmap, mouseRightCursor, NULL);
205 XtVaCreateManagedWidget("movesText",
206 xmLabelGadgetClass, rowcol,
207 XtVaTypedArg, XmNlabelString, XmRString, "Moves", 6, NULL);
208 moves = XtVaCreateManagedWidget("0",
209 xmLabelWidgetClass, rowcol, NULL);
210 XtVaCreateManagedWidget("recordText",
211 xmLabelGadgetClass, rowcol,
212 XtVaTypedArg, XmNlabelString, XmRString, "Record", 7, NULL);
213 record = XtVaCreateManagedWidget("0",
214 xmLabelWidgetClass, rowcol, NULL);
215
216 rowcol2 = XtVaCreateManagedWidget("Rowcol2",
217 xmRowColumnWidgetClass, panel2, NULL);
218 XtVaGetValues(rowcol2,
219 XmNforeground, &fg,
220 XmNbackground, &bg, NULL);
221 hex = XtVaCreateManagedWidget("hexs",
222 xmScaleWidgetClass, rowcol2,
223 XtVaTypedArg, XmNtitleString, XmRString, "Hexs", 5,
224 XmNminimum, MINHEXAGONS,
225 XmNmaximum, MAXHEXAGONS,
226 XmNvalue, MINHEXAGONS,
227 XmNshowValue, True,
228 XmNorientation, XmHORIZONTAL, NULL);
229 XtAddCallback(hex,
230 XmNvalueChangedCallback, (XtCallbackProc) HexSlider, (XtPointer) NULL);
231 cornersSwitch = XtVaCreateManagedWidget("Corners",
232 xmToggleButtonWidgetClass, rowcol2, NULL);
233 XtAddCallback(cornersSwitch,
234 XmNvalueChangedCallback, (XtCallbackProc) CornersToggle, (XtPointer) NULL);
235 message = XtVaCreateManagedWidget("Play Hexagons! (use mouse or keypad)",
236 xmLabelWidgetClass, rowcol2, NULL);
237
238 hexagons = XtCreateManagedWidget("hexagons",
239 hexagonsWidgetClass, panel, NULL, 0);
240 XtAddCallback(hexagons,
241 XtNselectCallback, (XtCallbackProc) CallbackHexagons, (XtPointer) NULL);
242 Initialize(hexagons);
243 XtRealizeWidget(toplevel);
244 XGrabButton(XtDisplay(hexagons), (unsigned int) AnyButton, AnyModifier,
245 XtWindow(hexagons), TRUE,
246 (unsigned int) (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask),
247 GrabModeAsync, GrabModeAsync, XtWindow(hexagons),
248 XCreateFontCursor(XtDisplay(hexagons), XC_crosshair));
249 XtMainLoop();
250
251 #ifdef VMS
252 return 1;
253 #else
254 return 0;
255 #endif
256 }
257
258 static void
Initialize(Widget w)259 Initialize(Widget w)
260 {
261 int size;
262 Boolean corners;
263 String username;
264
265 XtVaSetValues(w,
266 XtNstart, False, NULL);
267 XtVaGetValues(w,
268 XtNuserName, &username,
269 XtNsize, &size,
270 XtNcorners, &corners, NULL);
271 if (size <= MAXHEXAGONS)
272 XmScaleSetValue(hex, size);
273 XmToggleButtonSetState(cornersSwitch, corners, True);
274 InitRecords();
275 ReadRecords();
276 (void) strcpy(usernameDsp, username);
277 if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS) ||
278 !strcmp(usernameDsp, NOBODY)) {
279 /* The NOACCESS is not necasary, but it stops people from being cute. */
280 (void) sprintf(usernameDsp, "%s", getlogin());
281 if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS))
282 (void) sprintf(usernameDsp, "%s", NOBODY); /* It really IS nobody */
283 }
284 PrintRecord(size, corners);
285 }
286
287 static void
CallbackHexagons(Widget w,caddr_t clientData,hexagonsCallbackStruct * callData)288 CallbackHexagons(Widget w, caddr_t clientData,
289 hexagonsCallbackStruct * callData)
290 {
291 int size;
292 Boolean corners;
293
294 XtVaGetValues(w,
295 XtNsize, &size,
296 XtNcorners, &corners, NULL);
297 (void) strcpy(messageDsp, "");
298 switch (callData->reason) {
299 case HEXAGONS_RESTORE:
300 case HEXAGONS_RESET:
301 movesDsp = 0;
302 break;
303 case HEXAGONS_BLOCKED:
304 (void) strcpy(messageDsp, "Blocked");
305 break;
306 case HEXAGONS_SPACE:
307 #if 0
308 /* Too annoying */
309 (void) strcpy(messageDsp, "Spaces can not slide");
310 #endif
311 break;
312 case HEXAGONS_IGNORE:
313 (void) strcpy(messageDsp, "Randomize to start");
314 break;
315 case HEXAGONS_MOVED:
316 movesDsp++;
317 XtSetArg(arg[0], XtNstart, True);
318 XtSetValues(w, arg, 1);
319 break;
320 case HEXAGONS_SOLVED:
321 if (HandleSolved(movesDsp, size, corners))
322 (void) sprintf(messageDsp, "Congratulations %s!!", usernameDsp);
323 else
324 (void) strcpy(messageDsp, "Solved!");
325 XtSetArg(arg[0], XtNstart, False);
326 XtSetValues(w, arg, 1);
327 break;
328 case HEXAGONS_RANDOMIZE:
329 movesDsp = 0;
330 XtSetArg(arg[0], XtNstart, False);
331 XtSetValues(w, arg, 1);
332 break;
333 case HEXAGONS_DEC:
334 movesDsp = 0;
335 size--;
336 PrintRecord(size, corners);
337 XtSetArg(arg[0], XtNsize, size);
338 XtSetValues(w, arg, 1);
339 if (size <= MAXHEXAGONS)
340 XmScaleSetValue(hex, size);
341 break;
342 case HEXAGONS_INC:
343 movesDsp = 0;
344 size++;
345 PrintRecord(size, corners);
346 XtSetArg(arg[0], XtNsize, size);
347 XtSetValues(w, arg, 1);
348 if (size <= MAXHEXAGONS)
349 XmScaleSetValue(hex, size);
350 break;
351 case HEXAGONS_CORNERS:
352 movesDsp = 0;
353 corners = !corners;
354 PrintRecord(size, corners);
355 XtSetArg(arg[0], XtNcorners, corners);
356 XtSetValues(w, arg, 1);
357 XmToggleButtonSetState(cornersSwitch, corners, True);
358 break;
359 case HEXAGONS_COMPUTED:
360 XtSetArg(arg[0], XtNstart, False);
361 XtSetValues(w, arg, 1);
362 break;
363 case HEXAGONS_UNDO:
364 movesDsp--;
365 XtSetArg(arg[0], XtNstart, True);
366 XtSetValues(w, arg, 1);
367 break;
368 }
369 motif_print(message, messageDsp);
370 (void) sprintf(buff, "%d", movesDsp);
371 motif_print(moves, buff);
372 }
373
374 static void
HexSlider(Widget w,XtPointer clientData,XmScaleCallbackStruct * cbs)375 HexSlider(Widget w, XtPointer clientData, XmScaleCallbackStruct * cbs)
376 {
377 int size = cbs->value, old;
378
379 XtVaGetValues(hexagons,
380 XtNsize, &old, NULL);
381 if (old != size) {
382 XtVaSetValues(hexagons,
383 XtNsize, size, NULL);
384 movesDsp = 0;
385 (void) sprintf(buff, "%d", movesDsp);
386 motif_print(moves, buff);
387 PrintRecord(size, XmToggleButtonGetState(cornersSwitch));
388 }
389 }
390
391 static void
CornersToggle(Widget w,XtPointer clientData,XmToggleButtonCallbackStruct * cbs)392 CornersToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct * cbs)
393 {
394 int size;
395 Boolean corners = cbs->set;
396
397 XtVaSetValues(hexagons,
398 XtNcorners, corners, NULL);
399 XtVaGetValues(hexagons,
400 XtNsize, &size, NULL);
401 movesDsp = 0;
402 (void) sprintf(buff, "%d", movesDsp);
403 motif_print(moves, buff);
404 PrintRecord(size, corners);
405 }
406
407 static void
PrintRecord(int size,Boolean corners)408 PrintRecord(int size, Boolean corners)
409 {
410 int i = (corners) ? 1 : 0, j = size - MINHEXAGONS;
411
412 if (size > MAXHEXAGONS)
413 motif_print(record, "NOT RECORDED");
414 if (hexagonsRecord[i][j].score >= MAXRECORD) {
415 (void) sprintf(buff, "NEVER %s", NOACCESS);
416 motif_print(record, buff);
417 } else {
418 (void) sprintf(buff, "%d %s",
419 hexagonsRecord[i][j].score, hexagonsRecord[i][j].name);
420 motif_print(record, buff);
421 }
422 }
423
424
425 static Boolean
HandleSolved(int counter,int size,int corners)426 HandleSolved(int counter, int size, int corners)
427 {
428 int i = (corners) ? 1 : 0, j = size - MINHEXAGONS;
429
430 if (size <= MAXHEXAGONS && counter < hexagonsRecord[i][j].score) {
431 hexagonsRecord[i][j].score = counter;
432 (void) strcpy(hexagonsRecord[i][j].name, usernameDsp);
433 WriteRecords();
434 PrintRecord(size, corners);
435 return True;
436 }
437 return False;
438 }
439
440 static void
InitRecords(void)441 InitRecords(void)
442 {
443 int i, j;
444
445 for (i = 0; i < MAXORIENT; i++)
446 for (j = 0; j < MAXHEXAGONS - MINHEXAGONS + 1; j++) {
447 hexagonsRecord[i][j].score = MAXRECORD;
448 (void) strcpy(hexagonsRecord[i][j].name, NOACCESS);
449 }
450 }
451
452 static void
ReadRecords(void)453 ReadRecords(void)
454 {
455 FILE *fp;
456 int i, j, n;
457 char username[USERNAMELEN];
458
459 if ((fp = fopen(SCOREFILE, "r")) == NULL) {
460 (void) sprintf(buff, "Can not open %s, taking defaults.", SCOREFILE);
461 motif_print(message, buff);
462 } else {
463 for (i = 0; i < MAXORIENT; i++)
464 for (j = 0; j < MAXHEXAGONS - MINHEXAGONS + 1; j++) {
465 (void) fscanf(fp, "%d %s\n", &n, username);
466 if (n <= hexagonsRecord[i][j].score) {
467 hexagonsRecord[i][j].score = n;
468 (void) strcpy(hexagonsRecord[i][j].name, username);
469 }
470 }
471 (void) fclose(fp);
472 }
473 }
474
475 static void
WriteRecords(void)476 WriteRecords(void)
477 {
478 FILE *fp;
479 int i, j;
480
481 ReadRecords(); /* Maybe its been updated by another */
482 if ((fp = fopen(SCOREFILE, "w")) == NULL) {
483 (void) sprintf(buff, "Can not write to %s.", SCOREFILE);
484 motif_print(message, buff);
485 } else {
486 #if HAVE_FCNTL_H
487 int lfd;
488 char lockfile[FILENAMELEN];
489
490 (void) strcpy(lockfile, SCOREFILE);
491 (void) strcat(lockfile, ".lock");
492 while (((lfd = open(lockfile, O_CREAT | O_EXCL, 0644)) < 0) &&
493 errno == EEXIST)
494 (void) sleep(1);
495 if (lfd < 0) {
496 #if 1
497 (void) fprintf(stderr, "Lock file exists... guessing its an old one.\n");
498 #else
499 (void) fprintf(stderr, "Lock file exists... score not recorded - sorry.\n"
500 );
501 return;
502 #endif
503 }
504 #endif
505 for (i = 0; i < MAXORIENT; i++) {
506 for (j = 0; j < MAXHEXAGONS - MINHEXAGONS + 1; j++)
507 (void) fprintf(fp, "%d %s\n",
508 hexagonsRecord[i][j].score, hexagonsRecord[i][j].name);
509 (void) fprintf(fp, "\n");
510 }
511 #if HAVE_FCNTL_H
512 (void) close(lfd);
513 (void) unlink(lockfile);
514 #endif
515 (void) fclose(fp);
516 }
517 }
518
519 static void
motif_print(Widget w,char * text)520 motif_print(Widget w, char *text)
521 {
522 Arg wargs[1];
523 XmString xmstr;
524
525 if (!XtIsSubclass(w, xmLabelWidgetClass))
526 XtError("motif_print() requires a Label Widget");
527 xmstr = XmStringCreateLtoR(text, XmSTRING_DEFAULT_CHARSET);
528 XtSetArg(wargs[0], XmNlabelString, xmstr);
529 XtSetValues(w, wargs, 1);
530 }
531