1 /*-
2 # X-BASED MASTERBALL(tm)
3 #
4 #  Mball.c
5 #
6 ###
7 #
8 #  Copyright (c) 1994 - 99	David Albert Bagley, bagleyd@tux.org
9 #
10 #                   All Rights Reserved
11 #
12 #  Permission to use, copy, modify, and distribute this software and
13 #  its documentation for any purpose and without fee is hereby granted,
14 #  provided that the above copyright notice appear in all copies and
15 #  that both that copyright notice and this permission notice appear in
16 #  supporting documentation, and that the name of the author not be
17 #  used in advertising or publicity pertaining to distribution of the
18 #  software without specific, written prior permission.
19 #
20 #  This program is distributed in the hope that it will be "playable",
21 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
22 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23 #
24 */
25 
26 /* Methods file for Mball */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <math.h>
31 #ifndef M_PI
32 #define M_PI 3.14159265358979323846
33 #endif
34 #ifdef VMS
35 #include <unixlib.h>
36 #else
37 #if HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #endif
41 #include <X11/IntrinsicP.h>
42 #include <X11/Intrinsic.h>
43 #include <X11/StringDefs.h>
44 #include <X11/CoreP.h>
45 #include "MballP.h"
46 
47 #ifndef DATAFILE
48 #define DATAFILE "/usr/games/lib/mball.data"
49 #endif
50 
51 static void InitializeMball(Widget request, Widget renew);
52 static void ExposeMball(Widget renew, XEvent * event, Region region);
53 static void ResizeMball(MballWidget w);
54 static void DestroyMball(Widget old);
55 static Boolean SetValuesMball(Widget current, Widget request, Widget renew);
56 static void QuitMball(MballWidget w, XEvent * event, char **args, int nArgs);
57 static void PracticeMball(MballWidget w, XEvent * event, char **args, int nArgs);
58 static void PracticeMballMaybe(MballWidget w, XEvent * event, char **args, int nArgs);
59 static void RandomizeMball(MballWidget w, XEvent * event, char **args, int nArgs);
60 static void RandomizeMballMaybe(MballWidget w, XEvent * event, char **args, int nArgs);
61 static void GetMball(MballWidget w, XEvent * event, char **args, int nArgs);
62 static void WriteMball(MballWidget w, XEvent * event, char **args, int nArgs);
63 static void UndoMball(MballWidget w, XEvent * event, char **args, int nArgs);
64 static void SolveMball(MballWidget w, XEvent * event, char **args, int nArgs);
65 static void IncrementMball(MballWidget w, XEvent * event, char **args, int nArgs);
66 static void DecrementMball(MballWidget w, XEvent * event, char **args, int nArgs);
67 static void OrientizeMball(MballWidget w, XEvent * event, char **args, int nArgs);
68 static void Wedge2ModeMball(MballWidget w, XEvent * event, char **args, int nArgs);
69 static void Wedge4ModeMball(MballWidget w, XEvent * event, char **args, int nArgs);
70 static void Wedge6ModeMball(MballWidget w, XEvent * event, char **args, int nArgs);
71 static void Wedge8ModeMball(MballWidget w, XEvent * event, char **args, int nArgs);
72 static void Wedge10ModeMball(MballWidget w, XEvent * event, char **args, int nArgs);
73 static void Wedge12ModeMball(MballWidget w, XEvent * event, char **args, int nArgs);
74 static void MoveMballTl(MballWidget w, XEvent * event, char **args, int nArgs);
75 static void MoveMballTop(MballWidget w, XEvent * event, char **args, int nArgs);
76 static void MoveMballTr(MballWidget w, XEvent * event, char **args, int nArgs);
77 static void MoveMballLeft(MballWidget w, XEvent * event, char **args, int nArgs);
78 static void MoveMballCw(MballWidget w, XEvent * event, char **args, int nArgs);
79 static void MoveMballRight(MballWidget w, XEvent * event, char **args, int nArgs);
80 static void MoveMballBl(MballWidget w, XEvent * event, char **args, int nArgs);
81 static void MoveMballBottom(MballWidget w, XEvent * event, char **args, int nArgs);
82 static void MoveMballBr(MballWidget w, XEvent * event, char **args, int nArgs);
83 static void MoveMballCcw(MballWidget w, XEvent * event, char **args, int nArgs);
84 static void MoveMballInput(MballWidget w, int x, int y, int direction, int control);
85 static void SelectMball(MballWidget w, XEvent * event, char **args, int nArgs);
86 static void ReleaseMball(MballWidget w, XEvent * event, char **args, int nArgs);
87 
88 static void SetAllColors(MballWidget w, Boolean init);
89 static void GetColor(MballWidget w, int wedge, Boolean init);
90 static void MoveControlCb(MballWidget w, int wedge, int direction);
91 static void CheckWedges(MballWidget w);
92 static void ResetWedges(MballWidget w);
93 static void ResizeWedges(MballWidget w);
94 static Boolean SelectWedges(MballWidget w, int x, int y, int *wedge, int *ring, int *view);
95 static Boolean PositionWedges(MballWidget w, int x, int y, int *wedge, int *ring, int *direction);
96 static void MoveNoWedges(MballWidget w);
97 static void PracticeWedges(MballWidget w);
98 static void RandomizeWedges(MballWidget w);
99 static void MoveWedges(MballWidget w, int wedge, int ring, int direction);
100 
101 /* rcd : row, column, or diagonal */
102 static void SwapWedges(MballWidget w, int wedge1, int wedge2);
103 static void DrawFrame(MballWidget w, GC gc);
104 static void DrawWedge(MballWidget w, int wedge);
105 static void DrawSector(MballWidget w, int wedge, int ring, int offset);
106 static void DrawRadar(MballWidget w, GC gc, int startx, int starty, int lengthx, int lengthy);
107 static void DrawSect(MballWidget w, GC wedgeGC, GC borderGC, int r, int wedge, int startx, int starty, int lengthx, int lengthy);
108 static void LetterPosition(MballWidget w, int wedge, int ring, int lengthx, int lengthy, int *dx, int *dy);
109 static void OffsetSect(MballWidget w, int wedge, int *dx, int *dy);
110 static void XFillSector(Display * display, Drawable drawable, GC gc, int xo, int yo, int width1, int height1, int width2, int height2, int angle1, int angle2);
111 static void XDrawSector(Display * display, Drawable drawable, GC gc, int xo, int yo, int width1, int height1, int width2, int height2, int angle1, int angle2);
112 static int  int2String(char *buf, int number, int base, Boolean capital);
113 
114 #ifdef DEBUG
115 static void PrintMball(MballWidget w);
116 
117 #endif
118 
119 static char defaultTranslationsMball[] =
120 "<KeyPress>q: Quit()\n\
121    Ctrl<KeyPress>C: Quit()\n\
122    <KeyPress>KP_Divide: MoveCcw()\n\
123    <KeyPress>Home: MoveTl()\n\
124    <KeyPress>KP_7: MoveTl()\n\
125    <KeyPress>R7: MoveTl()\n\
126    <KeyPress>Up: MoveTop()\n\
127    <KeyPress>KP_8: MoveTop()\n\
128    <KeyPress>R8: MoveTop()\n\
129    <KeyPress>Prior: MoveTr()\n\
130    <KeyPress>KP_9: MoveTr()\n\
131    <KeyPress>R9: MoveTr()\n\
132    <KeyPress>Left: MoveLeft()\n\
133    <KeyPress>KP_4: MoveLeft()\n\
134    <KeyPress>R10: MoveLeft()\n\
135    <KeyPress>Begin: MoveCw()\n\
136    <KeyPress>KP_5: MoveCw()\n\
137    <KeyPress>R11: MoveCw()\n\
138    <KeyPress>Right: MoveRight()\n\
139    <KeyPress>KP_6: MoveRight()\n\
140    <KeyPress>R12: MoveRight()\n\
141    <KeyPress>End: MoveBl()\n\
142    <KeyPress>KP_1: MoveBl()\n\
143    <KeyPress>R13: MoveBl()\n\
144    <KeyPress>Down: MoveBottom()\n\
145    <KeyPress>KP_2: MoveBottom()\n\
146    <KeyPress>R14: MoveBottom()\n\
147    <KeyPress>Next: MoveBr()\n\
148    <KeyPress>KP_3: MoveBr()\n\
149    <KeyPress>R15: MoveBr()\n\
150    <KeyPress>p: Practice()\n\
151    <Btn1Down>: Select()\n\
152    <Btn1Up>: Release()\n\
153    <Btn2Down>(2+): Practice()\n\
154    <Btn2Down>: PracticeMaybe()\n\
155    <KeyPress>r: Randomize()\n\
156    <Btn3Down>(2+): Randomize()\n\
157    <Btn3Down>: RandomizeMaybe()\n\
158    <KeyPress>g: Get()\n\
159    <KeyPress>w: Write()\n\
160    <KeyPress>u: Undo()\n\
161    <KeyPress>s: Solve()\n\
162    <KeyPress>i: Increment()\n\
163    <KeyPress>d: Decrement()\n\
164    <KeyPress>o: Orientize()\n\
165    <KeyPress>2: Wedge2()\n\
166    <KeyPress>4: Wedge4()\n\
167    <KeyPress>6: Wedge6()\n\
168    <KeyPress>8: Wedge8()\n\
169    <KeyPress>0: Wedge10()\n\
170    <KeyPress>=: Wedge12()\n\
171    <KeyPress>.: Wedge12()";
172 
173 /* '=' is the 2nd key to the right of '0' on keyboard
174    '.' follows '0' on keypad
175  */
176 
177 static XtActionsRec actionsListMball[] =
178 {
179 	{"Quit", (XtActionProc) QuitMball},
180 	{"MoveCcw", (XtActionProc) MoveMballCcw},
181 	{"MoveTl", (XtActionProc) MoveMballTl},
182 	{"MoveTop", (XtActionProc) MoveMballTop},
183 	{"MoveTr", (XtActionProc) MoveMballTr},
184 	{"MoveLeft", (XtActionProc) MoveMballLeft},
185 	{"MoveCw", (XtActionProc) MoveMballCw},
186 	{"MoveRight", (XtActionProc) MoveMballRight},
187 	{"MoveBl", (XtActionProc) MoveMballBl},
188 	{"MoveBottom", (XtActionProc) MoveMballBottom},
189 	{"MoveBr", (XtActionProc) MoveMballBr},
190 	{"Select", (XtActionProc) SelectMball},
191 	{"Release", (XtActionProc) ReleaseMball},
192 	{"Practice", (XtActionProc) PracticeMball},
193 	{"PracticeMaybe", (XtActionProc) PracticeMballMaybe},
194 	{"Randomize", (XtActionProc) RandomizeMball},
195 	{"RandomizeMaybe", (XtActionProc) RandomizeMballMaybe},
196 	{"Get", (XtActionProc) GetMball},
197 	{"Write", (XtActionProc) WriteMball},
198 	{"Undo", (XtActionProc) UndoMball},
199 	{"Solve", (XtActionProc) SolveMball},
200 	{"Increment", (XtActionProc) IncrementMball},
201 	{"Decrement", (XtActionProc) DecrementMball},
202 	{"Orientize", (XtActionProc) OrientizeMball},
203 	{"Wedge2", (XtActionProc) Wedge2ModeMball},
204 	{"Wedge4", (XtActionProc) Wedge4ModeMball},
205 	{"Wedge6", (XtActionProc) Wedge6ModeMball},
206 	{"Wedge8", (XtActionProc) Wedge8ModeMball},
207 	{"Wedge10", (XtActionProc) Wedge10ModeMball},
208 	{"Wedge12", (XtActionProc) Wedge12ModeMball}
209 };
210 
211 static XtResource resourcesMball[] =
212 {
213 	{XtNuserName, XtCUserName, XtRString, sizeof (String),
214 	 XtOffset(MballWidget, mball.username), XtRString, "nobody"},
215 	{XtNwedgeColor0, XtCLabel, XtRString, sizeof (String),
216 	 XtOffset(MballWidget, mball.wedgeName[0]), XtRString, "Yellow"},
217 	{XtNwedgeColor1, XtCLabel, XtRString, sizeof (String),
218 	 XtOffset(MballWidget, mball.wedgeName[1]), XtRString, "Blue"},
219 	{XtNwedgeColor2, XtCLabel, XtRString, sizeof (String),
220 	 XtOffset(MballWidget, mball.wedgeName[2]), XtRString, "Red"},
221 	{XtNwedgeColor3, XtCLabel, XtRString, sizeof (String),
222 	 XtOffset(MballWidget, mball.wedgeName[3]), XtRString, "Magenta"},
223 	{XtNwedgeColor4, XtCLabel, XtRString, sizeof (String),
224 	 XtOffset(MballWidget, mball.wedgeName[4]), XtRString, "Green"},
225 	{XtNwedgeColor5, XtCLabel, XtRString, sizeof (String),
226 	 XtOffset(MballWidget, mball.wedgeName[5]), XtRString, "Orange"},
227 	{XtNwedgeColor6, XtCLabel, XtRString, sizeof (String),
228 	 XtOffset(MballWidget, mball.wedgeName[6]), XtRString, "Cyan"},
229 	{XtNwedgeColor7, XtCLabel, XtRString, sizeof (String),
230 	 XtOffset(MballWidget, mball.wedgeName[7]), XtRString, "Dark Green"},
231 	{XtNwedgeColor8, XtCLabel, XtRString, sizeof (String),
232 	 XtOffset(MballWidget, mball.wedgeName[8]), XtRString, "Pink"},
233 	{XtNwedgeColor9, XtCLabel, XtRString, sizeof (String),
234 	 XtOffset(MballWidget, mball.wedgeName[9]), XtRString, "Light Gray"},
235 	{XtNwedgeColor10, XtCLabel, XtRString, sizeof (String),
236 	 XtOffset(MballWidget, mball.wedgeName[10]), XtRString, "Aquamarine"},
237 	{XtNwedgeColor11, XtCLabel, XtRString, sizeof (String),
238 	 XtOffset(MballWidget, mball.wedgeName[11]), XtRString, "Turquoise"},
239 	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
240     XtOffset(MballWidget, mball.foreground), XtRString, XtDefaultForeground},
241 	{XtNpieceBorder, XtCColor, XtRPixel, sizeof (Pixel),
242    XtOffset(MballWidget, mball.borderColor), XtRString, XtDefaultForeground},
243 	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
244 	 XtOffset(MballWidget, core.width), XtRString, "200"},
245 	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
246 	 XtOffset(MballWidget, core.height), XtRString, "400"},
247 	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
248 	 XtOffset(MballWidget, mball.mono), XtRString, "FALSE"},
249 	{XtNreverse, XtCReverse, XtRBoolean, sizeof (Boolean),
250 	 XtOffset(MballWidget, mball.reverse), XtRString, "FALSE"},
251 	{XtNwedges, XtCWedges, XtRInt, sizeof (int),
252 	 XtOffset(MballWidget, mball.wedges), XtRString, "8"},	/*DEFAULTWEDGES */
253 	{XtNrings, XtCRings, XtRInt, sizeof (int),
254 	 XtOffset(MballWidget, mball.rings), XtRString, "4"},	/*DEFAULTRINGS */
255 	{XtNorient, XtCOrient, XtRBoolean, sizeof (Boolean),
256 	 XtOffset(MballWidget, mball.orient), XtRString, "FALSE"},	/*DEFAULTORIENT */
257 	{XtNpractice, XtCPractice, XtRBoolean, sizeof (Boolean),
258 	 XtOffset(MballWidget, mball.practice), XtRString, "FALSE"},	/*DEFAULTPRACTICE */
259 	{XtNbase, XtCBase, XtRInt, sizeof (int),
260 	 XtOffset(MballWidget, mball.base), XtRString, "16"},
261 	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
262 	 XtOffset(MballWidget, mball.started), XtRString, "FALSE"},
263 	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
264 	 XtOffset(MballWidget, mball.select), XtRCallback, NULL}
265 };
266 
267 MballClassRec mballClassRec =
268 {
269 	{
270 		(WidgetClass) & widgetClassRec,		/* superclass */
271 		"Mball",	/* class name */
272 		sizeof (MballRec),	/* widget size */
273 		NULL,		/* class initialize */
274 		NULL,		/* class part initialize */
275 		FALSE,		/* class inited */
276 		(XtInitProc) InitializeMball,	/* initialize */
277 		NULL,		/* initialize hook */
278 		XtInheritRealize,	/* realize */
279 		actionsListMball,	/* actions */
280 		XtNumber(actionsListMball),	/* num actions */
281 		resourcesMball,	/* resources */
282 		XtNumber(resourcesMball),	/* num resources */
283 		NULLQUARK,	/* xrm class */
284 		TRUE,		/* compress motion */
285 		TRUE,		/* compress exposure */
286 		TRUE,		/* compress enterleave */
287 		TRUE,		/* visible interest */
288 		(XtWidgetProc) DestroyMball,	/* destroy */
289 		(XtWidgetProc) ResizeMball,	/* resize */
290 		(XtExposeProc) ExposeMball,	/* expose */
291 		(XtSetValuesFunc) SetValuesMball,	/* set values */
292 		NULL,		/* set values hook */
293 		XtInheritSetValuesAlmost,	/* set values almost */
294 		NULL,		/* get values hook */
295 		NULL,		/* accept focus */
296 		XtVersion,	/* version */
297 		NULL,		/* callback private */
298 		defaultTranslationsMball,	/* tm table */
299 		NULL,		/* query geometry */
300 		NULL,		/* display accelerator */
301 		NULL		/* extension */
302 	},
303 	{
304 		0		/* ignore */
305 	}
306 };
307 
308 
309 /* COORD == 12 not 8 */
310 static int  mapDirToWedge[(MAXWEDGES - MINWEDGES) / 2 + 1][12] =
311 {
312 	{
313 		0, 4, 4, 4, 0, 4, 4, 4
314 	},
315 	{
316 		0, 4, 1, 4, 0, 4, 1, 4
317 	},
318 	{
319 		0, 1, 4, 2, 0, 1, 4, 2
320 	},
321 	{
322 		0, 1, 2, 3, 0, 1, 2, 3
323 	},
324 	{
325 		0, 1, 2, 3, 0, 1, 2, 3
326 	},
327 	{
328 		0, 1, 2, 3, 0, 1, 2, 3
329 	}
330 };
331 
332 /* COORD == 12 not 8 */
333 static int  mapWedgeToDir[(MAXWEDGES - MINWEDGES) / 2 + 1][12] =
334 {
335 	{
336 		0, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
337 	},
338 	{
339 		0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8
340 	},
341 	{
342 		0, 1, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8
343 	},
344 	{
345 		0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8
346 	},
347 	{
348 		0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8
349 	},
350 	{
351 		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
352 	}
353 };
354 
355 WidgetClass mballWidgetClass = (WidgetClass) & mballClassRec;
356 
357 static void
InitializeMball(Widget request,Widget renew)358 InitializeMball(Widget request, Widget renew)
359 {
360 	MballWidget w = (MballWidget) renew;
361 	int         wedge;
362 
363 	for (wedge = 0; wedge < MAXWEDGES; wedge++)
364 		w->mball.mballLoc[wedge] = NULL;
365 	CheckWedges(w);
366 	InitMoves();
367 	ResetWedges(w);
368 	(void) SRAND(getpid());
369 	w->mball.depth = DefaultDepthOfScreen(XtScreen(w));
370 	SetAllColors(w, True);
371 	ResizeMball(w);
372 }
373 
374 static void
DestroyMball(Widget old)375 DestroyMball(Widget old)
376 {
377 	MballWidget w = (MballWidget) old;
378 	int         wedge;
379 
380 	for (wedge = 0; wedge < MAXWEDGES; wedge++) {
381 		XtReleaseGC(old, w->mball.wedgeGC[wedge]);
382 	}
383 	XtReleaseGC(old, w->mball.borderGC);
384 	XtReleaseGC(old, w->mball.puzzleGC);
385 	XtReleaseGC(old, w->mball.inverseGC);
386 	XtRemoveCallbacks(old, XtNselectCallback, w->mball.select);
387 }
388 
389 static void
ResizeMball(MballWidget w)390 ResizeMball(MballWidget w)
391 {
392 	int         tempLength;
393 
394 	w->mball.delta = 4;
395 	w->mball.vertical = (w->core.height >= w->core.width);
396 	if (w->mball.vertical)
397 		tempLength = MIN(w->core.height / 2, w->core.width);
398 	else
399 		tempLength = MIN(w->core.height, w->core.width / 2);
400 	w->mball.mballLength = MAX((tempLength - w->mball.delta + 1) /
401 				   w->mball.wedges, 0);
402 	w->mball.wedgeLength = w->mball.wedges * w->mball.mballLength;
403 	w->mball.viewLength = w->mball.wedgeLength + w->mball.delta;
404 	w->mball.viewMiddle = w->mball.viewLength / 2;
405 	if (w->mball.vertical) {
406 		w->mball.puzzleSize.x = w->mball.viewLength - 1;
407 		w->mball.puzzleSize.y = 2 * w->mball.viewLength - w->mball.delta - 2;
408 	} else {
409 		w->mball.puzzleSize.x = 2 * w->mball.viewLength - w->mball.delta - 2;
410 		w->mball.puzzleSize.y = w->mball.viewLength - 1;
411 	}
412 	w->mball.puzzleOffset.x = ((int) w->core.width - w->mball.puzzleSize.x) / 2;
413 	w->mball.puzzleOffset.y = ((int) w->core.height - w->mball.puzzleSize.y) / 2;
414 	ResizeWedges(w);
415 }
416 
417 static void
ExposeMball(Widget renew,XEvent * event,Region region)418 ExposeMball(Widget renew, XEvent * event, Region region)
419 {
420 	MballWidget w = (MballWidget) renew;
421 
422 	if (w->core.visible) {
423 		if (w->mball.reverse)
424 			XFillRectangle(XtDisplay(w), XtWindow(w),
425 				       w->mball.inverseGC, 0, 0, w->core.width, w->core.height);
426 		DrawFrame(w, w->mball.puzzleGC);
427 		DrawAllWedges(w);
428 	}
429 }
430 
431 static      Boolean
SetValuesMball(Widget current,Widget request,Widget renew)432 SetValuesMball(Widget current, Widget request, Widget renew)
433 {
434 	MballWidget c = (MballWidget) current, w = (MballWidget) renew;
435 	Boolean     redraw = False, setColors = False;
436 	int         wedge;
437 
438 	CheckWedges(w);
439 	for (wedge = 0; wedge < MAXWEDGES; wedge++) {
440 		if (strcmp(w->mball.wedgeName[wedge], c->mball.wedgeName[wedge])) {
441 			setColors = True;
442 			break;
443 		}
444 	}
445 	if (w->core.background_pixel != c->core.background_pixel ||
446 	    w->mball.foreground != c->mball.foreground ||
447 	    w->mball.borderColor != c->mball.borderColor ||
448 	    w->mball.reverse != c->mball.reverse ||
449 	    w->mball.mono != c->mball.mono ||
450 	    setColors) {
451 		SetAllColors(w, False);
452 		redraw = True;
453 	}
454 	if (w->mball.orient != c->mball.orient ||
455 	    w->mball.base != c->mball.base ||
456 	    w->mball.practice != c->mball.practice) {
457 		ResetWedges(w);
458 		redraw = True;
459 	}
460 	if (w->mball.wedges != c->mball.wedges ||
461 	    w->mball.rings != c->mball.rings) {
462 		ResetWedges(w);
463 		ResizeMball(w);
464 		redraw = True;
465 	}
466 	if (w->mball.mballLength != c->mball.mballLength) {
467 		ResizeMball(w);
468 		redraw = True;
469 	}
470 	return (redraw);
471 }
472 
473 static void
QuitMball(MballWidget w,XEvent * event,char ** args,int nArgs)474 QuitMball(MballWidget w, XEvent * event, char **args, int nArgs)
475 {
476 	XtCloseDisplay(XtDisplay(w));
477 	exit(0);
478 }
479 
480 static void
SelectMball(MballWidget w,XEvent * event,char ** args,int nArgs)481 SelectMball(MballWidget w, XEvent * event, char **args, int nArgs)
482 {
483 	int         view, control;
484 
485 	if (SelectWedges(w, event->xbutton.x, event->xbutton.y,
486 		 &(w->mball.currentWedge), &(w->mball.currentRing), &view)) {
487 		control = (int) (event->xkey.state & ControlMask);
488 		if (control || w->mball.practice || !CheckSolved(w))
489 			DrawSector(w, w->mball.currentWedge, w->mball.currentRing, TRUE);
490 	} else
491 		w->mball.currentWedge = -1;
492 }
493 
494 static void
ReleaseMball(MballWidget w,XEvent * event,char ** args,int nArgs)495 ReleaseMball(MballWidget w, XEvent * event, char **args, int nArgs)
496 {
497 	int         control, wedge, ring, view, i, diff, opp;
498 	mballCallbackStruct cb;
499 
500 	if (w->mball.currentWedge == -1)
501 		return;
502 	DrawSector(w, w->mball.currentWedge, w->mball.currentRing, FALSE);
503 	control = (int) (event->xkey.state & ControlMask);
504 	if (!control && !w->mball.practice && CheckSolved(w))
505 		MoveNoWedges(w);
506 	else if (SelectWedges(w, event->xbutton.x, event->xbutton.y,
507 			      &wedge, &ring, &view)) {
508 		opp = (w->mball.currentWedge + w->mball.wedges / 2) % w->mball.wedges;
509 		if (ring == w->mball.currentRing) {
510 			if (wedge == w->mball.currentWedge)
511 				return;
512 			if (opp == (wedge + 1) % w->mball.wedges ||
513 			    wedge == (opp + 1) % w->mball.wedges) {
514 				cb.reason = MBALL_AMBIGUOUS;
515 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
516 			} else {
517 				diff = (w->mball.currentWedge - wedge + w->mball.wedges) %
518 					w->mball.wedges;
519 				if (diff > w->mball.wedges / 2)
520 					for (i = 0; i < w->mball.wedges - diff; i++)
521 						MoveMball(w, wedge, ring, CW, control);
522 				else
523 					for (i = 0; i < diff; i++)
524 						MoveMball(w, wedge, ring, CCW, control);
525 				if (!control && CheckSolved(w)) {
526 					cb.reason = MBALL_SOLVED;
527 					XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
528 				}
529 			}
530 		} else if (wedge == w->mball.currentWedge && w->mball.wedges > 2) {
531 			cb.reason = MBALL_AMBIGUOUS;
532 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
533 		} else if (opp == (wedge + 1) % w->mball.wedges)
534 			MoveMball(w, wedge, ring,
535 			     mapWedgeToDir[(w->mball.wedges - MINWEDGES) / 2]
536 				  [w->mball.currentWedge], control);
537 		else if (wedge == (opp + 1) % w->mball.wedges)
538 			MoveMball(w, wedge, ring,
539 				  mapWedgeToDir[(w->mball.wedges - MINWEDGES) / 2][wedge], control);
540 		else
541 			MoveNoWedges(w);
542 	}
543 }
544 
545 static void
PracticeMball(MballWidget w,XEvent * event,char ** args,int nArgs)546 PracticeMball(MballWidget w, XEvent * event, char **args, int nArgs)
547 {
548 	PracticeWedges(w);
549 }
550 
551 static void
PracticeMballMaybe(MballWidget w,XEvent * event,char ** args,int nArgs)552 PracticeMballMaybe(MballWidget w, XEvent * event, char **args, int nArgs)
553 {
554 	if (!w->mball.started)
555 		PracticeWedges(w);
556 }
557 
558 static void
RandomizeMball(MballWidget w,XEvent * event,char ** args,int nArgs)559 RandomizeMball(MballWidget w, XEvent * event, char **args, int nArgs)
560 {
561 	RandomizeWedges(w);
562 }
563 
564 static void
RandomizeMballMaybe(MballWidget w,XEvent * event,char ** args,int nArgs)565 RandomizeMballMaybe(MballWidget w, XEvent * event, char **args, int nArgs)
566 {
567 	if (!w->mball.started)
568 		RandomizeWedges(w);
569 }
570 
571 static void
GetMball(MballWidget w,XEvent * event,char ** args,int nArgs)572 GetMball(MballWidget w, XEvent * event, char **args, int nArgs)
573 {
574 	FILE       *fp;
575 	char        c;
576 	int         i, wedge, ring, orient, practice, moves;
577 	mballCallbackStruct cb;
578 
579 	if ((fp = fopen(DATAFILE, "r")) == NULL)
580 		(void) printf("Can not read %s for get.\n", DATAFILE);
581 	else {
582 		FlushMoves(w);
583 		while ((c = getc(fp)) != EOF && c != SYMBOL);
584 		(void) fscanf(fp, "%d", &wedge);
585 		if (wedge >= MINWEDGES && wedge <= MAXWEDGES && !(wedge % 2)) {
586 			if (w->mball.wedges != wedge) {
587 				cb.reason = (wedge - MINWEDGES) / 2 + MBALL_WEDGE2;
588 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
589 			}
590 		} else {
591 			(void) printf("%s corrupted: ", DATAFILE);
592 			(void) printf("wedge %d should be even and between %d and %d\n",
593 				      wedge, MINWEDGES, MAXWEDGES);
594 		}
595 		while ((c = getc(fp)) != EOF && c != SYMBOL);
596 		(void) fscanf(fp, "%d", &ring);
597 		if (ring >= MINRINGS) {
598 			for (i = w->mball.rings; i < ring; i++) {
599 				cb.reason = MBALL_INC;
600 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
601 			}
602 			for (i = w->mball.rings; i > ring; i--) {
603 				cb.reason = MBALL_DEC;
604 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
605 			}
606 		} else
607 			(void) printf("%s corrupted: ring %d should be between %d and MAXINT\n",
608 				      DATAFILE, ring, MINRINGS);
609 		while ((c = getc(fp)) != EOF && c != SYMBOL);
610 		(void) fscanf(fp, "%d", &orient);
611 		if (w->mball.orient != (Boolean) orient) {
612 			cb.reason = MBALL_ORIENT;
613 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
614 		}
615 		while ((c = getc(fp)) != EOF && c != SYMBOL);
616 		(void) fscanf(fp, "%d", &practice);
617 		if (w->mball.practice != (Boolean) practice) {
618 			cb.reason = MBALL_PRACTICE;
619 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
620 		}
621 		while ((c = getc(fp)) != EOF && c != SYMBOL);
622 		(void) fscanf(fp, "%d", &moves);
623 		ScanStartPosition(fp, w);
624 		cb.reason = MBALL_RESTORE;
625 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
626 		SetStartPosition(w);
627 		ScanMoves(fp, w, moves);
628 		(void) fclose(fp);
629 		(void) printf("%s: wedge %d, ring %d, orient %d, ",
630 			      DATAFILE, wedge, ring, orient);
631 		(void) printf("practice %d, moves %d\n", practice, moves);
632 	}
633 }
634 
635 static void
WriteMball(MballWidget w,XEvent * event,char ** args,int nArgs)636 WriteMball(MballWidget w, XEvent * event, char **args, int nArgs)
637 {
638 	FILE       *fp;
639 
640 	if ((fp = fopen(DATAFILE, "w")) == NULL)
641 		(void) printf("Can not write to %s.\n", DATAFILE);
642 	else {
643 		(void) fprintf(fp, "wedge%c %d\n", SYMBOL, w->mball.wedges);
644 		(void) fprintf(fp, "ring%c %d\n", SYMBOL, w->mball.rings);
645 		(void) fprintf(fp, "orient%c %d\n", SYMBOL, (w->mball.orient) ? 1 : 0);
646 		(void) fprintf(fp, "practice%c %d\n", SYMBOL, (w->mball.practice) ? 1 : 0);
647 		(void) fprintf(fp, "moves%c %d\n", SYMBOL, NumMoves());
648 		PrintStartPosition(fp, w);
649 		PrintMoves(fp);
650 		(void) fclose(fp);
651 		(void) printf("Saved to %s.\n", DATAFILE);
652 	}
653 }
654 
655 static void
UndoMball(MballWidget w,XEvent * event,char ** args,int nArgs)656 UndoMball(MballWidget w, XEvent * event, char **args, int nArgs)
657 {
658 	if (MadeMoves()) {
659 		int         wedge, ring, direction, control;
660 
661 		GetMove(&wedge, &ring, &direction, &control);
662 		direction = (direction < COORD) ? direction : 3 * COORD - direction;
663 		if (control)
664 			MoveControlCb(w, wedge, direction);
665 		else {
666 			mballCallbackStruct cb;
667 
668 			MoveWedges(w, wedge, ring, direction);
669 			cb.reason = MBALL_UNDO;
670 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
671 		}
672 	}
673 }
674 
675 static void
SolveMball(MballWidget w,XEvent * event,char ** args,int nArgs)676 SolveMball(MballWidget w, XEvent * event, char **args, int nArgs)
677 {
678 #if 0
679 	SolveWedges(w);		/* Sorry, this is not implemented */
680 #endif
681 }
682 
683 static void
IncrementMball(MballWidget w,XEvent * event,char ** args,int nArgs)684 IncrementMball(MballWidget w, XEvent * event, char **args, int nArgs)
685 {
686 	mballCallbackStruct cb;
687 
688 	cb.reason = MBALL_INC;
689 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
690 }
691 
692 static void
DecrementMball(MballWidget w,XEvent * event,char ** args,int nArgs)693 DecrementMball(MballWidget w, XEvent * event, char **args, int nArgs)
694 {
695 	mballCallbackStruct cb;
696 
697 	if (w->mball.rings <= MINRINGS)
698 		return;
699 	cb.reason = MBALL_DEC;
700 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
701 }
702 
703 static void
OrientizeMball(MballWidget w,XEvent * event,char ** args,int nArgs)704 OrientizeMball(MballWidget w, XEvent * event, char **args, int nArgs)
705 {
706 	mballCallbackStruct cb;
707 
708 	cb.reason = MBALL_ORIENT;
709 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
710 }
711 
712 static void
Wedge2ModeMball(MballWidget w,XEvent * event,char ** args,int nArgs)713 Wedge2ModeMball(MballWidget w, XEvent * event, char **args, int nArgs)
714 {
715 	mballCallbackStruct cb;
716 
717 	cb.reason = MBALL_WEDGE2;
718 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
719 }
720 
721 static void
Wedge4ModeMball(MballWidget w,XEvent * event,char ** args,int nArgs)722 Wedge4ModeMball(MballWidget w, XEvent * event, char **args, int nArgs)
723 {
724 	mballCallbackStruct cb;
725 
726 	cb.reason = MBALL_WEDGE4;
727 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
728 }
729 
730 static void
Wedge6ModeMball(MballWidget w,XEvent * event,char ** args,int nArgs)731 Wedge6ModeMball(MballWidget w, XEvent * event, char **args, int nArgs)
732 {
733 	mballCallbackStruct cb;
734 
735 	cb.reason = MBALL_WEDGE6;
736 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
737 }
738 
739 static void
Wedge8ModeMball(MballWidget w,XEvent * event,char ** args,int nArgs)740 Wedge8ModeMball(MballWidget w, XEvent * event, char **args, int nArgs)
741 {
742 	mballCallbackStruct cb;
743 
744 	cb.reason = MBALL_WEDGE8;
745 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
746 }
747 
748 static void
Wedge10ModeMball(MballWidget w,XEvent * event,char ** args,int nArgs)749 Wedge10ModeMball(MballWidget w, XEvent * event, char **args, int nArgs)
750 {
751 	mballCallbackStruct cb;
752 
753 	cb.reason = MBALL_WEDGE10;
754 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
755 }
756 
757 static void
Wedge12ModeMball(MballWidget w,XEvent * event,char ** args,int nArgs)758 Wedge12ModeMball(MballWidget w, XEvent * event, char **args, int nArgs)
759 {
760 	mballCallbackStruct cb;
761 
762 	cb.reason = MBALL_WEDGE12;
763 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
764 }
765 
766 static void
MoveMballCcw(MballWidget w,XEvent * event,char ** args,int nArgs)767 MoveMballCcw(MballWidget w, XEvent * event, char **args, int nArgs)
768 {
769 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, CCW,
770 		       (int) (event->xbutton.state & ControlMask));
771 }
772 
773 static void
MoveMballTl(MballWidget w,XEvent * event,char ** args,int nArgs)774 MoveMballTl(MballWidget w, XEvent * event, char **args, int nArgs)
775 {
776 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, TL,
777 		       (int) (event->xkey.state & ControlMask));
778 }
779 
780 static void
MoveMballTop(MballWidget w,XEvent * event,char ** args,int nArgs)781 MoveMballTop(MballWidget w, XEvent * event, char **args, int nArgs)
782 {
783 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, TOP,
784 		       (int) (event->xkey.state & ControlMask));
785 }
786 
787 static void
MoveMballTr(MballWidget w,XEvent * event,char ** args,int nArgs)788 MoveMballTr(MballWidget w, XEvent * event, char **args, int nArgs)
789 {
790 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, TR,
791 		       (int) (event->xkey.state & ControlMask));
792 }
793 
794 static void
MoveMballLeft(MballWidget w,XEvent * event,char ** args,int nArgs)795 MoveMballLeft(MballWidget w, XEvent * event, char **args, int nArgs)
796 {
797 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, LEFT,
798 		       (int) (event->xkey.state & ControlMask));
799 }
800 
801 static void
MoveMballCw(MballWidget w,XEvent * event,char ** args,int nArgs)802 MoveMballCw(MballWidget w, XEvent * event, char **args, int nArgs)
803 {
804 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, CW,
805 		       (int) (event->xkey.state & ControlMask));
806 }
807 
808 static void
MoveMballRight(MballWidget w,XEvent * event,char ** args,int nArgs)809 MoveMballRight(MballWidget w, XEvent * event, char **args, int nArgs)
810 {
811 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, RIGHT,
812 		       (int) (event->xkey.state & ControlMask));
813 }
814 
815 static void
MoveMballBl(MballWidget w,XEvent * event,char ** args,int nArgs)816 MoveMballBl(MballWidget w, XEvent * event, char **args, int nArgs)
817 {
818 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, BL,
819 		       (int) (event->xkey.state & ControlMask));
820 }
821 
822 static void
MoveMballBottom(MballWidget w,XEvent * event,char ** args,int nArgs)823 MoveMballBottom(MballWidget w, XEvent * event, char **args, int nArgs)
824 {
825 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, BOTTOM,
826 		       (int) (event->xkey.state & ControlMask));
827 }
828 
829 static void
MoveMballBr(MballWidget w,XEvent * event,char ** args,int nArgs)830 MoveMballBr(MballWidget w, XEvent * event, char **args, int nArgs)
831 {
832 	MoveMballInput(w, event->xbutton.x, event->xbutton.y, BR,
833 		       (int) (event->xkey.state & ControlMask));
834 }
835 
836 static void
MoveMballInput(MballWidget w,int x,int y,int direction,int control)837 MoveMballInput(MballWidget w, int x, int y, int direction, int control)
838 {
839 	int         wedge, ring;
840 
841 	if (CheckSolved(w) && !w->mball.practice && !control) {
842 		MoveNoWedges(w);
843 		return;
844 	}
845 	if (!PositionWedges(w, x, y, &wedge, &ring, &direction))
846 		return;
847 	control = (control) ? 1 : 0;
848 	MoveMball(w, wedge, ring, direction, control);
849 	if (!control && CheckSolved(w)) {
850 		mballCallbackStruct cb;
851 
852 		cb.reason = MBALL_SOLVED;
853 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
854 	}
855 }
856 
857 void
MoveMball(MballWidget w,int wedge,int ring,int direction,int control)858 MoveMball(MballWidget w, int wedge, int ring, int direction, int control)
859 {
860 	mballCallbackStruct cb;
861 
862 	if (control)
863 		MoveControlCb(w, wedge, direction);
864 	else {
865 		MoveWedges(w, wedge, ring, direction);
866 		cb.reason = MBALL_MOVED;
867 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
868 	}
869 	PutMove(wedge, ring, direction, control);
870 }
871 
872 static void
SetAllColors(MballWidget w,Boolean init)873 SetAllColors(MballWidget w, Boolean init)
874 {
875 	XGCValues   values;
876 	XtGCMask    valueMask;
877 	int         wedge;
878 
879 	valueMask = GCForeground | GCBackground;
880 
881 	if (w->mball.reverse) {
882 		values.background = w->core.background_pixel;
883 		values.foreground = w->mball.foreground;
884 	} else {
885 		values.foreground = w->core.background_pixel;
886 		values.background = w->mball.foreground;
887 	}
888 	if (!init)
889 		XtReleaseGC((Widget) w, w->mball.inverseGC);
890 	w->mball.inverseGC = XtGetGC((Widget) w, valueMask, &values);
891 	if (w->mball.reverse) {
892 		values.background = w->mball.foreground;
893 		values.foreground = w->core.background_pixel;
894 	} else {
895 		values.foreground = w->mball.foreground;
896 		values.background = w->core.background_pixel;
897 	}
898 	if (!init)
899 		XtReleaseGC((Widget) w, w->mball.puzzleGC);
900 	w->mball.puzzleGC = XtGetGC((Widget) w, valueMask, &values);
901 	if (w->mball.depth < 2 || w->mball.mono) {
902 		if (w->mball.reverse) {
903 			values.background = w->mball.foreground;
904 			values.foreground = w->core.background_pixel;
905 		} else {
906 			values.foreground = w->mball.foreground;
907 			values.background = w->core.background_pixel;
908 		}
909 	} else {
910 		values.foreground = w->mball.borderColor;
911 		values.background = w->core.background_pixel;
912 	}
913 	if (!init)
914 		XtReleaseGC((Widget) w, w->mball.borderGC);
915 	w->mball.borderGC = XtGetGC((Widget) w, valueMask, &values);
916 	for (wedge = 0; wedge < MAXWEDGES; wedge++)
917 		GetColor(w, wedge, init);
918 }
919 
920 static void
GetColor(MballWidget w,int wedge,Boolean init)921 GetColor(MballWidget w, int wedge, Boolean init)
922 {
923 	XGCValues   values;
924 	XtGCMask    valueMask;
925 	XColor      colorCell, rgb;
926 
927 	valueMask = GCForeground | GCBackground;
928 	if (w->mball.reverse) {
929 		values.background = w->mball.foreground;
930 	} else {
931 		values.background = w->core.background_pixel;
932 	}
933 	if (w->mball.depth > 1 && !w->mball.mono) {
934 		if (XAllocNamedColor(XtDisplay(w),
935 				  DefaultColormap(XtDisplay(w), XtWindow(w)),
936 			      w->mball.wedgeName[wedge], &colorCell, &rgb)) {
937 			values.foreground = w->mball.wedgeColor[wedge] = colorCell.pixel;
938 			if (!init)
939 				XtReleaseGC((Widget) w, w->mball.wedgeGC[wedge]);
940 			w->mball.wedgeGC[wedge] = XtGetGC((Widget) w, valueMask, &values);
941 			return;
942 		} else {
943 			char        buf[121];
944 
945 			(void) sprintf(buf, "Color name \"%s\" is not defined %d",
946 				       w->mball.wedgeName[wedge], wedge);
947 			XtWarning(buf);
948 		}
949 	}
950 	if (w->mball.reverse) {
951 		values.background = w->mball.foreground;
952 		values.foreground = w->core.background_pixel;
953 	} else {
954 		values.background = w->core.background_pixel;
955 		values.foreground = w->mball.foreground;
956 	}
957 	if (!init)
958 		XtReleaseGC((Widget) w, w->mball.wedgeGC[wedge]);
959 	w->mball.wedgeGC[wedge] = XtGetGC((Widget) w, valueMask, &values);
960 }
961 
962 static void
MoveControlCb(MballWidget w,int wedge,int direction)963 MoveControlCb(MballWidget w, int wedge, int direction)
964 {
965 	mballCallbackStruct cb;
966 	int         ring;
967 
968 	if (direction > COORD)
969 		for (ring = 0; ring < w->mball.rings; ring++) {
970 			MoveWedges(w, wedge, ring, direction);
971 			cb.reason = MBALL_CONTROL;
972 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
973 	} else {
974 		MoveWedges(w, 0, 0, direction);
975 		cb.reason = MBALL_CONTROL;
976 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
977 		MoveWedges(w, w->mball.wedges / 2, 0, direction);
978 		cb.reason = MBALL_CONTROL;
979 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
980 	}
981 }
982 
983 static void
CheckWedges(MballWidget w)984 CheckWedges(MballWidget w)
985 {
986 	if (w->mball.wedges < MINWEDGES || w->mball.wedges > MAXWEDGES ||
987 	    w->mball.wedges % 2) {
988 		char        buf[121];
989 
990 		(void) sprintf(buf,
991 			       "Number of wedges out of bounds, use even %d..%d", MINWEDGES, MAXWEDGES);
992 		XtWarning(buf);
993 		w->mball.wedges = DEFAULTWEDGES;
994 	}
995 	if (w->mball.rings < MINRINGS) {
996 		char        buf[121];
997 
998 		(void) sprintf(buf,
999 		  "Number of rings out of bounds, use %d..MAXINT", MINRINGS);
1000 		XtWarning(buf);
1001 		w->mball.rings = DEFAULTRINGS;
1002 	}
1003 	if (w->mball.base > 36) {
1004 		/* 10 numbers + 26 letters (ASCII or EBCDIC) */
1005 		XtWarning("Base must be less than or equal to 36");
1006 		w->mball.base = 16;	/* It has less digits for 12 wedges */
1007 	} else if (w->mball.base <= 1) {	/* Base 1 is rediculous :) */
1008 		XtWarning("Base must be greater than 1");
1009 		w->mball.base = 16;	/* It has less digits for 12 wedges */
1010 	}
1011 }
1012 
1013 static void
ResetWedges(MballWidget w)1014 ResetWedges(MballWidget w)
1015 {
1016 	int         wedge, ring;
1017 
1018 	for (wedge = 0; wedge < MAXWEDGES; wedge++) {
1019 		if (w->mball.mballLoc[wedge])
1020 			(void) free((void *) w->mball.mballLoc[wedge]);
1021 		if (!(w->mball.mballLoc[wedge] = (MballLoc *)
1022 		      malloc(sizeof (MballLoc) * w->mball.rings)))
1023 			XtError("Not enough memory, exiting.");
1024 		if (startLoc[wedge])
1025 			(void) free((void *) startLoc[wedge]);
1026 		if (!(startLoc[wedge] = (MballLoc *)
1027 		      malloc(sizeof (MballLoc) * w->mball.rings)))
1028 			XtError("Not enough memory, exiting.");
1029 	}
1030 	for (wedge = 0; wedge < w->mball.wedges; wedge++)
1031 		for (ring = 0; ring < w->mball.rings; ring++) {
1032 			w->mball.mballLoc[wedge][ring].wedge = wedge;
1033 			w->mball.mballLoc[wedge][ring].direction = DOWN;
1034 		}
1035 	FlushMoves(w);
1036 	w->mball.started = False;
1037 }
1038 
1039 static void
ResizeWedges(MballWidget w)1040 ResizeWedges(MballWidget w)
1041 {
1042 	w->mball.mballLength = w->mball.wedgeLength / (2 * w->mball.wedges) -
1043 		w->mball.delta - 1;
1044 	w->mball.letterOffset.x = -2;
1045 	w->mball.letterOffset.y = 4;
1046 	w->mball.dr = w->mball.wedges;
1047 }
1048 
1049 static      Boolean
SelectWedges(MballWidget w,int x,int y,int * wedge,int * ring,int * view)1050 SelectWedges(MballWidget w, int x, int y, int *wedge, int *ring, int *view)
1051 {
1052 	double      angle, radius;
1053 
1054 	x -= w->mball.puzzleOffset.x;
1055 	y -= w->mball.puzzleOffset.y;
1056 	if (w->mball.vertical && y > w->mball.viewLength - 1) {
1057 		y -= (w->mball.viewLength - 1);
1058 		*view = DOWN;
1059 	} else if (!w->mball.vertical && x > w->mball.viewLength - 1) {
1060 		x -= (w->mball.viewLength - 1);
1061 		*view = DOWN;
1062 	} else
1063 		*view = UP;
1064 	x -= (w->mball.wedgeLength + 1) / 2;
1065 	y -= (w->mball.wedgeLength + 1) / 2;
1066 	radius = sqrt((double) x * x + y * y);
1067 	if (y >= 0)
1068 		angle = atan2((double) -x, (double) y) + M_PI;
1069 	else if (x < 0)
1070 		angle = 2 * M_PI - atan2((double) -x, (double) -y);
1071 	else
1072 		angle = -atan2((double) -x, (double) -y);
1073 	*ring = (int) (radius * (double) w->mball.rings /
1074 		       ((double) w->mball.wedgeLength));
1075 	*wedge = (int) (angle * (double) w->mball.wedges / (2.0 * M_PI));
1076 	if (*view == DOWN) {
1077 		if (w->mball.vertical)
1078 			*wedge = (3 * w->mball.wedges / 2 - 1 - *wedge) % w->mball.wedges;
1079 		else
1080 			*wedge = (w->mball.wedges - 1 - *wedge) % w->mball.wedges;
1081 		*ring = w->mball.rings - 1 - *ring;
1082 	}
1083 	if (radius > w->mball.wedgeLength / 2 + w->mball.delta)
1084 		return False;
1085 	return True;
1086 }
1087 
1088 static      Boolean
PositionWedges(MballWidget w,int x,int y,int * wedge,int * ring,int * direction)1089 PositionWedges(MballWidget w, int x, int y, int *wedge, int *ring, int *direction)
1090 {
1091 	int         view, inside;
1092 
1093 	inside = SelectWedges(w, x, y, wedge, ring, &view);
1094 	if ((*direction == CW || *direction == CCW) && !inside)
1095 		return False;
1096 	if (view == DOWN) {
1097 		if (*direction == CCW)
1098 			*direction = CW;
1099 		else if (*direction == CW)
1100 			*direction = CCW;
1101 		else if (*direction < COORD)
1102 			*direction = (COORD - *direction) % COORD;
1103 	}
1104 	if (w->mball.wedges % 4 && (*direction == LEFT || *direction == RIGHT))
1105 		return False;
1106 	if (w->mball.wedges <= 4 && *direction % 2 && *direction < COORD)
1107 		return False;
1108 	return True;
1109 }
1110 
1111 static void
MoveNoWedges(MballWidget w)1112 MoveNoWedges(MballWidget w)
1113 {
1114 	mballCallbackStruct cb;
1115 
1116 	cb.reason = MBALL_ILLEGAL;
1117 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1118 }
1119 
1120 static void
PracticeWedges(MballWidget w)1121 PracticeWedges(MballWidget w)
1122 {
1123 	mballCallbackStruct cb;
1124 
1125 	cb.reason = MBALL_PRACTICE;
1126 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1127 }
1128 
1129 static void
RandomizeWedges(MballWidget w)1130 RandomizeWedges(MballWidget w)
1131 {
1132 	mballCallbackStruct cb;
1133 	int         randomDirection, wedge, ring;
1134 	int         big = w->mball.wedges * (w->mball.rings + 1) + NRAND(2);
1135 
1136 	if (big > 100)
1137 		big = 100;
1138 	if (w->mball.practice)
1139 		PracticeWedges(w);
1140 	cb.reason = MBALL_RESET;
1141 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1142 
1143 #ifdef DEBUG
1144 	big = 3;
1145 #endif
1146 
1147 	while (big--) {
1148 		wedge = NRAND(w->mball.wedges);
1149 		ring = NRAND(w->mball.rings);
1150 		do
1151 			randomDirection = NRAND(2 * COORD);
1152 		while (randomDirection < COORD &&
1153 		       mapDirToWedge[(w->mball.wedges - MINWEDGES) / 2][randomDirection] ==
1154 		       CUTS);
1155 		if (randomDirection >= COORD) {
1156 			if (randomDirection - COORD < CUTS)
1157 				randomDirection = CW;
1158 			else
1159 				randomDirection = CCW;
1160 		}
1161 		MoveMball(w, wedge, ring, randomDirection, FALSE);
1162 	}
1163 	FlushMoves(w);
1164 	cb.reason = MBALL_RANDOMIZE;
1165 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1166 	if (CheckSolved(w)) {
1167 		cb.reason = MBALL_SOLVED;
1168 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1169 	}
1170 }
1171 
1172 static void
SwapWedges(MballWidget w,int wedge1,int wedge2)1173 SwapWedges(MballWidget w, int wedge1, int wedge2)
1174 {
1175 	MballLoc    temp;
1176 	int         ring;
1177 
1178 	if (wedge1 == wedge2) {
1179 		for (ring = 0; ring < w->mball.rings / 2; ring++) {
1180 			temp = w->mball.mballLoc[wedge1][ring];
1181 			w->mball.mballLoc[wedge1][ring] =
1182 				w->mball.mballLoc[wedge1][w->mball.rings - 1 - ring];
1183 			w->mball.mballLoc[wedge1][w->mball.rings - 1 - ring] = temp;
1184 		}
1185 		for (ring = 0; ring < w->mball.rings; ring++)
1186 			w->mball.mballLoc[wedge1][ring].direction =
1187 				!w->mball.mballLoc[wedge1][ring].direction;
1188 		DrawWedge(w, wedge1);
1189 	} else {
1190 		for (ring = 0; ring < w->mball.rings; ring++) {
1191 			temp = w->mball.mballLoc[wedge1][ring];
1192 			w->mball.mballLoc[wedge1][ring] =
1193 				w->mball.mballLoc[wedge2][w->mball.rings - 1 - ring];
1194 			w->mball.mballLoc[wedge2][w->mball.rings - 1 - ring] = temp;
1195 			w->mball.mballLoc[wedge1][ring].direction =
1196 				!w->mball.mballLoc[wedge1][ring].direction;
1197 			w->mball.mballLoc[wedge2][w->mball.rings - 1 - ring].direction =
1198 				!w->mball.mballLoc[wedge2][w->mball.rings - 1 - ring].direction;
1199 		}
1200 		DrawWedge(w, wedge1);
1201 		DrawWedge(w, wedge2);
1202 	}
1203 }
1204 
1205 static void
MoveWedges(MballWidget w,int wedge,int ring,int direction)1206 MoveWedges(MballWidget w, int wedge, int ring, int direction)
1207 {
1208 	int         i;
1209 
1210 	if (direction == CW || direction == CCW) {	/* rotate ring */
1211 		int         newI;
1212 		MballLoc    temp1, temp2;
1213 
1214 		for (i = 0; i < w->mball.wedges; i++) {
1215 			newI = (direction == CW) ? i : w->mball.wedges - 1 - i;
1216 			if (newI == ((direction == CW) ? 0 : w->mball.wedges - 1)) {
1217 				temp1 = w->mball.mballLoc[newI][ring];
1218 				w->mball.mballLoc[newI][ring] = w->mball.mballLoc
1219 					[((direction == CW) ? w->mball.wedges - 1 : 0)][ring];
1220 			} else {
1221 				temp2 = temp1;
1222 				temp1 = w->mball.mballLoc[newI][ring];
1223 				w->mball.mballLoc[newI][ring] = temp2;
1224 			}
1225 			DrawSector(w, newI, ring, FALSE);
1226 		}
1227 	} else {		/* flip */
1228 		int         sphereDir = mapDirToWedge[(w->mball.wedges - MINWEDGES) / 2][direction];
1229 		int         offset = w->mball.wedges / 2;
1230 		int         wedge1, wedge2;
1231 
1232 		for (i = 0; i < w->mball.wedges / 2; i++)
1233 			if (wedge == i + sphereDir)
1234 				offset = 0;
1235 		for (i = 0; i < (w->mball.wedges + 2) / 4; i++) {
1236 			wedge1 = (i + sphereDir + offset) % w->mball.wedges;
1237 			wedge2 = (w->mball.wedges / 2 - 1 - i + sphereDir + offset) %
1238 				w->mball.wedges;
1239 			SwapWedges(w, wedge1, wedge2);
1240 		}
1241 	}
1242 }
1243 
1244 static void
DrawFrame(MballWidget w,GC gc)1245 DrawFrame(MballWidget w, GC gc)
1246 {
1247 	int         startx, starty, lengthx, lengthy;
1248 
1249 	startx = 1 + w->mball.puzzleOffset.x;
1250 	starty = 1 + w->mball.puzzleOffset.y;
1251 	lengthx = w->mball.viewLength - w->mball.delta + w->mball.puzzleOffset.x;
1252 	lengthy = w->mball.viewLength - w->mball.delta + w->mball.puzzleOffset.y;
1253 	DrawRadar(w, gc, startx, starty, lengthx - startx, lengthy - starty);
1254 	if (w->mball.vertical) {
1255 		XDrawLine(XtDisplay(w), XtWindow(w), gc, 0, lengthy + 1,
1256 			  (int) w->core.width - 1, lengthy + 1);
1257 		DrawRadar(w, gc, startx, lengthy + 3, lengthx - startx, lengthy - starty);
1258 	} else {
1259 		XDrawLine(XtDisplay(w), XtWindow(w), gc, lengthx + 1, 0,
1260 			  lengthx + 1, (int) w->core.height - 1);
1261 		DrawRadar(w, gc, lengthx + 3, starty, lengthx - startx, lengthy - starty);
1262 	}
1263 }
1264 
1265 void
DrawAllWedges(MballWidget w)1266 DrawAllWedges(MballWidget w)
1267 {
1268 	int         wedge;
1269 
1270 	for (wedge = 0; wedge < w->mball.wedges; wedge++)
1271 		DrawWedge(w, wedge);
1272 }
1273 
1274 static void
DrawWedge(MballWidget w,int wedge)1275 DrawWedge(MballWidget w, int wedge)
1276 {
1277 	int         ring;
1278 
1279 	for (ring = 0; ring < w->mball.rings; ring++)
1280 		DrawSector(w, wedge, ring, FALSE);
1281 }
1282 
1283 static void
LetterPosition(MballWidget w,int wedge,int ring,int lengthx,int lengthy,int * dx,int * dy)1284 LetterPosition(MballWidget w, int wedge, int ring, int lengthx, int lengthy, int *dx, int *dy)
1285 {
1286 	double      angle, radius;
1287 
1288 	angle = (double) (2 * wedge + 1) * M_PI / w->mball.wedges;
1289 	if (w->mball.rings % 2 && ring == w->mball.rings / 2)
1290 		radius = ((double) 4.0 * ring + 1.0) / ((double) 4.0 * w->mball.rings);
1291 	else
1292 		radius = ((double) 2.0 * ring + 1.0) / ((double) 2.0 * w->mball.rings);
1293 	*dx = lengthx / 2 + (int) ((double) lengthx * radius * cos(angle - M_PI / 2));
1294 	*dy = lengthy / 2 + (int) ((double) lengthy * radius * sin(angle - M_PI / 2));
1295 }
1296 
1297 static void
OffsetSect(MballWidget w,int wedge,int * dx,int * dy)1298 OffsetSect(MballWidget w, int wedge, int *dx, int *dy)
1299 {
1300 	double      angle = (double) (2 * wedge + 1) * M_PI / w->mball.wedges;
1301 
1302 	*dx = (int) ((double) w->mball.dr * cos(angle - M_PI / 2));
1303 	*dy = (int) ((double) w->mball.dr * sin(angle - M_PI / 2));
1304 }
1305 
1306 static void
DrawSector(MballWidget w,int wedge,int ring,int offset)1307 DrawSector(MballWidget w, int wedge, int ring, int offset)
1308 {
1309 	GC          wedgeGC, borderGC;
1310 	int         startx, starty, lengthx, lengthy, i, l;
1311 
1312 	startx = 1 + w->mball.puzzleOffset.x;
1313 	starty = 1 + w->mball.puzzleOffset.y;
1314 	lengthx = w->mball.viewLength - w->mball.delta + w->mball.puzzleOffset.x;
1315 	lengthy = w->mball.viewLength - w->mball.delta + w->mball.puzzleOffset.y;
1316 	if (offset) {
1317 		borderGC = w->mball.wedgeGC[w->mball.mballLoc[wedge][ring].wedge];
1318 		if (w->mball.depth < 2 || w->mball.mono) {
1319 			wedgeGC = w->mball.inverseGC;
1320 		} else {
1321 			wedgeGC = w->mball.borderGC;
1322 		}
1323 	} else {
1324 		wedgeGC = w->mball.wedgeGC[w->mball.mballLoc[wedge][ring].wedge];
1325 		borderGC = w->mball.borderGC;
1326 	}
1327 	/* if the number of rings is odd
1328 	 * the ring can straddle both hemispheres */
1329 	if (ring < (w->mball.rings + 1) / 2)
1330 		DrawSect(w, wedgeGC, borderGC, ring, wedge,
1331 			 startx, starty, lengthx - startx, lengthy - starty);
1332 	if (ring + 1 > w->mball.rings / 2) {
1333 		if (w->mball.vertical)
1334 			DrawSect(w, wedgeGC, borderGC,
1335 				 w->mball.rings - 1 - ring,
1336 				 (3 * w->mball.wedges / 2 - 1 - wedge) % w->mball.wedges,
1337 				 startx, lengthy + 3, lengthx - startx, lengthy - starty);
1338 		else
1339 			DrawSect(w, wedgeGC, borderGC,
1340 				 w->mball.rings - 1 - ring,
1341 				 w->mball.wedges - 1 - wedge,
1342 				 lengthx + 3, starty, lengthx - startx, lengthy - starty);
1343 	}
1344 	if (w->mball.depth < 2 || w->mball.mono) {
1345 		int         letterX, letterY;
1346 		char        buf[3];
1347 
1348 		if (offset) {
1349 			borderGC = w->mball.borderGC;
1350 		} else {
1351 			borderGC = w->mball.inverseGC;
1352 		}
1353 		if (ring < (w->mball.rings + 1) / 2) {
1354 			LetterPosition(w, wedge, ring, lengthx - startx, lengthy - starty,
1355 				       &letterX, &letterY);
1356 			letterX += startx + w->mball.letterOffset.x;
1357 			letterY += starty + w->mball.letterOffset.y;
1358 			if (w->mball.orient && !w->mball.mballLoc[wedge][ring].direction) {
1359 				int         last;
1360 
1361 				l = w->mball.mballLoc[wedge][ring].wedge;
1362 				last = int2String(buf, l, w->mball.base, False);
1363 				buf[last] = w->mball.wedgeName[l][0];
1364 				buf[last + 1] = '\0';
1365 				i = 0;
1366 				if (l == 0)
1367 					l = 1;
1368 				l *= w->mball.base;
1369 				while (l >= 1) {
1370 					l /= w->mball.base;
1371 					letterX += w->mball.letterOffset.x;
1372 					i++;
1373 				}
1374 			} else {
1375 				buf[0] = w->mball.wedgeName[w->mball.mballLoc[wedge][ring].wedge][0];
1376 				buf[1] = '\0';
1377 				i = 1;
1378 			}
1379 			XDrawString(XtDisplay(w), XtWindow(w), borderGC,
1380 				    letterX, letterY, buf, i);
1381 		}
1382 		if (ring + 1 > w->mball.rings / 2) {
1383 			if (w->mball.vertical) {
1384 				LetterPosition(w,
1385 					       (3 * w->mball.wedges / 2 - 1 - wedge) % w->mball.wedges,
1386 					       w->mball.rings - 1 - ring,
1387 					  lengthx - startx, lengthy - starty,
1388 					       &letterX, &letterY);
1389 				letterX += startx + w->mball.letterOffset.x;
1390 				letterY += lengthy + 3 + w->mball.letterOffset.y;
1391 			} else {
1392 				LetterPosition(w,
1393 					       w->mball.wedges - 1 - wedge,
1394 					       w->mball.rings - 1 - ring,
1395 					  lengthx - startx, lengthy - starty,
1396 					       &letterX, &letterY);
1397 				letterX += lengthx + 3 + w->mball.letterOffset.x;
1398 				letterY += starty + w->mball.letterOffset.y;
1399 			}
1400 			if (w->mball.orient && w->mball.mballLoc[wedge][ring].direction) {
1401 				int         last;
1402 
1403 				l = w->mball.mballLoc[wedge][ring].wedge;
1404 				last = int2String(buf, l, w->mball.base, False);
1405 				buf[last] = w->mball.wedgeName[l][0];
1406 				buf[last + 1] = '\0';
1407 				i = 0;
1408 				if (l == 0)
1409 					l = 1;
1410 				l *= w->mball.base;
1411 				while (l >= 1) {
1412 					l /= w->mball.base;
1413 					letterX += w->mball.letterOffset.x;
1414 					i++;
1415 				}
1416 			} else {
1417 				buf[0] = w->mball.wedgeName[w->mball.mballLoc[wedge][ring].wedge][0];
1418 				buf[1] = '\0';
1419 				i = 1;
1420 			}
1421 			XDrawString(XtDisplay(w), XtWindow(w), borderGC,
1422 				    letterX, letterY, buf, i);
1423 		}
1424 	} else if (w->mball.orient) {
1425 		int         letterX, letterY;
1426 		char        buf[2];
1427 
1428 		if (ring < (w->mball.rings + 1) / 2 &&
1429 		    !w->mball.mballLoc[wedge][ring].direction) {
1430 			LetterPosition(w, wedge, ring, lengthx - startx, lengthy - starty,
1431 				       &letterX, &letterY);
1432 			letterX += startx + w->mball.letterOffset.x;
1433 			letterY += starty + w->mball.letterOffset.y;
1434 			l = w->mball.mballLoc[wedge][ring].wedge;
1435 			(void) int2String(buf, l, w->mball.base, True);
1436 			i = 0;
1437 			if (l == 0)
1438 				l = 1;
1439 			while (l >= 1) {
1440 				l /= w->mball.base;
1441 				letterX += w->mball.letterOffset.x;
1442 				i++;
1443 			}
1444 			XDrawString(XtDisplay(w), XtWindow(w), borderGC,
1445 				    letterX, letterY, buf, i);
1446 		}
1447 		if (ring + 1 > w->mball.rings / 2 &&
1448 		    w->mball.mballLoc[wedge][ring].direction) {
1449 			if (w->mball.vertical) {
1450 				LetterPosition(w,
1451 					       (3 * w->mball.wedges / 2 - 1 - wedge) % w->mball.wedges,
1452 					       w->mball.rings - 1 - ring,
1453 					  lengthx - startx, lengthy - starty,
1454 					       &letterX, &letterY);
1455 				letterX += startx + w->mball.letterOffset.x;
1456 				letterY += lengthy + 3 + w->mball.letterOffset.y;
1457 			} else {
1458 				LetterPosition(w,
1459 					       w->mball.wedges - 1 - wedge,
1460 					       w->mball.rings - 1 - ring,
1461 					  lengthx - startx, lengthy - starty,
1462 					       &letterX, &letterY);
1463 				letterX += lengthx + 3 + w->mball.letterOffset.x;
1464 				letterY += starty + w->mball.letterOffset.y;
1465 			}
1466 			l = w->mball.mballLoc[wedge][ring].wedge;
1467 			(void) int2String(buf, l, w->mball.base, True);
1468 			i = 0;
1469 			if (l == 0)
1470 				l = 1;
1471 			while (l >= 1) {
1472 				l /= w->mball.base;
1473 				letterX += w->mball.letterOffset.x;
1474 				i++;
1475 			}
1476 			XDrawString(XtDisplay(w), XtWindow(w), borderGC,
1477 				    letterX, letterY, buf, i);
1478 		}
1479 	}
1480 }
1481 
1482 static void
DrawRadar(MballWidget w,GC gc,int startx,int starty,int lengthx,int lengthy)1483 DrawRadar(MballWidget w, GC gc, int startx, int starty, int lengthx, int lengthy)
1484 {
1485 	int         r, i;
1486 	double      angle, increment;
1487 
1488 	XDrawArc(XtDisplay(w), XtWindow(w), gc, startx, starty,
1489 		 lengthx, lengthy, 0, CIRCLE);
1490 	if (w->mball.rings % 2)
1491 		for (r = 1; r < w->mball.rings / 2 + 1; r++)
1492 			XDrawArc(XtDisplay(w), XtWindow(w), gc,
1493 				 startx - lengthx / (2 * w->mball.rings) +
1494 				 (w->mball.rings / 2 + 1 - r) * lengthx / w->mball.rings,
1495 				 starty - lengthy / (2 * w->mball.rings) +
1496 				 (w->mball.rings / 2 + 1 - r) * lengthy / w->mball.rings,
1497 				 r * 2 * lengthx / w->mball.rings,
1498 				 r * 2 * lengthy / w->mball.rings,
1499 				 0, CIRCLE);
1500 	else
1501 		for (r = 1; r < w->mball.rings / 2; r++)
1502 			XDrawArc(XtDisplay(w), XtWindow(w), gc,
1503 				 startx + (w->mball.rings / 2 - r) * lengthx / w->mball.rings,
1504 				 starty + (w->mball.rings / 2 - r) * lengthy / w->mball.rings,
1505 				 r * 2 * lengthx / w->mball.rings,
1506 				 r * 2 * lengthy / w->mball.rings,
1507 				 0, CIRCLE);
1508 	increment = RADIANS(NUM_DEGREES) / (double) w->mball.wedges;
1509 	angle = RADIANS(RT_ANG);
1510 	for (i = 0; i < w->mball.wedges; i++) {
1511 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1512 			  startx + lengthx / 2, starty + lengthy / 2,
1513 			  startx + lengthx / 2 + (int) ((double) lengthx * cos(angle) / 2.0),
1514 			  starty + lengthy / 2 + (int) ((double) lengthy * sin(angle) / 2.0));
1515 		angle += increment;
1516 	}
1517 }
1518 
1519 static void
DrawSect(MballWidget w,GC wedgeGC,GC borderGC,int r,int wedge,int startx,int starty,int lengthx,int lengthy)1520 DrawSect(MballWidget w, GC wedgeGC, GC borderGC, int r, int wedge, int startx, int starty, int lengthx, int lengthy)
1521 {
1522 	int         dx, dy;
1523 
1524 	OffsetSect(w, wedge, &dx, &dy);
1525 	if (w->mball.rings % 2) {
1526 		if (r == w->mball.rings / 2) {
1527 			XFillSector(XtDisplay(w), XtWindow(w), wedgeGC,
1528 			startx + lengthx / 2 + dx, starty + lengthy / 2 + dy,
1529 			  r * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1530 			  r * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1531 				    (r + 1) * 2 * lengthx / (w->mball.rings + 1) - 2 * w->mball.dr,
1532 				    (r + 1) * 2 * lengthy / (w->mball.rings + 1) - 2 * w->mball.dr,
1533 				 CIRCLE_4 - CIRCLE * wedge / w->mball.wedges,
1534 				    -CIRCLE / w->mball.wedges);
1535 			XDrawSector(XtDisplay(w), XtWindow(w), borderGC,
1536 			startx + lengthx / 2 + dx, starty + lengthy / 2 + dy,
1537 			  r * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1538 			  r * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1539 				    (r + 1) * 2 * lengthx / (w->mball.rings + 1) - 2 * w->mball.dr,
1540 				    (r + 1) * 2 * lengthy / (w->mball.rings + 1) - 2 * w->mball.dr,
1541 				 CIRCLE_4 - CIRCLE * wedge / w->mball.wedges,
1542 				    -CIRCLE / w->mball.wedges);
1543 		} else {
1544 			XFillSector(XtDisplay(w), XtWindow(w), wedgeGC,
1545 			startx + lengthx / 2 + dx, starty + lengthy / 2 + dy,
1546 			  r * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1547 			  r * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1548 				    (r + 1) * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1549 				    (r + 1) * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1550 				 CIRCLE_4 - CIRCLE * wedge / w->mball.wedges,
1551 				    -CIRCLE / w->mball.wedges);
1552 			XDrawSector(XtDisplay(w), XtWindow(w), borderGC,
1553 			startx + lengthx / 2 + dx, starty + lengthy / 2 + dy,
1554 			  r * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1555 			  r * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1556 				    (r + 1) * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1557 				    (r + 1) * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1558 				 CIRCLE_4 - CIRCLE * wedge / w->mball.wedges,
1559 				    -CIRCLE / w->mball.wedges);
1560 		}
1561 	} else {
1562 		XFillSector(XtDisplay(w), XtWindow(w), wedgeGC,
1563 			startx + lengthx / 2 + dx, starty + lengthy / 2 + dy,
1564 			  r * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1565 			  r * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1566 		    (r + 1) * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1567 		    (r + 1) * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1568 			    CIRCLE_4 - CIRCLE * wedge / w->mball.wedges,
1569 			    -CIRCLE / w->mball.wedges);
1570 		XDrawSector(XtDisplay(w), XtWindow(w), borderGC,
1571 			startx + lengthx / 2 + dx, starty + lengthy / 2 + dy,
1572 			  r * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1573 			  r * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1574 		    (r + 1) * 2 * lengthx / w->mball.rings - 2 * w->mball.dr,
1575 		    (r + 1) * 2 * lengthy / w->mball.rings - 2 * w->mball.dr,
1576 			    CIRCLE_4 - CIRCLE * wedge / w->mball.wedges,
1577 			    -CIRCLE / w->mball.wedges);
1578 	}
1579 }
1580 
1581 static void
XFillSector(Display * display,Drawable drawable,GC gc,int xo,int yo,int width1,int height1,int width2,int height2,int angle1,int angle2)1582 XFillSector(Display * display, Drawable drawable, GC gc, int xo, int yo, int width1, int height1, int width2, int height2, int angle1, int angle2)
1583 {
1584 	int         d, r1 = MIN(width1, height1) / 2, r2 = MIN(width2, height2) / 2;
1585 	int         w = MAX(r2 - r1 - 8, 1);
1586 
1587 	if (r1 > r2) {
1588 		d = r1;
1589 		r1 = r2;
1590 		r2 = d;
1591 	}
1592 	if (r1 < 0)
1593 		r1 = -3;
1594 	d = MAX(r1 + r2 + 2, 2);
1595 	XSetLineAttributes(display, gc, w, LineSolid, CapNotLast, JoinRound);
1596 	XDrawArc(display, drawable, gc, xo - d / 2, yo - d / 2, d, d,
1597 		 angle1, angle2);
1598 	XSetLineAttributes(display, gc, 1, LineSolid, CapNotLast, JoinRound);
1599 }
1600 
1601 static void
XDrawSector(Display * display,Drawable drawable,GC gc,int xo,int yo,int width1,int height1,int width2,int height2,int angle1,int angle2)1602 XDrawSector(Display * display, Drawable drawable, GC gc, int xo, int yo, int width1, int height1, int width2, int height2, int angle1, int angle2)
1603 {
1604 	int         d, r1 = MIN(width1, height1) / 2, r2 = MIN(width2, height2) / 2;
1605 
1606 	/*double ang, x, y; */
1607 
1608 	if (r1 > r2) {
1609 		d = r1;
1610 		r1 = r2;
1611 		r2 = d;
1612 	}
1613 	if (r1 < 0)
1614 		r1 = -3;
1615 	d = MAX(2 * (r1 + 3), 1);
1616 	XDrawArc(display, drawable, gc, xo - d / 2, yo - d / 2, d, d,
1617 		 angle1, angle2);
1618 	d = MAX(2 * (r2 - 1), 3);
1619 	XDrawArc(display, drawable, gc, xo - d / 2, yo - d / 2, d, d,
1620 		 angle1, angle2);
1621 
1622 	/*ang = RADIANS((double) angle1 / MULT);
1623 	   x = cos(ang);
1624 	   y = sin(ang);
1625 	   XDrawLine(display, drawable, gc,
1626 	   (int) ((double) r1 * x) + xo, (int) ((double) r1 * y) + yo,
1627 	   (int) ((double) r2 * x) + xo, (int) ((double) r2 * y) + yo);
1628 	   ang = RADIANS((double) angle2 / MULT);
1629 	   x = cos(ang);
1630 	   y = sin(ang);
1631 	   XDrawLine(display, drawable, gc,
1632 	   (int) ((double) r1 * x) + xo, (int) ((double) r1 * y) + yo,
1633 	   (int) ((double) r2 * x) + xo, (int) ((double) r2 * y) + yo); */
1634 }
1635 
1636 #if 0
1637 static void
1638 XFillSector(display, drawable, gc,
1639 	    xo, yo, width1, height1, width2, height2, angle1, angle2)
1640 	Display    *display;
1641 	Drawable    drawable;
1642 	GC          gc;
1643 	int         xo, yo;
1644 	int         width1, height1, width2, height2;
1645 	int         angle1, angle2;
1646 {
1647 	int         d, r1 = MIN(width1, height1) / 2, r2 = MIN(width2, height2) / 2;
1648 
1649 	if (r1 > r2) {
1650 		d = r1;
1651 		r1 = r2;
1652 		r2 = d;
1653 	}
1654 	if (r1 < 0)
1655 		r1 = -3;
1656 	for (d = 2 * (r1 + 3); d < 2 * (r2 - 1); d++)
1657 		XDrawArc(display, drawable, gc, xo - d / 2, yo - d / 2, d, d,
1658 			 angle1, angle2);
1659 }
1660 #endif
1661 
1662 static int
int2String(char * buf,int number,int base,Boolean capital)1663 int2String(char *buf, int number, int base, Boolean capital)
1664 {
1665 	int         digit, mult = base, last, position;
1666 	int         a, i, j, s, r;
1667 
1668 	if (capital) {
1669 		a = 'A', i = 'I', j = 'J', s = 'S', r = 'R';
1670 	} else {
1671 		a = 'a', i = 'i', j = 'j', s = 's', r = 'r';
1672 	}
1673 	if (number < 0) {
1674 		(void) printf("number %d < 0\n", number);
1675 		return 0;
1676 	}
1677 	last = 1;
1678 	while (number >= mult) {
1679 		last++;
1680 		mult *= base;
1681 	}
1682 	for (position = 0; position < last; position++) {
1683 		mult /= base;
1684 		digit = number / mult;
1685 		number -= digit * mult;
1686 		buf[position] = digit + '0';
1687 		if (buf[position] > '9') {	/* ASCII */
1688 			buf[position] += (a - '9' - 1);
1689 		} else if (buf[position] < '0') {	/* EBCDIC */
1690 			buf[position] += (a - '9' - 1);
1691 			if (buf[position] > i)
1692 				buf[position] += (j - i - 1);
1693 			if (buf[position] > r)
1694 				buf[position] += (s - r - 1);
1695 		}
1696 	}
1697 	buf[last] = '\0';
1698 	return last;
1699 }
1700 Boolean
CheckSolved(MballWidget w)1701 CheckSolved(MballWidget w)
1702 {
1703 	int         wedge, ring;
1704 	MballLoc    test;
1705 
1706 	if (w->mball.orient)
1707 		for (wedge = 0; wedge < w->mball.wedges; wedge++) {
1708 			if (wedge == 0) {
1709 				test.wedge = w->mball.mballLoc[wedge][0].wedge;
1710 				test.direction = w->mball.mballLoc[wedge][0].direction;
1711 			}
1712 			for (ring = 0; ring < w->mball.rings; ring++) {
1713 				if (test.direction != w->mball.mballLoc[wedge][ring].direction)
1714 					return False;
1715 				if (test.direction) {
1716 					if ((w->mball.wedges - w->mball.mballLoc[wedge][ring].wedge +
1717 					     test.wedge) % w->mball.wedges != wedge)
1718 						return False;
1719 				} else {
1720 					if ((w->mball.wedges + w->mball.mballLoc[wedge][ring].wedge -
1721 					     test.wedge) % w->mball.wedges != wedge)
1722 						return False;
1723 				}
1724 			}
1725 	} else
1726 		for (wedge = 0; wedge < w->mball.wedges; wedge++)
1727 			for (ring = 0; ring < w->mball.rings; ring++)
1728 				if (ring == 0) {
1729 					test.wedge = w->mball.mballLoc[wedge][ring].wedge;
1730 					test.direction = w->mball.mballLoc[wedge][ring].direction;
1731 				} else if (test.wedge != w->mball.mballLoc[wedge][ring].wedge)
1732 					return False;
1733 	return True;
1734 }
1735 
1736 #ifdef DEBUG
1737 
1738 static void
PrintMball(MballWidget w)1739 PrintMball(MballWidget w)
1740 {
1741 	int         wedge, ring;
1742 
1743 	for (wedge = 0; wedge < w->mball.wedges; wedge++) {
1744 		for (ring = 0; ring < w->mball.rings; ring++) {
1745 			(void) printf("%d %d  ", w->mball.mballLoc[wedge][ring].wedge,
1746 				   w->mball.mballLoc[wedge][ring].direction);
1747 		}
1748 		(void) printf("\n");
1749 	}
1750 	(void) printf("\n");
1751 }
1752 
1753 #endif
1754