4 * Original Author Unknow.
6 * 8/10/88 - Ported from X10 to X11R3 by:
8 Jonathan Greenblatt (jonnyg@rover.umd.edu)
10 * Cleaned up by Dave Lemke (lemke@sun.com)
12 * Ported to monocrome by Jonathan Greenblatt (jonnyg@rover.umd.edu)
14 * 05/02/1996 Added TrueColor support by TJ Phan (phan@aur.alcatel.com)
18 Parameter parsing needs to be redone.
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)
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)
30 #include <sys/types.h>
46 #include <X11/Xutil.h>
53 /* constants are based on rand(3C) returning an integer between 0 and 32767 */
55 #if defined(ultrix) || defined(sun) || defined(linux)
56 #define RAND_I_1_16 134217728
57 #define RAND_F_1_8 268435455.875
58 #define RAND_I_1_4 536870911
59 #define RAND_I_1_2 1073741823
60 #define RAND_I_3_4 1610612735
61 #define RAND_F_MAX 2147483647.0
62 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
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)
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
79 extern unsigned char *ReadBitmap();
82 /* externals for pixmap and bimaps from xfishy.h */
85 /* typedefs for bubble and fish structures, also caddr_t (not used in X.h) */
87 int x
, y
, s
, erased
, i
;
90 int x
, y
, d
, frame
, type
, i
;
92 typedef unsigned char *caddrt
;
95 /* bubble increment and yes check tables */
96 int binc
[] = { 0, 64, 56, 48, 40, 32, 24, 16, 8 };
97 char *yess
[] = { "yes", "Yes", "YES", "on", "On", "ON" };
100 char *pname
, /* program name from argv[0] */
101 sname
[64], /* host:display specification */
102 cname
[64]; /* colorname specification */
103 char picname
[256]; /* name of the background picture file */
104 int *Allocated
; /* mark the used colors */
105 int AllocCnt
; /* count number of colors used */
106 int mlimit
= 0; /* num colors to median cut to. 0 = no limit */
107 int climit
= 0; /* limit on color use. 0 = no limit */
108 int DoubleBuf
= 0; /* Should we use double buffering */
109 int Overlap
= 0; /* Should fish swim over each other */
110 int DoClipping
= 0; /* Should clip masks be used. */
111 int blimit
= 32, /* bubble limit */
112 flimit
= 10, /* fish limit */
113 pmode
= 1, /* pop mode, (1 for lower, 0 for raise) */
114 width
, /* width of initial window in pixels */
115 height
, /* height of initial window in pixels */
116 screen
, /* Default screen of this display */
117 Init_B
, *cmap
; /* Initialize bubbles with random y value */
118 int Pwidth
; /* width of background picture */
119 int Pheight
; /* height of background picture */
120 int Pcnt
; /* number of colors in background picture */
121 unsigned char *Pdata
; /* data from background picture */
122 double rate
= 0.2, /* update interval in seconds */
123 smooth
= 0.2; /* smoothness increment multiplier */
124 bubble
*binfo
; /* bubble info structures, allocated
126 fish
*finfo
; /* fish info structures, allocated dynamically */
129 XImage
*xfishA
[NUM_FISH
][3]; /* fish pixmaps (1 is left-fish, 2 is
131 XImage
*xfishB
[NUM_FISH
][3]; /* fish pixmaps (1 is left-fish, 2 is
133 Pixmap pfishA
[NUM_FISH
][3];
134 Pixmap pfishB
[NUM_FISH
][3];
136 Pixmap mfishA
[NUM_FISH
][3]; /* masking pixmaps for fish to use as */
137 Pixmap mfishB
[NUM_FISH
][3]; /* clipmasks */
139 Pixmap PicMap
; /* pixmap for background picture */
141 Pixmap PixBuf
; /* Pixmap buffer for double buffering */
142 Pixmap ClipBuf
; /* Clipmask buffer for double buffering */
144 Pixmap xbubbles
[9]; /* bubbles bitmaps (1 to 8, by size in pixels) */
145 Window wid
; /* aqaurium window */
146 unsigned long white
, black
, bcolor
;
148 GC c0gc
, cpgc
; /* GCs to operateon the Clipmask buffer */
153 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
154 Output desired error message and exit.
155 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
160 fprintf(stderr
, "%s: %s\n", pname
, message
);
165 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
166 Set up program defaults, get X defaults, parse command line using getopts.
167 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
176 extern double atof();
179 strncpy(sname
, getenv("DISPLAY"), sizeof(sname
) - 1);
180 strcpy(cname
, "MediumAquamarine");
182 while ((c
= getopt(argc
, argv
, "dDob:C:c:p:m:f:i:r:s")) != EOF
) {
194 blimit
= atoi(optarg
);
197 climit
= atoi(optarg
);
200 mlimit
= atoi(optarg
);
203 strncpy(cname
, optarg
, sizeof(cname
) - 1);
206 strncpy(picname
, optarg
, sizeof(picname
) - 1);
209 flimit
= atoi(optarg
);
212 smooth
= atof(optarg
);
221 fprintf(stderr
, "usage: %s\n", pname
);
222 fprintf(stderr
, "\t\t[-c color] background color\n");
223 fprintf(stderr
, "\t\t[-b limit] number of bubbles (default 32)\n");
224 fprintf(stderr
, "\t\t[-f limit] number of fish (default 10)\n");
225 fprintf(stderr
, "\t\t[-i mult] move interval (default 0.2)\n");
226 fprintf(stderr
, "\t\t[-r rate] move frequency (default 0.2)\n");
227 fprintf(stderr
, "\t\t[-m num] median cut to this many colors\n");
228 fprintf(stderr
, "\t\t[-C num] use only this many color cells\n");
229 fprintf(stderr
, "\t\t[-d] clip fish, swim on root window\n");
230 fprintf(stderr
, "\t\t[-p file] fish swim on picture in file\n");
231 fprintf(stderr
, "\t\t[host:display]\n");
239 strncpy(sname
, argv
[optind
], sizeof(sname
) - 1);
240 display
= (char *) malloc(strlen(sname
) + 9);
241 snprintf(display
, sizeof(display
) - 1, "DISPLAY=%s", sname
);
248 erasefish(f
, x
, y
, d
)
253 * for something as small as a bubble, it was never worth the
254 * effort of using clipmasks to only turn of the bubble itself, so
255 * we just clear the whole rectangle.
257 XClearArea(Dpy
, wid
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
], False
);
262 gcv
.foreground
= cmap
[0];
263 gcv
.fill_style
= FillTiled
;
264 gcv
.fill_style
= FillSolid
;
265 gcv
.tile
= pfishB
[f
->type
][d
];
266 gcv
.ts_x_origin
= f
->x
;
267 gcv
.ts_y_origin
= f
->y
;
268 gcv
.clip_mask
= mfishB
[f
->type
][d
];
269 gcv
.clip_x_origin
= x
;
270 gcv
.clip_y_origin
= y
;
271 XChangeGC(Dpy
, gc
, GCForeground
| GCClipMask
|
272 GCTile
| GCTileStipXOrigin
| GCTileStipYOrigin
|
273 GCFillStyle
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
274 XCopyPlane(Dpy
, mfishB
[f
->type
][d
], wid
, gc
, 0, 0,
275 rwidth
[f
->type
], rheight
[f
->type
], x
, y
, (unsigned long) 1);
277 gcv
.foreground
= cmap
[0];
278 gcv
.fill_style
= FillTiled
;
279 gcv
.fill_style
= FillSolid
;
280 gcv
.tile
= pfishA
[f
->type
][d
];
281 gcv
.ts_x_origin
= f
->x
;
282 gcv
.ts_y_origin
= f
->y
;
283 gcv
.clip_mask
= mfishA
[f
->type
][d
];
284 gcv
.clip_x_origin
= x
;
285 gcv
.clip_y_origin
= y
;
286 XChangeGC(Dpy
, gc
, GCForeground
| GCClipMask
|
287 GCTile
| GCTileStipXOrigin
| GCTileStipYOrigin
|
288 GCFillStyle
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
289 XCopyPlane(Dpy
, mfishA
[f
->type
][d
], wid
, gc
, 0, 0,
290 rwidth
[f
->type
], rheight
[f
->type
], x
, y
, (unsigned long) 1);
297 * Just places a fish. Normally this is all you need for animation, since
298 * placeing the fish places an entire rectangle which erases most of the old
299 * fish (the rest being cleaned up by the function that called putfish.
300 * If DoClipping is set, this function is only called when placing a new
301 * fish, otherwise newfish is called.
311 * If we have a pixmap of the fish use it, otherwise use
312 * the XImage of the fish. In reality we will never use
313 * the XImage since X dies if the pixmap create failed
315 if (pfishA
[f
->type
][f
->d
]) {
317 * Clipping overrides background picture because
318 * the clipping prevents the drawing of any background
320 * DoClipping says just print a fish leaving the
321 * background unchanged.
322 * If there is a background picture, we use a buffer
323 * to prevent flashing, we combine the background
324 * picture and the fish, and then copy the
325 * whole rectangle in.
326 * Default is just copy in fish in with a background
330 gcv
.clip_mask
= mfishA
[f
->type
][f
->d
];
331 gcv
.clip_x_origin
= f
->x
;
332 gcv
.clip_y_origin
= f
->y
;
333 XChangeGC(Dpy
, gc
, GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
334 XCopyArea(Dpy
, pfishA
[f
->type
][f
->d
], wid
, gc
,
335 0, 0, rwidth
[f
->type
], rheight
[f
->type
], f
->x
, f
->y
);
336 } else if (picname
[0] != '\0') {
337 gcv
.fill_style
= FillTiled
;
339 gcv
.ts_x_origin
= -(f
->x
);
340 gcv
.ts_y_origin
= -(f
->y
);
341 gcv
.clip_mask
= None
;
342 XChangeGC(Dpy
, pgc
, (GCFillStyle
|
343 GCTile
| GCTileStipXOrigin
|
344 GCTileStipYOrigin
| GCClipMask
), &gcv
);
345 XFillRectangle(Dpy
, PixBuf
, pgc
, 0, 0, rwidth
[f
->type
], rheight
[f
->type
]);
347 gcv
.clip_mask
= mfishA
[f
->type
][f
->d
];
348 gcv
.clip_x_origin
= 0;
349 gcv
.clip_y_origin
= 0;
350 XChangeGC(Dpy
, pgc
, GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
351 XCopyArea(Dpy
, pfishA
[f
->type
][f
->d
], PixBuf
, pgc
,
352 0, 0, rwidth
[f
->type
], rheight
[f
->type
], 0, 0);
354 XCopyArea(Dpy
, PixBuf
, wid
, gc
,
355 0, 0, rwidth
[f
->type
], rheight
[f
->type
], f
->x
, f
->y
);
357 XCopyArea(Dpy
, pfishA
[f
->type
][f
->d
], wid
, gc
,
358 0, 0, rwidth
[f
->type
], rheight
[f
->type
], f
->x
, f
->y
);
361 XPutImage(Dpy
, wid
, gc
, xfishA
[f
->type
][f
->d
], 0, 0,
362 f
->x
, f
->y
, rwidth
[f
->type
], rheight
[f
->type
]);
367 * same as the above, only for the second frame of animation
369 if (pfishB
[f
->type
][f
->d
]) {
371 gcv
.clip_mask
= mfishB
[f
->type
][f
->d
];
372 gcv
.clip_x_origin
= f
->x
;
373 gcv
.clip_y_origin
= f
->y
;
374 XChangeGC(Dpy
, gc
, GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
375 XCopyArea(Dpy
, pfishB
[f
->type
][f
->d
], wid
, gc
,
376 0, 0, rwidth
[f
->type
], rheight
[f
->type
], f
->x
, f
->y
);
377 } else if (picname
[0] != '\0') {
378 gcv
.fill_style
= FillTiled
;
380 gcv
.ts_x_origin
= -(f
->x
);
381 gcv
.ts_y_origin
= -(f
->y
);
382 gcv
.clip_mask
= None
;
383 XChangeGC(Dpy
, pgc
, (GCFillStyle
|
384 GCTile
| GCTileStipXOrigin
|
385 GCTileStipYOrigin
| GCClipMask
), &gcv
);
386 XFillRectangle(Dpy
, PixBuf
, pgc
, 0, 0, rwidth
[f
->type
], rheight
[f
->type
]);
388 gcv
.clip_mask
= mfishB
[f
->type
][f
->d
];
389 gcv
.clip_x_origin
= 0;
390 gcv
.clip_y_origin
= 0;
391 XChangeGC(Dpy
, pgc
, GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
392 XCopyArea(Dpy
, pfishB
[f
->type
][f
->d
], PixBuf
, pgc
,
393 0, 0, rwidth
[f
->type
], rheight
[f
->type
], 0, 0);
395 XCopyArea(Dpy
, PixBuf
, wid
, gc
,
396 0, 0, rwidth
[f
->type
], rheight
[f
->type
], f
->x
, f
->y
);
398 XCopyArea(Dpy
, pfishB
[f
->type
][f
->d
], wid
, gc
,
399 0, 0, rwidth
[f
->type
], rheight
[f
->type
], f
->x
, f
->y
);
402 XPutImage(Dpy
, wid
, gc
, xfishB
[f
->type
][f
->d
], 0, 0,
403 f
->x
, f
->y
, rwidth
[f
->type
], rheight
[f
->type
]);
411 * This function can only be called if DoClipping is True. It is used to
412 * move a clipmasked fish. First the area under the fish is cleared,
413 * and then the new fish is masked in.
414 * The parameters x, y, amd d are from the old fish that is being
415 * erased before the new fish is drawn.
426 * If we are going to double buffer, we need to find the bounding
427 * rectangle of the overlap of the bounding rectangles of the old
433 bw
= f
->x
- x
+ rwidth
[f
->type
];
436 bw
= x
- f
->x
+ rwidth
[f
->type
];
440 bh
= f
->y
- y
+ rheight
[f
->type
];
443 bh
= y
- f
->y
+ rheight
[f
->type
];
449 * If there is a pixmap use it.
450 * This branchis always taken since right now, if the pixmap
451 * allocation failed, the program dies.
453 if (pfishA
[f
->type
][f
->d
]) {
455 * A pointless if, you now only come here if
456 * DoClipping is set, I've just been too lazy to
461 * Set up the masked gc for when we eventually
462 * draw the fish. Origin is different for
463 * whether we are drawing into the buffer
466 gcv
.clip_mask
= mfishA
[f
->type
][f
->d
];
468 gcv
.clip_x_origin
= f
->x
- bx
;
469 gcv
.clip_y_origin
= f
->y
- by
;
471 gcv
.clip_x_origin
= f
->x
;
472 gcv
.clip_y_origin
= f
->y
;
474 XChangeGC(Dpy
, gc
, GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
477 * If we have a background picture we want to
478 * clear to that background, otherwise we just
479 * do an XCleararea, and let the root restore
482 if (picname
[0] != '\0') {
483 gcv
.fill_style
= FillTiled
;
485 gcv
.clip_mask
= mfishB
[f
->type
][d
];
487 gcv
.ts_x_origin
= 0 - bx
;
488 gcv
.ts_y_origin
= 0 - by
;
489 gcv
.clip_x_origin
= x
- bx
;
490 gcv
.clip_y_origin
= y
- by
;
494 gcv
.clip_x_origin
= x
;
495 gcv
.clip_y_origin
= y
;
497 XChangeGC(Dpy
, pgc
, (GCFillStyle
|
498 GCTile
| GCTileStipXOrigin
|
499 GCTileStipYOrigin
| GCClipMask
|
500 GCClipXOrigin
| GCClipYOrigin
), &gcv
);
503 * if bouble buffering we clear the buffer
504 * to the backgound picture, and then
505 * shape the clip buffer to the shape of
506 * the fish being erased.
509 XFillRectangle(Dpy
, PixBuf
, pgc
,
510 x
- bx
, y
- by
, rwidth
[f
->type
], rheight
[f
->type
]);
511 XFillRectangle(Dpy
, ClipBuf
, c0gc
, 0, 0, 500, 500);
512 XCopyArea(Dpy
, mfishB
[f
->type
][d
],
514 rwidth
[f
->type
], rheight
[f
->type
], x
- bx
, y
- by
);
516 XFillRectangle(Dpy
, wid
, pgc
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
]);
519 XClearArea(Dpy
, wid
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
], 0);
523 * Now we just copy in the new fish with a clipmasked gc.
524 * But if we doublebuffered, we copy the new fish into
525 * the buffer, combine the new fishes clipmask in, and
526 * then mask the whole lot from the buffer to the window.
529 XCopyArea(Dpy
, pfishA
[f
->type
][f
->d
], PixBuf
, gc
, 0, 0,
530 rwidth
[f
->type
], rheight
[f
->type
], f
->x
- bx
, f
->y
- by
);
531 XCopyArea(Dpy
, mfishA
[f
->type
][f
->d
], ClipBuf
, cpgc
,
532 0, 0, rwidth
[f
->type
], rheight
[f
->type
], f
->x
- bx
, f
->y
- by
);
533 gcv
.clip_mask
= ClipBuf
;
534 gcv
.clip_x_origin
= bx
;
535 gcv
.clip_y_origin
= by
;
536 XChangeGC(Dpy
, gc
, GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
537 XCopyArea(Dpy
, PixBuf
, wid
, gc
, 0, 0, bw
, bh
, bx
, by
);
539 XCopyArea(Dpy
, pfishA
[f
->type
][f
->d
], wid
, gc
, 0, 0,
540 rwidth
[f
->type
], rheight
[f
->type
], f
->x
, f
->y
);
544 if (picname
[0] != '\0') {
545 gcv
.fill_style
= FillTiled
;
549 gcv
.clip_mask
= mfishB
[f
->type
][d
];
550 gcv
.clip_x_origin
= x
;
551 gcv
.clip_y_origin
= y
;
552 XChangeGC(Dpy
, pgc
, (GCFillStyle
|
553 GCTile
| GCTileStipXOrigin
|
554 GCTileStipYOrigin
| GCClipMask
|
555 GCClipXOrigin
| GCClipYOrigin
), &gcv
);
556 XFillRectangle(Dpy
, wid
, pgc
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
]);
558 XClearArea(Dpy
, wid
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
], 0);
561 XPutImage(Dpy
, wid
, gc
, xfishA
[f
->type
][f
->d
], 0, 0,
562 f
->x
, f
->y
, rwidth
[f
->type
], rheight
[f
->type
]);
567 * Same as above, only for the second frame of animation.
569 if (pfishB
[f
->type
][f
->d
]) {
571 gcv
.clip_mask
= mfishB
[f
->type
][f
->d
];
573 gcv
.clip_x_origin
= f
->x
- bx
;
574 gcv
.clip_y_origin
= f
->y
- by
;
576 gcv
.clip_x_origin
= f
->x
;
577 gcv
.clip_y_origin
= f
->y
;
579 XChangeGC(Dpy
, gc
, GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
580 if (picname
[0] != '\0') {
581 gcv
.fill_style
= FillTiled
;
583 gcv
.clip_mask
= mfishA
[f
->type
][d
];
585 gcv
.ts_x_origin
= 0 - bx
;
586 gcv
.ts_y_origin
= 0 - by
;
587 gcv
.clip_x_origin
= x
- bx
;
588 gcv
.clip_y_origin
= y
- by
;
592 gcv
.clip_x_origin
= x
;
593 gcv
.clip_y_origin
= y
;
595 XChangeGC(Dpy
, pgc
, (GCFillStyle
|
596 GCTile
| GCTileStipXOrigin
|
597 GCTileStipYOrigin
| GCClipMask
|
598 GCClipXOrigin
| GCClipYOrigin
), &gcv
);
600 XFillRectangle(Dpy
, PixBuf
, pgc
,
601 x
- bx
, y
- by
, rwidth
[f
->type
], rheight
[f
->type
]);
602 XFillRectangle(Dpy
, ClipBuf
, c0gc
, 0, 0, 500, 500);
603 XCopyArea(Dpy
, mfishA
[f
->type
][d
],
605 rwidth
[f
->type
], rheight
[f
->type
], x
- bx
, y
- by
);
607 XFillRectangle(Dpy
, wid
, pgc
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
]);
610 XClearArea(Dpy
, wid
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
], 0);
614 XCopyArea(Dpy
, pfishB
[f
->type
][f
->d
], PixBuf
, gc
, 0, 0,
615 rwidth
[f
->type
], rheight
[f
->type
], f
->x
- bx
, f
->y
- by
);
616 XCopyArea(Dpy
, mfishB
[f
->type
][f
->d
], ClipBuf
, cpgc
,
617 0, 0, rwidth
[f
->type
], rheight
[f
->type
], f
->x
- bx
, f
->y
- by
);
618 gcv
.clip_mask
= ClipBuf
;
619 gcv
.clip_x_origin
= bx
;
620 gcv
.clip_y_origin
= by
;
621 XChangeGC(Dpy
, gc
, GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
622 XCopyArea(Dpy
, PixBuf
, wid
, gc
, 0, 0, bw
, bh
, bx
, by
);
624 XCopyArea(Dpy
, pfishB
[f
->type
][f
->d
], wid
, gc
, 0, 0,
625 rwidth
[f
->type
], rheight
[f
->type
], f
->x
, f
->y
);
629 if (picname
[0] != '\0') {
630 gcv
.fill_style
= FillTiled
;
634 gcv
.clip_mask
= mfishA
[f
->type
][d
];
635 gcv
.clip_x_origin
= x
;
636 gcv
.clip_y_origin
= y
;
637 XChangeGC(Dpy
, pgc
, (GCFillStyle
|
638 GCTile
| GCTileStipXOrigin
|
639 GCTileStipYOrigin
| GCClipMask
|
640 GCClipXOrigin
| GCClipYOrigin
), &gcv
);
641 XFillRectangle(Dpy
, wid
, pgc
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
]);
643 XClearArea(Dpy
, wid
, x
, y
, rwidth
[f
->type
], rheight
[f
->type
], 0);
646 XPutImage(Dpy
, wid
, gc
, xfishB
[f
->type
][f
->d
], 0, 0,
647 f
->x
, f
->y
, rwidth
[f
->type
], rheight
[f
->type
]);
658 XClearArea(Dpy
, wid
, b
->x
, b
->y
, s
, s
, 0);
670 gcv
.clip_mask
= xbubbles
[s
];
671 gcv
.clip_x_origin
= b
->x
;
672 gcv
.clip_y_origin
= b
->y
;
673 XChangeGC(Dpy
, bgc
, GCForeground
| GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
674 XFillRectangle(Dpy
, wid
, bgc
, b
->x
, b
->y
, s
, s
);
679 * Find the closest color by allocating it, or picking an already allocated
682 Visual(*visual_info
) = NULL
;
683 int r_mask
, g_mask
, b_mask
;
684 int r_shift
= 0, g_shift
= 0, b_shift
= 0;
685 int r_bits
= 0, g_bits
= 0, b_bits
= 0;
687 FindColor(Dpy
, colormap
, colr
)
693 double rd
, gd
, bd
, dist
, mindist
;
695 XColor def_colrs
[256];
698 if (visual_info
== NULL
&& DefaultDepth(Dpy
, DefaultScreen(Dpy
)) > 8) {
699 visual_info
= DefaultVisual(Dpy
, DefaultScreen(Dpy
));
700 r_mask
= visual_info
->red_mask
;
701 while (!(r_mask
& 1)) {
710 g_mask
= visual_info
->green_mask
;
711 while (!(g_mask
& 1)) {
720 b_mask
= visual_info
->blue_mask
;
721 while (!(b_mask
& 1)) {
731 if (DefaultDepth(Dpy
, DefaultScreen(Dpy
)) > 8) {
732 colr
->red
>>= 16 - r_bits
;
733 colr
->green
>>= 16 - g_bits
;
734 colr
->blue
>>= 16 - b_bits
;
736 colr
->pixel
= ((colr
->red
<< r_shift
) & visual_info
->red_mask
) |
737 ((colr
->green
<< g_shift
) & visual_info
->green_mask
) |
738 ((colr
->blue
<< b_shift
) & visual_info
->blue_mask
);
742 if (AllocCnt
< climit
) {
743 match
= XAllocColor(Dpy
, colormap
, colr
);
748 NumCells
= DisplayCells(Dpy
, DefaultScreen(Dpy
));
749 for (i
= 0; i
< NumCells
; i
++) {
750 def_colrs
[i
].pixel
= i
;
752 XQueryColors(Dpy
, colormap
, def_colrs
, NumCells
);
753 mindist
= 65536.0 * 65536.0;
755 for (i
= 0; i
< NumCells
; i
++) {
756 rd
= (def_colrs
[i
].red
- colr
->red
) / 256.0;
757 gd
= (def_colrs
[i
].green
- colr
->green
) / 256.0;
758 bd
= (def_colrs
[i
].blue
- colr
->blue
) / 256.0;
759 dist
= (rd
* rd
* rd
* rd
) + (gd
* gd
* gd
* gd
) + (bd
* bd
* bd
* bd
);
760 if (dist
< mindist
) {
762 cindx
= def_colrs
[i
].pixel
;
766 colr
->red
= def_colrs
[cindx
].red
;
767 colr
->green
= def_colrs
[cindx
].green
;
768 colr
->blue
= def_colrs
[cindx
].blue
;
770 if (Allocated
[colr
->pixel
] == 0) {
771 Allocated
[colr
->pixel
] = 1;
779 ColorUsage(data
, width
, height
, colrs
)
782 struct colr_data
*colrs
;
788 struct colr_data newcol
[256];
790 for (i
= 0; i
< 256; i
++) {
794 size
= width
* height
;
797 for (i
= 0; i
< size
; i
++) {
799 if (mapping
[indx
] == -1) {
801 newcol
[cnt
].red
= colrs
[indx
].red
;
802 newcol
[cnt
].green
= colrs
[indx
].green
;
803 newcol
[cnt
].blue
= colrs
[indx
].blue
;
810 for (i
= 0; i
< size
; i
++) {
812 *ptr
= (unsigned char) mapping
[indx
];
816 for (i
= 0; i
< cnt
; i
++) {
817 colrs
[i
].red
= newcol
[i
].red
;
818 colrs
[i
].green
= newcol
[i
].green
;
819 colrs
[i
].blue
= newcol
[i
].blue
;
821 for (i
= cnt
; i
< 256; i
++) {
831 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
832 Initialize colormap for background color and required fish colors.
833 The fish colors are coded in xfishy.h as a trio of tables.
834 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
842 struct colr_data
*cdp
;
843 struct colr_data colrs
[256];
845 colormap
= XDefaultColormap(Dpy
, screen
);
847 NumCells
= DisplayCells(Dpy
, DefaultScreen(Dpy
));
848 Allocated
= (int *) malloc(NumCells
* sizeof(int));
849 for (i
= 0; i
< NumCells
; i
++) {
853 if ((climit
<= 0) || (climit
> NumCells
)) {
858 if (picname
[0] != '\0') {
859 fp
= fopen(picname
, "r");
861 fprintf(stderr
, "Cannot open picture %s for reading\n", picname
);
863 Pdata
= ReadBitmap(fp
, &Pwidth
, &Pheight
, colrs
);
865 Pcnt
= ColorUsage(Pdata
, Pwidth
, Pheight
, colrs
);
871 for (i
= 0; i
< NUM_FISH
; i
++) {
874 cmap
= (int *) malloc((cnt
+ 1) * sizeof(int));
876 XLookupColor(Dpy
, colormap
, cname
, &hdef
, &edef
);
877 hdef
.flags
= DoRed
| DoGreen
| DoBlue
;
878 FindColor(Dpy
, colormap
, &hdef
);
879 cmap
[0] = hdef
.pixel
;
886 if (picname
[0] != '\0') {
887 MedianCount(Pdata
, Pwidth
, Pheight
, colrs
);
889 for (j
= 0; j
< NUM_FISH
; j
++) {
892 cdp
= (struct colr_data
*) malloc(rcolors
[j
] * sizeof(struct colr_data
));
896 for (i
= 0; i
< rcolors
[j
]; i
++) {
898 cdp
[i
].green
= *gp
++;
901 MedianCount((unsigned char *) xfishRasterA
[j
],
902 (int) rwidth
[j
], (int) rheight
[j
], cdp
);
909 if (picname
[0] != '\0') {
910 for (i
= 0; i
< Pcnt
; i
++) {
918 ConvertColor(&rv
, &gv
, &bv
);
924 hdef
.flags
= DoRed
| DoGreen
| DoBlue
;
925 FindColor(Dpy
, colormap
, &hdef
);
926 cmap
[cnt
] = hdef
.pixel
;
930 for (j
= 0; j
< NUM_FISH
; j
++) {
936 for (i
= 0; i
< rcolors
[j
]; i
++) {
944 ConvertColor(&rv
, &gv
, &bv
);
950 hdef
.flags
= DoRed
| DoGreen
| DoBlue
;
951 FindColor(Dpy
, colormap
, &hdef
);
952 cmap
[cnt
] = hdef
.pixel
;
965 * Make am image of appropriate depth for display from image data.
968 MakeImage(data
, width
, height
)
972 int linepad
, shiftnum
;
973 int shiftstart
, shiftstop
, shiftinc
;
978 unsigned char *bit_data
, *bitp
, *datap
;
980 depth
= DefaultDepth(Dpy
, DefaultScreen(Dpy
));
981 if ((depth
!= 1) && (depth
!= 2) && (depth
!= 4) && (depth
!= 8)) {
982 fprintf(stderr
, "Don't know how to format image for display of depth %d\n", depth
);
986 if (BitmapBitOrder(Dpy
) == LSBFirst
) {
991 shiftstart
= 8 - depth
;
995 linepad
= 8 - (width
% 8);
996 bit_data
= (unsigned char *) malloc(((width
+ linepad
) * height
) + 1);
1000 shiftnum
= shiftstart
;
1001 for (h
= 0; h
< height
; h
++) {
1002 for (w
= 0; w
< width
; w
++) {
1003 temp
= *datap
++ << shiftnum
;
1004 *bitp
= *bitp
| temp
;
1005 shiftnum
= shiftnum
+ shiftinc
;
1006 if (shiftnum
== shiftstop
) {
1007 shiftnum
= shiftstart
;
1012 for (w
= 0; w
< linepad
; w
++) {
1013 shiftnum
= shiftnum
+ shiftinc
;
1014 if (shiftnum
== shiftstop
) {
1015 shiftnum
= shiftstart
;
1022 bytesperline
= (width
* depth
/ 8 + linepad
);
1023 newimage
= XCreateImage(Dpy
, DefaultVisual(Dpy
, screen
), depth
,
1024 ZPixmap
, 0, (char *) bit_data
,
1025 (width
+ linepad
), height
, 8, bytesperline
);
1032 static unsigned char bits
[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
1034 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1035 Calibrate the pixmaps and bimaps. The right-fish data is coded in xfishy.h,
1036 this is transformed to create the left-fish. The eight bubbles are coded
1037 in bubbles.h as a two dimensional array.
1038 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1042 register caddrt p
, q
, x1A
, x1B
, x2A
, x2B
;
1043 unsigned char *data
;
1044 register int i
, j
, k
;
1049 for (k
= 0; k
< NUM_FISH
; k
++) {
1052 * The clipmasks must be created before we remap colors.
1053 * otherwise an opaque color might get remapped to a
1054 * transparent color.
1056 if ((DoClipping
) || (picname
[0] != '\0')) {
1057 data
= (unsigned char *) malloc((rwidth
[k
] + 7) / 8 * rheight
[k
]);
1059 p
= (caddrt
) xfishRasterA
[k
];
1062 for (i
= 0; i
< ((rwidth
[k
] + 7) / 8 * rheight
[k
]); i
++) {
1063 unsigned char bt
= 0x00;
1064 for (j
= 0; j
< 8; j
++) {
1065 if (*p
!= rback
[k
]) {
1070 if (wcnt
== rwidth
[k
]) {
1077 mfishA
[k
][2] = XCreateBitmapFromData(Dpy
, wid
,
1078 (char *) data
, rwidth
[k
], rheight
[k
]);
1080 p
= (caddrt
) xfishRasterA
[k
];
1081 p
= p
+ rwidth
[k
] - 1;
1084 for (i
= 0; i
< ((rwidth
[k
] + 7) / 8 * rheight
[k
]); i
++) {
1085 unsigned char bt
= 0x00;
1086 for (j
= 0; j
< 8; j
++) {
1087 if (*p
!= rback
[k
]) {
1092 if (wcnt
== rwidth
[k
]) {
1094 p
= p
+ (2 * rwidth
[k
]);
1100 mfishA
[k
][1] = XCreateBitmapFromData(Dpy
, wid
,
1101 (char *) data
, rwidth
[k
], rheight
[k
]);
1103 p
= (caddrt
) xfishRasterB
[k
];
1106 for (i
= 0; i
< ((rwidth
[k
] + 7) / 8 * rheight
[k
]); i
++) {
1107 unsigned char bt
= 0x00;
1108 for (j
= 0; j
< 8; j
++) {
1109 if (*p
!= rback
[k
]) {
1114 if (wcnt
== rwidth
[k
]) {
1121 mfishB
[k
][2] = XCreateBitmapFromData(Dpy
, wid
,
1122 (char *) data
, rwidth
[k
], rheight
[k
]);
1124 p
= (caddrt
) xfishRasterB
[k
];
1125 p
= p
+ rwidth
[k
] - 1;
1128 for (i
= 0; i
< ((rwidth
[k
] + 7) / 8 * rheight
[k
]); i
++) {
1129 unsigned char bt
= 0x00;
1130 for (j
= 0; j
< 8; j
++) {
1131 if (*p
!= rback
[k
]) {
1136 if (wcnt
== rwidth
[k
]) {
1138 p
= p
+ (2 * rwidth
[k
]);
1144 mfishB
[k
][1] = XCreateBitmapFromData(Dpy
, wid
,
1145 (char *) data
, rwidth
[k
], rheight
[k
]);
1147 free((char *) data
);
1150 if (DisplayPlanes(Dpy
, screen
) < 8) {
1152 j
= rwidth
[k
] * rheight
[k
];
1153 x1A
= (caddrt
) malloc(rwidth
[k
] * rheight
[k
]);
1154 p
= (caddrt
) xfishRasterA
[k
];
1158 for (i
= 0; i
< j
; i
++) {
1159 *q
= cmap
[cnt
+ (int) (*p
)];
1164 x1B
= (caddrt
) malloc(rwidth
[k
] * rheight
[k
]);
1165 p
= (caddrt
) xfishRasterB
[k
];
1167 for (i
= 0; i
< j
; i
++) {
1168 *q
= cmap
[cnt
+ (int) (*p
)];
1173 x2A
= (caddrt
) malloc(rwidth
[k
] * rheight
[k
]);
1174 for (i
= 0; i
< rheight
[k
]; i
++) {
1175 p
= x1A
+ i
* rwidth
[k
];
1176 q
= x2A
+ (i
+ 1) * rwidth
[k
] - 1;
1177 for (j
= 0; j
< rwidth
[k
]; j
++) {
1182 x2B
= (caddrt
) malloc(rwidth
[k
] * rheight
[k
]);
1183 for (i
= 0; i
< rheight
[k
]; i
++) {
1184 p
= x1B
+ i
* rwidth
[k
];
1185 q
= x2B
+ (i
+ 1) * rwidth
[k
] - 1;
1186 for (j
= 0; j
< rwidth
[k
]; j
++) {
1191 xfishA
[k
][2] = MakeImage(x1A
, rwidth
[k
], rheight
[k
]);
1192 xfishA
[k
][1] = MakeImage(x2A
, rwidth
[k
], rheight
[k
]);
1193 xfishB
[k
][2] = MakeImage(x1B
, rwidth
[k
], rheight
[k
]);
1194 xfishB
[k
][1] = MakeImage(x2B
, rwidth
[k
], rheight
[k
]);
1201 i
= DisplayPlanes(Dpy
, screen
);
1204 XGetImage(Dpy
, root_window
, 0, 0, rwidth
[k
], rheight
[k
], 0, ZPixmap
);
1206 p
= (caddrt
) xfishRasterA
[k
];
1208 for (j
= 0; j
< rheight
[k
]; j
++) {
1209 for (i
= 0; i
< rwidth
[k
]; i
++) {
1210 XPutPixel(xfishA
[k
][2], i
, j
, cmap
[cnt
+ (int) (*p
)]);
1216 XGetImage(Dpy
, root_window
, 0, 0, rwidth
[k
], rheight
[k
], 0, ZPixmap
);
1218 p
= (caddrt
) xfishRasterB
[k
];
1220 for (j
= 0; j
< rheight
[k
]; j
++) {
1221 for (i
= 0; i
< rwidth
[k
]; i
++) {
1222 XPutPixel(xfishB
[k
][2], i
, j
, cmap
[cnt
+ (int) (*p
)]);
1228 XGetImage(Dpy
, root_window
, 0, 0, rwidth
[k
], rheight
[k
], 0, ZPixmap
);
1230 for (j
= 0; j
< rheight
[k
]; j
++) {
1231 for (i
= 0; i
< rwidth
[k
]; i
++) {
1232 XPutPixel(xfishA
[k
][1], i
, j
,
1233 XGetPixel(xfishA
[k
][2], rwidth
[k
] - i
- 1, j
));
1238 XGetImage(Dpy
, root_window
, 0, 0, rwidth
[k
], rheight
[k
], 0, ZPixmap
);
1240 for (j
= 0; j
< rheight
[k
]; j
++) {
1241 for (i
= 0; i
< rwidth
[k
]; i
++) {
1242 XPutPixel(xfishB
[k
][1], i
, j
,
1243 XGetPixel(xfishB
[k
][2], rwidth
[k
] - i
- 1, j
));
1250 i
= DisplayPlanes(Dpy
, screen
);
1252 pfishA
[k
][1] = XCreatePixmap(Dpy
, wid
, rwidth
[k
], rheight
[k
], i
);
1253 pfishA
[k
][2] = XCreatePixmap(Dpy
, wid
, rwidth
[k
], rheight
[k
], i
);
1254 pfishB
[k
][1] = XCreatePixmap(Dpy
, wid
, rwidth
[k
], rheight
[k
], i
);
1255 pfishB
[k
][2] = XCreatePixmap(Dpy
, wid
, rwidth
[k
], rheight
[k
], i
);
1258 XPutImage(Dpy
, pfishA
[k
][1], gc
, xfishA
[k
][1], 0, 0, 0, 0, rwidth
[k
], rheight
[k
]);
1261 XPutImage(Dpy
, pfishA
[k
][2], gc
, xfishA
[k
][2], 0, 0, 0, 0, rwidth
[k
], rheight
[k
]);
1264 XPutImage(Dpy
, pfishB
[k
][1], gc
, xfishB
[k
][1], 0, 0, 0, 0, rwidth
[k
], rheight
[k
]);
1267 XPutImage(Dpy
, pfishB
[k
][2], gc
, xfishB
[k
][2], 0, 0, 0, 0, rwidth
[k
], rheight
[k
]);
1273 for (i
= 1; i
<= 8; i
++) {
1274 xbubbles
[i
] = XCreateBitmapFromData(Dpy
, wid
, (char *) xbBits
[i
], i
, i
);
1279 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1280 Toggle secure mode on receipt of signal
1281 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1291 XLowerWindow(Dpy
, wid
);
1293 XRaiseWindow(Dpy
, wid
);
1301 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1302 Initialize signal so that SIGUSR1 causes secure mode to toggle.
1303 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1309 signal(SIGUSR1
, toggle_secure
);
1311 #if defined(MOTOROLA) || defined(SCO)
1312 sigset(SIGUSR1
, toggle_secure
);
1316 vec
.sv_handler
= toggle_secure
;
1321 ret
= sigvec(SIGUSR1
, &vec
, &vec
);
1324 fprintf(stderr
, "sigvec call failed\n");
1326 fprintf(stderr
, "sigvec call OK\n");
1330 sigvector(SIGUSR1
, &vec
, &vec
);
1332 #endif /* MOTOROLA */
1337 set_window_type_desktop(Display
*dpy
, Window wid
)
1339 Atom _NET_WM_WINDOW_TYPE
, _NET_WM_WINDOW_TYPE_DESKTOP
;
1341 _NET_WM_WINDOW_TYPE
= XInternAtom(dpy
, "_NET_WM_WINDOW_TYPE", False
);
1342 _NET_WM_WINDOW_TYPE_DESKTOP
= XInternAtom(dpy
, "_NET_WM_WINDOW_TYPE_DESKTOP", False
);
1344 XChangeProperty(dpy
, wid
, _NET_WM_WINDOW_TYPE
, XA_ATOM
, 32,
1345 PropModeReplace
, (unsigned char *) &_NET_WM_WINDOW_TYPE_DESKTOP
, 1);
1349 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1350 Variety of initialization calls, including getting the window up and running.
1351 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1355 XWindowAttributes winfo
;
1356 XSetWindowAttributes attr
;
1362 unsigned char *ndata
;
1363 unsigned char *ptr1
, *ptr2
;
1365 root_window
= VirtualRootWindowOfScreen(DefaultScreenOfDisplay(Dpy
));
1367 XGetWindowAttributes(Dpy
, root_window
, &winfo
);
1368 width
= winfo
.width
;
1369 height
= winfo
.height
;
1372 if ((p
= XGetDefault(Dpy
, pname
, "BubbleLimit")) != NULL
)
1374 if ((p
= XGetDefault(Dpy
, pname
, "ColorLimit")) != NULL
)
1376 if ((p
= XGetDefault(Dpy
, pname
, "MedianCutLimit")) != NULL
)
1378 if ((p
= XGetDefault(Dpy
, pname
, "DoClipping")) != NULL
)
1379 DoClipping
= atoi(p
);
1380 if ((p
= XGetDefault(Dpy
, pname
, "DoubleBuffer")) != NULL
)
1381 DoubleBuf
= atoi(p
);
1382 if ((p
= XGetDefault(Dpy
, pname
, "Overlap")) != NULL
)
1384 if ((p
= XGetDefault(Dpy
, pname
, "Color")) != NULL
)
1386 if ((p
= XGetDefault(Dpy
, pname
, "Picture")) != NULL
)
1388 if ((p
= XGetDefault(Dpy
, pname
, "FishLimit")) != NULL
)
1390 if ((p
= XGetDefault(Dpy
, pname
, "IncMult")) != NULL
)
1392 if ((p
= XGetDefault(Dpy
, pname
, "Rate")) != NULL
)
1394 if ((p
= XGetDefault(Dpy
, pname
, "Secure")) != NULL
)
1395 for (i
= 0; i
< 6; i
++)
1396 if (strcmp(p
, yess
[i
]) == 0)
1400 * DoubleBuf is only useful if we are doing clipping on our
1401 * own background picture, otherwise turn it off.
1403 if ((DoubleBuf
) && ((!DoClipping
) || (picname
[0] == '\0'))) {
1409 if (picname
[0] != '\0') {
1410 size
= Pwidth
* Pheight
;
1411 ndata
= (unsigned char *) malloc(size
);
1415 for (i
= 0; i
< size
; i
++) {
1416 *ptr2
= cmap
[cnt
+ (int) (*ptr1
)];
1420 pimage
= MakeImage(ndata
, Pwidth
, Pheight
);
1421 free((char *) ndata
);
1422 i
= DisplayPlanes(Dpy
, screen
);
1423 PicMap
= XCreatePixmap(Dpy
, root_window
, Pwidth
, Pheight
, i
);
1426 if ((DoubleBuf
) || (picname
[0] != '\0')) {
1427 i
= DisplayPlanes(Dpy
, screen
);
1428 PixBuf
= XCreatePixmap(Dpy
, root_window
, 500, 500, i
);
1429 ClipBuf
= XCreatePixmap(Dpy
, root_window
, 500, 500, 1);
1430 c0gc
= XCreateGC(Dpy
, ClipBuf
, 0, NULL
);
1431 XSetForeground(Dpy
, c0gc
, (unsigned long) 0);
1432 XSetFunction(Dpy
, c0gc
, GXcopy
);
1433 cpgc
= XCreateGC(Dpy
, ClipBuf
, 0, NULL
);
1434 XSetFunction(Dpy
, cpgc
, GXor
);
1437 attr
.override_redirect
= True
;
1438 attr
.background_pixel
= cmap
[0];
1440 if (!DoClipping
|| picname
[0] != '\0') {
1441 wid
= XCreateWindow(Dpy
, root_window
,
1442 1, 1, width
- 2, height
- 2, 0,
1443 CopyFromParent
, CopyFromParent
, CopyFromParent
,
1444 CWBackPixel
| CWOverrideRedirect
, &attr
);
1447 msgdie("XCreateWindow failed");
1448 set_window_type_desktop(Dpy
, wid
);
1451 XClearArea(Dpy
, wid
, 0, 0, 0, 0, False
);
1454 vals
.foreground
= vals
.background
= cmap
[0];
1455 vals
.graphics_exposures
= False
;
1456 gc
= XCreateGC(Dpy
, wid
, GCForeground
| GCBackground
| GCGraphicsExposures
, &vals
);
1457 pgc
= XCreateGC(Dpy
, wid
, GCForeground
| GCBackground
| GCGraphicsExposures
, &vals
);
1458 bgc
= XCreateGC(Dpy
, wid
, GCForeground
| GCBackground
| GCGraphicsExposures
, &vals
);
1460 for (i
= 0; i
< NUM_FISH
; i
++) {
1479 if (!DoClipping
|| picname
[0] != '\0') {
1480 XStoreName(Dpy
, wid
, pname
);
1482 xsh
.flags
= USSize
| USPosition
| PPosition
| PSize
;
1485 xsh
.height
= height
;
1486 XSetNormalHints(Dpy
, wid
, &xsh
);
1488 if (picname
[0] != '\0') {
1489 XPutImage(Dpy
, PicMap
, gc
, pimage
, 0, 0, 0, 0, Pwidth
, Pheight
);
1490 XSetWindowBackgroundPixmap(Dpy
, wid
, PicMap
);
1493 XMapWindow(Dpy
, wid
);
1496 binfo
= (bubble
*) malloc(blimit
* sizeof(bubble
));
1497 finfo
= (fish
*) malloc(flimit
* sizeof(fish
));
1501 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1502 Create a new bubble. Placement along the x axis is random, as is the size of
1503 the bubble. Increment value is determined by speed.
1504 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1510 register bubble
*b
= b0
;
1512 b
->x
= width
* (rand() / RAND_F_MAX
);
1514 b
->y
= (height
/ 16) * (rand() / RAND_I_1_16
+ 1) - 1;
1517 b
->s
= s
= 1.0 + rand() / RAND_F_1_8
;
1518 if ((b
->i
= smooth
* height
/ (float) binc
[s
]) == 0)
1521 putbubble(b
, s
, bcolor
);
1524 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1525 Erase old bubbles, move and draw new bubbles. Random left-right factor
1526 can move bubble one size-unit in either direction.
1527 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1531 register int i
, j
, s
;
1534 for (i
= 0; i
< blimit
; i
++) {
1538 if ((b
->y
> 0) && (b
->erased
== 0)) {
1539 if ((DoClipping
) || (picname
[0] != '\0')) {
1542 putbubble(b
, s
, cmap
[0]);
1545 if ((b
->y
-= b
->i
) > 0) {
1547 if (j
< RAND_I_1_4
) {
1549 } else if (j
> RAND_I_3_4
) {
1552 putbubble(b
, s
, bcolor
);
1554 if (rand() < RAND_I_1_4
) {
1563 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1564 Fish over bubble collision detection. The specified fish is checked against
1565 all bubbles for overlap. This way we don't try and erase bubbles that are
1567 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1569 collide_bubbles(f0
, ofx
, ofy
)
1574 register fish
*f
= f0
;
1577 for (i
= 0; i
< blimit
; i
++) {
1580 if ((delta
>= 0) && (delta
<= (rwidth
[f
->type
] - b
->s
))) {
1582 if ((delta
>= 0) && (delta
<= (rheight
[f
->type
] - b
->s
))) {
1587 delta
= b
->x
- f
->x
;
1588 if ((delta
>= 0) && (delta
<= (rwidth
[f
->type
] - b
->s
))) {
1589 delta
= b
->y
- f
->y
;
1590 if ((delta
>= 0) && (delta
<= (rheight
[f
->type
] - b
->s
))) {
1598 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1599 Fish collision detection. The specified fish is checked against all other
1600 fish for overlap. The xt parameter specifies a x axis multiplier for overlap.
1601 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1603 collide_fish(f0
, xt
)
1608 register fish
*f
= f0
;
1614 for (i
= 0; i
< flimit
; i
++) {
1615 if (&finfo
[i
] != f
) {
1616 j
= finfo
[i
].y
- f
->y
;
1617 if ((j
> -rheight
[finfo
[i
].type
]) && (j
< rheight
[f
->type
])) {
1618 j
= finfo
[i
].x
- f
->x
;
1619 if ((j
> -xt
* rwidth
[finfo
[i
].type
]) && (j
< xt
* rwidth
[f
->type
])) {
1629 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1630 Create a new fish. Placement along the y axis is random, as is the side
1631 >from which the fish appears. Direction is determined from side. Increment
1633 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1641 f
->type
= rand() % NUM_FISH
;
1642 for (i
= 0, collide
= 1; (i
< 16) && (collide
); i
++) {
1643 f
->y
= (height
- rheight
[f
->type
]) * (rand() / RAND_F_MAX
);
1644 if ((f
->i
= smooth
* width
/ (8.0 * (1.0 + rand() / RAND_F_1_8
))) == 0) {
1647 if (rand() < RAND_I_1_2
) {
1652 f
->x
= -rwidth
[f
->type
];
1654 collide
= collide_fish(f
, 2);
1667 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1668 Move all the fish. Clearing old fish is accomplished by masking only the
1669 exposed areas of the old fish. Random up-down factor can move fish 1/4 a
1670 fish height in either direction, if no collisions are caused.
1671 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1675 register int i
, j
, x
, y
, ofx
, ofy
, ofd
, done
;
1678 for (i
= 0; i
< flimit
; i
++) {
1686 done
= ((f
->x
-= f
->i
) < -rwidth
[f
->type
]);
1687 x
= f
->x
+ rwidth
[f
->type
];
1688 } else if (f
->d
== 2) {
1689 done
= ((f
->x
+= f
->i
) > width
);
1693 if (!collide_fish(f
, 1)) {
1696 if (j
< RAND_I_1_4
) {
1698 } else if (j
> RAND_I_3_4
) {
1706 if (collide_fish(f
, 1)) {
1713 j
= f
->y
+ rheight
[f
->type
];
1719 movefish(f
, ofx
, ofy
, ofd
);
1722 XClearArea(Dpy
, wid
, x
, ofy
, f
->i
, rheight
[f
->type
], 0);
1724 XClearArea(Dpy
, wid
, ofx
, j
, rwidth
[f
->type
], y
, 0);
1729 XClearArea(Dpy
, wid
, x
, f
->y
, f
->i
, rheight
[f
->type
], 0);
1733 if ((f
->d
= 3 - f
->d
) == 1) {
1734 f
->x
= f
->x
- 2 * f
->i
;
1735 x
= f
->x
+ rwidth
[f
->type
];
1737 f
->x
= f
->x
+ 2 * f
->i
;
1741 movefish(f
, ofx
, ofy
, ofd
);
1744 XClearArea(Dpy
, wid
, x
, f
->y
, f
->i
, rheight
[f
->type
], 0);
1747 if ((!DoClipping
) || (picname
[0] == '\0')) {
1748 collide_bubbles(f
, ofx
, ofy
);
1757 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1758 Higher-resolution sleep
1759 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1761 high_res_sleep(seconds
)
1764 struct timeval timeout
;
1766 timeout
.tv_sec
= seconds
;
1767 timeout
.tv_usec
= (seconds
- timeout
.tv_sec
) * 1000000.0;
1768 select(0, NULL
, NULL
, NULL
, &timeout
);
1772 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1773 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1783 if ((Dpy
= XOpenDisplay(sname
)) == 0)
1784 msgdie("XOpenDisplay failed");
1785 screen
= DefaultScreen(Dpy
);
1787 white
= WhitePixel(Dpy
, screen
);
1788 black
= BlackPixel(Dpy
, screen
);
1791 srand((unsigned) getpid());
1794 for (i
= 0; i
< blimit
; i
++)
1795 new_bubble(&binfo
[i
]);
1796 for (i
= 0; i
< flimit
; i
++) {
1801 for (i
= 0; i
< flimit
; i
++)
1802 new_fish(&finfo
[i
]);
1804 XLowerWindow(Dpy
, wid
);
1806 XRaiseWindow(Dpy
, wid
);
1813 XNextEvent(Dpy
, &ev
);
1815 high_res_sleep(rate
);
1822 XLowerWindow(Dpy
, wid
);
1824 XRaiseWindow(Dpy
, wid
);