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