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