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