1 
2 /*-
3 # MOTIF-BASED MASTERBALL(tm)
4 #
5 #  xmmball.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 5: 95/10/02 Xt/Motif
29   Version 1: 94/09/15 Xt
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 "Mball.h"
60 #include "mball.xbm"
61 #include "mouse-l.xbm"
62 #include "mouse-r.xbm"
63 
64 #ifndef SCOREFILE
65 #define SCOREFILE "/usr/games/lib/mball.scores"
66 #endif
67 
68 /* The following are in MballP.h also */
69 #define MINWEDGES 2
70 #define MAXWEDGES 12
71 #define MINRINGS 1
72 
73 #define MAXRINGS 6
74 #define MAXRECORD 32767
75 #define FILENAMELEN 1024
76 #define USERNAMELEN 128
77 #define NOACCESS "noaccess"
78 #define NOBODY "nobody"
79 
80 typedef struct {
81 	int         score;
82 	char        name[USERNAMELEN];
83 } GameRecord;
84 
85 static void Initialize(Widget w);
86 static void CallbackMball(Widget w, caddr_t clientData, mballCallbackStruct * callData);
87 
88 static void PrintRecord(int wedges, int rings, Boolean orient, Boolean practice);
89 static Boolean HandleSolved(int counter, int wedges, int rings, Boolean orient);
90 static void InitRecords(void);
91 static void ReadRecords(void);
92 static void WriteRecords(void);
93 
94 static void WedgeToggle(Widget w, int bit, XmToggleButtonCallbackStruct * cbs);
95 static void RingSlider(Widget w, XtPointer clientData, XmScaleCallbackStruct * cbs);
96 static void OrientToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct * cbs);
97 static void PracticeToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct * cbs);
98 static void motif_print(Widget w, char *text);
99 
100 static Arg  arg[5];
101 static Widget moves, record, message, mball, orientSwitch, practiceSwitch,
102             wedge[(MAXWEDGES - MINWEDGES) / 2 + 1], ring;
103 static GameRecord mballRecord[2][(MAXWEDGES - MINWEDGES) / 2 + 1]
104 [MAXRINGS - MINRINGS + 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 *wedgeString[] =
111 {
112 	"Two", "Four", "Six", "Eight", "Ten", "Twelve"
113 };
114 
115 static void
usage(void)116 usage(void)
117 {
118 	(void) fprintf(stderr, "usage: xmmball\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}] [-wedge{0|1|2|3|4|5|6|7} {color}]\n");
127 	(void) fprintf(stderr,
128 		       "\t[-{wedges {int}}] [-{rings {int}}] [-[no]orient] [-[no]practice]\n");
129 	(void) fprintf(stderr,
130 		       "\t[-base {int}] [-username {string}]\n");
131 	exit(1);
132 }
133 
134 static XrmOptionDescRec options[] =
135 {
136 	{"-mono", "*mball.mono", XrmoptionNoArg, "TRUE"},
137 	{"-nomono", "*mball.mono", XrmoptionNoArg, "FALSE"},
138 	{"-rv", "*mball.reverse", XrmoptionNoArg, "TRUE"},
139 	{"-reverse", "*mball.reverse", XrmoptionNoArg, "TRUE"},
140 	{"-norv", "*mball.reverse", XrmoptionNoArg, "FALSE"},
141 	{"-noreverse", "*mball.reverse", XrmoptionNoArg, "FALSE"},
142 	{"-fg", "*mball.Foreground", XrmoptionSepArg, NULL},
143 	{"-foreground", "*mball.Foreground", XrmoptionSepArg, NULL},
144 	{"-bg", "*Background", XrmoptionSepArg, NULL},
145 	{"-background", "*Background", XrmoptionSepArg, NULL},
146 	{"-bd", "*mball.pieceBorder", XrmoptionSepArg, NULL},
147 	{"-border", "*mball.pieceBorder", XrmoptionSepArg, NULL},
148 	{"-wedge0", "*mball.wedgeColor0", XrmoptionSepArg, NULL},
149 	{"-wedge1", "*mball.wedgeColor1", XrmoptionSepArg, NULL},
150 	{"-wedge2", "*mball.wedgeColor2", XrmoptionSepArg, NULL},
151 	{"-wedge3", "*mball.wedgeColor3", XrmoptionSepArg, NULL},
152 	{"-wedge4", "*mball.wedgeColor4", XrmoptionSepArg, NULL},
153 	{"-wedge5", "*mball.wedgeColor5", XrmoptionSepArg, NULL},
154 	{"-wedge6", "*mball.wedgeColor6", XrmoptionSepArg, NULL},
155 	{"-wedge7", "*mball.wedgeColor7", XrmoptionSepArg, NULL},
156 	{"-wedges", "*mball.wedges", XrmoptionSepArg, NULL},
157 	{"-rings", "*mball.rings", XrmoptionSepArg, NULL},
158 	{"-orient", "*mball.orient", XrmoptionNoArg, "TRUE"},
159 	{"-noorient", "*mball.orient", XrmoptionNoArg, "FALSE"},
160 	{"-practice", "*mball.practice", XrmoptionNoArg, "TRUE"},
161 	{"-nopractice", "*mball.practice", XrmoptionNoArg, "FALSE"},
162 	{"-base", "*mball.base", XrmoptionSepArg, NULL},
163 	{"-username", "*mball.userName", XrmoptionSepArg, NULL}
164 };
165 
166 int
main(int argc,char ** argv)167 main(int argc, char **argv)
168 {
169 	Widget      toplevel;
170 	Widget      panel, panel2;
171 	Widget      rowcol, rowcol2, rowcol3, rowcol4, rowcol5, rowcol6;
172 	Pixmap      mouseLeftCursor, mouseRightCursor;
173 	Pixel       fg, bg;
174 	int         i;
175 
176 	toplevel = XtInitialize(argv[0], "Mball",
177 				options, XtNumber(options), &argc, argv);
178 	if (argc != 1)
179 		usage();
180 
181 	XtSetArg(arg[0],
182 		 XtNiconPixmap, XCreateBitmapFromData(XtDisplay(toplevel),
183 				      RootWindowOfScreen(XtScreen(toplevel)),
184 			    (char *) mball_bits, mball_width, mball_height));
185 	XtSetArg(arg[1], XmNkeyboardFocusPolicy, XmPOINTER);	/* not XmEXPLICIT */
186 	XtSetValues(toplevel, arg, 2);
187 	panel = XtCreateManagedWidget("panel",
188 				xmPanedWindowWidgetClass, toplevel, NULL, 0);
189 	panel2 = XtVaCreateManagedWidget("panel2",
190 					 xmPanedWindowWidgetClass, panel,
191 					 XmNseparatorOn, False,
192 					 XmNsashWidth, 1,
193 					 XmNsashHeight, 1, NULL);
194 
195 	rowcol = XtVaCreateManagedWidget("Rowcol",
196 					 xmRowColumnWidgetClass, panel2,
197 					 XmNnumColumns, 2,
198 					 XmNorientation, XmHORIZONTAL,
199 					 XmNpacking, XmPACK_COLUMN, NULL);
200 	XtVaGetValues(rowcol,
201 		      XmNforeground, &fg,
202 		      XmNbackground, &bg, NULL);
203 	mouseLeftCursor = XCreatePixmapFromBitmapData(XtDisplay(rowcol),
204 	      RootWindowOfScreen(XtScreen(rowcol)), (char *) mouse_left_bits,
205 				 mouse_left_width, mouse_left_height, fg, bg,
206 				     DefaultDepthOfScreen(XtScreen(rowcol)));
207 	mouseRightCursor = XCreatePixmapFromBitmapData(XtDisplay(rowcol),
208 	     RootWindowOfScreen(XtScreen(rowcol)), (char *) mouse_right_bits,
209 			       mouse_right_width, mouse_right_height, fg, bg,
210 				     DefaultDepthOfScreen(XtScreen(rowcol)));
211 	XtVaCreateManagedWidget("mouseLeftText",
212 				xmLabelGadgetClass, rowcol,
213 		   XtVaTypedArg, XmNlabelString, XmRString, "Move", 5, NULL);
214 	XtVaCreateManagedWidget("mouseLeft",
215 				xmLabelGadgetClass, rowcol,
216 	      XmNlabelType, XmPIXMAP, XmNlabelPixmap, mouseLeftCursor, NULL);
217 	XtVaCreateManagedWidget("mouseRightText",
218 				xmLabelGadgetClass, rowcol,
219 	     XtVaTypedArg, XmNlabelString, XmRString, "Randomize", 10, NULL);
220 	XtVaCreateManagedWidget("mouseRight",
221 				xmLabelGadgetClass, rowcol,
222 	     XmNlabelType, XmPIXMAP, XmNlabelPixmap, mouseRightCursor, NULL);
223 	XtVaCreateManagedWidget("CallbackMball",
224 				xmLabelGadgetClass, rowcol,
225 		  XtVaTypedArg, XmNlabelString, XmRString, "Moves", 6, NULL);
226 	moves = XtVaCreateManagedWidget("0",
227 					xmLabelWidgetClass, rowcol, NULL);
228 	XtVaCreateManagedWidget("recordText",
229 				xmLabelGadgetClass, rowcol,
230 		 XtVaTypedArg, XmNlabelString, XmRString, "Record", 7, NULL);
231 	record = XtVaCreateManagedWidget("0",
232 					 xmLabelWidgetClass, rowcol, NULL);
233 
234 	rowcol2 = XtVaCreateManagedWidget("Rowcol3",
235 				       xmRowColumnWidgetClass, panel2, NULL);
236 	XtVaGetValues(rowcol2,
237 		      XmNforeground, &fg,
238 		      XmNbackground, &bg, NULL);
239 	rowcol3 = XtVaCreateManagedWidget("Rowcol2",
240 					  xmRowColumnWidgetClass, rowcol2,
241 					  XmNnumColumns, 1,
242 					  XmNorientation, XmHORIZONTAL,
243 					  XmNpacking, XmPACK_COLUMN,
244 					  XmNradioBehavior, True, NULL);
245 	for (i = 0; i < XtNumber(wedgeString); i++) {
246 		wedge[i] = XtVaCreateManagedWidget(wedgeString[i],
247 					  xmToggleButtonGadgetClass, rowcol3,
248 					       XmNradioBehavior, True, NULL);
249 		XtAddCallback(wedge[i],
250 			      XmNvalueChangedCallback, (XtCallbackProc) WedgeToggle, (XtPointer) i);
251 	}
252 	XtVaCreateManagedWidget("WedgeText",
253 				xmLabelGadgetClass, rowcol2,
254 		 XtVaTypedArg, XmNlabelString, XmRString, "Wedges", 7, NULL);
255 
256 	rowcol4 = XtVaCreateManagedWidget("Rowcol4",
257 				       xmRowColumnWidgetClass, panel2, NULL);
258 	ring = XtVaCreateManagedWidget("ring",
259 				       xmScaleWidgetClass, rowcol4,
260 			 XtVaTypedArg, XmNtitleString, XmRString, "Rings", 6,
261 				       XmNminimum, MINRINGS,
262 				       XmNmaximum, MAXRINGS,
263 				       XmNvalue, MINRINGS,
264 				       XmNshowValue, True,
265 				       XmNorientation, XmHORIZONTAL, NULL);
266 	XtAddCallback(ring,
267 		      XmNvalueChangedCallback, (XtCallbackProc) RingSlider, (XtPointer) NULL);
268 
269 	rowcol5 = XtVaCreateManagedWidget("Rowcol5",
270 					  xmRowColumnWidgetClass, panel2,
271 					  XmNnumColumns, 1,
272 					  XmNorientation, XmHORIZONTAL,
273 					  XmNpacking, XmPACK_COLUMN, NULL);
274 	orientSwitch = XtVaCreateManagedWidget("Orient",
275 				   xmToggleButtonWidgetClass, rowcol5, NULL);
276 	XtAddCallback(orientSwitch,
277 		      XmNvalueChangedCallback, (XtCallbackProc) OrientToggle, (XtPointer) NULL);
278 	practiceSwitch = XtVaCreateManagedWidget("Practice",
279 				   xmToggleButtonWidgetClass, rowcol5, NULL);
280 	XtAddCallback(practiceSwitch,
281 		      XmNvalueChangedCallback, (XtCallbackProc) PracticeToggle, (XtPointer) NULL);
282 
283 	rowcol6 = XtVaCreateManagedWidget("Rowcol6",
284 				       xmRowColumnWidgetClass, panel2, NULL);
285 	message = XtVaCreateManagedWidget("Play Masterball! (use mouse and keypad)",
286 					  xmLabelWidgetClass, rowcol6, NULL);
287 
288 	mball = XtCreateManagedWidget("mball",
289 				      mballWidgetClass, panel, NULL, 0);
290 	XtAddCallback(mball,
291 	XtNselectCallback, (XtCallbackProc) CallbackMball, (XtPointer) NULL);
292 	Initialize(mball);
293 	XtRealizeWidget(toplevel);
294 	XGrabButton(XtDisplay(mball), (unsigned int) AnyButton, AnyModifier,
295 		    XtWindow(mball), TRUE,
296 		    (unsigned int) (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask),
297 		    GrabModeAsync, GrabModeAsync, XtWindow(mball),
298 		    XCreateFontCursor(XtDisplay(mball), XC_crosshair));
299 	XtMainLoop();
300 
301 #ifdef VMS
302 	return 1;
303 #else
304 	return 0;
305 #endif
306 }
307 
308 static void
Initialize(Widget w)309 Initialize(Widget w)
310 {
311 	int         wedges, rings;
312 	Boolean     orient, practice;
313 	String      username;
314 
315 	XtVaSetValues(w,
316 		      XtNstart, False, NULL);
317 	XtVaGetValues(w,
318 		      XtNuserName, &username,
319 		      XtNwedges, &wedges,
320 		      XtNrings, &rings,
321 		      XtNorient, &orient,
322 		      XtNpractice, &practice, NULL);
323 	XmToggleButtonSetState(wedge[(wedges - MINWEDGES) / 2], True, False);
324 	if (rings <= MAXRINGS)
325 		XmScaleSetValue(ring, rings);
326 	XmToggleButtonSetState(orientSwitch, orient, True);
327 	XmToggleButtonSetState(practiceSwitch, practice, True);
328 	InitRecords();
329 	ReadRecords();
330 	(void) strcpy(usernameDsp, username);
331 	if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS) ||
332 	    !strcmp(usernameDsp, NOBODY)) {
333 		/* The NOACCESS is not necasary, but it stops people from being cute. */
334 		(void) sprintf(usernameDsp, "%s", getlogin());
335 		if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS))
336 			(void) sprintf(usernameDsp, "%s", NOBODY);	/* It really IS nobody */
337 	}
338 	PrintRecord(wedges, rings, orient, practice);
339 }
340 
341 static void
CallbackMball(Widget w,caddr_t clientData,mballCallbackStruct * callData)342 CallbackMball(Widget w, caddr_t clientData, mballCallbackStruct * callData)
343 {
344 	int         wedges, rings;
345 	Boolean     orient, practice, start;
346 
347 	XtVaGetValues(w,
348 		      XtNwedges, &wedges,
349 		      XtNrings, &rings,
350 		      XtNorient, &orient,
351 		      XtNpractice, &practice,
352 		      XtNstart, &start, NULL);
353 	(void) strcpy(messageDsp, "");
354 	switch (callData->reason) {
355 		case MBALL_RESTORE:
356 			if (practice)
357 				motif_print(record, "practice");
358 			movesDsp = 0;
359 			break;
360 		case MBALL_RESET:
361 			movesDsp = 0;
362 			break;
363 		case MBALL_AMBIGUOUS:
364 			(void) strcpy(messageDsp, "Ambiguous move");
365 			break;
366 		case MBALL_ILLEGAL:
367 			if (practice || start)
368 				(void) strcpy(messageDsp, "Illegal move");
369 			else
370 				(void) strcpy(messageDsp, "Randomize to start");
371 			break;
372 		case MBALL_MOVED:
373 			movesDsp++;
374 			XtSetArg(arg[0], XtNstart, True);
375 			XtSetValues(w, arg, 1);
376 			break;
377 		case MBALL_CONTROL:
378 			return;
379 		case MBALL_SOLVED:
380 			if (practice)
381 				movesDsp = 0;
382 			else {
383 				if (HandleSolved(movesDsp, wedges, rings, orient))
384 					(void) sprintf(messageDsp, "Congratulations %s!!", usernameDsp);
385 				else
386 					(void) strcpy(messageDsp, "Solved!");
387 			}
388 			XtSetArg(arg[0], XtNstart, False);
389 			XtSetValues(w, arg, 1);
390 			break;
391 		case MBALL_PRACTICE:
392 			movesDsp = 0;
393 			practice = !practice;
394 			if (!practice)
395 				(void) strcpy(messageDsp, "Randomize to start");
396 			PrintRecord(wedges, rings, orient, practice);
397 			XtSetArg(arg[0], XtNpractice, practice);
398 			XtSetArg(arg[1], XtNstart, False);
399 			XtSetValues(w, arg, 2);
400 			XmToggleButtonSetState(practiceSwitch, practice, True);
401 			break;
402 		case MBALL_RANDOMIZE:
403 			movesDsp = 0;
404 			XtSetArg(arg[0], XtNpractice, False);
405 			XtSetArg(arg[1], XtNstart, False);
406 			XtSetValues(w, arg, 2);
407 			break;
408 		case MBALL_DEC:
409 			movesDsp = 0;
410 			rings--;
411 			PrintRecord(wedges, rings, orient, practice);
412 			XtSetArg(arg[0], XtNrings, rings);
413 			XtSetValues(w, arg, 1);
414 			if (rings <= MAXRINGS)
415 				XmScaleSetValue(ring, rings);
416 			break;
417 		case MBALL_ORIENT:
418 			movesDsp = 0;
419 			orient = !orient;
420 			PrintRecord(wedges, rings, orient, practice);
421 			XtSetArg(arg[0], XtNorient, orient);
422 			XtSetValues(w, arg, 1);
423 			XmToggleButtonSetState(orientSwitch, orient, True);
424 			break;
425 		case MBALL_INC:
426 			movesDsp = 0;
427 			rings++;
428 			PrintRecord(wedges, rings, orient, practice);
429 			XtSetArg(arg[0], XtNrings, rings);
430 			XtSetValues(w, arg, 1);
431 			if (rings <= MAXRINGS)
432 				XmScaleSetValue(ring, rings);
433 			break;
434 		case MBALL_WEDGE2:
435 		case MBALL_WEDGE4:
436 		case MBALL_WEDGE6:
437 		case MBALL_WEDGE8:
438 		case MBALL_WEDGE10:
439 		case MBALL_WEDGE12:
440 			movesDsp = 0;
441 			wedges = 2 * (callData->reason - MBALL_WEDGE2) + MINWEDGES;
442 			PrintRecord(wedges, rings, orient, practice);
443 			XtSetArg(arg[0], XtNwedges, wedges);
444 			XtSetValues(w, arg, 1);
445 			XmToggleButtonSetState(wedge[(wedges - MINWEDGES) / 2], True, True);
446 			break;
447 		case MBALL_COMPUTED:
448 			XtSetArg(arg[0], XtNstart, False);
449 			XtSetValues(w, arg, 1);
450 			break;
451 		case MBALL_UNDO:
452 			movesDsp--;
453 			XtSetArg(arg[0], XtNstart, True);
454 			XtSetValues(w, arg, 1);
455 			break;
456 	}
457 	motif_print(message, messageDsp);
458 	(void) sprintf(buff, "%d", movesDsp);
459 	motif_print(moves, buff);
460 }
461 
462 static void
WedgeToggle(Widget w,int bit,XmToggleButtonCallbackStruct * cbs)463 WedgeToggle(Widget w, int bit, XmToggleButtonCallbackStruct * cbs)
464 {
465 	int         wedges, rings;
466 	Boolean     orient, practice;
467 
468 	if (cbs->set) {
469 		XtVaGetValues(mball,
470 			      XtNrings, &rings,
471 			      XtNorient, &orient,
472 			      XtNpractice, &practice, NULL);
473 		wedges = bit * 2 + MINWEDGES;
474 		XtVaSetValues(mball,
475 			      XtNwedges, wedges, NULL);
476 		movesDsp = 0;
477 		(void) sprintf(buff, "%d", movesDsp);
478 		motif_print(moves, buff);
479 		PrintRecord(wedges, rings, orient, practice);
480 	}
481 }
482 
483 static void
RingSlider(Widget w,XtPointer clientData,XmScaleCallbackStruct * cbs)484 RingSlider(Widget w, XtPointer clientData, XmScaleCallbackStruct * cbs)
485 {
486 	int         wedges, rings = cbs->value, old;
487 	Boolean     orient, practice;
488 
489 	XtVaGetValues(mball,
490 		      XtNwedges, &wedges,
491 		      XtNrings, &old,
492 		      XtNorient, &orient,
493 		      XtNpractice, &practice, NULL);
494 	if (old != rings) {
495 		XtVaSetValues(mball,
496 			      XtNrings, rings, NULL);
497 		movesDsp = 0;
498 		(void) sprintf(buff, "%d", movesDsp);
499 		motif_print(moves, buff);
500 		PrintRecord(wedges, rings, orient, practice);
501 	}
502 }
503 
504 static void
OrientToggle(Widget w,XtPointer clientData,XmToggleButtonCallbackStruct * cbs)505 OrientToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct * cbs)
506 {
507 	int         wedges, rings;
508 	Boolean     orient = cbs->set, practice;
509 
510 	XtVaSetValues(mball,
511 		      XtNorient, orient, NULL);
512 	XtVaGetValues(mball,
513 		      XtNwedges, &wedges,
514 		      XtNrings, &rings,
515 		      XtNpractice, &practice, NULL);
516 	movesDsp = 0;
517 	(void) sprintf(buff, "%d", movesDsp);
518 	motif_print(moves, buff);
519 	PrintRecord(wedges, rings, orient, practice);
520 }
521 
522 static void
PracticeToggle(Widget w,XtPointer clientData,XmToggleButtonCallbackStruct * cbs)523 PracticeToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct * cbs)
524 {
525 	int         wedges, rings;
526 	Boolean     orient, practice = cbs->set;
527 
528 	XtVaSetValues(mball,
529 		      XtNpractice, practice,
530 		      XtNstart, False, NULL);
531 	XtVaGetValues(mball,
532 		      XtNwedges, &wedges,
533 		      XtNrings, &rings,
534 		      XtNorient, &orient, NULL);
535 	movesDsp = 0;
536 	(void) sprintf(buff, "%d", movesDsp);
537 	motif_print(moves, buff);
538 	if (!practice)
539 		(void) strcpy(messageDsp, "Randomize to start");
540 	PrintRecord(wedges, rings, orient, practice);
541 }
542 
543 static void
PrintRecord(int wedges,int rings,Boolean orient,Boolean practice)544 PrintRecord(int wedges, int rings, Boolean orient, Boolean practice)
545 {
546 	int         i = (orient) ? 1 : 0;
547 	int         w = (wedges - MINWEDGES) / 2;
548 	int         r = rings - MINRINGS;
549 
550 	if (practice)
551 		motif_print(record, "practice");
552 	else if (rings > MAXRINGS)
553 		motif_print(record, "NOT RECORDED");
554 	else if (mballRecord[i][w][r].score >= MAXRECORD) {
555 		(void) sprintf(buff, "NEVER %s", NOACCESS);
556 		motif_print(record, buff);
557 	} else {
558 		(void) sprintf(buff, "%d %s",
559 		      mballRecord[i][w][r].score, mballRecord[i][w][r].name);
560 		motif_print(record, buff);
561 	}
562 }
563 
564 static      Boolean
HandleSolved(int counter,int wedges,int rings,Boolean orient)565 HandleSolved(int counter, int wedges, int rings, Boolean orient)
566 {
567 	int         i = (orient) ? 1 : 0;
568 	int         w = (wedges - MINWEDGES) / 2;
569 	int         r = rings - MINRINGS;
570 
571 	if (rings <= MAXRINGS && counter < mballRecord[i][w][r].score) {
572 		mballRecord[i][w][r].score = counter;
573 		(void) strcpy(mballRecord[i][w][r].name, usernameDsp);
574 		if (orient && counter < mballRecord[!i][w][r].score) {
575 			mballRecord[!i][w][r].score = counter;
576 			(void) strcpy(mballRecord[!i][w][r].name, usernameDsp);
577 		}
578 		WriteRecords();
579 		PrintRecord(wedges, rings, orient, False);
580 		return True;
581 	}
582 	return False;
583 }
584 
585 static void
InitRecords(void)586 InitRecords(void)
587 {
588 	int         i, j, orient;
589 
590 	for (orient = 0; orient < 2; orient++)
591 		for (i = 0; i < (MAXWEDGES - MINWEDGES) / 2 + 1; i++)
592 			for (j = 0; j < MAXRINGS - MINRINGS + 1; j++) {
593 				mballRecord[orient][i][j].score = MAXRECORD;
594 				(void) strcpy(mballRecord[orient][i][j].name, NOACCESS);
595 			}
596 }
597 
598 
599 static void
ReadRecords(void)600 ReadRecords(void)
601 {
602 	FILE       *fp;
603 	int         i, j, n, orient;
604 	char        username[USERNAMELEN];
605 
606 	if ((fp = fopen(SCOREFILE, "r")) == NULL) {
607 		(void) sprintf(buff, "Can not open %s, taking defaults.", SCOREFILE);
608 		motif_print(message, buff);
609 	} else {
610 		for (orient = 0; orient < 2; orient++)
611 			for (i = 0; i < (MAXWEDGES - MINWEDGES) / 2 + 1; i++)
612 				for (j = 0; j < MAXRINGS - MINRINGS + 1; j++) {
613 					(void) fscanf(fp, "%d %s\n", &n, username);
614 					if (n <= mballRecord[orient][i][j].score) {
615 						mballRecord[orient][i][j].score = n;
616 						(void) strcpy(mballRecord[orient][i][j].name, username);
617 					}
618 				}
619 		(void) fclose(fp);
620 	}
621 }
622 
623 static void
WriteRecords(void)624 WriteRecords(void)
625 {
626 	FILE       *fp;
627 	int         i, j, orient;
628 
629 	ReadRecords();		/* Maybe its been updated by another */
630 	if ((fp = fopen(SCOREFILE, "w")) == NULL) {
631 		(void) sprintf(buff, "Can not write to %s.", SCOREFILE);
632 		motif_print(message, buff);
633 	} else {
634 #if HAVE_FCNTL_H
635 		int         lfd;
636 		char        lockfile[FILENAMELEN];
637 
638 		(void) strcpy(lockfile, SCOREFILE);
639 		(void) strcat(lockfile, ".lock");
640 		while (((lfd = open(lockfile, O_CREAT | O_EXCL, 0644)) < 0) &&
641 		       errno == EEXIST)
642 			(void) sleep(1);
643 		if (lfd < 0) {
644 #if 1
645 			(void) fprintf(stderr, "Lock file exists... guessing its an old one.\n");
646 #else
647 			(void) fprintf(stderr, "Lock file exists... score not recorded - sorry.\n"
648 				);
649 			return;
650 #endif
651 		}
652 #endif
653 		for (orient = 0; orient < 2; orient++) {
654 			for (i = 0; i < (MAXWEDGES - MINWEDGES) / 2 + 1; i++) {
655 				for (j = 0; j < MAXRINGS - MINRINGS + 1; j++)
656 					(void) fprintf(fp, "%d %s\n",
657 						       mballRecord[orient][i][j].score, mballRecord[orient][i][j].name);
658 				(void) fprintf(fp, "\n");
659 			}
660 			(void) fprintf(fp, "\n");
661 		}
662 #if HAVE_FCNTL_H
663 		(void) close(lfd);
664 		(void) unlink(lockfile);
665 #endif
666 		(void) fclose(fp);
667 	}
668 }
669 
670 static void
motif_print(Widget w,char * text)671 motif_print(Widget w, char *text)
672 {
673 	Arg         wargs[1];
674 	XmString    xmstr;
675 
676 	if (!XtIsSubclass(w, xmLabelWidgetClass))
677 		XtError("motif_print() requires a Label Widget");
678 	xmstr = XmStringCreateLtoR(text, XmSTRING_DEFAULT_CHARSET);
679 	XtSetArg(wargs[0], XmNlabelString, xmstr);
680 	XtSetValues(w, wargs, 1);
681 }
682