1
2 /*-
3 # MOTIF-BASED TRIANGLES
4 #
5 # xmtriangles.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/06 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
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 "Triangles.h"
61 #include "triangles.xbm"
62 #include "mouse-l.xbm"
63 #include "mouse-r.xbm"
64
65 #ifndef SCOREFILE
66 #define SCOREFILE "/usr/games/lib/triangles.scores"
67 #endif
68
69 /* The following is in TrianglesP.h also */
70 #define MINTRIANGLES 1
71
72 #define MAXTRIANGLES 16
73 #define MAXRECORD 32767
74 #define FILENAMELEN 1024
75 #define USERNAMELEN 128
76 #define NOACCESS "noaccess"
77 #define NOBODY "nobody"
78
79 typedef struct {
80 int score;
81 char name[USERNAMELEN];
82 } GameRecord;
83
84 static void Initialize(Widget w);
85 static void CallbackTriangles(Widget w, caddr_t clientData,
86 trianglesCallbackStruct * callData);
87
88 static void PrintRecord(int size);
89 static Boolean HandleSolved(int counter, int size);
90 static void InitRecords(void);
91 static void ReadRecords(void);
92 static void WriteRecords(void);
93
94 static void triSlider(Widget w, XtPointer clientData,
95 XmScaleCallbackStruct * cbs);
96 static void motif_print(Widget w, char *text);
97
98 static Arg arg[2];
99 static Widget moves, record, message, triangles, tri;
100 static GameRecord trianglesRecord[MAXTRIANGLES - MINTRIANGLES + 1];
101 static int movesDsp = 0;
102 static char messageDsp[128] = "Welcome";
103 static char usernameDsp[USERNAMELEN] = "";
104 static char buff[256];
105
106 static void
Usage(void)107 Usage(void)
108 {
109 (void) fprintf(stderr, "usage: xmtriangles\n");
110 (void) fprintf(stderr,
111 "\t[-geometry [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]]\n");
112 (void) fprintf(stderr,
113 "\t[-display [{host}]:[{vs}]][-fg {color}] [-[no]mono] [-[no]{reverse|rv}]\n");
114 (void) fprintf(stderr,
115 "\t[-{foreground|fg} {color}] [-{background|bg} {color}]\n");
116 (void) fprintf(stderr,
117 "\t[-{border|bd} {color}] [-tile {color}] [-size {int}]\n");
118 (void) fprintf(stderr,
119 "\t[-base {int}] [-username {string}]\n");
120 exit(1);
121 }
122
123 static XrmOptionDescRec options[] =
124 {
125 {"-mono", "*triangles.mono", XrmoptionNoArg, "TRUE"},
126 {"-nomono", "*triangles.mono", XrmoptionNoArg, "FALSE"},
127 {"-rv", "*triangles.reverse", XrmoptionNoArg, "TRUE"},
128 {"-reverse", "*triangles.reverse", XrmoptionNoArg, "TRUE"},
129 {"-norv", "*triangles.reverse", XrmoptionNoArg, "FALSE"},
130 {"-noreverse", "*triangles.reverse", XrmoptionNoArg, "FALSE"},
131 {"-fg", "*triangles.Foreground", XrmoptionSepArg, NULL},
132 {"-foreground", "*triangles.Foreground", XrmoptionSepArg, NULL},
133 {"-bg", "*Background", XrmoptionSepArg, NULL},
134 {"-background", "*Background", XrmoptionSepArg, NULL},
135 {"-bd", "*triangles.tileBorder", XrmoptionSepArg, NULL},
136 {"-border", "*triangles.tileBorder", XrmoptionSepArg, NULL},
137 {"-tile", "*triangles.tileColor", XrmoptionSepArg, NULL},
138 {"-size", "*triangles.size", XrmoptionSepArg, NULL},
139 {"-base", "*triangles.base", XrmoptionSepArg, NULL},
140 {"-username", "*triangles.userName", XrmoptionSepArg, NULL}
141 };
142
143 int
main(int argc,char ** argv)144 main(int argc, char **argv)
145 {
146 Widget toplevel;
147 Widget panel, panel2, rowcol, rowcol2, rowcol3;
148 Pixmap mouseLeftCursor, mouseRightCursor;
149 Pixel fg, bg;
150
151 toplevel = XtInitialize(argv[0], "Triangles",
152 options, XtNumber(options), &argc, argv);
153 if (argc != 1)
154 Usage();
155
156 XtSetArg(arg[0],
157 XtNiconPixmap, XCreateBitmapFromData(XtDisplay(toplevel),
158 RootWindowOfScreen(XtScreen(toplevel)),
159 (char *) triangles_bits, triangles_width, triangles_height));
160 XtSetArg(arg[1], XmNkeyboardFocusPolicy, XmPOINTER); /* not XmEXPLICIT */
161 XtSetValues(toplevel, arg, 2);
162 panel = XtCreateManagedWidget("panel",
163 xmPanedWindowWidgetClass, toplevel, NULL, 0);
164 panel2 = XtVaCreateManagedWidget("panel2",
165 xmPanedWindowWidgetClass, panel,
166 XmNseparatorOn, False,
167 XmNsashWidth, 1,
168 XmNsashHeight, 1, NULL);
169
170 rowcol = XtVaCreateManagedWidget("Rowcol",
171 xmRowColumnWidgetClass, panel2,
172 XmNnumColumns, 2,
173 XmNorientation, XmHORIZONTAL,
174 XmNpacking, XmPACK_COLUMN, NULL);
175 XtVaGetValues(rowcol,
176 XmNforeground, &fg,
177 XmNbackground, &bg, NULL);
178 mouseLeftCursor = XCreatePixmapFromBitmapData(XtDisplay(rowcol),
179 RootWindowOfScreen(XtScreen(rowcol)), (char *) mouse_left_bits,
180 mouse_left_width, mouse_left_height, fg, bg,
181 DefaultDepthOfScreen(XtScreen(rowcol)));
182 mouseRightCursor = XCreatePixmapFromBitmapData(XtDisplay(rowcol),
183 RootWindowOfScreen(XtScreen(rowcol)), (char *) mouse_right_bits,
184 mouse_right_width, mouse_right_height, fg, bg,
185 DefaultDepthOfScreen(XtScreen(rowcol)));
186 XtVaCreateManagedWidget("mouseLeftText",
187 xmLabelGadgetClass, rowcol,
188 XtVaTypedArg, XmNlabelString, XmRString, "Move tile", 10, NULL);
189 XtVaCreateManagedWidget("mouseLeft",
190 xmLabelGadgetClass, rowcol,
191 XmNlabelType, XmPIXMAP, XmNlabelPixmap, mouseLeftCursor, NULL);
192 XtVaCreateManagedWidget("mouseRightText",
193 xmLabelGadgetClass, rowcol,
194 XtVaTypedArg, XmNlabelString, XmRString, "Randomize", 10, NULL);
195 XtVaCreateManagedWidget("mouseRight",
196 xmLabelGadgetClass, rowcol,
197 XmNlabelType, XmPIXMAP, XmNlabelPixmap, mouseRightCursor, NULL);
198 XtVaCreateManagedWidget("movesText",
199 xmLabelGadgetClass, rowcol,
200 XtVaTypedArg, XmNlabelString, XmRString, "Moves", 6, NULL);
201 moves = XtVaCreateManagedWidget("0",
202 xmLabelWidgetClass, rowcol, NULL);
203 XtVaCreateManagedWidget("recordText",
204 xmLabelGadgetClass, rowcol,
205 XtVaTypedArg, XmNlabelString, XmRString, "Record", 7, NULL);
206 record = XtVaCreateManagedWidget("0",
207 xmLabelWidgetClass, rowcol, NULL);
208
209 rowcol2 = XtVaCreateManagedWidget("Rowcol2",
210 xmRowColumnWidgetClass, panel2, NULL);
211 XtVaGetValues(rowcol2,
212 XmNforeground, &fg,
213 XmNbackground, &bg, NULL);
214 tri = XtVaCreateManagedWidget("tris",
215 xmScaleWidgetClass, rowcol2,
216 XtVaTypedArg, XmNtitleString, XmRString, "Tris", 5,
217 XmNminimum, MINTRIANGLES,
218 XmNmaximum, MAXTRIANGLES,
219 XmNvalue, MINTRIANGLES,
220 XmNshowValue, True,
221 XmNorientation, XmHORIZONTAL, NULL);
222 XtAddCallback(tri,
223 XmNvalueChangedCallback, (XtCallbackProc) triSlider, (XtPointer) NULL);
224 rowcol3 = XtVaCreateManagedWidget("Rowcol3",
225 xmRowColumnWidgetClass, panel2, NULL);
226 message = XtVaCreateManagedWidget("Play Triangles! (use mouse or keypad)",
227 xmLabelWidgetClass, rowcol3, NULL);
228
229 triangles = XtCreateManagedWidget("triangles",
230 trianglesWidgetClass, panel, NULL, 0);
231 XtAddCallback(triangles,
232 XtNselectCallback, (XtCallbackProc) CallbackTriangles, (XtPointer) NULL);
233 Initialize(triangles);
234 XtRealizeWidget(toplevel);
235 XGrabButton(XtDisplay(triangles), (unsigned int) AnyButton, AnyModifier,
236 XtWindow(triangles), TRUE,
237 (unsigned int) (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask),
238 GrabModeAsync, GrabModeAsync, XtWindow(triangles),
239 XCreateFontCursor(XtDisplay(triangles), XC_crosshair));
240 XtMainLoop();
241
242 #ifdef VMS
243 return 1;
244 #else
245 return 0;
246 #endif
247 }
248
249 static void
Initialize(Widget w)250 Initialize(Widget w)
251 {
252 int size;
253 String username;
254
255 XtVaSetValues(w,
256 XtNstart, False, NULL);
257 XtVaGetValues(w,
258 XtNuserName, &username,
259 XtNsize, &size, NULL);
260 if (size <= MAXTRIANGLES)
261 XmScaleSetValue(tri, size);
262 InitRecords();
263 ReadRecords();
264 (void) strcpy(usernameDsp, username);
265 if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS) ||
266 !strcmp(usernameDsp, NOBODY)) {
267 /* The NOACCESS is not necasary, but it stops people from being cute. */
268 (void) sprintf(usernameDsp, "%s", getlogin());
269 if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS))
270 (void) sprintf(usernameDsp, "%s", NOBODY); /* It really IS nobody */
271 }
272 PrintRecord(size);
273 }
274
275 static void
CallbackTriangles(Widget w,caddr_t clientData,trianglesCallbackStruct * callData)276 CallbackTriangles(Widget w, caddr_t clientData,
277 trianglesCallbackStruct * callData)
278 {
279 int size;
280
281 XtVaGetValues(w,
282 XtNsize, &size, NULL);
283 (void) strcpy(messageDsp, "");
284 switch (callData->reason) {
285 case TRIANGLES_RESTORE:
286 case TRIANGLES_RESET:
287 movesDsp = 0;
288 break;
289 case TRIANGLES_BLOCKED:
290 (void) strcpy(messageDsp, "Blocked");
291 break;
292 case TRIANGLES_SPACE:
293 #if 0
294 /* Too annoying */
295 (void) strcpy(messageDsp, "Spaces can not slide");
296 #endif
297 break;
298 case TRIANGLES_IGNORE:
299 (void) strcpy(messageDsp, "Randomize to start");
300 break;
301 case TRIANGLES_MOVED:
302 movesDsp++;
303 XtSetArg(arg[0], XtNstart, True);
304 XtSetValues(w, arg, 1);
305 break;
306 case TRIANGLES_SOLVED:
307 if (HandleSolved(movesDsp, size))
308 (void) sprintf(messageDsp, "Congratulations %s!!", usernameDsp);
309 else
310 (void) strcpy(messageDsp, "Solved!");
311 XtSetArg(arg[0], XtNstart, False);
312 XtSetValues(w, arg, 1);
313 break;
314 case TRIANGLES_RANDOMIZE:
315 movesDsp = 0;
316 XtSetArg(arg[0], XtNstart, False);
317 XtSetValues(w, arg, 1);
318 break;
319 case TRIANGLES_DEC:
320 movesDsp = 0;
321 size--;
322 PrintRecord(size);
323 XtSetArg(arg[0], XtNsize, size);
324 XtSetValues(w, arg, 1);
325 if (size <= MAXTRIANGLES)
326 XmScaleSetValue(tri, size);
327 break;
328 case TRIANGLES_INC:
329 movesDsp = 0;
330 size++;
331 PrintRecord(size);
332 XtSetArg(arg[0], XtNsize, size);
333 XtSetValues(w, arg, 1);
334 if (size <= MAXTRIANGLES)
335 XmScaleSetValue(tri, size);
336 break;
337 case TRIANGLES_COMPUTED:
338 XtSetArg(arg[0], XtNstart, False);
339 XtSetValues(w, arg, 1);
340 break;
341 case TRIANGLES_UNDO:
342 movesDsp--;
343 XtSetArg(arg[0], XtNstart, True);
344 XtSetValues(w, arg, 1);
345 break;
346 }
347 motif_print(message, messageDsp);
348 (void) sprintf(buff, "%d", movesDsp);
349 motif_print(moves, buff);
350 }
351
352 static void
triSlider(Widget w,XtPointer clientData,XmScaleCallbackStruct * cbs)353 triSlider(Widget w, XtPointer clientData, XmScaleCallbackStruct * cbs)
354 {
355 int size = cbs->value, old;
356
357 XtVaGetValues(triangles,
358 XtNsize, &old, NULL);
359 if (old != size) {
360 XtVaSetValues(triangles,
361 XtNsize, size, NULL);
362 movesDsp = 0;
363 (void) sprintf(buff, "%d", movesDsp);
364 motif_print(moves, buff);
365 PrintRecord(size);
366 }
367 }
368
369 static void
PrintRecord(int size)370 PrintRecord(int size)
371 {
372 int i = size - MINTRIANGLES;
373
374 if (size > MAXTRIANGLES)
375 motif_print(record, "NOT RECORDED");
376 else if (trianglesRecord[i].score >= MAXRECORD) {
377 (void) sprintf(buff, "NEVER %s", NOACCESS);
378 motif_print(record, buff);
379 } else {
380 (void) sprintf(buff, "%d %s",
381 trianglesRecord[i].score, trianglesRecord[i].name);
382 motif_print(record, buff);
383 }
384 }
385
386 static Boolean
HandleSolved(int counter,int size)387 HandleSolved(int counter, int size)
388 {
389 int i = size - MINTRIANGLES;
390
391 if (size <= MAXTRIANGLES && counter < trianglesRecord[i].score) {
392 trianglesRecord[i].score = counter;
393 (void) strcpy(trianglesRecord[i].name, usernameDsp);
394 WriteRecords();
395 PrintRecord(size);
396 return True;
397 }
398 return False;
399 }
400
401 static void
InitRecords(void)402 InitRecords(void)
403 {
404 int i;
405
406 for (i = 0; i < MAXTRIANGLES - MINTRIANGLES + 1; i++) {
407 trianglesRecord[i].score = MAXRECORD;
408 (void) strcpy(trianglesRecord[i].name, NOACCESS);
409 }
410 }
411
412 static void
ReadRecords(void)413 ReadRecords(void)
414 {
415 FILE *fp;
416 int i, n;
417 char username[USERNAMELEN];
418
419 if ((fp = fopen(SCOREFILE, "r")) == NULL) {
420 (void) sprintf(buff, "Can not open %s, taking defaults.\n", SCOREFILE);
421 motif_print(message, buff);
422 } else {
423 for (i = 0; i < MAXTRIANGLES - MINTRIANGLES + 1; i++) {
424 (void) fscanf(fp, "%d %s\n", &n, username);
425 if (n <= trianglesRecord[i].score) {
426 trianglesRecord[i].score = n;
427 (void) strcpy(trianglesRecord[i].name, username);
428 }
429 }
430 (void) fclose(fp);
431 }
432 }
433
434 static void
WriteRecords(void)435 WriteRecords(void)
436 {
437 FILE *fp;
438 int i;
439
440 ReadRecords(); /* Maybe its been updated by another */
441 if ((fp = fopen(SCOREFILE, "w")) == NULL) {
442 (void) sprintf(buff, "Can not write to %s.\n", SCOREFILE);
443 motif_print(message, buff);
444 } else {
445 #if HAVE_FCNTL_H
446 int lfd;
447 char lockfile[FILENAMELEN];
448
449 (void) strcpy(lockfile, SCOREFILE);
450 (void) strcat(lockfile, ".lock");
451 while (((lfd = open(lockfile, O_CREAT | O_EXCL, 0644)) < 0) &&
452 errno == EEXIST)
453 (void) sleep(1);
454 if (lfd < 0) {
455 #if 1
456 (void) fprintf(stderr, "Lock file exists... guessing its an old one.\n");
457 #else
458 (void) fprintf(stderr, "Lock file exists... score not recorded - sorry.\n"
459 );
460 return;
461 #endif
462 }
463 #endif
464 for (i = 0; i < MAXTRIANGLES - MINTRIANGLES + 1; i++)
465 (void) fprintf(fp, "%d %s\n",
466 trianglesRecord[i].score, trianglesRecord[i].name);
467 #if HAVE_FCNTL_H
468 (void) close(lfd);
469 (void) unlink(lockfile);
470 #endif
471 (void) fclose(fp);
472 }
473 }
474
475 static void
motif_print(Widget w,char * text)476 motif_print(Widget w, char *text)
477 {
478 Arg wargs[1];
479 XmString xmstr;
480
481 if (!XtIsSubclass(w, xmLabelWidgetClass))
482 XtError("motif_print() requires a Label Widget");
483 xmstr = XmStringCreateLtoR(text, XmSTRING_DEFAULT_CHARSET);
484 XtSetArg(wargs[0], XmNlabelString, xmstr);
485 XtSetValues(w, wargs, 1);
486 }
487