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