1 
2 /*-
3 # X-BASED SKEWB
4 #
5 #  xskewb.c
6 #
7 ###
8 #
9 #  Copyright (c) 1994 - 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 4: 94/05/30 Xt
29   Version 3: 93/10/14 Motif
30   Version 2: 92/01/22 XView
31   Version 1: 91/03/19 SunView
32 */
33 
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #ifdef VMS
38 #include <unixlib.h>
39 #define getlogin() cuserid(NULL)
40 #else
41 #if HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #endif
45 #if HAVE_FCNTL_H
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #endif
50 #include <X11/Intrinsic.h>
51 #include <X11/StringDefs.h>
52 #include <X11/Shell.h>
53 #include <X11/cursorfont.h>
54 #include "Skewb.h"
55 #include "Skewb2d.h"
56 #include "Skewb3d.h"
57 #include "skewb.xbm"
58 
59 #ifndef SCOREFILE
60 #define SCOREFILE "/usr/games/lib/skewb.scores"
61 #endif
62 
63 /* The following is in SkewbP.h also */
64 #define MAXFACES 6
65 
66 #define MAXRECORD 32767
67 #define FILENAMELEN 1024
68 #define USERNAMELEN 128
69 #define NOACCESS "noaccess"
70 #define NOBODY "nobody"
71 
72 typedef struct {
73 	int         score;
74 	char        name[USERNAMELEN];
75 } GameRecord;
76 
77 static void Initialize(void);
78 static void CallbackSkewb(Widget w, caddr_t clientData,
79 			  skewbCallbackStruct * callData);
80 
81 static void PrintRecord(Boolean orient, Boolean practice, char *record);
82 static Boolean HandleSolved(int counter, Boolean orient);
83 static void PrintState(Widget w,
84 		       char *prog, int dim, int moves,
85 		       char *record, char *message);
86 static void InitRecords(void);
87 static void ReadRecords(void);
88 static void WriteRecords(void);
89 
90 static Widget skewb2d, skewb3d;
91 static Arg  arg[4];
92 static GameRecord skewbRecord[2];
93 static int  movesDsp = 0;
94 static char progDsp[64] = "xskewb";
95 static char recordDsp[16] = "INF";
96 static char messageDsp[128] = "Welcome";
97 static char titleDsp[256] = "";
98 static char usernameDsp[USERNAMELEN] = "";
99 
100 static void
Usage(void)101 Usage(void)
102 {
103 	(void) fprintf(stderr, "usage: xskewb\n");
104 	(void) fprintf(stderr,
105 	     "\t[-geometry [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]]\n");
106 	(void) fprintf(stderr,
107 	   "\t[-display [{host}]:[{vs}]] [-[no]mono] [-[no]{reverse|rv}]\n");
108 	(void) fprintf(stderr,
109 		"\t[-{foreground|fg} {color}] [-{background|bg} {color}]\n");
110 	(void) fprintf(stderr,
111 		  "\t[-{border|bd} {color}] [-face{0|1|2|3|4|5} {color}]\n");
112 	(void) fprintf(stderr,
113 		   "\t[-[no]orient] [-[no]practice] [-username {string}]\n");
114 	exit(1);
115 }
116 
117 static XrmOptionDescRec options[] =
118 {
119 	{"-mono", "*skewb.mono", XrmoptionNoArg, "TRUE"},
120 	{"-nomono", "*skewb.mono", XrmoptionNoArg, "FALSE"},
121 	{"-rv", "*skewb.reverse", XrmoptionNoArg, "TRUE"},
122 	{"-reverse", "*skewb.reverse", XrmoptionNoArg, "TRUE"},
123 	{"-norv", "*skewb.reverse", XrmoptionNoArg, "FALSE"},
124 	{"-noreverse", "*skewb.reverse", XrmoptionNoArg, "FALSE"},
125 	{"-fg", "*skewb.Foreground", XrmoptionSepArg, NULL},
126 	{"-foreground", "*skewb.Foreground", XrmoptionSepArg, NULL},
127 	{"-bg", "*Background", XrmoptionSepArg, NULL},
128 	{"-background", "*Background", XrmoptionSepArg, NULL},
129 	{"-bd", "*skewb.pieceBorder", XrmoptionSepArg, NULL},
130 	{"-border", "*skewb.pieceBorder", XrmoptionSepArg, NULL},
131 	{"-face0", "*skewb.faceColor0", XrmoptionSepArg, NULL},
132 	{"-face1", "*skewb.faceColor1", XrmoptionSepArg, NULL},
133 	{"-face2", "*skewb.faceColor2", XrmoptionSepArg, NULL},
134 	{"-face3", "*skewb.faceColor3", XrmoptionSepArg, NULL},
135 	{"-face4", "*skewb.faceColor4", XrmoptionSepArg, NULL},
136 	{"-face5", "*skewb.faceColor5", XrmoptionSepArg, NULL},
137 	{"-orient", "*skewb.orient", XrmoptionNoArg, "TRUE"},
138 	{"-noorient", "*skewb.orient", XrmoptionNoArg, "FALSE"},
139 	{"-practice", "*skewb.practice", XrmoptionNoArg, "TRUE"},
140 	{"-nopractice", "*skewb.practice", XrmoptionNoArg, "FALSE"},
141 	{"-username", "*skewb.userName", XrmoptionSepArg, NULL}
142 };
143 
144 int
main(int argc,char ** argv)145 main(int argc, char **argv)
146 {
147 	Widget      toplevel, shell;
148 
149 	toplevel = XtInitialize(argv[0], "Skewb",
150 				options, XtNumber(options), &argc, argv);
151 	if (argc != 1)
152 		Usage();
153 
154 	shell = XtCreateApplicationShell(argv[0],
155 					 topLevelShellWidgetClass, NULL, 0);
156 	XtSetArg(arg[0],
157 		 XtNiconPixmap, XCreateBitmapFromData(XtDisplay(toplevel),
158 				      RootWindowOfScreen(XtScreen(toplevel)),
159 			    (char *) skewb_bits, skewb_width, skewb_height));
160 	XtSetArg(arg[1], XtNinput, True);
161 	XtSetValues(toplevel, arg, 2);
162 	XtSetArg(arg[0], XtNiconPixmap, XCreateBitmapFromData(XtDisplay(shell),
163 					 RootWindowOfScreen(XtScreen(shell)),
164 			    (char *) skewb_bits, skewb_width, skewb_height));
165 	XtSetArg(arg[1], XtNinput, True);
166 	XtSetValues(shell, arg, 2);
167 	skewb2d = XtCreateManagedWidget("skewb",
168 				      skewb2dWidgetClass, toplevel, NULL, 0);
169 	XtAddCallback(skewb2d,
170 	XtNselectCallback, (XtCallbackProc) CallbackSkewb, (XtPointer) NULL);
171 	skewb3d = XtCreateManagedWidget("skewb",
172 					skewb3dWidgetClass, shell, NULL, 0);
173 	XtAddCallback(skewb3d,
174 	XtNselectCallback, (XtCallbackProc) CallbackSkewb, (XtPointer) NULL);
175 	Initialize();
176 	XtRealizeWidget(toplevel);
177 	XtRealizeWidget(shell);
178 	XGrabButton(XtDisplay(skewb2d), (unsigned int) AnyButton, AnyModifier,
179 		    XtWindow(skewb2d), TRUE,
180 		    (unsigned int) (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask),
181 		    GrabModeAsync, GrabModeAsync, XtWindow(skewb2d),
182 		    XCreateFontCursor(XtDisplay(skewb2d), XC_crosshair));
183 	XGrabButton(XtDisplay(skewb3d), (unsigned int) AnyButton, AnyModifier,
184 		    XtWindow(skewb3d), TRUE,
185 		    (unsigned int) (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask),
186 		    GrabModeAsync, GrabModeAsync, XtWindow(skewb3d),
187 		    XCreateFontCursor(XtDisplay(skewb3d), XC_crosshair));
188 	XtMainLoop();
189 
190 #ifdef VMS
191 	return 1;
192 #else
193 	return 0;
194 #endif
195 }
196 
197 /* There's probably a better way to assure that they are the same but I don't
198    know it off hand. */
199 static void
MakeEquivalent(String * username,Boolean * orient,Boolean * practice)200 MakeEquivalent(String * username, Boolean * orient, Boolean * practice)
201 {
202 	Boolean     mono, reverse;
203 	Pixel       foreground, background, pieceBorder;
204 	String      faceColor[MAXFACES];
205 
206 	XtVaGetValues(skewb2d,
207 		      XtNuserName, username,
208 		      XtNorient, orient,
209 		      XtNpractice, practice,
210 		      XtNmono, &mono,
211 		      XtNreverse, &reverse,
212 		      XtNforeground, &foreground,
213 		      XtNbackground, &background,
214 		      XtNpieceBorder, &pieceBorder,
215 		      XtNfaceColor0, &(faceColor[0]),
216 		      XtNfaceColor1, &(faceColor[1]),
217 		      XtNfaceColor2, &(faceColor[2]),
218 		      XtNfaceColor3, &(faceColor[3]),
219 		      XtNfaceColor4, &(faceColor[4]),
220 		      XtNfaceColor5, &(faceColor[5]), NULL);
221 	XtVaSetValues(skewb2d,
222 		      XtNdirection, SKEWB_IGNORE,
223 		      XtNstart, False, NULL);
224 	XtVaSetValues(skewb3d,
225 		      XtNuserName, *username,
226 		      XtNorient, *orient,
227 		      XtNpractice, *practice,
228 		      XtNmono, mono,
229 		      XtNreverse, reverse,
230 		      XtNdirection, SKEWB_IGNORE,
231 		      XtNstart, False,
232 		      XtNforeground, foreground,
233 		      XtNbackground, background,
234 		      XtNpieceBorder, pieceBorder,
235 		      XtNfaceColor0, faceColor[0],
236 		      XtNfaceColor1, faceColor[1],
237 		      XtNfaceColor2, faceColor[2],
238 		      XtNfaceColor3, faceColor[3],
239 		      XtNfaceColor4, faceColor[4],
240 		      XtNfaceColor5, faceColor[5], NULL);
241 }
242 
243 static void
Initialize(void)244 Initialize(void)
245 {
246 	Boolean     orient, practice;
247 	String      username;
248 
249 	MakeEquivalent(&username, &orient, &practice);
250 	InitRecords();
251 	ReadRecords();
252 	(void) strcpy(usernameDsp, username);
253 	if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS) ||
254 	    !strcmp(usernameDsp, NOBODY)) {
255 		/* The NOACCESS is not necasary, but it stops people from being cute. */
256 		(void) sprintf(usernameDsp, "%s", getlogin());
257 		if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS))
258 			(void) sprintf(usernameDsp, "%s", NOBODY);	/* It really IS nobody */
259 	}
260 	PrintRecord(orient, practice, recordDsp);
261 	PrintState(XtParent(skewb2d), progDsp, 2, movesDsp,
262 		   recordDsp, messageDsp);
263 	PrintState(XtParent(skewb3d), progDsp, 3, movesDsp,
264 		   recordDsp, messageDsp);
265 }
266 
267 static void
CallbackSkewb(Widget w,caddr_t clientData,skewbCallbackStruct * callData)268 CallbackSkewb(Widget w, caddr_t clientData, skewbCallbackStruct * callData)
269 {
270 	int         dim, otherdim;
271 	Boolean     orient, practice, start;
272 	Widget      otherw;
273 
274 	if (w == skewb2d) {
275 		dim = 2;
276 		otherw = skewb3d;
277 		otherdim = 3;
278 	} else {		/* (w == skewb3d) */
279 		dim = 3;
280 		otherw = skewb2d;
281 		otherdim = 2;
282 	}
283 	XtVaGetValues(w,
284 		      XtNorient, &orient,
285 		      XtNpractice, &practice,
286 		      XtNstart, &start, NULL);
287 	(void) strcpy(messageDsp, "");
288 	switch (callData->reason) {
289 		case SKEWB_RESTORE:
290 			XtSetArg(arg[0], XtNdirection, SKEWB_RESTORE);
291 			XtSetValues(otherw, arg, 1);
292 			XtSetValues(w, arg, 1);
293 			movesDsp = 0;
294 			break;
295 		case SKEWB_RESET:
296 			movesDsp = 0;
297 			break;
298 		case SKEWB_ILLEGAL:
299 			if (practice || start)
300 				(void) strcpy(messageDsp, "Illegal move");
301 			else
302 				(void) strcpy(messageDsp, "Randomize to start");
303 			break;
304 		case SKEWB_MOVED:
305 			movesDsp++;
306 #ifdef DEBUG
307 			if (movesDsp > 256)
308 				exit(1);
309 #endif
310 			XtSetArg(arg[0], XtNstart, True);
311 			XtSetArg(arg[1], XtNface, callData->face);
312 			XtSetArg(arg[2], XtNpos, callData->position);
313 			XtSetArg(arg[3], XtNdirection, callData->direction);
314 			XtSetValues(otherw, arg, 4);
315 			XtSetValues(w, arg, 1);
316 			break;
317 		case SKEWB_CONTROL:
318 			XtSetArg(arg[0], XtNface, callData->face);
319 			XtSetArg(arg[1], XtNpos, callData->position);
320 			XtSetArg(arg[2], XtNdirection, callData->direction);
321 			XtSetValues(otherw, arg, 3);
322 			return;
323 		case SKEWB_SOLVED:
324 			if (practice)
325 				movesDsp = 0;
326 			else {
327 				if (HandleSolved(movesDsp, orient))
328 					(void) sprintf(messageDsp, "Congratulations %s!!", usernameDsp);
329 				else
330 					(void) strcpy(messageDsp, "Solved!");
331 			}
332 			XtSetArg(arg[0], XtNstart, False);
333 			XtSetValues(w, arg, 1);
334 			XtSetValues(otherw, arg, 1);
335 			break;
336 		case SKEWB_PRACTICE:
337 			movesDsp = 0;
338 			practice = !practice;
339 			if (!practice)
340 				(void) strcpy(messageDsp, "Randomize to start");
341 			PrintRecord(orient, practice, recordDsp);
342 			XtSetArg(arg[0], XtNpractice, practice);
343 			XtSetArg(arg[1], XtNstart, False);
344 			XtSetValues(w, arg, 2);
345 			XtSetValues(otherw, arg, 2);
346 			break;
347 		case SKEWB_RANDOMIZE:
348 			movesDsp = 0;
349 			XtSetArg(arg[0], XtNpractice, False);
350 			XtSetArg(arg[1], XtNstart, False);
351 			XtSetValues(w, arg, 2);
352 			XtSetValues(otherw, arg, 2);
353 			break;
354 		case SKEWB_ORIENT:
355 			movesDsp = 0;
356 			orient = !orient;
357 			PrintRecord(orient, practice, recordDsp);
358 			XtSetArg(arg[0], XtNorient, orient);
359 			XtSetValues(w, arg, 1);
360 			XtSetValues(otherw, arg, 1);
361 			break;
362 		case SKEWB_COMPUTED:
363 			XtSetArg(arg[0], XtNstart, False);
364 			XtSetValues(w, arg, 1);
365 			XtSetValues(otherw, arg, 1);
366 			break;
367 		case SKEWB_UNDO:
368 			movesDsp--;
369 			XtSetArg(arg[0], XtNstart, True);
370 			XtSetArg(arg[1], XtNface, callData->face);
371 			XtSetArg(arg[2], XtNpos, callData->position);
372 			XtSetArg(arg[3], XtNdirection, callData->direction);
373 			XtSetValues(otherw, arg, 4);
374 			XtSetValues(w, arg, 1);
375 			break;
376 	}
377 	PrintState(XtParent(w), progDsp, dim, movesDsp,
378 		   recordDsp, messageDsp);
379 	PrintState(XtParent(otherw), progDsp, otherdim, movesDsp,
380 		   recordDsp, messageDsp);
381 }
382 
383 static void
PrintRecord(Boolean orient,Boolean practice,char * record)384 PrintRecord(Boolean orient, Boolean practice, char *record)
385 {
386 	int         i = (orient) ? 1 : 0;
387 
388 	if (practice)
389 		(void) strcpy(record, "practice");
390 	else if (skewbRecord[i].score >= MAXRECORD)
391 		(void) sprintf(record, "NEVER %s", NOACCESS);
392 	else
393 		(void) sprintf(record, "%d %s",
394 			       skewbRecord[i].score, skewbRecord[i].name);
395 }
396 
397 static      Boolean
HandleSolved(int counter,Boolean orient)398 HandleSolved(int counter, Boolean orient)
399 {
400 	int         i = (orient) ? 1 : 0;
401 
402 	if (counter < skewbRecord[i].score) {
403 		skewbRecord[i].score = counter;
404 		(void) strcpy(skewbRecord[i].name, usernameDsp);
405 		if (orient && (counter < skewbRecord[!i].score)) {
406 			skewbRecord[!i].score = counter;
407 			(void) strcpy(skewbRecord[!i].name, usernameDsp);
408 		}
409 		WriteRecords();
410 		PrintRecord(orient, False, recordDsp);
411 		return True;
412 	}
413 	return False;
414 }
415 
416 static void
PrintState(Widget w,char * prog,int dim,int moves,char * record,char * message)417 PrintState(Widget w, char *prog, int dim, int moves, char *record, char *message)
418 {
419 	(void) sprintf(titleDsp, "%s%dd: (%d/%s) - %s", prog, dim, moves,
420 		       record, message);
421 	XtSetArg(arg[0], XtNtitle, titleDsp);
422 	XtSetValues(w, arg, 1);
423 }
424 
425 static void
InitRecords(void)426 InitRecords(void)
427 {
428 	int         orient;
429 
430 	for (orient = 0; orient < 2; orient++) {
431 		skewbRecord[orient].score = MAXRECORD;
432 		(void) strcpy(skewbRecord[orient].name, NOACCESS);
433 	}
434 }
435 
436 static void
ReadRecords(void)437 ReadRecords(void)
438 {
439 	FILE       *fp;
440 	int         n, orient;
441 	char        username[USERNAMELEN];
442 
443 	if ((fp = fopen(SCOREFILE, "r")) == NULL)
444 		(void) sprintf(messageDsp, "Can not open %s, taking defaults.", SCOREFILE);
445 	else {
446 		for (orient = 0; orient < 2; orient++) {
447 			(void) fscanf(fp, "%d %s\n", &n, username);
448 			if (n <= skewbRecord[orient].score) {
449 				skewbRecord[orient].score = n;
450 				(void) strcpy(skewbRecord[orient].name, username);
451 			}
452 		}
453 		(void) fclose(fp);
454 	}
455 }
456 
457 static void
WriteRecords(void)458 WriteRecords(void)
459 {
460 	FILE       *fp;
461 	int         orient;
462 
463 	ReadRecords();		/* Maybe its been updated by another */
464 	if ((fp = fopen(SCOREFILE, "w")) == NULL)
465 		(void) sprintf(messageDsp, "Can not write to %s.", SCOREFILE);
466 	else {
467 #if HAVE_FCNTL_H
468 		int         lfd;
469 		char        lockfile[FILENAMELEN];
470 
471 		(void) strcpy(lockfile, SCOREFILE);
472 		(void) strcat(lockfile, ".lock");
473 		while (((lfd = open(lockfile, O_CREAT | O_EXCL, 0644)) < 0) &&
474 		       errno == EEXIST)
475 			(void) sleep(1);
476 		if (lfd < 0) {
477 #if 1
478 			(void) fprintf(stderr, "Lock file exists... guessing its an old one.\n");
479 #else
480 			(void) fprintf(stderr, "Lock file exists... score not recorded - sorry.\n"
481 				);
482 			return;
483 #endif
484 		}
485 #endif
486 		for (orient = 0; orient < 2; orient++)
487 			(void) fprintf(fp, "%d %s\n",
488 			skewbRecord[orient].score, skewbRecord[orient].name);
489 #if HAVE_FCNTL_H
490 		(void) close(lfd);
491 		(void) unlink(lockfile);
492 #endif
493 		(void) fclose(fp);
494 	}
495 }
496