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