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