1 /*-
2 # X-BASED MISSING LINK(tm)
3 #
4 # Mlink.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 Mlink */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #ifdef VMS
31 #include <unixlib.h>
32 #else
33 #if HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #endif
37 #include <X11/IntrinsicP.h>
38 #include <X11/Intrinsic.h>
39 #include <X11/StringDefs.h>
40 #include <X11/CoreP.h>
41 #include "MlinkP.h"
42
43 #ifndef DATAFILE
44 #define DATAFILE "/usr/games/lib/mlink.data"
45 #endif
46
47 static void InitializeMlink(Widget request, Widget renew);
48 static void ExposeMlink(Widget renew, XEvent * event, Region region);
49 static void ResizeMlink(MlinkWidget w);
50 static void DestroyMlink(Widget old);
51 static Boolean SetValuesMlink(Widget current, Widget request, Widget renew);
52 static void QuitMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
53 static void MoveMlinkTop(MlinkWidget w, XEvent * event, char **args, int nArgs);
54 static void MoveMlinkLeft(MlinkWidget w, XEvent * event, char **args, int nArgs);
55 static void MoveMlinkRight(MlinkWidget w, XEvent * event, char **args, int nArgs);
56 static void MoveMlinkBottom(MlinkWidget w, XEvent * event, char **args, int nArgs);
57 static void MoveMlinkInput(MlinkWidget w, int x, int direction, int shift, int control);
58 static void SelectMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
59 static void ReleaseMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
60 static void RandomizeMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
61 static void RandomizeMlinkMaybe(MlinkWidget w, XEvent * event, char **args, int nArgs);
62 static void GetMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
63 static void WriteMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
64 static void UndoMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
65 static void SolveMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
66 static void OrientizeMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
67 static void MiddleMlink(MlinkWidget w, XEvent * event, char **args, int nArgs);
68 static void ControlMlink(MlinkWidget w, int direction);
69 static void RotateMlink(MlinkWidget w, int direction, int tile);
70 static void SlideMlink(MlinkWidget w, int direction);
71
72 static int SelectTiles(MlinkWidget w, int x, int y, int *i, int *j);
73 static void SelectSlideTiles(MlinkWidget w, int pos);
74 static void SetAllColors(MlinkWidget w, Boolean init);
75 static void GetColor(MlinkWidget w, int face, Boolean init);
76 static void MoveShiftCb(MlinkWidget w, int direction);
77 static void CheckTiles(MlinkWidget w);
78 static void ResetTiles(MlinkWidget w);
79 static void ResizeTiles(MlinkWidget w);
80 static void MoveNoTiles(MlinkWidget w);
81 static Boolean MoveTilesDir(MlinkWidget w, int direction, int tile);
82 static void RandomizeTiles(MlinkWidget w);
83 static void MoveTiles(MlinkWidget w, int from);
84 static void RotateTiles(MlinkWidget w, int c, int direction);
85 static int ExchangeTiles(MlinkWidget w, int pos1, int pos2);
86 static void DrawFrame(MlinkWidget w, GC gc);
87 static void DrawTile(MlinkWidget w, int pos, Boolean blank, Boolean erase, int offset);
88 static void DrawLink(MlinkWidget w, GC gc, int pos, int offset);
89 static Boolean PositionTile(MlinkWidget w, int x, int *r);
90 static int Row(MlinkWidget w, int pos);
91 static int Column(MlinkWidget w, int pos);
92 static int int2String(char *buf, int number, int base, Boolean capital);
93
94 static char defaultTranslationsMlink[] =
95 "<KeyPress>q: Quit()\n\
96 Ctrl<KeyPress>C: Quit()\n\
97 <KeyPress>Up: MoveTop()\n\
98 <KeyPress>KP_8: MoveTop()\n\
99 <KeyPress>R8: MoveTop()\n\
100 <KeyPress>Left: MoveLeft()\n\
101 <KeyPress>KP_4: MoveLeft()\n\
102 <KeyPress>R10: MoveLeft()\n\
103 <KeyPress>Right: MoveRight()\n\
104 <KeyPress>KP_6: MoveRight()\n\
105 <KeyPress>R12: MoveRight()\n\
106 <KeyPress>Down: MoveBottom()\n\
107 <KeyPress>KP_2: MoveBottom()\n\
108 <KeyPress>R14: MoveBottom()\n\
109 <Btn1Down>: Select()\n\
110 <Btn1Up>: Release()\n\
111 <KeyPress>r: Randomize()\n\
112 <Btn3Down>(2+): Randomize()\n\
113 <Btn3Down>: RandomizeMaybe()\n\
114 <KeyPress>g: Get()\n\
115 <KeyPress>w: Write()\n\
116 <KeyPress>u: Undo()\n\
117 <KeyPress>s: Solve()\n\
118 <KeyPress>o: Orientize()\n\
119 <KeyPress>m: Middle()";
120
121 static XtActionsRec actionsListMlink[] =
122 {
123 {"Quit", (XtActionProc) QuitMlink},
124 {"MoveTop", (XtActionProc) MoveMlinkTop},
125 {"MoveLeft", (XtActionProc) MoveMlinkLeft},
126 {"MoveRight", (XtActionProc) MoveMlinkRight},
127 {"MoveBottom", (XtActionProc) MoveMlinkBottom},
128 {"Select", (XtActionProc) SelectMlink},
129 {"Release", (XtActionProc) ReleaseMlink},
130 {"Randomize", (XtActionProc) RandomizeMlink},
131 {"RandomizeMaybe", (XtActionProc) RandomizeMlinkMaybe},
132 {"Get", (XtActionProc) GetMlink},
133 {"Write", (XtActionProc) WriteMlink},
134 {"Undo", (XtActionProc) UndoMlink},
135 {"Solve", (XtActionProc) SolveMlink},
136 {"Orientize", (XtActionProc) OrientizeMlink},
137 {"Middle", (XtActionProc) MiddleMlink}
138 };
139
140 static XtResource resourcesMlink[] =
141 {
142 {XtNuserName, XtCUserName, XtRString, sizeof (String),
143 XtOffset(MlinkWidget, mlink.username), XtRString, "nobody"},
144 {XtNfaceColor0, XtCLabel, XtRString, sizeof (String),
145 XtOffset(MlinkWidget, mlink.faceName[0]), XtRString, "White"},
146 {XtNfaceColor1, XtCLabel, XtRString, sizeof (String),
147 XtOffset(MlinkWidget, mlink.faceName[1]), XtRString, "Yellow"},
148 {XtNfaceColor2, XtCLabel, XtRString, sizeof (String),
149 XtOffset(MlinkWidget, mlink.faceName[2]), XtRString, "Green"},
150 {XtNfaceColor3, XtCLabel, XtRString, sizeof (String),
151 XtOffset(MlinkWidget, mlink.faceName[3]), XtRString, "Red"},
152 {XtNfaceColor4, XtCLabel, XtRString, sizeof (String),
153 XtOffset(MlinkWidget, mlink.faceName[4]), XtRString, "Blue"},
154 {XtNfaceColor5, XtCLabel, XtRString, sizeof (String),
155 XtOffset(MlinkWidget, mlink.faceName[5]), XtRString, "Magenta"},
156 {XtNfaceColor6, XtCLabel, XtRString, sizeof (String),
157 XtOffset(MlinkWidget, mlink.faceName[6]), XtRString, "Cyan"},
158 {XtNfaceColor7, XtCLabel, XtRString, sizeof (String),
159 XtOffset(MlinkWidget, mlink.faceName[7]), XtRString, "Orange"},
160 {XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
161 XtOffset(MlinkWidget, mlink.foreground), XtRString, XtDefaultForeground},
162 {XtNtileColor, XtCBackground, XtRPixel, sizeof (Pixel),
163 XtOffset(MlinkWidget, mlink.tileColor), XtRString, XtDefaultForeground},
164 {XtNtileBorder, XtCForeground, XtRPixel, sizeof (Pixel),
165 XtOffset(MlinkWidget, mlink.borderColor), XtRString, XtDefaultBackground},
166 {XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
167 XtOffset(MlinkWidget, core.width), XtRString, "200"},
168 {XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
169 XtOffset(MlinkWidget, core.height), XtRString, "200"},
170 {XtNtiles, XtCTiles, XtRInt, sizeof (int),
171 XtOffset(MlinkWidget, mlink.tiles), XtRString, "4"}, /* DEFAULTTILES */
172 {XtNfaces, XtCFaces, XtRInt, sizeof (int),
173 XtOffset(MlinkWidget, mlink.faces), XtRString, "4"}, /* DEFAULTFACES */
174 {XtNorient, XtCOrient, XtRBoolean, sizeof (Boolean),
175 XtOffset(MlinkWidget, mlink.orient), XtRString, "FALSE"}, /*DEFAULTORIENT */
176 {XtNmiddle, XtCMiddle, XtRBoolean, sizeof (Boolean),
177 XtOffset(MlinkWidget, mlink.middle), XtRString,
178 "TRUE"}, /*DEFAULTMIDDLE */
179 {XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
180 XtOffset(MlinkWidget, mlink.mono), XtRString, "FALSE"},
181 {XtNreverse, XtCReverse, XtRBoolean, sizeof (Boolean),
182 XtOffset(MlinkWidget, mlink.reverse), XtRString, "FALSE"},
183 {XtNbase, XtCBase, XtRInt, sizeof (int),
184 XtOffset(MlinkWidget, mlink.base), XtRString, "10"},
185 {XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
186 XtOffset(MlinkWidget, mlink.started), XtRString, "FALSE"},
187 {XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
188 XtOffset(MlinkWidget, mlink.select), XtRCallback, NULL}
189 };
190
191 MlinkClassRec mlinkClassRec =
192 {
193 {
194 (WidgetClass) & widgetClassRec, /* superclass */
195 "Mlink", /* class name */
196 sizeof (MlinkRec), /* widget size */
197 NULL, /* class initialize */
198 NULL, /* class part initialize */
199 FALSE, /* class inited */
200 (XtInitProc) InitializeMlink, /* initialize */
201 NULL, /* initialize hook */
202 XtInheritRealize, /* realize */
203 actionsListMlink, /* actions */
204 XtNumber(actionsListMlink), /* num actions */
205 resourcesMlink, /* resources */
206 XtNumber(resourcesMlink), /* num resources */
207 NULLQUARK, /* xrm class */
208 TRUE, /* compress motion */
209 TRUE, /* compress exposure */
210 TRUE, /* compress enterleave */
211 TRUE, /* visible interest */
212 (XtWidgetProc) DestroyMlink, /* destroy */
213 (XtWidgetProc) ResizeMlink, /* resize */
214 (XtExposeProc) ExposeMlink, /* expose */
215 (XtSetValuesFunc) SetValuesMlink, /* set values */
216 NULL, /* set values hook */
217 XtInheritSetValuesAlmost, /* set values almost */
218 NULL, /* get values hook */
219 NULL, /* accept focus */
220 XtVersion, /* version */
221 NULL, /* callback private */
222 defaultTranslationsMlink, /* tm table */
223 NULL, /* query geometry */
224 NULL, /* display accelerator */
225 NULL /* extension */
226 },
227 {
228 0 /* ignore */
229 }
230 };
231
232 WidgetClass mlinkWidgetClass = (WidgetClass) & mlinkClassRec;
233
234 static void
InitializeMlink(Widget request,Widget renew)235 InitializeMlink(Widget request, Widget renew)
236 {
237 MlinkWidget w = (MlinkWidget) renew;
238
239 w->mlink.tileOfPosition = NULL;
240 CheckTiles(w);
241 InitMoves();
242 ResetTiles(w);
243 (void) SRAND(getpid());
244 w->mlink.depth = DefaultDepthOfScreen(XtScreen(w));
245 SetAllColors(w, True);
246 ResizeMlink(w);
247 }
248
249 static void
DestroyMlink(Widget old)250 DestroyMlink(Widget old)
251 {
252 MlinkWidget w = (MlinkWidget) old;
253 int face;
254
255 for (face = 0; face < MAXFACES; face++)
256 XtReleaseGC(old, w->mlink.faceGC[face]);
257 XtReleaseGC(old, w->mlink.tileGC);
258 XtReleaseGC(old, w->mlink.puzzleGC);
259 XtReleaseGC(old, w->mlink.inverseGC);
260 XtRemoveCallbacks(old, XtNselectCallback, w->mlink.select);
261 }
262
263 static void
ResizeMlink(MlinkWidget w)264 ResizeMlink(MlinkWidget w)
265 {
266 w->mlink.delta.x = 3;
267 w->mlink.delta.y = 3;
268 w->mlink.offset.x = MAX(((int) w->core.width - w->mlink.delta.x) /
269 w->mlink.tiles, 0);
270 w->mlink.offset.y = MAX(((int) w->core.height - w->mlink.delta.y) /
271 w->mlink.faces, 0);
272 w->mlink.faceSize.x = w->mlink.offset.x * w->mlink.tiles +
273 w->mlink.delta.x;
274 w->mlink.faceSize.y = w->mlink.offset.y + w->mlink.delta.y;
275 w->mlink.puzzleSize.x = w->mlink.faceSize.x + w->mlink.delta.x;
276 w->mlink.puzzleSize.y = (w->mlink.faceSize.y - w->mlink.delta.y) *
277 w->mlink.faces + w->mlink.delta.y;
278 w->mlink.puzzleOffset.x = ((int) w->core.width -
279 w->mlink.puzzleSize.x + 2) / 2;
280 w->mlink.puzzleOffset.y = ((int) w->core.height -
281 w->mlink.puzzleSize.y + 2) / 2;
282 w->mlink.tileSize.x = MAX(w->mlink.offset.x - w->mlink.delta.x, 0);
283 w->mlink.tileSize.y = MAX(w->mlink.offset.y - w->mlink.delta.y, 0);
284 ResizeTiles(w);
285 }
286
287 static void
ExposeMlink(Widget renew,XEvent * event,Region region)288 ExposeMlink(Widget renew, XEvent * event, Region region)
289 {
290 MlinkWidget w = (MlinkWidget) renew;
291
292 if (w->core.visible) {
293 if (w->mlink.reverse)
294 XFillRectangle(XtDisplay(w), XtWindow(w),
295 w->mlink.inverseGC, 0, 0, w->core.width, w->core.height);
296 DrawFrame(w, w->mlink.puzzleGC);
297 DrawAllTiles(w);
298 }
299 }
300
301 static Boolean
SetValuesMlink(Widget current,Widget request,Widget renew)302 SetValuesMlink(Widget current, Widget request, Widget renew)
303 {
304 MlinkWidget c = (MlinkWidget) current, w = (MlinkWidget) renew;
305 Boolean redraw = False, setColors = False;
306 Boolean redrawTiles = False;
307 int face;
308
309 CheckTiles(w);
310 for (face = 0; face < MAXFACES; face++) {
311 if (strcmp(w->mlink.faceName[face], c->mlink.faceName[face])) {
312 setColors = True;
313 break;
314 }
315 }
316 if (w->core.background_pixel != c->core.background_pixel ||
317 w->mlink.foreground != c->mlink.foreground ||
318 w->mlink.borderColor != c->mlink.borderColor ||
319 w->mlink.tileColor != c->mlink.tileColor ||
320 w->mlink.reverse != c->mlink.reverse ||
321 w->mlink.mono != c->mlink.mono ||
322 setColors) {
323 SetAllColors(w, False);
324 redrawTiles = True;
325 }
326 if (w->mlink.tiles != c->mlink.tiles ||
327 w->mlink.faces != c->mlink.faces ||
328 w->mlink.orient != c->mlink.orient ||
329 w->mlink.middle != c->mlink.middle ||
330 w->mlink.base != c->mlink.base) {
331 ResetTiles(w);
332 ResizeMlink(w);
333 redraw = True;
334 } else if (w->mlink.offset.x != c->mlink.offset.x ||
335 w->mlink.offset.y != c->mlink.offset.y) {
336 ResizeMlink(w);
337 redraw = True;
338 }
339 if (redrawTiles && !redraw && XtIsRealized(renew) && renew->core.visible) {
340 DrawFrame(w, c->mlink.inverseGC);
341 DrawFrame(w, w->mlink.puzzleGC);
342 DrawAllTiles(w);
343 }
344 return (redraw);
345 }
346
347 static void
QuitMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)348 QuitMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
349 {
350 XtCloseDisplay(XtDisplay(w));
351 exit(0);
352 }
353
354 static void
SelectMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)355 SelectMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
356 {
357 int i, j, pos, shift;
358
359 pos = SelectTiles(w, event->xbutton.x, event->xbutton.y, &i, &j);
360 if (-w->mlink.tileFaces != pos) {
361 w->mlink.currentTile = i;
362 w->mlink.currentFace = j;
363 w->mlink.currentRef = pos;
364 shift = (int) (event->xkey.state & (ShiftMask | LockMask));
365 if (shift || !CheckSolved(w)) {
366 pos = w->mlink.currentTile + w->mlink.currentFace * w->mlink.tiles;
367 DrawTile(w, pos, (w->mlink.tileOfPosition[i + j * w->mlink.tiles] <= 0),
368 False, TRUE);
369 }
370 } else
371 w->mlink.currentRef = -w->mlink.tileFaces;
372 }
373
374 static void
ReleaseMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)375 ReleaseMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
376 {
377 int i, j, pos, diff, shift;
378
379 if (w->mlink.currentRef == -w->mlink.tileFaces)
380 return;
381 pos = w->mlink.currentTile + w->mlink.currentFace * w->mlink.tiles;
382 DrawTile(w, pos, (w->mlink.tileOfPosition[pos] <= 0), True, TRUE);
383 if (w->mlink.tileOfPosition[pos] > 0) {
384 DrawTile(w, pos, False, False, FALSE);
385 }
386 shift = (int) (event->xkey.state & (ShiftMask | LockMask));
387 if (!shift && CheckSolved(w))
388 MoveNoTiles(w);
389 else {
390 pos = SelectTiles(w, event->xbutton.x, event->xbutton.y, &i, &j);
391 if (-w->mlink.tileFaces != pos) {
392 if (j == w->mlink.currentFace) {
393 pos = w->mlink.currentRef;
394 if (pos / w->mlink.tiles == 0 &&
395 j == Column(w, w->mlink.spacePosition) && pos != 0) {
396 if (shift && CheckSolved(w))
397 MoveNoTiles(w);
398 else {
399 SelectSlideTiles(w, pos);
400 w->mlink.currentTile = w->mlink.tiles;
401 if (CheckSolved(w)) {
402 mlinkCallbackStruct cb;
403
404 cb.reason = MLINK_SOLVED;
405 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
406 }
407 }
408 }
409 } else {
410 diff = (w->mlink.currentFace - j + w->mlink.faces) % w->mlink.faces;
411 if (diff > w->mlink.faces / 2)
412 for (i = 0; i < w->mlink.faces - diff; i++)
413 (void) MoveMlink(w, BOTTOM, w->mlink.currentTile, shift);
414 else
415 for (i = 0; i < diff; i++)
416 (void) MoveMlink(w, TOP, w->mlink.currentTile, shift);
417 if (!shift && CheckSolved(w)) {
418 mlinkCallbackStruct cb;
419
420 cb.reason = MLINK_SOLVED;
421 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
422 }
423 }
424 }
425 }
426 }
427
428 static void
RandomizeMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)429 RandomizeMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
430 {
431 RandomizeTiles(w);
432 }
433
434 static void
RandomizeMlinkMaybe(MlinkWidget w,XEvent * event,char ** args,int nArgs)435 RandomizeMlinkMaybe(MlinkWidget w, XEvent * event, char **args, int nArgs)
436 {
437 if (!w->mlink.started)
438 RandomizeTiles(w);
439 }
440
441 static void
GetMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)442 GetMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
443 {
444 FILE *fp;
445 char c;
446 int i, tiles, faces, middle, orient, moves;
447 mlinkCallbackStruct cb;
448
449 if ((fp = fopen(DATAFILE, "r")) == NULL)
450 (void) printf("Can not read %s for get.\n", DATAFILE);
451 else {
452 FlushMoves(w);
453 while ((c = getc(fp)) != EOF && c != SYMBOL);
454 (void) fscanf(fp, "%d", &tiles);
455 if (tiles >= MINTILES) {
456 for (i = w->mlink.tiles; i < tiles; i++) {
457 cb.reason = MLINK_INC_X;
458 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
459 }
460 for (i = w->mlink.tiles; i > tiles; i--) {
461 cb.reason = MLINK_DEC_X;
462 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
463 }
464 } else
465 (void) printf("%s corrupted: tiles %d should be between %d and MAXINT\n",
466 DATAFILE, tiles, MINTILES);
467 while ((c = getc(fp)) != EOF && c != SYMBOL);
468 (void) fscanf(fp, "%d", &faces);
469 if (faces >= MINFACES && faces <= MAXFACES) {
470 for (i = w->mlink.faces; i < faces; i++) {
471 cb.reason = MLINK_INC_Y;
472 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
473 }
474 for (i = w->mlink.faces; i > faces; i--) {
475 cb.reason = MLINK_DEC_Y;
476 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
477 }
478 } else
479 (void) printf("%s corrupted: faces %d should be between %d and %d\n",
480 DATAFILE, faces, MINFACES, MAXFACES);
481 while ((c = getc(fp)) != EOF && c != SYMBOL);
482 (void) fscanf(fp, "%d", &middle);
483 if (w->mlink.middle != (Boolean) middle) {
484 cb.reason = MLINK_MIDDLE;
485 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
486 }
487 while ((c = getc(fp)) != EOF && c != SYMBOL);
488 (void) fscanf(fp, "%d", &orient);
489 if (w->mlink.orient != (Boolean) orient) {
490 cb.reason = MLINK_ORIENT;
491 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
492 }
493 while ((c = getc(fp)) != EOF && c != SYMBOL);
494 (void) fscanf(fp, "%d", &moves);
495 ScanStartPosition(fp, w);
496 cb.reason = MLINK_RESTORE;
497 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
498 SetStartPosition(w);
499 ScanMoves(fp, w, moves);
500 (void) fclose(fp);
501 (void) printf("%s: tiles %d, faces %d, middle %d, orient %d, moves %d.\n",
502 DATAFILE, tiles, faces, middle, orient, moves);
503 }
504 }
505
506 static void
WriteMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)507 WriteMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
508 {
509 FILE *fp;
510
511 if ((fp = fopen(DATAFILE, "w")) == NULL)
512 (void) printf("Can not write to %s.\n", DATAFILE);
513 else {
514 (void) fprintf(fp, "tiles%c %d\n", SYMBOL, w->mlink.tiles);
515 (void) fprintf(fp, "faces%c %d\n", SYMBOL, w->mlink.faces);
516 (void) fprintf(fp, "middle%c %d\n", SYMBOL, (w->mlink.middle) ? 1 : 0);
517 (void) fprintf(fp, "orient%c %d\n", SYMBOL, (w->mlink.orient) ? 1 : 0);
518 (void) fprintf(fp, "moves%c %d\n", SYMBOL, NumMoves());
519 PrintStartPosition(fp, w);
520 PrintMoves(fp);
521 (void) fclose(fp);
522 (void) printf("Saved to %s.\n", DATAFILE);
523 }
524 }
525
526 static void
UndoMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)527 UndoMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
528 {
529 if (MadeMoves()) {
530 int direction, tile, shift;
531 mlinkCallbackStruct cb;
532
533 GetMove(&direction, &tile, &shift);
534 direction = (direction + COORD / 2) % COORD;
535 if (shift && (direction == TOP || direction == BOTTOM))
536 MoveShiftCb(w, direction);
537 else if (MoveTilesDir(w, direction, tile)) {
538 cb.reason = MLINK_UNDO;
539 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
540 }
541 }
542 }
543
544 static void
SolveMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)545 SolveMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
546 {
547 #if 0
548 SolveTiles(w); /* Sorry, this is not implemented */
549 #endif
550 }
551
552 static void
OrientizeMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)553 OrientizeMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
554 {
555 mlinkCallbackStruct cb;
556
557 cb.reason = MLINK_ORIENT;
558 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
559 }
560
561 static void
MiddleMlink(MlinkWidget w,XEvent * event,char ** args,int nArgs)562 MiddleMlink(MlinkWidget w, XEvent * event, char **args, int nArgs)
563 {
564 mlinkCallbackStruct cb;
565
566 cb.reason = MLINK_MIDDLE;
567 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
568 }
569
570 static void
MoveMlinkTop(MlinkWidget w,XEvent * event,char ** args,int nArgs)571 MoveMlinkTop(MlinkWidget w, XEvent * event, char **args, int nArgs)
572 {
573 MoveMlinkInput(w, event->xbutton.x, TOP,
574 (int) (event->xkey.state & (ShiftMask | LockMask)),
575 (int) (event->xkey.state & ControlMask));
576 }
577
578 static void
MoveMlinkLeft(MlinkWidget w,XEvent * event,char ** args,int nArgs)579 MoveMlinkLeft(MlinkWidget w, XEvent * event, char **args, int nArgs)
580 {
581 MoveMlinkInput(w, event->xbutton.x, LEFT,
582 (int) (event->xkey.state & (ShiftMask | LockMask)),
583 (int) (event->xkey.state & ControlMask));
584 }
585
586 static void
MoveMlinkRight(MlinkWidget w,XEvent * event,char ** args,int nArgs)587 MoveMlinkRight(MlinkWidget w, XEvent * event, char **args, int nArgs)
588 {
589 MoveMlinkInput(w, event->xbutton.x, RIGHT,
590 (int) (event->xkey.state & (ShiftMask | LockMask)),
591 (int) (event->xkey.state & ControlMask));
592 }
593
594 static void
MoveMlinkBottom(MlinkWidget w,XEvent * event,char ** args,int nArgs)595 MoveMlinkBottom(MlinkWidget w, XEvent * event, char **args, int nArgs)
596 {
597 MoveMlinkInput(w, event->xbutton.x, BOTTOM,
598 (int) (event->xkey.state & (ShiftMask | LockMask)),
599 (int) (event->xkey.state & ControlMask));
600 }
601
602 static void
MoveMlinkInput(MlinkWidget w,int x,int direction,int shift,int control)603 MoveMlinkInput(MlinkWidget w, int x, int direction, int shift, int control)
604 {
605 mlinkCallbackStruct cb;
606 int r;
607
608 if (control)
609 ControlMlink(w, direction);
610 else if (shift && (direction == TOP || direction == BOTTOM)) {
611 MoveShiftCb(w, direction);
612 PutMove(direction, 0, 1);
613 } else if (CheckSolved(w)) {
614 MoveNoTiles(w);
615 return;
616 } else if (direction == LEFT || direction == RIGHT) {
617 SlideMlink(w, direction);
618 if (CheckSolved(w)) {
619 cb.reason = MLINK_SOLVED;
620 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
621 }
622 } else {
623 if (!PositionTile(w, x, &r))
624 return;
625 RotateMlink(w, direction, r);
626 if (CheckSolved(w)) {
627 cb.reason = MLINK_SOLVED;
628 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
629 }
630 }
631 }
632
633 Boolean
MoveMlink(MlinkWidget w,int direction,int tile,int shift)634 MoveMlink(MlinkWidget w, int direction, int tile, int shift)
635 {
636 mlinkCallbackStruct cb;
637
638 if (shift && (direction == TOP || direction == BOTTOM)) {
639 MoveShiftCb(w, direction);
640 return True;
641 } else if (CheckSolved(w)) {
642 MoveNoTiles(w);
643 return False;
644 } else if (MoveTilesDir(w, direction, tile)) {
645 cb.reason = MLINK_MOVED;
646 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
647 PutMove(direction, tile, 0);
648 return True;
649 }
650 return False;
651 }
652
653 static void
ControlMlink(MlinkWidget w,int direction)654 ControlMlink(MlinkWidget w, int direction)
655 {
656 mlinkCallbackStruct cb;
657
658 cb.reason = MLINK_IGNORE;
659 switch (direction) {
660 case TOP:
661 if (w->mlink.faces <= MINFACES)
662 return;
663 cb.reason = MLINK_DEC_Y;
664 break;
665 case RIGHT:
666 cb.reason = MLINK_INC_X;
667 break;
668 case BOTTOM:
669 if (w->mlink.faces >= MAXFACES)
670 return;
671 cb.reason = MLINK_INC_Y;
672 break;
673 case LEFT:
674 if (w->mlink.tiles <= MINTILES)
675 return;
676 cb.reason = MLINK_DEC_X;
677 break;
678 default:
679 (void) printf("ControlMlink: direction %d\n", direction);
680 }
681 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
682 }
683
684 static void
RotateMlink(MlinkWidget w,int direction,int tile)685 RotateMlink(MlinkWidget w, int direction, int tile)
686 {
687 mlinkCallbackStruct cb;
688
689 if (CheckSolved(w)) {
690 MoveNoTiles(w);
691 return;
692 }
693 (void) MoveMlink(w, direction, tile, FALSE);
694 if (CheckSolved(w)) {
695 cb.reason = MLINK_SOLVED;
696 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
697 }
698 return;
699 }
700
701 static void
SlideMlink(MlinkWidget w,int direction)702 SlideMlink(MlinkWidget w, int direction)
703 {
704 mlinkCallbackStruct cb;
705
706 if (CheckSolved(w)) {
707 MoveNoTiles(w);
708 return;
709 }
710 if (!MoveMlink(w, direction, 0, FALSE)) {
711 cb.reason = MLINK_BLOCKED;
712 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
713 return;
714 }
715 if (CheckSolved(w)) {
716 cb.reason = MLINK_SOLVED;
717 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
718 }
719 return;
720 }
721
722 static int
SelectTiles(MlinkWidget w,int x,int y,int * i,int * j)723 SelectTiles(MlinkWidget w, int x, int y, int *i, int *j)
724 {
725 *i = (x - w->mlink.delta.x / 2 - w->mlink.puzzleOffset.x) /
726 w->mlink.offset.x;
727 *j = ((y - w->mlink.delta.y / 2 - w->mlink.puzzleOffset.y) %
728 (w->mlink.faces * w->mlink.offset.y + w->mlink.delta.y - 1)) /
729 w->mlink.offset.y;
730 if (*i >= 0 && *j >= 0 &&
731 *i < w->mlink.tiles && *j < w->mlink.faces)
732 return (*i + w->mlink.tiles * *j -
733 w->mlink.spacePosition % w->mlink.tileFaces);
734 return -w->mlink.tileFaces;
735 }
736
737 static void
SelectSlideTiles(MlinkWidget w,int pos)738 SelectSlideTiles(MlinkWidget w, int pos)
739 {
740 mlinkCallbackStruct cb;
741 int n;
742
743 if (pos < 0) {
744 for (n = 1; n <= -pos % w->mlink.tiles; n++) {
745 MoveTiles(w, w->mlink.spacePosition - 1);
746 cb.reason = MLINK_MOVED;
747 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
748 PutMove(RIGHT, 0, 0);
749 }
750 } else if (pos > 0) {
751 for (n = 1; n <= pos % w->mlink.tiles; n++) {
752 MoveTiles(w, w->mlink.spacePosition + 1);
753 cb.reason = MLINK_MOVED;
754 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
755 PutMove(LEFT, 0, 0);
756 }
757 }
758 }
759
760 static void
SetAllColors(MlinkWidget w,Boolean init)761 SetAllColors(MlinkWidget w, Boolean init)
762 {
763 XGCValues values;
764 XtGCMask valueMask;
765 int face;
766
767 valueMask = GCForeground | GCBackground;
768
769 if (w->mlink.reverse) {
770 values.foreground = w->mlink.foreground;
771 values.background = w->core.background_pixel;
772 } else {
773 values.foreground = w->core.background_pixel;
774 values.background = w->mlink.foreground;
775 }
776 if (!init)
777 XtReleaseGC((Widget) w, w->mlink.inverseGC);
778 w->mlink.inverseGC = XtGetGC((Widget) w, valueMask, &values);
779 if (w->mlink.reverse) {
780 values.foreground = w->core.background_pixel;
781 values.background = w->mlink.foreground;
782 } else {
783 values.foreground = w->mlink.foreground;
784 values.background = w->core.background_pixel;
785 }
786 if (!init)
787 XtReleaseGC((Widget) w, w->mlink.puzzleGC);
788 w->mlink.puzzleGC = XtGetGC((Widget) w, valueMask, &values);
789 if (w->mlink.depth < 2 || w->mlink.mono) {
790 if (w->mlink.reverse) {
791 values.background = w->mlink.foreground;
792 values.foreground = w->core.background_pixel;
793 } else {
794 values.foreground = w->mlink.foreground;
795 values.background = w->core.background_pixel;
796 }
797 } else {
798 values.foreground = w->mlink.tileColor;
799 values.background = w->mlink.borderColor;
800 }
801 if (!init)
802 XtReleaseGC((Widget) w, w->mlink.tileGC);
803 w->mlink.tileGC = XtGetGC((Widget) w, valueMask, &values);
804 if (w->mlink.depth < 2 || w->mlink.mono) {
805 if (w->mlink.reverse) {
806 values.foreground = w->mlink.foreground;
807 values.background = w->core.background_pixel;
808 } else {
809 values.background = w->mlink.foreground;
810 values.foreground = w->core.background_pixel;
811 }
812 } else {
813 values.foreground = w->mlink.borderColor;
814 values.background = w->mlink.tileColor;
815 }
816 if (!init)
817 XtReleaseGC((Widget) w, w->mlink.borderGC);
818 w->mlink.borderGC = XtGetGC((Widget) w, valueMask, &values);
819 for (face = 0; face < MAXFACES; face++)
820 GetColor(w, face, init);
821 }
822
823 static void
GetColor(MlinkWidget w,int face,Boolean init)824 GetColor(MlinkWidget w, int face, Boolean init)
825 {
826 XGCValues values;
827 XtGCMask valueMask;
828 XColor colorCell, rgb;
829
830 valueMask = GCForeground | GCBackground;
831 if (w->mlink.reverse) {
832 values.background = w->mlink.foreground;
833 } else {
834 values.background = w->core.background_pixel;
835 }
836 if (w->mlink.depth > 1 && !w->mlink.mono) {
837 if (XAllocNamedColor(XtDisplay(w),
838 DefaultColormap(XtDisplay(w), XtWindow(w)),
839 w->mlink.faceName[face], &colorCell, &rgb)) {
840 values.foreground = w->mlink.faceColor[face] = colorCell.pixel;
841 if (!init)
842 XtReleaseGC((Widget) w, w->mlink.faceGC[face]);
843 w->mlink.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);
844 return;
845 } else {
846 char buf[121];
847
848 (void) sprintf(buf, "Color name \"%s\" is not defined",
849 w->mlink.faceName[face]);
850 XtWarning(buf);
851 }
852 }
853 if (w->mlink.reverse) {
854 values.background = w->core.background_pixel;
855 values.foreground = w->mlink.foreground;
856 } else {
857 values.background = w->mlink.foreground;
858 values.foreground = w->core.background_pixel;
859 }
860 if (!init)
861 XtReleaseGC((Widget) w, w->mlink.faceGC[face]);
862 w->mlink.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);
863 }
864
865 static void
MoveShiftCb(MlinkWidget w,int direction)866 MoveShiftCb(MlinkWidget w, int direction)
867 {
868 mlinkCallbackStruct cb;
869
870 if (w->mlink.middle) {
871 (void) MoveTilesDir(w, direction, 0);
872 (void) MoveTilesDir(w, direction, 1);
873 (void) MoveTilesDir(w, direction, w->mlink.tiles - 1);
874 } else {
875 int r;
876
877 for (r = 0; r < w->mlink.tiles; r++)
878 (void) MoveTilesDir(w, direction, r);
879 }
880 cb.reason = MLINK_CONTROL;
881 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
882 }
883
884 static void
CheckTiles(MlinkWidget w)885 CheckTiles(MlinkWidget w)
886 {
887 char buf[121];
888
889 if (w->mlink.tiles < MINTILES) {
890 (void) sprintf(buf,
891 "Number of Mlink in X direction out of bounds, use %d..MAXINT",
892 MINTILES);
893 XtWarning(buf);
894 w->mlink.tiles = DEFAULTTILES;
895 }
896 if (w->mlink.faces < MINFACES || w->mlink.faces > MAXFACES) {
897 (void) sprintf(buf,
898 "Number of Mlink in Y direction out of bounds, use %d..%d",
899 MINFACES, MAXFACES);
900 XtWarning(buf);
901 w->mlink.faces = DEFAULTFACES;
902 }
903 if (w->mlink.base > 36) {
904 /* 10 numbers + 26 letters (ASCII or EBCDIC) */
905 XtWarning("Base must be less than or equal to 36");
906 w->mlink.base = 10;
907 } else if (w->mlink.base <= 1) { /* Base 1 is rediculous :) */
908 XtWarning("Base must be greater than 1");
909 w->mlink.base = 10;
910 }
911 }
912
913 static void
ResetTiles(MlinkWidget w)914 ResetTiles(MlinkWidget w)
915 {
916 int i;
917
918 w->mlink.tileFaces = w->mlink.tiles * w->mlink.faces;
919 if (w->mlink.tileOfPosition)
920 (void) free((void *) w->mlink.tileOfPosition);
921 if (!(w->mlink.tileOfPosition = (int *)
922 malloc(sizeof (int) * w->mlink.tileFaces)))
923 XtError("Not enough memory, exiting.");
924
925 if (startPosition)
926 (void) free((void *) startPosition);
927 if (!(startPosition = (int *)
928 malloc(sizeof (int) * w->mlink.tileFaces)))
929 XtError("Not enough memory, exiting.");
930
931 w->mlink.spacePosition = w->mlink.tileFaces - 1;
932 w->mlink.tileOfPosition[w->mlink.tileFaces - 1] = 0;
933 for (i = 1; i < w->mlink.tileFaces; i++)
934 w->mlink.tileOfPosition[i - 1] = i;
935 FlushMoves(w);
936 w->mlink.started = False;
937 }
938
939 static void
ResizeTiles(MlinkWidget w)940 ResizeTiles(MlinkWidget w)
941 {
942 w->mlink.digitOffset.x = 3;
943 w->mlink.digitOffset.y = 4;
944 }
945
946 static void
MoveNoTiles(MlinkWidget w)947 MoveNoTiles(MlinkWidget w)
948 {
949 mlinkCallbackStruct cb;
950
951 cb.reason = MLINK_IGNORE;
952 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
953 }
954
955 static Boolean
MoveTilesDir(MlinkWidget w,int direction,int tile)956 MoveTilesDir(MlinkWidget w, int direction, int tile)
957 {
958 switch (direction) {
959 case TOP:
960 RotateTiles(w, tile, TOP);
961 return True;
962 case RIGHT:
963 if (Row(w, w->mlink.spacePosition) > 0) {
964 MoveTiles(w, w->mlink.spacePosition - 1);
965 return True;
966 }
967 break;
968 case BOTTOM:
969 RotateTiles(w, tile, BOTTOM);
970 return True;
971 case LEFT:
972 if (Row(w, w->mlink.spacePosition) < w->mlink.tiles - 1) {
973 MoveTiles(w, w->mlink.spacePosition + 1);
974 return True;
975 }
976 break;
977 default:
978 (void) printf("MoveTilesDir: direction %d\n", direction);
979 }
980 return False;
981 }
982
983 static void
RandomizeTiles(MlinkWidget w)984 RandomizeTiles(MlinkWidget w)
985 {
986 mlinkCallbackStruct cb;
987
988 /* First interchange tiles an even number of times */
989 if (w->mlink.tiles > 1 && w->mlink.faces > 1 && w->mlink.tileFaces > 4) {
990 int pos, count = 0;
991
992 for (pos = 0; pos < w->mlink.tileFaces; pos++) {
993 int randomPos = pos;
994
995 while (randomPos == pos)
996 randomPos = NRAND(w->mlink.tileFaces);
997 count += ExchangeTiles(w, pos, randomPos);
998 }
999 if (count % 2)
1000 if (!ExchangeTiles(w, 0, 1))
1001 if (!ExchangeTiles(w,
1002 w->mlink.tileFaces - 2, w->mlink.tileFaces - 1))
1003 (void) printf("RandomizeTiles: should not get here\n");
1004 DrawAllTiles(w);
1005 }
1006 /* Now move the space around randomly */
1007 if (w->mlink.tiles > 1 || w->mlink.faces > 1) {
1008 int big = w->mlink.tileFaces + NRAND(2);
1009 int lastDirection = 0;
1010 int randomDirection;
1011
1012 cb.reason = MLINK_RESET;
1013 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1014
1015 #ifdef DEBUG
1016 big = 3;
1017 #endif
1018 if (big > 100)
1019 big = 100;
1020 while (big--) {
1021 randomDirection = NRAND(COORD);
1022
1023 #ifdef DEBUG
1024 sleep(1);
1025 #endif
1026
1027 if ((randomDirection + COORD / 2) % COORD != lastDirection ||
1028 (w->mlink.tiles == 1 && w->mlink.faces == 1)) {
1029 if (MoveMlink(w, randomDirection, NRAND(w->mlink.tiles), FALSE))
1030 lastDirection = randomDirection;
1031 else
1032 big++;
1033 }
1034 }
1035 FlushMoves(w);
1036 cb.reason = MLINK_RANDOMIZE;
1037 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1038 }
1039 if (CheckSolved(w)) {
1040 cb.reason = MLINK_SOLVED;
1041 XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1042 }
1043 }
1044
1045 static void
MoveTiles(MlinkWidget w,int from)1046 MoveTiles(MlinkWidget w, int from)
1047 {
1048 int tempTile;
1049
1050 tempTile = w->mlink.tileOfPosition[from];
1051 w->mlink.tileOfPosition[from] =
1052 w->mlink.tileOfPosition[w->mlink.spacePosition];
1053 w->mlink.tileOfPosition[w->mlink.spacePosition] = tempTile;
1054 DrawTile(w, w->mlink.spacePosition, False, False, FALSE);
1055 w->mlink.spacePosition = from;
1056 DrawTile(w, w->mlink.spacePosition, True, True, FALSE);
1057 }
1058
1059 static void
RotateTiles(MlinkWidget w,int c,int direction)1060 RotateTiles(MlinkWidget w, int c, int direction)
1061 {
1062 int newR, k, tempTile, currTile, pos, newPos;
1063 int r = 0, space = -1, loop = FALSE;
1064
1065 if (w->mlink.middle && c > 0 && c < w->mlink.tiles - 1) {
1066 loop = TRUE;
1067 c = 1;
1068 }
1069 do {
1070 currTile = w->mlink.tileOfPosition[r * w->mlink.tiles + c];
1071 for (k = 1; k <= w->mlink.faces; k++) {
1072 newR = (direction == TOP) ? (r + w->mlink.faces - 1) % w->mlink.faces :
1073 (r + 1) % w->mlink.faces;
1074 pos = r * w->mlink.tiles + c;
1075 newPos = newR * w->mlink.tiles + c;
1076 tempTile = w->mlink.tileOfPosition[newPos];
1077 w->mlink.tileOfPosition[newPos] = currTile;
1078 currTile = tempTile;
1079 if (w->mlink.spacePosition == pos) {
1080 DrawTile(w, newPos, True, True, FALSE);
1081 space = newPos;
1082 } else
1083 DrawTile(w, newPos, False, False, FALSE);
1084 r = newR;
1085 }
1086 if (space >= 0)
1087 w->mlink.spacePosition = space;
1088 } while (loop && ++c < w->mlink.tiles - 1);
1089 }
1090
1091 static int
ExchangeTiles(MlinkWidget w,int pos1,int pos2)1092 ExchangeTiles(MlinkWidget w, int pos1, int pos2)
1093 {
1094 int tempTile;
1095
1096 if (w->mlink.tileOfPosition[pos1] <= 0)
1097 return FALSE;
1098 else if (w->mlink.tileOfPosition[pos2] <= 0)
1099 return FALSE;
1100 tempTile = w->mlink.tileOfPosition[pos1];
1101 w->mlink.tileOfPosition[pos1] = w->mlink.tileOfPosition[pos2];
1102 w->mlink.tileOfPosition[pos2] = tempTile;
1103 return TRUE;
1104 }
1105
1106 static void
DrawFrame(MlinkWidget w,GC gc)1107 DrawFrame(MlinkWidget w, GC gc)
1108 {
1109 int sumX, sumY, offsetX, offsetY, r;
1110
1111 sumX = w->mlink.tiles * w->mlink.offset.x + w->mlink.delta.x / 2 + 1;
1112 sumY = w->mlink.offset.y;
1113 offsetX = w->mlink.puzzleOffset.x;
1114 offsetY = w->mlink.puzzleOffset.y;
1115 for (r = 0; r < w->mlink.faces; r++) {
1116 XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1117 offsetX, offsetY, 1, sumY);
1118 XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1119 offsetX, offsetY, sumX, 1);
1120 XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1121 sumX + offsetX, offsetY, 1, sumY + 1);
1122 XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1123 offsetX, sumY + offsetY, sumX + 1, 1);
1124 offsetY += sumY;
1125 }
1126 }
1127
1128 void
DrawAllTiles(MlinkWidget w)1129 DrawAllTiles(MlinkWidget w)
1130 {
1131 int k;
1132
1133 for (k = 0; k < w->mlink.tileFaces; k++)
1134 DrawTile(w, k, (w->mlink.tileOfPosition[k] <= 0),
1135 (w->mlink.tileOfPosition[k] <= 0), FALSE);
1136 }
1137
1138 static void
DrawTile(MlinkWidget w,int pos,Boolean blank,Boolean erase,int offset)1139 DrawTile(MlinkWidget w, int pos, Boolean blank, Boolean erase, int offset)
1140 {
1141 int dx, dy, tile = w->mlink.tileOfPosition[pos];
1142 GC linkGC, tileGC, borderGC;
1143
1144 if (erase) {
1145 tileGC = w->mlink.inverseGC;
1146 borderGC = w->mlink.inverseGC;
1147 } else if (offset) {
1148 tileGC = w->mlink.borderGC;
1149 borderGC = w->mlink.tileGC;
1150 } else {
1151 tileGC = w->mlink.tileGC;
1152 borderGC = w->mlink.borderGC;
1153 }
1154 if ((w->mlink.depth < 2 || w->mlink.mono) && offset) {
1155 linkGC = borderGC;
1156 } else {
1157 int face = ((tile - 1) / w->mlink.tiles + 1) % w->mlink.faces;
1158
1159 linkGC = w->mlink.faceGC[face];
1160 if (w->mlink.depth >= 2 && !w->mlink.mono) {
1161 if (((w->mlink.faceColor[face] == w->mlink.borderColor) && offset) ||
1162 ((w->mlink.faceColor[face] == w->mlink.tileColor) && !offset))
1163 linkGC = borderGC;
1164 }
1165 }
1166 dx = Row(w, pos) * w->mlink.offset.x + w->mlink.delta.x +
1167 w->mlink.puzzleOffset.x + offset;
1168 dy = Column(w, pos) * w->mlink.offset.y + w->mlink.delta.y +
1169 w->mlink.puzzleOffset.y + offset;
1170 XFillRectangle(XtDisplay(w), XtWindow(w), tileGC, dx, dy,
1171 w->mlink.tileSize.x, w->mlink.tileSize.y - 2);
1172 if (!blank) {
1173 int i = 0, offsetX = 0, t = tile;
1174 char buf[121];
1175
1176 buf[0] = '\0';
1177 if (w->mlink.orient || w->mlink.depth < 2 || w->mlink.mono) {
1178 if (w->mlink.depth < 2 || w->mlink.mono) {
1179 if (w->mlink.orient) {
1180 int last;
1181
1182 last = int2String(buf, tile, w->mlink.base, False);
1183 buf[last] = w->mlink.faceName[((tile - 1) / w->mlink.tiles + 1) %
1184 w->mlink.faces][0];
1185 buf[last + 1] = '\0';
1186 t *= w->mlink.base;
1187 } else {
1188 buf[0] = w->mlink.faceName[((tile - 1) /
1189 w->mlink.tiles + 1) % w->mlink.faces][0];
1190 buf[1] = '\0';
1191 t = 1;
1192 }
1193 } else {
1194 (void) int2String(buf, tile, w->mlink.base, True);
1195 }
1196 while (t >= 1) {
1197 t /= w->mlink.base;
1198 offsetX += w->mlink.digitOffset.x;
1199 i++;
1200 }
1201 XDrawString(XtDisplay(w), XtWindow(w), linkGC,
1202 dx + w->mlink.tileSize.x / 2 - offsetX,
1203 dy + w->mlink.tileSize.y / 2 + w->mlink.digitOffset.y, buf, i);
1204 }
1205 }
1206 if (tile != 0 &&
1207 ((tile != w->mlink.tileFaces - 1 && w->mlink.tiles > 1) ||
1208 w->mlink.tiles > 2))
1209 DrawLink(w, linkGC, pos, offset);
1210 XDrawRectangle(XtDisplay(w), XtWindow(w), borderGC, dx, dy,
1211 w->mlink.tileSize.x, w->mlink.tileSize.y - 2);
1212 }
1213
1214 #define MULT 64
1215 #define THICKNESS 7
1216 static void
DrawLink(MlinkWidget w,GC gc,int pos,int offset)1217 DrawLink(MlinkWidget w, GC gc, int pos, int offset)
1218 {
1219 int dx, dy, sizex, sizey, tile = w->mlink.tileOfPosition[pos],
1220 thick;
1221
1222 sizex = w->mlink.offset.x * 3 / 2 - 3;
1223 sizey = w->mlink.offset.y * 3 / 4 - 3;
1224 dx = Row(w, pos) * w->mlink.offset.x + w->mlink.delta.x +
1225 w->mlink.puzzleOffset.x - sizex / 2 + offset;
1226 dy = Column(w, pos) * w->mlink.offset.y + w->mlink.delta.y +
1227 w->mlink.puzzleOffset.y + w->mlink.tileSize.y / 2 - sizey / 2 - 1 + offset;
1228 thick = MIN(sizey, sizex) / 3;
1229 thick = MIN(thick, THICKNESS);
1230 if (thick > 1) {
1231 XSetLineAttributes(XtDisplay(w), gc, thick,
1232 LineSolid, CapNotLast, JoinRound);
1233 if (tile % w->mlink.tiles == 0 || tile == w->mlink.tileFaces - 1)
1234 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1235 sizex, sizey, 89 * MULT, -179 * MULT);
1236 else if (tile % w->mlink.tiles == 1)
1237 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx + w->mlink.tileSize.x - 1, dy,
1238 sizex, sizey, 90 * MULT, 180 * MULT);
1239 else {
1240 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1241 sizex, sizey, 89 * MULT, -124 * MULT);
1242 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1243 sizex, sizey, -90 * MULT, 30 * MULT);
1244 dx += w->mlink.tileSize.x - 1;
1245 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1246 sizex, sizey, 90 * MULT, 30 * MULT);
1247 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1248 sizex, sizey, -90 * MULT, -125 * MULT);
1249 }
1250 XSetLineAttributes(XtDisplay(w), gc, 1, LineSolid, CapNotLast, JoinRound);
1251 }
1252 }
1253
1254 #if 0
1255 #define MULT 64
1256 #define THICKNESS 8
1257 static void
1258 DrawLink(w, gc, pos, offset)
1259 MlinkWidget w;
1260 GC gc;
1261 int pos;
1262 {
1263 int dx, dy, sizex, sizey, i, tile = w->mlink.tileOfPosition[pos];
1264
1265 for (i = 0; i < THICKNESS; i++) {
1266 sizex = w->mlink.offset.x * 3 / 2 - i;
1267 sizey = w->mlink.offset.y * 3 / 4 - i;
1268 dx = Row(w, pos) * w->mlink.offset.x + w->mlink.delta.x +
1269 w->mlink.puzzleOffset.x - sizex / 2 + offset;
1270 dy = Column(w, pos) * w->mlink.offset.y + w->mlink.delta.y +
1271 w->mlink.puzzleOffset.y + w->mlink.tileSize.y / 2 - sizey / 2 + offset;
1272 if (tile % w->mlink.tiles == 0 || tile == w->mlink.tileFaces - 1)
1273 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1274 sizex, sizey, 89 * MULT, -179 * MULT);
1275 else if (tile % w->mlink.tiles == 1)
1276 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx + w->mlink.tileSize.x - 1, dy,
1277 sizex, sizey, 90 * MULT, 180 * MULT);
1278 else {
1279 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1280 sizex, sizey, 89 * MULT, -32 * MULT);
1281 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1282 sizex, sizey, -90 * MULT, 128 * MULT);
1283 dx += w->mlink.tileSize.x - 1;
1284 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1285 sizex, sizey, 90 * MULT, 128 * MULT);
1286 XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
1287 sizex, sizey, -90 * MULT, -33 * MULT);
1288 }
1289 }
1290 }
1291 #endif
1292
1293 static Boolean
PositionTile(MlinkWidget w,int x,int * r)1294 PositionTile(MlinkWidget w, int x, int *r)
1295 {
1296 *r = (x - w->mlink.delta.x / 2 - w->mlink.puzzleOffset.x) / w->mlink.offset.x;
1297 if (*r < 0 || *r >= w->mlink.tiles)
1298 return False;
1299 return True;
1300 }
1301
1302 static int
Row(MlinkWidget w,int pos)1303 Row(MlinkWidget w, int pos)
1304 {
1305 return (pos % w->mlink.tiles);
1306 }
1307
1308 static int
Column(MlinkWidget w,int pos)1309 Column(MlinkWidget w, int pos)
1310 {
1311 return (pos / w->mlink.tiles);
1312 }
1313
1314 static int
int2String(char * buf,int number,int base,Boolean capital)1315 int2String(char *buf, int number, int base, Boolean capital)
1316 {
1317 int digit, mult = base, last, position;
1318 int a, i, j, s, r;
1319
1320 if (capital) {
1321 a = 'A', i = 'I', j = 'J', s = 'S', r = 'R';
1322 } else {
1323 a = 'a', i = 'i', j = 'j', s = 's', r = 'r';
1324 }
1325 if (number < 0) {
1326 (void) printf("number %d < 0\n", number);
1327 return 0;
1328 }
1329 last = 1;
1330 while (number >= mult) {
1331 last++;
1332 mult *= base;
1333 }
1334 for (position = 0; position < last; position++) {
1335 mult /= base;
1336 digit = number / mult;
1337 number -= digit * mult;
1338 buf[position] = digit + '0';
1339 if (buf[position] > '9') { /* ASCII */
1340 buf[position] += (a - '9' - 1);
1341 } else if (buf[position] < '0') { /* EBCDIC */
1342 buf[position] += (a - '9' - 1);
1343 if (buf[position] > i)
1344 buf[position] += (j - i - 1);
1345 if (buf[position] > r)
1346 buf[position] += (s - r - 1);
1347 }
1348 }
1349 buf[last] = '\0';
1350 return last;
1351 }
1352
1353 Boolean
CheckSolved(MlinkWidget w)1354 CheckSolved(MlinkWidget w)
1355 {
1356 int i, j, firstTile, lastTile, midTile;
1357
1358 if (w->mlink.tiles < 2)
1359 return True;
1360 if (w->mlink.orient) {
1361 firstTile = w->mlink.tileOfPosition[0];
1362 if (firstTile != 0 && (firstTile - 1) % w->mlink.tiles == 0) {
1363 for (i = 1; i < w->mlink.tileFaces; i++) {
1364 midTile = w->mlink.tileOfPosition[i];
1365 if (midTile && (midTile - firstTile + w->mlink.tileFaces) %
1366 w->mlink.tileFaces != i)
1367 return False;
1368 }
1369 } else
1370 return False;
1371 } else {
1372 for (i = 0; i < w->mlink.faces; i++) {
1373 firstTile = w->mlink.tileOfPosition[i * w->mlink.tiles];
1374 if (firstTile == 0)
1375 firstTile = w->mlink.tileOfPosition[i * w->mlink.tiles + 1];
1376 lastTile = w->mlink.tileOfPosition[(i + 1) * w->mlink.tiles - 1];
1377 if (lastTile == 0)
1378 lastTile = w->mlink.tileOfPosition[(i + 1) * w->mlink.tiles - 2];
1379 if (firstTile % w->mlink.tiles != 1 ||
1380 (lastTile % w->mlink.tiles != 0 &&
1381 lastTile != w->mlink.tileFaces - 1) ||
1382 (lastTile - 1) / w->mlink.tiles !=
1383 (firstTile - 1) / w->mlink.tiles)
1384 return False;
1385 for (j = 1; j < w->mlink.tiles - 1; j++) {
1386 midTile = w->mlink.tileOfPosition[i * w->mlink.tiles + j];
1387 if ((midTile - 1) / w->mlink.tiles !=
1388 (firstTile - 1) / w->mlink.tiles || midTile == 0)
1389 return False;
1390 }
1391 }
1392 }
1393 return True;
1394 }
1395