1
2 /*
3
4 * Original Author Unknow.
5
6 * 8/10/88 - Ported from X10 to X11R3 by:
7
8 Jonathan Greenblatt (jonnyg@rover.umd.edu)
9
10 * Cleaned up by Dave Lemke (lemke@sun.com)
11
12 * Ported to monocrome by Jonathan Greenblatt (jonnyg@rover.umd.edu)
13
14 * 05/02/1996 Added TrueColor support by TJ Phan (phan@aur.alcatel.com)
15
16 TODO:
17
18 Parameter parsing needs to be redone.
19
20 * Throughout 1991 improved for animation and color and multiple
21 fish types. Broke monocrome in the process.
22 Eric Bina (ebina@ncsa.uiuc.edu)
23
24 * 1992 added extra color remapping control options, as well as ways
25 to let the fish swim on the root window, or an image of the users
26 choice. Eric Bina (ebina@ncsa.uiuc.edu)
27
28 */
29
30 #ifndef hpux
31 #include <sys/time.h>
32 #else
33 #include <time.h>
34 #endif
35
36 #include <stdio.h>
37 #ifdef sgi
38 #define _BSD_SIGNALS
39 #endif
40 #include <signal.h>
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43
44 #include "vroot.h"
45 #include "xfishy.h"
46 #include "bubbles.h"
47 #include "medcut.h"
48
49 /* constants are based on rand(3C) returning an integer between 0 and 32767 */
50
51 #if defined(ultrix) || defined(sun)
52 #define RAND_I_1_16 134217728
53 #define RAND_F_1_8 268435455.875
54 #define RAND_I_1_4 536870911
55 #define RAND_I_1_2 1073741823
56 #define RAND_I_3_4 1610612735
57 #define RAND_F_MAX 2147483647.0
58 #else
59 #if defined(__FreeBSD__) || defined(__OpenBSD__)
60 #include <stdlib.h>
61 #include <unistd.h>
62
63 #define RAND_I_1_16 (RAND_MAX>>4)
64 #define RAND_F_1_8 ((float)(RAND_MAX>>3))
65 #define RAND_I_1_4 (RAND_MAX>>2)
66 #define RAND_I_1_2 (RAND_MAX>>1)
67 #define RAND_I_3_4 ((RAND_MAX>>2)*3)
68 #define RAND_F_MAX ((float)RAND_MAX)
69 #else
70 #define RAND_I_1_16 2048
71 #define RAND_F_1_8 4096.0
72 #define RAND_I_1_4 8096
73 #define RAND_I_1_2 16384
74 #define RAND_I_3_4 24575
75 #define RAND_F_MAX 32767.0
76 #endif
77 #endif
78
79 extern unsigned char *ReadBitmap();
80
81
82 /* externals for pixmap and bimaps from xfishy.h */
83
84
85 /* typedefs for bubble and fish structures, also caddr_t (not used in X.h) */
86 typedef struct {
87 int x,
88 y,
89 s,
90 erased,
91 i;
92 } bubble;
93 typedef struct {
94 int x,
95 y,
96 d,
97 frame,
98 type,
99 i;
100 } fish;
101 typedef unsigned char *caddrt;
102
103
104 /* bubble increment and yes check tables */
105 int binc[] = {0, 64, 56, 48, 40, 32, 24, 16, 8};
106 char *yess[] = {"yes", "Yes", "YES", "on", "On", "ON"};
107
108
109 char *pname, /* program name from argv[0] */
110 sname[64], /* host:display specification */
111 cname[64]; /* colorname specification */
112 char picname[256]; /* name of the background picture file */
113 int *Allocated; /* mark the used colors */
114 int AllocCnt; /* count number of colors used */
115 int mlimit = 0; /* num colors to median cut to. 0 = no limit */
116 int climit = 0; /* limit on color use. 0 = no limit */
117 int DoubleBuf = 0; /* Should we use double buffering */
118 int Overlap = 0; /* Should fish swim over each other */
119 int DoClipping = 0; /* Should clip masks be used. */
120 int blimit = 32, /* bubble limit */
121 flimit = 10, /* fish limit */
122 pmode = 1, /* pop mode, (1 for lower, 0 for raise) */
123 width, /* width of initial window in pixels */
124 height, /* height of initial window in pixels */
125 screen, /* Default screen of this display */
126 Init_B,
127 *cmap; /* Initialize bubbles with random y value */
128 int Pwidth; /* width of background picture */
129 int Pheight; /* height of background picture */
130 int Pcnt; /* number of colors in background picture */
131 unsigned char *Pdata; /* data from background picture */
132 double rate = 0.2, /* update interval in seconds */
133 smooth = 0.2; /* smoothness increment multiplier */
134 bubble *binfo; /* bubble info structures, allocated
135 * dynamically */
136 fish *finfo; /* fish info structures, allocated dynamically */
137 Display *Dpy;
138 XImage *xfishA[NUM_FISH][3]; /* fish pixmaps (1 is left-fish, 2 is
139 * right-fish) */
140 XImage *xfishB[NUM_FISH][3]; /* fish pixmaps (1 is left-fish, 2 is
141 * right-fish) */
142 Pixmap pfishA[NUM_FISH][3];
143 Pixmap pfishB[NUM_FISH][3];
144
145 Pixmap mfishA[NUM_FISH][3]; /* masking pixmaps for fish to use as */
146 Pixmap mfishB[NUM_FISH][3]; /* clipmasks */
147
148 Pixmap PicMap; /* pixmap for background picture */
149
150 Pixmap PixBuf; /* Pixmap buffer for double buffering */
151 Pixmap ClipBuf; /* Clipmask buffer for double buffering */
152
153 Pixmap xbubbles[9]; /* bubbles bitmaps (1 to 8, by size in pixels)*/
154 Window wid; /* aqaurium window */
155 unsigned long white, black,bcolor;
156 Colormap colormap;
157 GC c0gc, cpgc; /* GCs to operateon the Clipmask buffer */
158 GC pgc;
159 GC gc,
160 bgc;
161
162
163 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
164 Output desired error message and exit.
165 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
166 void
msgdie(message)167 msgdie(message)
168 char *message;
169 {
170 fprintf(stderr, "%s: %s\n", pname, message);
171 exit(1);
172 }
173
174
175 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
176 Set up program defaults, get X defaults, parse command line using getopts.
177 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
178 void
parse(argc,argv)179 parse(argc, argv)
180 int argc;
181 char **argv;
182 {
183 int c,
184 i;
185 char *p;
186 extern int optind;
187 extern char *optarg;
188 extern double atof();
189
190 pname = argv[0];
191 strcpy(sname, getenv("DISPLAY"));
192 strcpy(cname, "MediumAquamarine");
193 picname[0] = '\0';
194
195 if ((p = XGetDefault(Dpy, pname, "BubbleLimit")) != NULL)
196 blimit = atoi(p);
197 if ((p = XGetDefault(Dpy, pname, "ColorLimit")) != NULL)
198 climit = atoi(p);
199 if ((p = XGetDefault(Dpy, pname, "MedianCutLimit")) != NULL)
200 mlimit = atoi(p);
201 if ((p = XGetDefault(Dpy, pname, "DoClipping")) != NULL)
202 DoClipping = atoi(p);
203 if ((p = XGetDefault(Dpy, pname, "DoubleBuffer")) != NULL)
204 DoubleBuf = atoi(p);
205 if ((p = XGetDefault(Dpy, pname, "Overlap")) != NULL)
206 Overlap = atoi(p);
207 if ((p = XGetDefault(Dpy, pname, "Color")) != NULL)
208 strcpy(cname, p);
209 if ((p = XGetDefault(Dpy, pname, "Picture")) != NULL)
210 strcpy(picname, p);
211 if ((p = XGetDefault(Dpy, pname, "FishLimit")) != NULL)
212 flimit = atoi(p);
213 if ((p = XGetDefault(Dpy, pname, "IncMult")) != NULL)
214 smooth = atof(p);
215 if ((p = XGetDefault(Dpy, pname, "Rate")) != NULL)
216 rate = atof(p);
217 if ((p = XGetDefault(Dpy, pname, "Secure")) != NULL)
218 for (i = 0; i < 6; i++)
219 if (strcmp(p, yess[i]) == 0)
220 pmode = 0;
221
222 while ((c = getopt(argc, argv, "dDob:C:c:p:m:f:i:r:s")) != EOF) {
223 switch (c) {
224 case 'd':
225 DoClipping = 1;
226 break;
227 case 'D':
228 DoubleBuf = 1;
229 break;
230 case 'o':
231 Overlap = 1;
232 break;
233 case 'b':
234 blimit = atoi(optarg);
235 break;
236 case 'C':
237 climit = atoi(optarg);
238 break;
239 case 'm':
240 mlimit = atoi(optarg);
241 break;
242 case 'c':
243 strcpy(cname, optarg);
244 break;
245 case 'p':
246 strcpy(picname, optarg);
247 break;
248 case 'f':
249 flimit = atoi(optarg);
250 break;
251 case 'i':
252 smooth = atof(optarg);
253 break;
254 case 'r':
255 rate = atof(optarg);
256 break;
257 case 's':
258 pmode = 0;
259 break;
260 case '?':
261 fprintf(stderr, "usage: %s\n", pname);
262 fprintf(stderr, "\t\t[-c color] background color\n");
263 fprintf(stderr, "\t\t[-b limit] number of bubbles (default 32)\n");
264 fprintf(stderr, "\t\t[-f limit] number of fish (default 10)\n");
265 fprintf(stderr, "\t\t[-i mult] move interval (default 0.2)\n");
266 fprintf(stderr, "\t\t[-r rate] move frequency (default 0.2)\n");
267 fprintf(stderr, "\t\t[-m num] median cut to this many colors\n");
268 fprintf(stderr, "\t\t[-C num] use only this many color cells\n");
269 fprintf(stderr, "\t\t[-d] clip fish, swim on root window\n");
270 fprintf(stderr, "\t\t[-p file] fish swim on picture in file\n");
271 fprintf(stderr, "\t\t[host:display]\n");
272 exit(1);
273 }
274 }
275
276 if (optind < argc)
277 strcpy(sname, argv[optind]);
278
279 /*
280 * DoubleBuf is only useful if we are doing clipping on our
281 * own background picture, otherwise turn it off.
282 */
283 if ((DoubleBuf)&&((!DoClipping)||(picname[0] == '\0')))
284 {
285 DoubleBuf = 0;
286 }
287 }
288
289
290 void
erasefish(f,x,y,d)291 erasefish(f, x, y, d)
292 fish *f;
293 int x, y, d;
294 {
295 /*
296 * for something as small as a bubble, it was never worth the
297 * effort of using clipmasks to only turn of the bubble itself, so
298 * we just clear the whole rectangle.
299 */
300 XClearArea(Dpy, wid, x, y, rwidth[f->type], rheight[f->type], False);
301 /*
302 XGCValues gcv;
303
304 if (f->frame)
305 {
306 gcv.foreground = cmap[0];
307 gcv.fill_style = FillTiled;
308 gcv.fill_style = FillSolid;
309 gcv.tile = pfishB[f->type][d];
310 gcv.ts_x_origin = f->x;
311 gcv.ts_y_origin = f->y;
312 gcv.clip_mask = mfishB[f->type][d];
313 gcv.clip_x_origin = x;
314 gcv.clip_y_origin = y;
315 XChangeGC(Dpy, gc, GCForeground | GCClipMask |
316 GCTile | GCTileStipXOrigin | GCTileStipYOrigin |
317 GCFillStyle | GCClipXOrigin | GCClipYOrigin,
318 &gcv);
319 XCopyPlane(Dpy, mfishB[f->type][d], wid, gc, 0, 0,
320 rwidth[f->type], rheight[f->type],
321 x, y, (unsigned long)1);
322 }
323 else
324 {
325 gcv.foreground = cmap[0];
326 gcv.fill_style = FillTiled;
327 gcv.fill_style = FillSolid;
328 gcv.tile = pfishA[f->type][d];
329 gcv.ts_x_origin = f->x;
330 gcv.ts_y_origin = f->y;
331 gcv.clip_mask = mfishA[f->type][d];
332 gcv.clip_x_origin = x;
333 gcv.clip_y_origin = y;
334 XChangeGC(Dpy, gc, GCForeground | GCClipMask |
335 GCTile | GCTileStipXOrigin | GCTileStipYOrigin |
336 GCFillStyle | GCClipXOrigin | GCClipYOrigin,
337 &gcv);
338 XCopyPlane(Dpy, mfishA[f->type][d], wid, gc, 0, 0,
339 rwidth[f->type], rheight[f->type],
340 x, y, (unsigned long)1);
341 }
342 */
343 }
344
345
346 /*
347 * Just places a fish. Normally this is all you need for animation, since
348 * placeing the fish places an entire rectangle which erases most of the old
349 * fish (the rest being cleaned up by the function that called putfish.
350 * If DoClipping is set, this function is only called when placing a new
351 * fish, otherwise newfish is called.
352 */
353 void
putfish(f)354 putfish(f)
355 fish *f;
356 {
357 XGCValues gcv;
358
359 if (f->frame)
360 {
361 /*
362 * If we have a pixmap of the fish use it, otherwise use
363 * the XImage of the fish. In reality we will never use
364 * the XImage since X dies if the pixmap create failed
365 */
366 if (pfishA[f->type][f->d])
367 {
368 /*
369 * Clipping overrides background picture because
370 * the clipping prevents the drawing of any background
371 * anyway.
372 * DoClipping says just print a fish leaving the
373 * background unchanged.
374 * If there is a background picture, we use a buffer
375 * to prevent flashing, we combine the background
376 * picture and the fish, and then copy the
377 * whole rectangle in.
378 * Default is just copy in fish in with a background
379 * color.
380 */
381 if (DoClipping)
382 {
383 gcv.clip_mask = mfishA[f->type][f->d];
384 gcv.clip_x_origin = f->x;
385 gcv.clip_y_origin = f->y;
386 XChangeGC(Dpy, gc,
387 GCClipMask | GCClipXOrigin | GCClipYOrigin,
388 &gcv);
389 XCopyArea(Dpy, pfishA[f->type][f->d], wid, gc,
390 0, 0, rwidth[f->type], rheight[f->type],
391 f->x, f->y);
392 }
393 else if (picname[0] != '\0')
394 {
395 gcv.fill_style = FillTiled;
396 gcv.tile = PicMap;
397 gcv.ts_x_origin = -(f->x);
398 gcv.ts_y_origin = -(f->y);
399 gcv.clip_mask = None;
400 XChangeGC(Dpy, pgc, (GCFillStyle |
401 GCTile | GCTileStipXOrigin |
402 GCTileStipYOrigin | GCClipMask),
403 &gcv);
404 XFillRectangle(Dpy, PixBuf, pgc, 0, 0,
405 rwidth[f->type], rheight[f->type]);
406
407 gcv.clip_mask = mfishA[f->type][f->d];
408 gcv.clip_x_origin = 0;
409 gcv.clip_y_origin = 0;
410 XChangeGC(Dpy, pgc,
411 GCClipMask | GCClipXOrigin | GCClipYOrigin,
412 &gcv);
413 XCopyArea(Dpy, pfishA[f->type][f->d],PixBuf,pgc,
414 0, 0, rwidth[f->type], rheight[f->type],
415 0, 0);
416
417 XCopyArea(Dpy, PixBuf, wid, gc,
418 0, 0, rwidth[f->type], rheight[f->type],
419 f->x, f->y);
420 }
421 else
422 {
423 XCopyArea(Dpy, pfishA[f->type][f->d], wid, gc,
424 0, 0, rwidth[f->type], rheight[f->type],
425 f->x, f->y);
426 }
427 }
428 else
429 {
430 XPutImage(Dpy, wid, gc, xfishA[f->type][f->d], 0, 0,
431 f->x, f->y, rwidth[f->type], rheight[f->type]);
432 }
433 f->frame = 0;
434 }
435 else
436 {
437 /*
438 * same as the above, only for the second frame of animation
439 */
440 if (pfishB[f->type][f->d])
441 {
442 if (DoClipping)
443 {
444 gcv.clip_mask = mfishB[f->type][f->d];
445 gcv.clip_x_origin = f->x;
446 gcv.clip_y_origin = f->y;
447 XChangeGC(Dpy, gc,
448 GCClipMask | GCClipXOrigin | GCClipYOrigin,
449 &gcv);
450 XCopyArea(Dpy, pfishB[f->type][f->d], wid, gc,
451 0, 0, rwidth[f->type], rheight[f->type],
452 f->x, f->y);
453 }
454 else if (picname[0] != '\0')
455 {
456 gcv.fill_style = FillTiled;
457 gcv.tile = PicMap;
458 gcv.ts_x_origin = -(f->x);
459 gcv.ts_y_origin = -(f->y);
460 gcv.clip_mask = None;
461 XChangeGC(Dpy, pgc, (GCFillStyle |
462 GCTile | GCTileStipXOrigin |
463 GCTileStipYOrigin | GCClipMask),
464 &gcv);
465 XFillRectangle(Dpy, PixBuf, pgc, 0, 0,
466 rwidth[f->type], rheight[f->type]);
467
468 gcv.clip_mask = mfishB[f->type][f->d];
469 gcv.clip_x_origin = 0;
470 gcv.clip_y_origin = 0;
471 XChangeGC(Dpy, pgc,
472 GCClipMask | GCClipXOrigin | GCClipYOrigin,
473 &gcv);
474 XCopyArea(Dpy, pfishB[f->type][f->d],PixBuf,pgc,
475 0, 0, rwidth[f->type], rheight[f->type],
476 0, 0);
477
478 XCopyArea(Dpy, PixBuf, wid, gc,
479 0, 0, rwidth[f->type], rheight[f->type],
480 f->x, f->y);
481 }
482 else
483 {
484 XCopyArea(Dpy, pfishB[f->type][f->d], wid, gc,
485 0, 0, rwidth[f->type], rheight[f->type],
486 f->x, f->y);
487 }
488 }
489 else
490 {
491 XPutImage(Dpy, wid, gc, xfishB[f->type][f->d], 0, 0,
492 f->x, f->y, rwidth[f->type], rheight[f->type]);
493 }
494 f->frame = 1;
495 }
496 }
497
498
499 /*
500 * This function can only be called if DoClipping is True. It is used to
501 * move a clipmasked fish. First the area under the fish is cleared,
502 * and then the new fish is masked in.
503 * The parameters x, y, amd d are from the old fish that is being
504 * erased before the new fish is drawn.
505 */
506 void
movefish(f,x,y,d)507 movefish(f, x, y, d)
508 fish *f;
509 int x, y, d;
510 {
511 XGCValues gcv;
512 int bx, by, bw, bh;
513
514 /*
515 * If we are going to double buffer, we need to find the bounding
516 * rectangle of the overlap of the bounding rectangles of the old
517 * and the new fish.
518 */
519 if (DoubleBuf)
520 {
521 if (x < f->x)
522 {
523 bx = x;
524 bw = f->x - x + rwidth[f->type];
525 }
526 else
527 {
528 bx = f->x;
529 bw = x - f->x + rwidth[f->type];
530 }
531 if (y < f->y)
532 {
533 by = y;
534 bh = f->y - y +rheight[f->type];
535 }
536 else
537 {
538 by = f->y;
539 bh = y - f->y +rheight[f->type];
540 }
541 }
542
543 if (f->frame)
544 {
545 /*
546 * If there is a pixmap use it.
547 * This branchis always taken since right now, if the pixmap
548 * allocation failed, the program dies.
549 */
550 if (pfishA[f->type][f->d])
551 {
552 /*
553 * A pointless if, you now only come here if
554 * DoClipping is set, I've just been too lazy to
555 * clean up my code.
556 */
557 if (DoClipping)
558 {
559 /*
560 * Set up the masked gc for when we eventually
561 * draw the fish. Origin is different for
562 * whether we are drawing into the buffer
563 * or into the window
564 */
565 gcv.clip_mask = mfishA[f->type][f->d];
566 if (DoubleBuf)
567 {
568 gcv.clip_x_origin = f->x - bx;
569 gcv.clip_y_origin = f->y - by;
570 }
571 else
572 {
573 gcv.clip_x_origin = f->x;
574 gcv.clip_y_origin = f->y;
575 }
576 XChangeGC(Dpy, gc,
577 GCClipMask | GCClipXOrigin | GCClipYOrigin,
578 &gcv);
579
580 /*
581 * If we have a background picture we want to
582 * clear to that background, otherwise we just
583 * do an XCleararea, and let the root restore
584 * the background.
585 */
586 if (picname[0] != '\0')
587 {
588 gcv.fill_style = FillTiled;
589 gcv.tile = PicMap;
590 gcv.clip_mask = mfishB[f->type][d];
591 if (DoubleBuf)
592 {
593 gcv.ts_x_origin = 0 - bx;
594 gcv.ts_y_origin = 0 - by;
595 gcv.clip_x_origin = x - bx;
596 gcv.clip_y_origin = y - by;
597 }
598 else
599 {
600 gcv.ts_x_origin = 0;
601 gcv.ts_y_origin = 0;
602 gcv.clip_x_origin = x;
603 gcv.clip_y_origin = y;
604 }
605 XChangeGC(Dpy, pgc, (GCFillStyle |
606 GCTile | GCTileStipXOrigin |
607 GCTileStipYOrigin | GCClipMask |
608 GCClipXOrigin | GCClipYOrigin),
609 &gcv);
610
611 /*
612 * if bouble buffering we clear the buffer
613 * to the backgound picture, and then
614 * shape the clip buffer to the shape of
615 * the fish being erased.
616 */
617 if (DoubleBuf)
618 {
619 XFillRectangle(Dpy, PixBuf, pgc,
620 x - bx, y - by,
621 rwidth[f->type],
622 rheight[f->type]);
623 XFillRectangle(Dpy, ClipBuf, c0gc, 0, 0,
624 500, 500);
625 XCopyArea(Dpy, mfishB[f->type][d],
626 ClipBuf, cpgc, 0, 0,
627 rwidth[f->type],
628 rheight[f->type],
629 x - bx, y - by);
630 }
631 else
632 {
633 XFillRectangle(Dpy, wid, pgc, x, y,
634 rwidth[f->type],
635 rheight[f->type]);
636 }
637 }
638 else
639 {
640 XClearArea(Dpy, wid, x, y,
641 rwidth[f->type], rheight[f->type], 0);
642 }
643 }
644 /*
645 * Now we just copy in the new fish with a clipmasked gc.
646 * But if we doublebuffered, we copy the new fish into
647 * the buffer, combine the new fishes clipmask in, and
648 * then mask the whole lot from the buffer to the window.
649 */
650 if (DoubleBuf)
651 {
652 XCopyArea(Dpy, pfishA[f->type][f->d], PixBuf, gc, 0, 0,
653 rwidth[f->type], rheight[f->type],
654 f->x - bx, f->y - by);
655 XCopyArea(Dpy, mfishA[f->type][f->d], ClipBuf, cpgc,
656 0, 0, rwidth[f->type], rheight[f->type],
657 f->x - bx, f->y - by);
658 gcv.clip_mask = ClipBuf;
659 gcv.clip_x_origin = bx;
660 gcv.clip_y_origin = by;
661 XChangeGC(Dpy, gc,
662 GCClipMask | GCClipXOrigin | GCClipYOrigin,
663 &gcv);
664 XCopyArea(Dpy, PixBuf, wid, gc, 0, 0, bw, bh, bx, by);
665 }
666 else
667 {
668 XCopyArea(Dpy, pfishA[f->type][f->d], wid, gc, 0, 0,
669 rwidth[f->type], rheight[f->type], f->x, f->y);
670 }
671 }
672 else
673 {
674 if (DoClipping)
675 {
676 if (picname[0] != '\0')
677 {
678 gcv.fill_style = FillTiled;
679 gcv.tile = PicMap;
680 gcv.ts_x_origin = 0;
681 gcv.ts_y_origin = 0;
682 gcv.clip_mask = mfishB[f->type][d];
683 gcv.clip_x_origin = x;
684 gcv.clip_y_origin = y;
685 XChangeGC(Dpy, pgc, (GCFillStyle |
686 GCTile | GCTileStipXOrigin |
687 GCTileStipYOrigin | GCClipMask |
688 GCClipXOrigin | GCClipYOrigin),
689 &gcv);
690 XFillRectangle(Dpy, wid, pgc, x, y,
691 rwidth[f->type],
692 rheight[f->type]);
693 }
694 else
695 {
696 XClearArea(Dpy, wid, x, y,
697 rwidth[f->type], rheight[f->type], 0);
698 }
699 }
700 XPutImage(Dpy, wid, gc, xfishA[f->type][f->d], 0, 0,
701 f->x, f->y, rwidth[f->type], rheight[f->type]);
702 }
703 f->frame = 0;
704 }
705 else
706 {
707 /*
708 * Same as above, only for the second frame of animation.
709 */
710 if (pfishB[f->type][f->d])
711 {
712 if (DoClipping)
713 {
714 gcv.clip_mask = mfishB[f->type][f->d];
715 if (DoubleBuf)
716 {
717 gcv.clip_x_origin = f->x - bx;
718 gcv.clip_y_origin = f->y - by;
719 }
720 else
721 {
722 gcv.clip_x_origin = f->x;
723 gcv.clip_y_origin = f->y;
724 }
725 XChangeGC(Dpy, gc,
726 GCClipMask | GCClipXOrigin | GCClipYOrigin,
727 &gcv);
728 if (picname[0] != '\0')
729 {
730 gcv.fill_style = FillTiled;
731 gcv.tile = PicMap;
732 gcv.clip_mask = mfishA[f->type][d];
733 if (DoubleBuf)
734 {
735 gcv.ts_x_origin = 0 - bx;
736 gcv.ts_y_origin = 0 - by;
737 gcv.clip_x_origin = x - bx;
738 gcv.clip_y_origin = y - by;
739 }
740 else
741 {
742 gcv.ts_x_origin = 0;
743 gcv.ts_y_origin = 0;
744 gcv.clip_x_origin = x;
745 gcv.clip_y_origin = y;
746 }
747 XChangeGC(Dpy, pgc, (GCFillStyle |
748 GCTile | GCTileStipXOrigin |
749 GCTileStipYOrigin | GCClipMask |
750 GCClipXOrigin | GCClipYOrigin),
751 &gcv);
752 if (DoubleBuf)
753 {
754 XFillRectangle(Dpy, PixBuf, pgc,
755 x - bx, y - by,
756 rwidth[f->type],
757 rheight[f->type]);
758 XFillRectangle(Dpy, ClipBuf, c0gc, 0, 0,
759 500, 500);
760 XCopyArea(Dpy, mfishA[f->type][d],
761 ClipBuf, cpgc, 0, 0,
762 rwidth[f->type],
763 rheight[f->type],
764 x - bx, y - by);
765 }
766 else
767 {
768 XFillRectangle(Dpy, wid, pgc, x, y,
769 rwidth[f->type],
770 rheight[f->type]);
771 }
772 }
773 else
774 {
775 XClearArea(Dpy, wid, x, y,
776 rwidth[f->type], rheight[f->type], 0);
777 }
778 }
779 if (DoubleBuf)
780 {
781 XCopyArea(Dpy, pfishB[f->type][f->d], PixBuf, gc, 0, 0,
782 rwidth[f->type], rheight[f->type],
783 f->x - bx, f->y - by);
784 XCopyArea(Dpy, mfishB[f->type][f->d], ClipBuf, cpgc,
785 0, 0, rwidth[f->type], rheight[f->type],
786 f->x - bx, f->y - by);
787 gcv.clip_mask = ClipBuf;
788 gcv.clip_x_origin = bx;
789 gcv.clip_y_origin = by;
790 XChangeGC(Dpy, gc,
791 GCClipMask | GCClipXOrigin | GCClipYOrigin,
792 &gcv);
793 XCopyArea(Dpy, PixBuf, wid, gc, 0, 0, bw, bh, bx, by);
794 }
795 else
796 {
797 XCopyArea(Dpy, pfishB[f->type][f->d], wid, gc, 0, 0,
798 rwidth[f->type], rheight[f->type], f->x, f->y);
799 }
800 }
801 else
802 {
803 if (DoClipping)
804 {
805 if (picname[0] != '\0')
806 {
807 gcv.fill_style = FillTiled;
808 gcv.tile = PicMap;
809 gcv.ts_x_origin = 0;
810 gcv.ts_y_origin = 0;
811 gcv.clip_mask = mfishA[f->type][d];
812 gcv.clip_x_origin = x;
813 gcv.clip_y_origin = y;
814 XChangeGC(Dpy, pgc, (GCFillStyle |
815 GCTile | GCTileStipXOrigin |
816 GCTileStipYOrigin | GCClipMask |
817 GCClipXOrigin | GCClipYOrigin),
818 &gcv);
819 XFillRectangle(Dpy, wid, pgc, x, y,
820 rwidth[f->type],
821 rheight[f->type]);
822 }
823 else
824 {
825 XClearArea(Dpy, wid, x, y,
826 rwidth[f->type], rheight[f->type], 0);
827 }
828 }
829 XPutImage(Dpy, wid, gc, xfishB[f->type][f->d], 0, 0,
830 f->x, f->y, rwidth[f->type], rheight[f->type]);
831 }
832 f->frame = 1;
833 }
834 }
835
836
erasebubble(b,s)837 erasebubble(b, s)
838 bubble *b;
839 int s;
840 {
841 XClearArea(Dpy, wid, b->x, b->y, s, s, 0);
842 }
843
844
putbubble(b,s,c)845 putbubble(b, s, c)
846 bubble *b;
847 int s;
848 unsigned long c;
849 {
850 XGCValues gcv;
851
852 gcv.foreground = c;
853 gcv.clip_mask = xbubbles[s];
854 gcv.clip_x_origin = b->x;
855 gcv.clip_y_origin = b->y;
856 XChangeGC(Dpy, bgc, GCForeground | GCClipMask | GCClipXOrigin |
857 GCClipYOrigin, &gcv);
858 XFillRectangle(Dpy, wid, bgc, b->x, b->y, s, s);
859 }
860
861
862 /*
863 * Find the closest color by allocating it, or picking an already allocated
864 * color
865 */
866 Visual (*visual_info) = NULL;
867 int r_mask, g_mask, b_mask;
868 int r_shift=0, g_shift=0, b_shift=0;
869 int r_bits=0, g_bits=0, b_bits=0;
870 void
FindColor(Dpy,colormap,colr)871 FindColor(Dpy, colormap, colr)
872 Display *Dpy;
873 Colormap colormap;
874 XColor *colr;
875 {
876 int i, match;
877 double rd, gd, bd, dist, mindist;
878 int cindx;
879 XColor def_colrs[256];
880 int NumCells;
881
882 if( visual_info == NULL && DefaultDepth(Dpy, DefaultScreen(Dpy)) >= 16 )
883 {
884 visual_info = DefaultVisual(Dpy, DefaultScreen(Dpy));
885 r_mask = visual_info->red_mask;
886 while( !(r_mask & 1) )
887 {
888 r_mask >>= 1;
889 r_shift++;
890 }
891 while( r_mask & 1 )
892 {
893 r_mask >>= 1;
894 r_bits++;
895 }
896
897 g_mask = visual_info->green_mask;
898 while( !(g_mask & 1) )
899 {
900 g_mask >>= 1;
901 g_shift++;
902 }
903 while( g_mask & 1 )
904 {
905 g_mask >>= 1;
906 g_bits++;
907 }
908
909 b_mask = visual_info->blue_mask;
910 while( !(b_mask &1) )
911 {
912 b_mask >>= 1;
913 b_shift++;
914 }
915 while( b_mask & 1 )
916 {
917 b_mask >>= 1;
918 b_bits++;
919 }
920 }
921
922 if( DefaultDepth(Dpy, DefaultScreen(Dpy)) > 8 )
923 {
924 colr->red >>= 16 - r_bits;
925 colr->green >>= 16 - g_bits;
926 colr->blue >>= 16 - b_bits;
927
928 colr->pixel = ((colr->red << r_shift) & visual_info->red_mask) |
929 ((colr->green << g_shift) & visual_info->green_mask) |
930 ((colr->blue << b_shift) & visual_info->blue_mask);
931 return;
932 }
933
934 if (AllocCnt < climit)
935 {
936 match = XAllocColor(Dpy, colormap, colr);
937 }
938 else
939 {
940 match = 0;
941 }
942 if (match == 0)
943 {
944 NumCells = DisplayCells(Dpy, DefaultScreen(Dpy));
945 for (i=0; i<NumCells; i++)
946 {
947 def_colrs[i].pixel = i;
948 }
949 XQueryColors(Dpy, colormap, def_colrs, NumCells);
950 mindist = 65536.0 * 65536.0;
951 cindx = colr->pixel;
952 for (i=0; i<NumCells; i++)
953 {
954 rd = (def_colrs[i].red - colr->red) / 256.0;
955 gd = (def_colrs[i].green - colr->green) / 256.0;
956 bd = (def_colrs[i].blue - colr->blue) / 256.0;
957 dist = (rd * rd * rd * rd) +
958 (gd * gd * gd * gd) +
959 (bd * bd * bd * bd);
960 if (dist < mindist)
961 {
962 mindist = dist;
963 cindx = def_colrs[i].pixel;
964 }
965 }
966 colr->pixel = cindx;
967 colr->red = def_colrs[cindx].red;
968 colr->green = def_colrs[cindx].green;
969 colr->blue = def_colrs[cindx].blue;
970 }
971 else
972 {
973 if (Allocated[colr->pixel] == 0)
974 {
975 Allocated[colr->pixel] = 1;
976 AllocCnt++;
977 }
978 }
979 }
980
981
982 int
ColorUsage(data,width,height,colrs)983 ColorUsage(data, width, height, colrs)
984 unsigned char *data;
985 int width, height;
986 struct colr_data *colrs;
987 {
988 int mapping[256];
989 int i, size;
990 int cnt, indx;
991 unsigned char *ptr;
992 struct colr_data newcol[256];
993
994 for (i=0; i<256; i++)
995 {
996 mapping[i] = -1;
997 }
998
999 size = width * height;
1000 cnt = 0;
1001 ptr = data;
1002 for (i=0; i<size; i++)
1003 {
1004 indx = (int)*ptr;
1005 if (mapping[indx] == -1)
1006 {
1007 mapping[indx] = cnt;
1008 newcol[cnt].red = colrs[indx].red;
1009 newcol[cnt].green = colrs[indx].green;
1010 newcol[cnt].blue = colrs[indx].blue;
1011 cnt++;
1012 }
1013 ptr++;
1014 }
1015
1016 ptr = data;
1017 for (i=0; i<size; i++)
1018 {
1019 indx = (int)*ptr;
1020 *ptr = (unsigned char)mapping[indx];
1021 ptr++;
1022 }
1023
1024 for (i=0; i<cnt; i++)
1025 {
1026 colrs[i].red = newcol[i].red;
1027 colrs[i].green = newcol[i].green;
1028 colrs[i].blue = newcol[i].blue;
1029 }
1030 for (i=cnt; i<256; i++)
1031 {
1032 colrs[i].red = 0;
1033 colrs[i].green = 0;
1034 colrs[i].blue = 0;
1035 }
1036
1037 return(cnt);
1038 }
1039
1040
1041 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1042 Initialize colormap for background color and required fish colors.
1043 The fish colors are coded in xfishy.h as a trio of tables.
1044 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1045 void
init_colormap()1046 init_colormap()
1047 {
1048 FILE *fp;
1049 int i, j, cnt;
1050 int NumCells;
1051 XColor hdef, edef;
1052 struct colr_data *cdp;
1053 struct colr_data colrs[256];
1054
1055 colormap = XDefaultColormap(Dpy, screen);
1056
1057 if (colormap == NULL)
1058 {
1059 return;
1060 }
1061
1062 NumCells = DisplayCells(Dpy, DefaultScreen(Dpy));
1063 Allocated = (int *)malloc(NumCells * sizeof(int));
1064 for (i=0; i<NumCells; i++)
1065 {
1066 Allocated[i] = 0;
1067 }
1068 AllocCnt = 0;
1069 if ((climit <= 0)||(climit > NumCells))
1070 {
1071 climit = NumCells;
1072 }
1073
1074 Pcnt = 0;
1075 if (picname[0] != '\0')
1076 {
1077 fp = fopen(picname, "r");
1078 if (fp == NULL)
1079 {
1080 fprintf(stderr, "Cannot open picture %s for reading\n",
1081 picname);
1082 }
1083 else
1084 {
1085 Pdata = ReadBitmap(fp, &Pwidth, &Pheight, colrs);
1086 fclose(fp);
1087 Pcnt = ColorUsage(Pdata, Pwidth, Pheight, colrs);
1088 }
1089 }
1090
1091 cnt = 0;
1092 cnt += Pcnt;
1093 for (i=0; i<NUM_FISH; i++)
1094 {
1095 cnt += rcolors[i];
1096 }
1097 cmap = (int *) malloc((cnt + 1) * sizeof(int));
1098
1099 XLookupColor(Dpy, colormap, cname, &hdef, &edef);
1100 hdef.flags = DoRed|DoGreen|DoBlue;
1101 FindColor(Dpy, colormap, &hdef);
1102 cmap[0] = hdef.pixel;
1103
1104 if (mlimit > 0)
1105 {
1106 MedianInit();
1107 }
1108
1109 if (mlimit > 0)
1110 {
1111 if (picname[0] != '\0')
1112 {
1113 MedianCount(Pdata, Pwidth, Pheight, colrs);
1114 }
1115 for (j=0; j<NUM_FISH; j++)
1116 {
1117 int *rp, *gp, *bp;
1118
1119 cdp = (struct colr_data *)malloc(rcolors[j] *
1120 sizeof(struct colr_data));
1121 rp = rreds[j];
1122 gp = rgreens[j];
1123 bp = rblues[j];
1124 for (i = 0; i < rcolors[j]; i++)
1125 {
1126 cdp[i].red = *rp++;
1127 cdp[i].green = *gp++;
1128 cdp[i].blue = *bp++;
1129 }
1130 MedianCount((unsigned char *)xfishRasterA[j],
1131 (int)rwidth[j], (int)rheight[j], cdp);
1132 free((char *)cdp);
1133 }
1134 MedianSplit(mlimit);
1135 }
1136
1137 cnt = 1;
1138 if (picname[0] != '\0')
1139 {
1140 for (i = 0; i < Pcnt; i++)
1141 {
1142 int rv, gv, bv;
1143
1144 rv = colrs[i].red;
1145 gv = colrs[i].green;
1146 bv = colrs[i].blue;
1147
1148 if (mlimit > 0)
1149 {
1150 ConvertColor(&rv, &gv, &bv);
1151 }
1152
1153 hdef.red = rv;
1154 hdef.green = gv;
1155 hdef.blue = bv;
1156 hdef.flags = DoRed|DoGreen|DoBlue;
1157 FindColor(Dpy, colormap, &hdef);
1158 cmap[cnt] = hdef.pixel;
1159 cnt++;
1160 }
1161 }
1162 for (j=0; j<NUM_FISH; j++)
1163 {
1164 int *rp, *gp, *bp;
1165
1166 rp = rreds[j];
1167 gp = rgreens[j];
1168 bp = rblues[j];
1169 for (i = 0; i < rcolors[j]; i++)
1170 {
1171 int rv, gv, bv;
1172
1173 rv = *rp++;
1174 gv = *gp++;
1175 bv = *bp++;
1176
1177 if (mlimit > 0)
1178 {
1179 ConvertColor(&rv, &gv, &bv);
1180 }
1181
1182 hdef.red = rv;
1183 hdef.green = gv;
1184 hdef.blue = bv;
1185 hdef.flags = DoRed|DoGreen|DoBlue;
1186 FindColor(Dpy, colormap, &hdef);
1187 cmap[cnt] = hdef.pixel;
1188 if (i == rback[j])
1189 {
1190 cmap[cnt] = cmap[0];
1191 }
1192 cnt++;
1193 }
1194 }
1195
1196 bcolor = white;
1197 }
1198
1199
1200 /*
1201 * Make am image of appropriate depth for display from image data.
1202 */
1203 XImage *
MakeImage(data,width,height)1204 MakeImage(data, width, height)
1205 unsigned char *data;
1206 int width, height;
1207 {
1208 int linepad, shiftnum;
1209 int shiftstart, shiftstop, shiftinc;
1210 int bytesperline;
1211 int depth, temp;
1212 int w, h;
1213 XImage *newimage;
1214 unsigned char *bit_data, *bitp, *datap;
1215
1216 depth = DefaultDepth(Dpy, DefaultScreen(Dpy));
1217 if ((depth != 1)&&(depth != 2)&&(depth != 4)&&(depth != 8))
1218 {
1219 fprintf(stderr, "Don't know how to format image for display of depth %d\n", depth);
1220 exit(1);
1221 }
1222
1223 if (BitmapBitOrder(Dpy) == LSBFirst)
1224 {
1225 shiftstart = 0;
1226 shiftstop = 8;
1227 shiftinc = depth;
1228 }
1229 else
1230 {
1231 shiftstart = 8 - depth;
1232 shiftstop = -depth;
1233 shiftinc = -depth;
1234 }
1235 linepad = 8 - (width % 8);
1236 bit_data = (unsigned char *)malloc(((width + linepad) * height) + 1);
1237 bitp = bit_data;
1238 datap = data;
1239 *bitp = 0;
1240 shiftnum = shiftstart;
1241 for (h=0; h<height; h++)
1242 {
1243 for (w=0; w<width; w++)
1244 {
1245 temp = *datap++ << shiftnum;
1246 *bitp = *bitp | temp;
1247 shiftnum = shiftnum + shiftinc;
1248 if (shiftnum == shiftstop)
1249 {
1250 shiftnum = shiftstart;
1251 bitp++;
1252 *bitp = 0;
1253 }
1254 }
1255 for (w=0; w<linepad; w++)
1256 {
1257 shiftnum = shiftnum + shiftinc;
1258 if (shiftnum == shiftstop)
1259 {
1260 shiftnum = shiftstart;
1261 bitp++;
1262 *bitp = 0;
1263 }
1264 }
1265 }
1266
1267 bytesperline = (width * depth / 8 + linepad);
1268 newimage = XCreateImage(Dpy, DefaultVisual(Dpy, screen), depth,
1269 ZPixmap, 0, (char *)bit_data,
1270 (width + linepad), height, 8, bytesperline);
1271
1272 return(newimage);
1273 }
1274
1275
1276
1277 static unsigned char bits[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
1278
1279 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1280 Calibrate the pixmaps and bimaps. The right-fish data is coded in xfishy.h,
1281 this is transformed to create the left-fish. The eight bubbles are coded
1282 in bubbles.h as a two dimensional array.
1283 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1284 void
init_pixmap()1285 init_pixmap()
1286 {
1287 register caddrt p, q, x1A, x1B, x2A, x2B;
1288 unsigned char *data;
1289 register int i, j, k;
1290 int cnt, wcnt;
1291
1292 cnt = 1;
1293 cnt += Pcnt;
1294 for (k=0; k<NUM_FISH; k++)
1295 {
1296
1297 /*
1298 * The clipmasks must be created before we remap colors.
1299 * otherwise an opaque color might get remapped to a
1300 * transparent color.
1301 */
1302 if ((DoClipping)||(picname[0] != '\0'))
1303 {
1304 data = (unsigned char *) malloc((rwidth[k]+7) / 8 * rheight[k]);
1305
1306 p = (caddrt) xfishRasterA[k];
1307 q = data;
1308 wcnt = 0;
1309 for (i = 0; i < ((rwidth[k]+7) / 8 * rheight[k]); i++)
1310 {
1311 unsigned char bt = 0x00;
1312 for (j = 0; j < 8; j++)
1313 {
1314 if (*p != rback[k])
1315 {
1316 bt = bt | bits[j];
1317 }
1318 wcnt++;
1319 p++;
1320 if (wcnt == rwidth[k])
1321 {
1322 wcnt = 0;
1323 break;
1324 }
1325 }
1326 *q++ = bt;
1327 }
1328 mfishA[k][2] = XCreateBitmapFromData(Dpy, wid,
1329 (char *)data, rwidth[k], rheight[k]);
1330
1331 p = (caddrt) xfishRasterA[k];
1332 p = p + rwidth[k] - 1;
1333 q = data;
1334 wcnt = 0;
1335 for (i = 0; i < ((rwidth[k]+7) / 8 * rheight[k]); i++)
1336 {
1337 unsigned char bt = 0x00;
1338 for (j = 0; j < 8; j++)
1339 {
1340 if (*p != rback[k])
1341 {
1342 bt = bt | bits[j];
1343 }
1344 wcnt++;
1345 p--;
1346 if (wcnt == rwidth[k])
1347 {
1348 wcnt = 0;
1349 p = p + (2 * rwidth[k]);
1350 break;
1351 }
1352 }
1353 *q++ = bt;
1354 }
1355 mfishA[k][1] = XCreateBitmapFromData(Dpy, wid,
1356 (char *)data, rwidth[k], rheight[k]);
1357
1358 p = (caddrt) xfishRasterB[k];
1359 q = data;
1360 wcnt = 0;
1361 for (i = 0; i < ((rwidth[k]+7) / 8 * rheight[k]); i++)
1362 {
1363 unsigned char bt = 0x00;
1364 for (j = 0; j < 8; j++)
1365 {
1366 if (*p != rback[k])
1367 {
1368 bt = bt | bits[j];
1369 }
1370 wcnt++;
1371 p++;
1372 if (wcnt == rwidth[k])
1373 {
1374 wcnt = 0;
1375 break;
1376 }
1377 }
1378 *q++ = bt;
1379 }
1380 mfishB[k][2] = XCreateBitmapFromData(Dpy, wid,
1381 (char *)data, rwidth[k], rheight[k]);
1382
1383 p = (caddrt) xfishRasterB[k];
1384 p = p + rwidth[k] - 1;
1385 q = data;
1386 wcnt = 0;
1387 for (i = 0; i < ((rwidth[k]+7) / 8 * rheight[k]); i++)
1388 {
1389 unsigned char bt = 0x00;
1390 for (j = 0; j < 8; j++)
1391 {
1392 if (*p != rback[k])
1393 {
1394 bt = bt | bits[j];
1395 }
1396 wcnt++;
1397 p--;
1398 if (wcnt == rwidth[k])
1399 {
1400 wcnt = 0;
1401 p = p + (2 * rwidth[k]);
1402 break;
1403 }
1404 }
1405 *q++ = bt;
1406 }
1407 mfishB[k][1] = XCreateBitmapFromData(Dpy, wid,
1408 (char *)data, rwidth[k], rheight[k]);
1409
1410 free((char *)data);
1411 }
1412
1413 if( DisplayPlanes(Dpy, screen) < 8 )
1414 {
1415
1416 j = rwidth[k] * rheight[k];
1417 x1A = (caddrt) malloc(rwidth[k] * rheight[k]);
1418 p = (caddrt) xfishRasterA[k];
1419
1420
1421 q = x1A;
1422 for (i = 0; i < j; i++)
1423 {
1424 *q = cmap[cnt + (int)(*p)];
1425 p++;
1426 q++;
1427 }
1428
1429 x1B = (caddrt) malloc(rwidth[k] * rheight[k]);
1430 p = (caddrt) xfishRasterB[k];
1431 q = x1B;
1432 for (i = 0; i < j; i++)
1433 {
1434 *q = cmap[cnt + (int)(*p)];
1435 p++;
1436 q++;
1437 }
1438
1439 x2A = (caddrt) malloc(rwidth[k] * rheight[k]);
1440 for (i = 0; i < rheight[k]; i++)
1441 {
1442 p = x1A + i * rwidth[k];
1443 q = x2A + (i + 1) * rwidth[k] - 1;
1444 for (j = 0; j < rwidth[k]; j++)
1445 {
1446 *q-- = *p++;
1447 }
1448 }
1449
1450 x2B = (caddrt) malloc(rwidth[k] * rheight[k]);
1451 for (i = 0; i < rheight[k]; i++)
1452 {
1453 p = x1B + i * rwidth[k];
1454 q = x2B + (i + 1) * rwidth[k] - 1;
1455 for (j = 0; j < rwidth[k]; j++)
1456 {
1457 *q-- = *p++;
1458 }
1459 }
1460
1461 xfishA[k][2] = MakeImage(x1A, rwidth[k], rheight[k]);
1462 xfishA[k][1] = MakeImage(x2A, rwidth[k], rheight[k]);
1463 xfishB[k][2] = MakeImage(x1B, rwidth[k], rheight[k]);
1464 xfishB[k][1] = MakeImage(x2B, rwidth[k], rheight[k]);
1465
1466 free((char *)x1A);
1467 free((char *)x2A);
1468 free((char *)x1B);
1469 free((char *)x2B);
1470
1471 }
1472 else
1473 {
1474 i = DisplayPlanes(Dpy, screen);
1475
1476 xfishA[k][2] = XGetImage(Dpy, DefaultRootWindow(Dpy), 0, 0, rwidth[k], rheight[k], 0, ZPixmap);
1477
1478 p = (caddrt) xfishRasterA[k];
1479
1480 for (j = 0; j < rheight[k]; j++)
1481 {
1482 for( i = 0; i < rwidth[k]; i++ )
1483 {
1484 XPutPixel(xfishA[k][2], i, j, cmap[cnt + (int)(*p)]);
1485 p++;
1486 }
1487 }
1488
1489 xfishB[k][2] = XGetImage(Dpy, DefaultRootWindow(Dpy), 0, 0, rwidth[k], rheight[k], 0, ZPixmap);
1490
1491 p = (caddrt) xfishRasterB[k];
1492
1493 for (j = 0; j < rheight[k]; j++)
1494 {
1495 for( i = 0; i < rwidth[k]; i++ )
1496 {
1497 XPutPixel(xfishB[k][2], i, j, cmap[cnt + (int)(*p)]);
1498 p++;
1499 }
1500 }
1501
1502 xfishA[k][1] = XGetImage(Dpy, DefaultRootWindow(Dpy), 0, 0, rwidth[k], rheight[k], 0, ZPixmap);
1503
1504 for (j = 0; j < rheight[k]; j++)
1505 {
1506 for( i = 0; i < rwidth[k]; i++ )
1507 {
1508 XPutPixel(xfishA[k][1], i, j,
1509 XGetPixel(xfishA[k][2], rwidth[k] - i -1, j));
1510 }
1511 }
1512
1513 xfishB[k][1] = XGetImage(Dpy, DefaultRootWindow(Dpy), 0, 0, rwidth[k], rheight[k], 0, ZPixmap);
1514
1515 for (j = 0; j < rheight[k]; j++)
1516 {
1517 for( i = 0; i < rwidth[k]; i++ )
1518 {
1519 XPutPixel(xfishB[k][1], i, j,
1520 XGetPixel(xfishB[k][2], rwidth[k] - i - 1, j));
1521 }
1522 }
1523
1524 }
1525
1526
1527 i = DisplayPlanes(Dpy, screen);
1528
1529 pfishA[k][1] = XCreatePixmap(Dpy, wid,
1530 rwidth[k], rheight[k], i);
1531 pfishA[k][2] = XCreatePixmap(Dpy, wid,
1532 rwidth[k], rheight[k], i);
1533 pfishB[k][1] = XCreatePixmap(Dpy, wid,
1534 rwidth[k], rheight[k], i);
1535 pfishB[k][2] = XCreatePixmap(Dpy, wid,
1536 rwidth[k], rheight[k], i);
1537
1538 if (pfishA[k][1])
1539 {
1540 XPutImage(Dpy, pfishA[k][1], gc, xfishA[k][1], 0, 0,
1541 0, 0, rwidth[k], rheight[k]);
1542 }
1543 if (pfishA[k][2])
1544 {
1545 XPutImage(Dpy, pfishA[k][2], gc, xfishA[k][2], 0, 0,
1546 0, 0, rwidth[k], rheight[k]);
1547 }
1548 if (pfishB[k][1])
1549 {
1550 XPutImage(Dpy, pfishB[k][1], gc, xfishB[k][1], 0, 0,
1551 0, 0, rwidth[k], rheight[k]);
1552 }
1553 if (pfishB[k][2])
1554 {
1555 XPutImage(Dpy, pfishB[k][2], gc, xfishB[k][2], 0, 0,
1556 0, 0, rwidth[k], rheight[k]);
1557 }
1558
1559 cnt += rcolors[k];
1560 }
1561
1562 for (i = 1; i <= 8; i++)
1563 {
1564 xbubbles[i] = XCreateBitmapFromData(Dpy, wid,
1565 (char *)xbBits[i], i, i);
1566 }
1567 }
1568
1569
1570 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1571 Toggle secure mode on receipt of signal
1572 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1573 #ifdef sgi
1574 int
1575 #else
1576 void
1577 #endif
toggle_secure()1578 toggle_secure()
1579 {
1580 pmode = !pmode;
1581 if (pmode)
1582 XLowerWindow(Dpy, wid);
1583 else
1584 XRaiseWindow(Dpy, wid);
1585 XFlush(Dpy);
1586 #ifdef sgi
1587 return(1);
1588 #endif
1589 }
1590
1591
1592 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1593 Initialize signal so that SIGUSR1 causes secure mode to toggle.
1594 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1595 void
init_signals()1596 init_signals()
1597 {
1598 }
1599
1600
1601 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1602 Variety of initialization calls, including getting the window up and running.
1603 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1604 void
initialize()1605 initialize()
1606 {
1607 XWindowAttributes winfo;
1608 XSetWindowAttributes attr;
1609 XGCValues vals;
1610 XSizeHints xsh;
1611 XImage *pimage;
1612 int i, size, cnt;
1613 unsigned char *ndata;
1614 unsigned char *ptr1, *ptr2;
1615
1616 XGetWindowAttributes(Dpy, DefaultRootWindow(Dpy), &winfo);
1617 width = winfo.width;
1618 height = winfo.height;
1619
1620 init_colormap();
1621
1622 if (picname[0] != '\0')
1623 {
1624 size = Pwidth * Pheight;
1625 ndata = (unsigned char *)malloc(size);
1626 ptr1 = Pdata;
1627 ptr2 = ndata;
1628 cnt = 1;
1629 for (i = 0; i < size; i++)
1630 {
1631 *ptr2 = cmap[cnt + (int)(*ptr1)];
1632 ptr1++;
1633 ptr2++;
1634 }
1635 pimage = MakeImage(ndata, Pwidth, Pheight);
1636 free((char *)ndata);
1637 i = DisplayPlanes(Dpy, screen);
1638 PicMap = XCreatePixmap(Dpy, DefaultRootWindow(Dpy), Pwidth, Pheight, i);
1639 if (PicMap == NULL)
1640 {
1641 fprintf(stderr, "Cannot create background pixmap\n");
1642 picname[0] = '\0';
1643 }
1644 }
1645
1646 if ((DoubleBuf)||(picname[0] != '\0'))
1647 {
1648 i = DisplayPlanes(Dpy, screen);
1649 PixBuf = XCreatePixmap(Dpy, DefaultRootWindow(Dpy), 500, 500, i);
1650 ClipBuf = XCreatePixmap(Dpy, DefaultRootWindow(Dpy), 500, 500, 1);
1651 c0gc = XCreateGC(Dpy, ClipBuf, 0, NULL);
1652 XSetForeground(Dpy, c0gc, (unsigned long)0);
1653 XSetFunction(Dpy, c0gc, GXcopy);
1654 cpgc = XCreateGC(Dpy, ClipBuf, 0, NULL);
1655 XSetFunction(Dpy, cpgc, GXor);
1656 }
1657
1658
1659 attr.override_redirect = True;
1660 attr.background_pixel = cmap[0];
1661
1662 if ((!DoClipping)||(picname[0] != '\0'))
1663 {
1664 wid = XCreateWindow(Dpy, DefaultRootWindow(Dpy),
1665 1, 1, width - 2, height - 2, 0,
1666 CopyFromParent, CopyFromParent, CopyFromParent,
1667 CWBackPixel | CWOverrideRedirect, &attr);
1668
1669 if (!wid)
1670 msgdie("XCreateWindow failed");
1671 }
1672 else
1673 {
1674 wid = DefaultRootWindow(Dpy);
1675 XClearArea(Dpy, wid, 0, 0, 0, 0, False);
1676 }
1677
1678 vals.foreground = vals.background = cmap[0];
1679 vals.graphics_exposures = False;
1680 gc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures,
1681 &vals);
1682 pgc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures,
1683 &vals);
1684 bgc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures,
1685 &vals);
1686
1687 for (i=0; i<NUM_FISH; i++)
1688 {
1689 pfishA[i][0] = 0;
1690 pfishA[i][1] = 0;
1691 pfishA[i][2] = 0;
1692 pfishB[i][0] = 0;
1693 pfishB[i][1] = 0;
1694 pfishB[i][2] = 0;
1695
1696 mfishA[i][0] = 0;
1697 mfishA[i][1] = 0;
1698 mfishA[i][2] = 0;
1699 mfishB[i][0] = 0;
1700 mfishB[i][1] = 0;
1701 mfishB[i][2] = 0;
1702 }
1703
1704 init_pixmap();
1705 init_signals();
1706
1707 if ((!DoClipping)||(picname[0] != '\0'))
1708 {
1709 XStoreName(Dpy, wid, pname);
1710
1711 xsh.flags = USSize | USPosition | PPosition | PSize;
1712 xsh.x = xsh.y = 0;
1713 xsh.width = width;
1714 xsh.height = height;
1715 XSetNormalHints(Dpy, wid, &xsh);
1716
1717 XMapWindow(Dpy, wid);
1718 if (picname[0] != '\0')
1719 {
1720 XPutImage(Dpy, PicMap, gc, pimage, 0, 0, 0, 0, Pwidth, Pheight);
1721 XSetWindowBackgroundPixmap(Dpy, wid, PicMap);
1722 }
1723 }
1724
1725 binfo = (bubble *) malloc(blimit * sizeof(bubble));
1726 finfo = (fish *) malloc(flimit * sizeof(fish));
1727 }
1728
1729
1730 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1731 Create a new bubble. Placement along the x axis is random, as is the size of
1732 the bubble. Increment value is determined by speed.
1733 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1734 void
new_bubble(b0)1735 new_bubble(b0)
1736 bubble *b0;
1737 {
1738 register int s;
1739 register bubble *b = b0;
1740
1741 b->x = width * (rand() / RAND_F_MAX);
1742 if (Init_B)
1743 b->y = (height / 16) * (rand() / RAND_I_1_16 + 1) - 1;
1744 else
1745 b->y = height - 1;
1746 b->s = s = 1.0 + rand() / RAND_F_1_8;
1747 if ((b->i = smooth * height / (float) binc[s]) == 0)
1748 b->i = 1;
1749 b->erased = 0;
1750 putbubble(b, s, bcolor);
1751 }
1752
1753 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1754 Erase old bubbles, move and draw new bubbles. Random left-right factor
1755 can move bubble one size-unit in either direction.
1756 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1757 void
step_bubbles()1758 step_bubbles()
1759 {
1760 register int i, j, s;
1761 register bubble *b;
1762
1763 for (i = 0; i < blimit; i++)
1764 {
1765 b = &binfo[i];
1766 s = b->s;
1767 /* clear */
1768 if ((b->y > 0)&&(b->erased == 0))
1769 {
1770 if ((DoClipping)||(picname[0] != '\0'))
1771 {
1772 erasebubble(b, s);
1773 }
1774 else
1775 {
1776 putbubble(b, s, cmap[0]);
1777 }
1778 }
1779 if ((b->y -= b->i) > 0)
1780 {
1781 j = rand();
1782 if (j < RAND_I_1_4)
1783 {
1784 b->x -= s;
1785 }
1786 else if (j > RAND_I_3_4)
1787 {
1788 b->x += s;
1789 }
1790 putbubble(b, s, bcolor);
1791 }
1792 else
1793 {
1794 if (rand() < RAND_I_1_4)
1795 {
1796 new_bubble(b);
1797 }
1798 }
1799 b->erased = 0;
1800 }
1801 }
1802
1803
1804 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1805 Fish over bubble collision detection. The specified fish is checked against
1806 all bubbles for overlap. This way we don't try and erase bubbles that are
1807 already gone.
1808 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1809 void
collide_bubbles(f0,ofx,ofy)1810 collide_bubbles(f0, ofx, ofy)
1811 fish *f0;
1812 int ofx, ofy;
1813 {
1814 int i, delta;
1815 register fish *f = f0;
1816 register bubble *b;
1817
1818 for (i = 0; i < blimit; i++)
1819 {
1820 b = &binfo[i];
1821 delta = b->x - ofx;
1822 if ((delta >= 0)&&(delta <= (rwidth[f->type] - b->s)))
1823 {
1824 delta = b->y - ofy;
1825 if ((delta >= 0)&&(delta <= (rheight[f->type] - b->s)))
1826 {
1827 b->erased = 1;
1828 continue;
1829 }
1830 }
1831 delta = b->x - f->x;
1832 if ((delta >= 0)&&(delta <= (rwidth[f->type] - b->s)))
1833 {
1834 delta = b->y - f->y;
1835 if ((delta >= 0)&&(delta <= (rheight[f->type] - b->s)))
1836 {
1837 b->erased = 1;
1838 continue;
1839 }
1840 }
1841 }
1842 }
1843
1844 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1845 Fish collision detection. The specified fish is checked against all other
1846 fish for overlap. The xt parameter specifies a x axis multiplier for overlap.
1847 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1848 int
collide_fish(f0,xt)1849 collide_fish(f0, xt)
1850 fish *f0;
1851 int xt;
1852 {
1853 int i, j;
1854 register fish *f = f0;
1855
1856 if (Overlap)
1857 {
1858 return(0);
1859 }
1860
1861 for (i = 0; i < flimit; i++)
1862 {
1863 if (&finfo[i] != f)
1864 {
1865 j = finfo[i].y - f->y;
1866 if ((j > -rheight[finfo[i].type]) &&
1867 (j < rheight[f->type]))
1868 {
1869 j = finfo[i].x - f->x;
1870 if ((j > -xt * rwidth[finfo[i].type]) &&
1871 (j < xt * rwidth[f->type]))
1872 {
1873 return (1);
1874 }
1875 }
1876 }
1877 }
1878 return (0);
1879 }
1880
1881
1882 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1883 Create a new fish. Placement along the y axis is random, as is the side
1884 >from which the fish appears. Direction is determined from side. Increment
1885 is also random.
1886 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1887 void
new_fish(f0)1888 new_fish(f0)
1889 fish *f0;
1890 {
1891 int i, collide;
1892 fish *f = f0;
1893
1894 f->type = rand() % NUM_FISH;
1895 for (i = 0, collide = 1; (i < 16) && (collide); i++)
1896 {
1897 f->y = (height - rheight[f->type]) * (rand() / RAND_F_MAX);
1898 if ((f->i = smooth * width /
1899 (8.0 * (1.0 + rand() / RAND_F_1_8))) == 0)
1900 {
1901 f->i = 1;
1902 }
1903 if (rand() < RAND_I_1_2)
1904 {
1905 f->d = 1;
1906 f->x = width;
1907 }
1908 else
1909 {
1910 f->d = 2;
1911 f->x = -rwidth[f->type];
1912 }
1913 collide = collide_fish(f, 2);
1914 }
1915
1916 if (!collide)
1917 {
1918 putfish(f);
1919 }
1920 else
1921 {
1922 f->d = 0;
1923 }
1924
1925 f->frame = 0;
1926 }
1927
1928
1929 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1930 Move all the fish. Clearing old fish is accomplished by masking only the
1931 exposed areas of the old fish. Random up-down factor can move fish 1/4 a
1932 fish height in either direction, if no collisions are caused.
1933 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1934 void
move_fish()1935 move_fish()
1936 {
1937 register int i, j, x, y, ofx, ofy, ofd, done;
1938 register fish *f;
1939
1940 for (i = 0; i < flimit; i++)
1941 {
1942 f = &finfo[i];
1943 if (f->d)
1944 {
1945 ofx = f->x;
1946 ofy = f->y;
1947 ofd = f->d;
1948
1949 if (f->d == 1)
1950 {
1951 done = ((f->x -= f->i) < -rwidth[f->type]);
1952 x = f->x + rwidth[f->type];
1953 }
1954 else if (f->d == 2)
1955 {
1956 done = ((f->x += f->i) > width);
1957 x = f->x - f->i;
1958 }
1959
1960 if (!collide_fish(f, 1))
1961 {
1962 if (!done)
1963 {
1964 j = rand();
1965 if (j < RAND_I_1_4)
1966 {
1967 y = f->i / 4;
1968 }
1969 else if (j > RAND_I_3_4)
1970 {
1971 y = f->i / -4;
1972 }
1973 else
1974 {
1975 y = 0;
1976 }
1977
1978 if (y)
1979 {
1980 f->y += y;
1981 if (collide_fish(f, 1))
1982 {
1983 f->y -= y;
1984 y = 0;
1985 }
1986 else
1987 {
1988 if (y > 0)
1989 {
1990 j = f->y - y;
1991 }
1992 else
1993 {
1994 j = f->y +
1995 rheight
1996 [f->type];
1997 y *= -1;
1998 }
1999 }
2000 }
2001 if (DoClipping)
2002 {
2003 movefish(f, ofx, ofy, ofd);
2004 }
2005 else
2006 {
2007 putfish(f);
2008 XClearArea(Dpy, wid, x, ofy,
2009 f->i, rheight[f->type], 0);
2010 if (y)
2011 {
2012 XClearArea(Dpy, wid, ofx, j,
2013 rwidth[f->type], y, 0);
2014 }
2015 }
2016
2017 }
2018 else
2019 {
2020 XClearArea(Dpy, wid, x, f->y,
2021 f->i, rheight[f->type], 0);
2022 new_fish(f);
2023 }
2024 }
2025 else
2026 {
2027 if ((f->d = 3 - f->d) == 1)
2028 {
2029 f->x = f->x - 2 * f->i;
2030 x = f->x + rwidth[f->type];
2031 }
2032 else
2033 {
2034 f->x = f->x + 2 * f->i;
2035 x = f->x - f->i;
2036 }
2037 if (DoClipping)
2038 {
2039 movefish(f, ofx, ofy, ofd);
2040 }
2041 else
2042 {
2043 putfish(f);
2044 XClearArea(Dpy, wid, x, f->y,
2045 f->i, rheight[f->type], 0);
2046 }
2047 }
2048 if ((!DoClipping)||(picname[0] == '\0'))
2049 {
2050 collide_bubbles(f, ofx, ofy);
2051 }
2052 }
2053 else
2054 {
2055 new_fish(f);
2056 }
2057 }
2058 }
2059
2060
2061 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2062 Higher-resolution sleep
2063 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2064 void
high_res_sleep(seconds)2065 high_res_sleep(seconds)
2066 double seconds;
2067 {
2068 #ifndef __FreeBSD__
2069 int fds = 0;
2070 #endif
2071 struct timeval timeout;
2072 #ifdef __FreeBSD__
2073 fd_set fds;
2074 FD_ZERO(&fds);
2075 #endif
2076
2077 timeout.tv_sec = seconds;
2078 timeout.tv_usec = (seconds - timeout.tv_sec) * 1000000.0;
2079 select(0, &fds, &fds, &fds, &timeout);
2080 }
2081
2082
2083 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2084 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2085 void
main(argc,argv)2086 main(argc, argv)
2087 int argc;
2088 char **argv;
2089 {
2090 int i;
2091 XEvent ev;
2092
2093 if ((Dpy = XOpenDisplay("")) == 0)
2094 msgdie("XOpenDisplay failed");
2095 screen = DefaultScreen(Dpy);
2096
2097 white = WhitePixel(Dpy, screen);
2098 black = BlackPixel(Dpy, screen);
2099 parse(argc, argv);
2100 initialize();
2101
2102 srand((unsigned) getpid());
2103
2104 Init_B = 1;
2105 for (i = 0; i < blimit; i++)
2106 new_bubble(&binfo[i]);
2107 for (i = 0; i < flimit; i++)
2108 {
2109 finfo[i].x = 0;
2110 finfo[i].y = 0;
2111 finfo[i].type = 0;
2112 }
2113 for (i = 0; i < flimit; i++)
2114 new_fish(&finfo[i]);
2115 if (pmode)
2116 XLowerWindow(Dpy, wid);
2117 else
2118 XRaiseWindow(Dpy, wid);
2119 XFlush(Dpy);
2120
2121 Init_B = 0;
2122
2123 for (;;) {
2124 if (XPending(Dpy))
2125 XNextEvent(Dpy, &ev);
2126
2127 high_res_sleep(rate);
2128
2129 move_fish();
2130
2131 step_bubbles();
2132
2133 if (pmode)
2134 XLowerWindow(Dpy, wid);
2135 else
2136 XRaiseWindow(Dpy, wid);
2137 }
2138 }
2139
2140