1
2 /*-
3 # X-BASED MISSING LINK(tm)
4 #
5 # xmlink.c
6 #
7 ###
8 #
9 # Copyright (c) 1994 - 99 David Albert Bagley, bagleyd@tux.org
10 #
11 # All Rights Reserved
12 #
13 # Permission to use, copy, modify, and distribute this software and
14 # its documentation for any purpose and without fee is hereby granted,
15 # provided that the above copyright notice appear in all copies and
16 # that both that copyright notice and this permission notice appear in
17 # supporting documentation, and that the name of the author not be
18 # used in advertising or publicity pertaining to distribution of the
19 # software without specific, written prior permission.
20 #
21 # This program is distributed in the hope that it will be "playable",
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 #
25 */
26
27 /*-
28 Version 1: 94/08/30 Xt
29 */
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #ifdef VMS
35 #include <unixlib.h>
36 #define getlogin() cuserid(NULL)
37 #else
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #endif
42 #if HAVE_FCNTL_H
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #endif
47 #include <X11/Intrinsic.h>
48 #include <X11/StringDefs.h>
49 #include <X11/Shell.h>
50 #include <X11/cursorfont.h>
51 #include "Mlink.h"
52 #include "mlink.xbm"
53
54 #ifndef SCOREFILE
55 #define SCOREFILE "/usr/games/lib/mlink.scores"
56 #endif
57
58 /* The following are in MlinkP.h also */
59 #define MINFACES 1
60 #define MAXFACES 8
61 #define MINTILES 1
62
63 #define MAXTILES 8
64 #define MAXRECORD 32767
65 #define FILENAMELEN 1024
66 #define USERNAMELEN 128
67 #define NOACCESS "noaccess"
68 #define NOBODY "nobody"
69
70 typedef struct {
71 int score;
72 char name[USERNAMELEN];
73 } GameRecord;
74
75 static void Initialize(Widget w);
76 static void CallbackMlink(Widget w, caddr_t clientData,
77 mlinkCallbackStruct * callData);
78
79 static void PrintRecord(int tiles, int faces, Boolean orient, Boolean middle,
80 char *record);
81 static Boolean HandleSolved(int counter,
82 int tiles, int faces, Boolean orient, Boolean middle);
83 static void PrintState(Widget w,
84 char *prog, int tiles, int faces, Boolean middle, int moves,
85 char *record, char *message);
86 static void InitRecords(void);
87 static void ReadRecords(void);
88 static void WriteRecords(void);
89
90 static Arg arg[2];
91 static GameRecord mlinkRecord[2][2][MAXFACES - MINFACES + 1]
92 [MAXTILES - MINTILES + 1];
93 static int movesDsp = 0;
94 static char progDsp[64] = "xmlink";
95 static char recordDsp[16] = "INF";
96 static char messageDsp[128] = "Welcome";
97 static char titleDsp[256] = "";
98 static char usernameDsp[USERNAMELEN] = "";
99
100 static void
Usage(void)101 Usage(void)
102 {
103 (void) fprintf(stderr, "usage: xmlink\n");
104 (void) fprintf(stderr,
105 "\t[-geometry [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]]\n");
106 (void) fprintf(stderr,
107 "\t[-display [{host}]:[{vs}]] [-[no]mono] [-[no]{reverse|rv}]\n");
108 (void) fprintf(stderr,
109 "\t[-{foreground|fg} {color}] [-{background|bg} {color}]\n");
110 (void) fprintf(stderr,
111 "\t[-{border|bd} {color}] [-tile {color}]\n");
112 (void) fprintf(stderr,
113 "\t[-face{0|1|2|3|4|5|6|7} {color}]\n");
114 (void) fprintf(stderr,
115 "\t[-tiles {int}] [-faces {int}] [-[no]orient] [-[no]middle]\n");
116 (void) fprintf(stderr,
117 "\t[-base {int}] [-username {string}]\n");
118 exit(1);
119 }
120
121 static XrmOptionDescRec options[] =
122 {
123 {"-mono", "*mlink.mono", XrmoptionNoArg, "TRUE"},
124 {"-nomono", "*mlink.mono", XrmoptionNoArg, "FALSE"},
125 {"-rv", "*mlink.reverse", XrmoptionNoArg, "TRUE"},
126 {"-reverse", "*mlink.reverse", XrmoptionNoArg, "TRUE"},
127 {"-norv", "*mlink.reverse", XrmoptionNoArg, "FALSE"},
128 {"-noreverse", "*mlink.reverse", XrmoptionNoArg, "FALSE"},
129 {"-fg", "*mlink.Foreground", XrmoptionSepArg, NULL},
130 {"-foreground", "*mlink.Foreground", XrmoptionSepArg, NULL},
131 {"-bg", "*Background", XrmoptionSepArg, NULL},
132 {"-background", "*Background", XrmoptionSepArg, NULL},
133 {"-bd", "*mlink.tileBorder", XrmoptionSepArg, NULL},
134 {"-border", "*mlink.tileBorder", XrmoptionSepArg, NULL},
135 {"-tile", "*mlink.tileColor", XrmoptionSepArg, NULL},
136 {"-face0", "*mlink.faceColor0", XrmoptionSepArg, NULL},
137 {"-face1", "*mlink.faceColor1", XrmoptionSepArg, NULL},
138 {"-face2", "*mlink.faceColor2", XrmoptionSepArg, NULL},
139 {"-face3", "*mlink.faceColor3", XrmoptionSepArg, NULL},
140 {"-face4", "*mlink.faceColor4", XrmoptionSepArg, NULL},
141 {"-face5", "*mlink.faceColor5", XrmoptionSepArg, NULL},
142 {"-face6", "*mlink.faceColor6", XrmoptionSepArg, NULL},
143 {"-face7", "*mlink.faceColor7", XrmoptionSepArg, NULL},
144 {"-tiles", "*mlink.tiles", XrmoptionSepArg, NULL},
145 {"-faces", "*mlink.faces", XrmoptionSepArg, NULL},
146 {"-orient", "*mlink.orient", XrmoptionNoArg, "TRUE"},
147 {"-noorient", "*mlink.orient", XrmoptionNoArg, "FALSE"},
148 {"-middle", "*mlink.middle", XrmoptionNoArg, "TRUE"},
149 {"-nomiddle", "*mlink.middle", XrmoptionNoArg, "FALSE"},
150 {"-base", "*mlink.base", XrmoptionSepArg, NULL},
151 {"-username", "*mlink.userName", XrmoptionSepArg, NULL}
152 };
153
154 int
main(int argc,char ** argv)155 main(int argc, char **argv)
156 {
157 Widget toplevel, mlink;
158
159 toplevel = XtInitialize(argv[0], "Mlink",
160 options, XtNumber(options), &argc, argv);
161 if (argc != 1)
162 Usage();
163
164 XtSetArg(arg[0],
165 XtNiconPixmap, XCreateBitmapFromData(XtDisplay(toplevel),
166 RootWindowOfScreen(XtScreen(toplevel)),
167 (char *) mlink_bits, mlink_width, mlink_height));
168 XtSetArg(arg[1], XtNinput, True);
169 XtSetValues(toplevel, arg, 2);
170 mlink = XtCreateManagedWidget("mlink",
171 mlinkWidgetClass, toplevel, NULL, 0);
172 XtAddCallback(mlink,
173 XtNselectCallback, (XtCallbackProc) CallbackMlink, (XtPointer) NULL);
174 Initialize(mlink);
175 XtRealizeWidget(toplevel);
176 XGrabButton(XtDisplay(mlink), (unsigned int) AnyButton, AnyModifier,
177 XtWindow(mlink), TRUE,
178 (unsigned int) (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask),
179 GrabModeAsync, GrabModeAsync, XtWindow(mlink),
180 XCreateFontCursor(XtDisplay(mlink), XC_crosshair));
181 XtMainLoop();
182
183 #ifdef VMS
184 return 1;
185 #else
186 return 0;
187 #endif
188 }
189
190 static void
Initialize(Widget w)191 Initialize(Widget w)
192 {
193 int tiles, faces;
194 Boolean orient, middle;
195 String username;
196
197 XtVaSetValues(w,
198 XtNstart, False, NULL);
199 XtVaGetValues(w,
200 XtNuserName, &username,
201 XtNtiles, &tiles,
202 XtNfaces, &faces,
203 XtNorient, &orient,
204 XtNmiddle, &middle, NULL);
205 InitRecords();
206 ReadRecords();
207 (void) strcpy(usernameDsp, username);
208 if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS) ||
209 !strcmp(usernameDsp, NOBODY)) {
210 /* The NOACCESS is not necasary, but it stops people from being cute. */
211 (void) sprintf(usernameDsp, "%s", getlogin());
212 if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS))
213 (void) sprintf(usernameDsp, "%s", NOBODY); /* It really IS nobody */
214 }
215 PrintRecord(tiles, faces, orient, middle, recordDsp);
216 PrintState(XtParent(w), progDsp, tiles, faces, middle, movesDsp,
217 recordDsp, messageDsp);
218 }
219
220 static void
CallbackMlink(Widget w,caddr_t clientData,mlinkCallbackStruct * callData)221 CallbackMlink(Widget w, caddr_t clientData, mlinkCallbackStruct * callData)
222 {
223 int tiles, faces;
224 Boolean orient, middle;
225
226 XtVaGetValues(w,
227 XtNtiles, &tiles,
228 XtNfaces, &faces,
229 XtNorient, &orient,
230 XtNmiddle, &middle, NULL);
231 (void) strcpy(messageDsp, "");
232 switch (callData->reason) {
233 case MLINK_RESTORE:
234 case MLINK_RESET:
235 movesDsp = 0;
236 break;
237 case MLINK_BLOCKED:
238 (void) strcpy(messageDsp, "Blocked");
239 break;
240 case MLINK_SPACE:
241 #if 0
242 /* Too annoying */
243 (void) strcpy(messageDsp, "A space can not slide");
244 #endif
245 break;
246 case MLINK_IGNORE:
247 (void) strcpy(messageDsp, "Randomize to start");
248 break;
249 case MLINK_MOVED:
250 movesDsp++;
251 XtSetArg(arg[0], XtNstart, True);
252 XtSetValues(w, arg, 1);
253 break;
254 case MLINK_CONTROL:
255 return;
256 case MLINK_SOLVED:
257 if (HandleSolved(movesDsp, tiles, faces, orient, middle))
258 (void) sprintf(messageDsp, "Congratulations %s!!", usernameDsp);
259 else
260 (void) strcpy(messageDsp, "Solved!");
261 XtSetArg(arg[0], XtNstart, False);
262 XtSetValues(w, arg, 1);
263 break;
264 case MLINK_RANDOMIZE:
265 movesDsp = 0;
266 XtSetArg(arg[0], XtNstart, False);
267 XtSetValues(w, arg, 1);
268 break;
269 case MLINK_ORIENT:
270 movesDsp = 0;
271 orient = !orient;
272 PrintRecord(tiles, faces, orient, middle, recordDsp);
273 XtSetArg(arg[0], XtNorient, orient);
274 XtSetValues(w, arg, 1);
275 break;
276 case MLINK_MIDDLE:
277 movesDsp = 0;
278 middle = !middle;
279 PrintRecord(tiles, faces, orient, middle, recordDsp);
280 XtSetArg(arg[0], XtNmiddle, middle);
281 XtSetValues(w, arg, 1);
282 break;
283 case MLINK_DEC_X:
284 movesDsp = 0;
285 tiles--;
286 PrintRecord(tiles, faces, orient, middle, recordDsp);
287 XtSetArg(arg[0], XtNtiles, tiles);
288 XtSetValues(w, arg, 1);
289 break;
290 case MLINK_INC_X:
291 movesDsp = 0;
292 tiles++;
293 PrintRecord(tiles, faces, orient, middle, recordDsp);
294 XtSetArg(arg[0], XtNtiles, tiles);
295 XtSetValues(w, arg, 1);
296 break;
297 case MLINK_DEC_Y:
298 movesDsp = 0;
299 faces--;
300 PrintRecord(tiles, faces, orient, middle, recordDsp);
301 XtSetArg(arg[0], XtNfaces, faces);
302 XtSetValues(w, arg, 1);
303 break;
304 case MLINK_INC_Y:
305 movesDsp = 0;
306 faces++;
307 PrintRecord(tiles, faces, orient, middle, recordDsp);
308 XtSetArg(arg[0], XtNfaces, faces);
309 XtSetValues(w, arg, 1);
310 break;
311 case MLINK_COMPUTED:
312 XtSetArg(arg[0], XtNstart, False);
313 XtSetValues(w, arg, 1);
314 break;
315 case MLINK_UNDO:
316 movesDsp--;
317 XtSetArg(arg[0], XtNstart, True);
318 XtSetValues(w, arg, 1);
319 break;
320 }
321 PrintState(XtParent(w), progDsp, tiles, faces, middle, movesDsp,
322 recordDsp, messageDsp);
323 }
324
325 static void
PrintRecord(int tiles,int faces,Boolean orient,Boolean middle,char * record)326 PrintRecord(int tiles, int faces, Boolean orient, Boolean middle, char *record)
327 {
328 int i = tiles - MINTILES, j = faces - MINFACES;
329 int k = (orient) ? 1 : 0, l = (middle) ? 1 : 0;
330
331 if (tiles > MAXTILES)
332 (void) strcpy(record, "NOT RECORDED");
333 else if (mlinkRecord[l][k][j][i].score >= MAXRECORD)
334 (void) sprintf(record, "NEVER %s", NOACCESS);
335 else
336 (void) sprintf(record, "%d %s",
337 mlinkRecord[l][k][j][i].score, mlinkRecord[l][k][j][i].name);
338 }
339
340 static Boolean
HandleSolved(int counter,int tiles,int faces,Boolean orient,Boolean middle)341 HandleSolved(int counter, int tiles, int faces, Boolean orient, Boolean middle)
342 {
343 int i = tiles - MINTILES, j = faces - MINFACES;
344 int k = (orient) ? 1 : 0, l = (middle) ? 1 : 0;
345
346 if (tiles <= MAXTILES && counter < mlinkRecord[l][k][j][i].score) {
347 mlinkRecord[l][k][j][i].score = counter;
348 (void) strcpy(mlinkRecord[l][k][j][i].name, usernameDsp);
349 if (tiles < 4 || faces < 2) {
350 mlinkRecord[!l][k][j][i].score = counter;
351 (void) strcpy(mlinkRecord[!l][k][j][i].name, usernameDsp);
352 }
353 WriteRecords();
354 PrintRecord(tiles, faces, orient, middle, recordDsp);
355 return True;
356 }
357 return False;
358 }
359
360 static void
PrintState(Widget w,char * prog,int tiles,int faces,Boolean middle,int moves,char * record,char * message)361 PrintState(Widget w, char *prog, int tiles, int faces, Boolean middle, int moves, char *record, char *message)
362 {
363 if (middle)
364 (void) sprintf(titleDsp, "%s: %dx%d norm@ (%d/%s) - %s",
365 prog, tiles, faces, moves, record, message);
366 else
367 (void) sprintf(titleDsp, "%s: %dx%d ind@ (%d/%s) - %s",
368 prog, tiles, faces, moves, record, message);
369 XtSetArg(arg[0], XtNtitle, titleDsp);
370 XtSetValues(w, arg, 1);
371 }
372
373 static void
InitRecords(void)374 InitRecords(void)
375 {
376 int i, j, k, l;
377
378 for (l = 0; l < 2; l++)
379 for (k = 0; k < 2; k++)
380 for (j = 0; j < MAXFACES - MINFACES + 1; j++)
381 for (i = 0; i < MAXTILES - MINTILES + 1; i++) {
382 mlinkRecord[l][k][j][i].score = MAXRECORD;
383 (void) strcpy(mlinkRecord[l][k][j][i].name, NOACCESS);
384 }
385 }
386
387 static void
ReadRecords(void)388 ReadRecords(void)
389 {
390 FILE *fp;
391 int i, j, k, l, n;
392 char username[USERNAMELEN];
393
394 if ((fp = fopen(SCOREFILE, "r")) == NULL)
395 (void) sprintf(messageDsp, "Can not open %s, taking defaults.", SCOREFILE);
396 else {
397 for (l = 0; l < 2; l++)
398 for (k = 0; k < 2; k++)
399 for (j = 0; j < MAXFACES - MINFACES + 1; j++)
400 for (i = 0; i < MAXTILES - MINTILES + 1; i++) {
401 (void) fscanf(fp, "%d %s\n", &n, username);
402 if (n <= mlinkRecord[l][k][j][i].score) {
403 mlinkRecord[l][k][j][i].score = n;
404 (void) strcpy(mlinkRecord[l][k][j][i].name, username);
405 }
406 }
407 (void) fclose(fp);
408 }
409 }
410
411 static void
WriteRecords(void)412 WriteRecords(void)
413 {
414 FILE *fp;
415 int i, j, k, l;
416
417 ReadRecords(); /* Maybe its been updated by another */
418 if ((fp = fopen(SCOREFILE, "w")) == NULL)
419 (void) sprintf(messageDsp, "Can not write to %s.", SCOREFILE);
420 else {
421 #if HAVE_FCNTL_H
422 int lfd;
423 char lockfile[FILENAMELEN];
424
425 (void) strcpy(lockfile, SCOREFILE);
426 (void) strcat(lockfile, ".lock");
427 while (((lfd = open(lockfile, O_CREAT | O_EXCL, 0644)) < 0) &&
428 errno == EEXIST)
429 (void) sleep(1);
430 if (lfd < 0) {
431 #if 1
432 (void) fprintf(stderr, "Lock file exists... guessing its an old one.\n");
433 #else
434 (void) fprintf(stderr, "Lock file exists... score not recorded - sorry.\n"
435 );
436 return;
437 #endif
438 }
439 #endif
440 for (l = 0; l < 2; l++) {
441 for (k = 0; k < 2; k++) {
442 for (j = 0; j < MAXFACES - MINFACES + 1; j++) {
443 for (i = 0; i < MAXTILES - MINTILES + 1; i++)
444 (void) fprintf(fp, "%d %s\n",
445 mlinkRecord[l][k][j][i].score, mlinkRecord[l][k][j][i].name);
446 (void) fprintf(fp, "\n");
447 }
448 (void) fprintf(fp, "\n");
449 }
450 (void) fprintf(fp, "\n");
451 }
452 #if HAVE_FCNTL_H
453 (void) close(lfd);
454 (void) unlink(lockfile);
455 #endif
456 (void) fclose(fp);
457 }
458 }
459