New upstream version 3.1.1
[xfishtank.git] / src / main.c
1 /* -copyright-
2 #-# Copyright © 2021 Eric Bina, Dave Black, TJ Phan,
3 #-# Vincent Renardias, Willem Vermin
4 #-#
5 #-# Permission is hereby granted, free of charge, to any person
6 #-# obtaining a copy of this software and associated documentation
7 #-# files (the “Software”), to deal in the Software without
8 #-# restriction, including without limitation the rights to use,
9 #-# copy, modify, merge, publish, distribute, sublicense, and/or
10 #-# sell copies of the Software, and to permit persons to whom
11 #-# the Software is furnished to do so, subject to the following
12 #-# conditions:
13 #-#
14 #-# The above copyright notice and this permission notice shall
15 #-# be included in all copies or substantial portions of the Software.
16 #-#
17 #-# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
18 #-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 #-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 #-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 #-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 #-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 #-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 #-# OTHER DEALINGS IN THE SOFTWARE.
25 #-#
26 */
27
28 /*
29
30 * Original Author Unknow.
31
32 * 8/10/88 - Ported from X10 to X11R3 by:
33
34 Jonathan Greenblatt (jonnyg@rover.umd.edu)
35
36 * Cleaned up by Dave Lemke (lemke@sun.com)
37
38 * Ported to monocrome by Jonathan Greenblatt (jonnyg@rover.umd.edu)
39
40 * 05/02/1996 Added TrueColor support by TJ Phan (phan@aur.alcatel.com)
41
42 */
43
44 #include <sys/types.h>
45 #include <sys/time.h>
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <signal.h>
52 #include <gtk/gtk.h>
53 #include <X11/Xlib.h>
54 #include <X11/Xutil.h>
55 #include <X11/xpm.h>
56 #include <ctype.h>
57 #include <assert.h>
58
59 #include "xfishtank.h"
60 #include "vroot.h"
61 #include "bubbles.h"
62 #include "debug.h"
63 #include "transwindow.h"
64 #include "utils.h"
65 #include "fishes.h"
66 #include "ixpm.h"
67 #include "ui.h"
68
69
70 #ifdef DOUBLE_BUFFER
71 #include <X11/extensions/Xdbe.h>
72 static int UseXdbe;
73 static XdbeSwapAction BMETHOD;
74 static int XdbeAvailable = -42;
75 #endif
76
77 extern unsigned char *ReadBitmap();
78
79 /* externals for pixmap and bimaps from xfishy.h */
80
81
82 /* typedefs for bubble and fish structures, also caddr_t (not used in X.h) */
83 typedef struct _bubble {
84 float x;
85 float y;
86 int size; // size of bubble
87 float bstep; // increment in placement
88 } bubble;
89
90 typedef struct _fish {
91 float x;
92 float y;
93 int direction; // direction: 0: r->l
94 int frame; // animation frames: 0 .. NUM_FRAMES
95 int type;
96 float fstep; // increment in placement
97 int animtime; // # drawings between change of frame
98 } fish;
99
100 typedef struct _fishtype {
101 Pixmap pix;
102 GC gc;
103 int w;
104 int h;
105 } fishtype;
106
107
108 static int binc[] = { 0, 64, 56, 48, 40, 32, 24, 16, 8 }; /* bubble increment and yes check tables */
109 static int DoubleBuf = 0; /* Should we use double buffering */
110 static int width; /* width of initial window in pixels */
111 static int height; /* height of initial window in pixels */
112 static int screen; /* Default screen of this display */
113 static int inxscreensaver = 0; /* are we running in xscreensaver? */
114 static float smooth = 0.02; /* smoothness increment multiplier */
115 static bubble *binfo = NULL; /* bubble info structures, allocated dynamically */
116 static fish *finfo = NULL; /* fish info structures, allocated dynamically */
117 static Window root_window;
118 static fishtype xfish[NUM_FISH][2][NUM_FRAMES]; /* [type][left/right][frame] */
119
120 static Pixmap xbubbles[9]; /* bubbles bitmaps (1 to 8, by size in pixels) */
121 static Window wid; /* aquarium window */
122 static Window wid1;
123 static Window UserWindow = 0;
124 static Pixel white;
125 static Pixel black;
126 static Pixel bcolor;
127 static Pixel bgcolor;
128 //static GC pgc;
129 //static GC gc;
130 static GC bgc;
131 //static GC draw_gc;
132 static int xfishtank_trans = 0;
133 //static int rwidth[NUM_FISH];
134 //static int rheight[NUM_FISH];
135 static XdbeBackBuffer backbuf = 0;
136 static int WantXdbe = 1;
137 static const int maxanimtime = 20;
138 static int nomenu = 0;
139 static int ForceRoot = 0;
140 static int HaltedByInterrupt = 0;
141 static int Done = 0;
142
143 static Pixel AllocNamedColor(Display *display, char *colorName, Pixel dfltPix);
144 static char *display_name=NULL;
145 static int do_move(void *dummy);
146 static int do_testfish(void *dummy);
147 static int do_write_flags(void *dummy);
148 static void erasebubble(bubble *b);
149 static void erasefish(fish *f);
150 static void init_pixmap(void);
151 static void init_signals(void);
152 static void initialize(void);
153 static void move_fish(void);
154 static void movefish(fish *f);
155 static void new_bubble(bubble *b, int init);
156 static void new_fish(fish *f0, int init);
157 static void parse(int argc, char **argv);
158 static void putbubble(bubble *b, unsigned long c);
159 static void remove_all_bubbles(void);
160 static void remove_all_fishes(void);
161 static void setanimtime(fish*f);
162 static void step_bubbles(void);
163 static void Usage(void);
164 static void Thanks(void);
165 static void SigHandler(int signum);
166 static void print_changelog(void);
167 static void selfrep(void);
168
169
170 Display *Dpy = NULL;
171 Window xfishtankWin;
172 int counter;
173 int blimit = 32; /* bubble limit */
174 int flimit = 10; /* fish limit */
175 char *bcolorstring = NULL;
176 char *bgcolorstring = NULL;
177 float speedfactor;
178
179
180
181 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
182 parse command line
183 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
184 void parse(int argc, char *argv[])
185 {
186
187 int p = 0;
188
189 #define CHECK do{if(++p >= argc){ Usage(); Thanks(); exit(1);}}while(0)
190 while (++p < argc)
191 {
192 P("argv[p] %s\n",argv[p]);
193 if (!strcmp(argv[p],"-b"))
194 {
195 CHECK;
196 blimit = strtod(argv[p],NULL);
197 }
198 else if (!strcmp(argv[p],"-f"))
199 {
200 CHECK;
201 flimit = strtod(argv[p],NULL);
202 }
203 else if (!strcmp(argv[p],"-display"))
204 {
205 CHECK;
206 display_name = strdup(argv[p]);
207 }
208 else if (!strcmp(argv[p],"-bc"))
209 {
210 CHECK;
211 if(bcolorstring)
212 free(bcolorstring);
213 bcolorstring = strdup(argv[p]);
214 }
215 else if (!strcmp(argv[p],"-bgc"))
216 {
217 CHECK;
218 if(bgcolorstring)
219 free(bgcolorstring);
220 bgcolorstring = strdup(argv[p]);
221 }
222 else if (!strcmp(argv[p],"-double"))
223 {
224 CHECK;
225 WantXdbe = (strtod(argv[p],NULL)>0);
226 }
227 else if (!strcmp(argv[p],"-speed"))
228 {
229 CHECK;
230 speedfactor = (strtod(argv[p],NULL)/100.0);
231 }
232 else if (!strcmp(argv[p],"-h"))
233 {
234 Usage();
235 Thanks();
236 }
237 else if (!strcmp(argv[p],"-defaults"))
238 {
239 set_defaults();
240 }
241 else if (!strcmp(argv[p],"-nomenu"))
242 {
243 nomenu = 1;
244 }
245 else if (!strcmp(argv[p],"-root"))
246 {
247 ForceRoot = 1;
248 }
249 else if (!strcmp(argv[p],"-window-id"))
250 {
251 CHECK;
252 UserWindow = strtod(argv[p],NULL);
253 }
254 else if (!strcmp(argv[p],"-changelog"))
255 {
256 print_changelog();
257 Thanks();
258 }
259 #ifdef SELFREP
260 else if (!strcmp(argv[p],"-selfrep"))
261 {
262 selfrep();
263 exit(0);
264 }
265 #endif
266 else
267 {
268 printf("Not understood: %s\n",argv[p]);
269 Usage();
270 Thanks();
271 }
272 }
273 }
274
275 void set_defaults()
276 {
277 flimit = 10;
278 blimit = 32;
279 if(bcolorstring)
280 free(bcolorstring);
281 bcolorstring = strdup("lightblue");
282 if(bgcolorstring)
283 free(bgcolorstring);
284 bgcolorstring = strdup("darkblue");
285 speedfactor = 1.0;
286 }
287
288 void Usage()
289 {
290 printf("\nUsage: xfishtank [options]\n\n");
291 printf("Options:\n");
292 printf(" -b n number of bubbles (default 32)\n");
293 printf(" -f n number of fish (default 10)\n");
294 printf(" -bc color color of bubbles (default \"lightblue\")\n");
295 printf(" -double n 1: double buffering, 0: do not use (default: 1)\n");
296 printf(" -speed speed of fishes (default: 100)\n");
297 printf(" -defaults all options to default\n");
298 printf(" -nomenu do not show menu\n");
299 printf(" -display DISPLAY to draw on (default \"\")\n");
300 printf(" -root use root-window or xscreensaver-provided window to draw in\n");
301 printf(" -window-id window to draw on\n");
302 printf(" -h show this info\n");
303 printf(" -changelog show ChangeLog and exit\n");
304 printf(" -selfrep output gzipped tarfile of the source and exit\n");
305 printf("\n");
306 }
307
308 int do_write_flags(void *dummy)
309 {
310 if (FlagsChanged)
311 {
312 FlagsChanged = 0;
313 WriteFlags();
314 }
315 return TRUE;
316 (void)dummy;
317 }
318
319
320 void erasefish(fish *f)
321 {
322 /*
323 * for something as small as a bubble, it was never worth the
324 * effort of using clipmasks to only turn of the bubble itself, so
325 * we just clear the whole rectangle.
326 */
327 //XClearArea(Dpy, wid, f->x, f->y, rwidth[f->type], rheight[f->type], False);
328 fishtype *ft = &xfish[f->type][f->direction][f->frame];
329 XClearArea(Dpy, wid, f->x, f->y, ft->w, ft->h, False);
330 }
331
332
333 /*
334 * This function can only be called if DoClipping is True. It is used to
335 * move a clipmasked fish. First the area under the fish is cleared,
336 * and then the new fish is masked in.
337 * The parameters x, y, amd d are from the old fish that is being
338 * erased before the new fish is drawn.
339 */
340 void movefish(fish *f)
341 {
342 fishtype *ft = &xfish[f->type][f->direction][f->frame];
343 XSetClipOrigin(Dpy, ft->gc, f->x, f->y);
344 XCopyArea(Dpy, ft->pix, wid,ft->gc,0,0, ft->w, ft->h, f->x, f->y);
345
346 P("movefish: %#lx %d %d %d %d %d %d %d\n",wid,f->type,f->direction,f->frame,(int)f->x,(int)f->y,ft->w,ft->h);
347 f->animtime --;
348 if (f->animtime <= 0)
349 {
350 setanimtime(f);
351 f->frame = (f->frame + 1)%NUM_FRAMES;
352 }
353 }
354
355 void setanimtime(fish*f)
356 {
357 f->animtime = 0.75*(drand48()+0.333)*maxanimtime;
358 }
359
360 void erasebubble(bubble *b)
361 {
362 XClearArea(Dpy, wid, b->x, b->y, b->size, b->size, 0);
363 P("erasebubble: %d %d %d\n",b->x, b->y, b->size);
364 }
365
366
367 void putbubble(bubble *b, unsigned long c)
368 {
369 XGCValues gcv;
370
371 int s = b->size;
372 gcv.foreground = c;
373 gcv.clip_mask = xbubbles[s];
374 gcv.clip_x_origin = b->x;
375 gcv.clip_y_origin = b->y;
376 XChangeGC(Dpy, bgc, GCForeground | GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
377 XFillRectangle(Dpy, wid, bgc, b->x, b->y, s, s);
378 P("putbubble: %d %d\n", b->x, b->y);
379 }
380
381
382 /*
383 Allocate a color by name.
384 */
385 Pixel AllocNamedColor(Display *display, char *colorName, Pixel dfltPix)
386 {
387 Pixel pix;
388 XColor exactcolor;
389 XColor scrncolor;
390
391 if (XAllocNamedColor(display,
392 DefaultColormap(display, DefaultScreen(Dpy)),
393 colorName,
394 &scrncolor,
395 &exactcolor))
396 pix = scrncolor.pixel;
397 else
398 pix = dfltPix;
399
400 return pix;
401 }
402
403
404
405 //static unsigned char bits[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
406
407 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
408 Calibrate the pixmaps and bitmaps. The right-fish data is coded in xfishy.h,
409 this is transformed to create the left-fish. The eight bubbles are coded
410 in bubbles.h as a two dimensional array.
411 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
412 void init_pixmap()
413 {
414
415 XWindowAttributes attr;
416 XGetWindowAttributes(Dpy, wid, &attr);
417
418 XpmAttributes attributes;
419 attributes.valuemask = (
420 XpmExactColors | XpmCloseness | XpmDepth);
421
422 attributes.exactColors = False; // does not matter, true or false
423 attributes.closeness = 40000; // recommended in xpm manual
424 attributes.depth = attr.depth; // essential
425
426 int k;
427 // debugging : print all fishes
428 if(0)
429 for (k=0; k<NUM_FISH*2; k++)
430 {
431 int w,h,c,m,i;
432 sscanf(fishes[k][0],"%d %d %d %d",&w,&h,&c,&m);
433 printf("k %d %d %d %d %d\n",k,w,h,c,m);
434 for (i=0; i<h+c+1; i++)
435 printf("k i: %d %d %s\n",k,i,fishes[k][i]);
436 }
437 //XGCValues gc_values;
438 //gc_values.function = GXcopy;
439 //gc_values.graphics_exposures = False;
440 //gc_values.fill_style = FillTiled;
441 //int gmask = GCFunction | GCFillStyle | GCGraphicsExposures;
442 //(void)gmask;
443 //(void)gc_values;
444 for (k = 0; k < NUM_FISH; k++)
445 {
446 int i;
447 for (i=0; i<2; i++)
448 {
449 int j;
450 for(j=0; j<NUM_FRAMES; j++)
451 {
452 Pixmap pix;
453 fishtype *ft = &xfish[k][i][j];
454 iXpmCreatePixmapFromData(Dpy, wid, fishes[2*k+j], &ft->pix, &pix, &attributes, i);
455 ft->gc = XCreateGC(Dpy, wid, 0, NULL);
456 //XChangeGC(Dpy, draw_gc, gmask, &gc_values);
457 XSetClipMask(Dpy, ft->gc, pix);
458 XFreePixmap(Dpy,pix);
459 sscanf(fishes[2*k][0],"%d %d",&(ft->w),&(ft->h));
460 }
461 }
462 }
463
464 int i;
465 for (i = 1; i <= 8; i++)
466 xbubbles[i] = XCreateBitmapFromData(Dpy, wid, (char *) xbBits[i], i, i);
467 }
468
469
470
471
472 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
473 Initialize signal so that SIGUSR1 causes secure mode to toggle.
474 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
475 void init_signals()
476 {
477 }
478
479
480 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
481 Variety of initialization calls, including getting the window up and running.
482 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
483 void initialize()
484 {
485 XWindowAttributes winfo;
486 XSetWindowAttributes attr;
487 (void)attr;
488 XGCValues vals;
489 int i,j,k;
490
491 root_window = VirtualRootWindowOfScreen(DefaultScreenOfDisplay(Dpy));
492
493 XGetWindowAttributes(Dpy, root_window, &winfo);
494 width = winfo.width;
495 height = winfo.height;
496
497 DoubleBuf=0;
498
499 int x,y;
500 unsigned int w,h,b,depth;
501 Window root, searchWin;
502 GtkWidget *gtkwin;
503 XGetGeometry(Dpy,root_window,&root,
504 &x, &y, &w, &h, &b, &depth);
505 gtkwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
506 gtk_window_set_title (GTK_WINDOW(gtkwin),"Xfishtank-A");
507 gtk_window_set_skip_taskbar_hint (GTK_WINDOW(gtkwin),TRUE);
508 gtk_window_set_skip_pager_hint (GTK_WINDOW(gtkwin),TRUE);
509
510 if (ForceRoot)
511 {
512 searchWin = DefaultRootWindow(Dpy);
513 // Are we started by xscreensaver?
514 if (getenv("XSCREENSAVER_WINDOW"))
515 {
516 searchWin = strtol(getenv("XSCREENSAVER_WINDOW"),NULL,0);
517 inxscreensaver = 1;
518 }
519 }
520 else if(UserWindow)
521 {
522 searchWin = UserWindow;
523 }
524 else
525 {
526 int HaveTrans = make_trans_window(gtkwin,
527 1 /*fullscreen*/,
528 1 /*sticky*/,
529 1 /* below*/,
530 1 /* dock*/ ,
531 NULL,
532 &searchWin);
533 if (HaveTrans)
534 {
535 xfishtank_trans = 1;
536 }
537 else
538 {
539 searchWin = root_window;
540 // Maybe, it is LXDE: find window with name pcmanfm
541 char *DesktopSession = NULL;
542 if (getenv("DESKTOP_SESSION"))
543 {
544 DesktopSession = strdup(getenv("DESKTOP_SESSION"));
545 char *a = DesktopSession;
546 while (*a) { *a = toupper(*a); a++; }
547 if (!strncmp(DesktopSession,"LXDE",4))
548 {
549 Window w = Window_With_Name(Dpy, root_window, "pcmanfm");
550 if(w)
551 {
552 searchWin = w;
553 printf("LXDE session found, using window pcmanfm\n");
554 }
555 }
556 }
557 if(DesktopSession)
558 free(DesktopSession);
559 }
560 if(xfishtank_trans)
561 {
562 XMoveWindow(Dpy,searchWin,0,0);
563 XSetWindowBackground(Dpy,searchWin,0);
564 }
565 }
566
567 P("searchWin: %#lx\n",searchWin);
568
569 wid = searchWin;
570
571 xfishtankWin = searchWin;
572
573
574 vals.foreground = vals.background = bcolor;
575 vals.graphics_exposures = False;
576 //gc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals);
577 //pgc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals);
578 bgc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals);
579 //draw_gc = XCreateGC(Dpy, wid, 0, NULL);
580 //XGCValues gc_values;
581 //gc_values.function = GXcopy;
582 //gc_values.graphics_exposures = False;
583 //gc_values.fill_style = FillTiled;
584 //XChangeGC(Dpy, draw_gc, GCFunction | GCFillStyle | GCGraphicsExposures, &gc_values);
585
586 for (i = 0; i < NUM_FISH; i++)
587 for (j=0; j<2; j++)
588 for (k=0; k<NUM_FRAMES; k++)
589 {
590 fishtype *ft = &xfish[i][j][k];
591 ft->pix = 0;
592 ft->gc = 0;
593 ft->w = 42;
594 ft->h = 42;
595 }
596
597 init_pixmap();
598 init_signals();
599
600 binfo = (bubble *) malloc(blimit * sizeof(bubble));
601 //finfo = (fish *) malloc(flimit * sizeof(fish));
602
603 wid1 = wid;
604 #ifdef DOUBLE_BUFFER
605 int xdbemajor;
606 int xdbeminor;
607 if (XdbeAvailable == -42)
608 XdbeAvailable = XdbeQueryExtension(Dpy,&xdbemajor,&xdbeminor);
609 #else
610 XdbeAvailable = 0;
611 #endif
612 UseXdbe = XdbeAvailable && WantXdbe;
613
614 #ifdef DOUBLE_BUFFER
615 //BMETHOD = XdbeBackground;
616 BMETHOD = XdbeBackground;
617 // BMETHOD = XdbeUndefined;
618 // BMETHOD = XdbeUntouched;
619 // BMETHOD = XdbeCopied; // use this to check if dbe works; fishes and bubbles will not be erased
620
621 if (UseXdbe)
622 {
623 if (backbuf)
624 XdbeDeallocateBackBufferName(Dpy,backbuf);
625 backbuf = XdbeAllocateBackBufferName(Dpy, wid1, BMETHOD);
626 wid = backbuf;
627 printf("Using double buffer: %#lx window: %#lx\n",backbuf,wid1);
628 }
629 else
630 printf("Using window: %#lx\n",wid);
631 #endif
632
633 if(xfishtank_trans)
634 printf("This is a new transparent window.\n");
635 else
636 {
637 if(UserWindow)
638 printf("This is an existing window.\n");
639 else
640 printf("This is an existing window, probably the root window.\n");
641 }
642 XClearWindow(Dpy,wid1);
643
644 // drawing on wid, XClearWindow, XSetWindowBackground on wid1
645 }
646
647
648 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
649 Create a new bubble. Placement along the x axis is random, as is the size of
650 the bubble. Increment value is determined by speed.
651 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
652 void new_bubble(bubble *b, int init) // init=0: postions at bottom, else random
653 {
654 int s;
655
656 b->x = 5+(width-10) * drand48();
657 if (init)
658 b->y = (height / 16) * (16*drand48() + 1) - 1;
659 else
660 b->y = height - 1;
661 b->size = s = 1.0 + 8*drand48();
662 if ((b->bstep = smooth * height / (float) binc[s]) == 0)
663 b->bstep = 1;
664 P("newbubble %d %d\n",b->x, b->y);
665 }
666
667 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
668 Erase old bubbles, move and draw new bubbles. Random left-right factor
669 can move bubble one size-unit in either direction.
670 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
671 void step_bubbles()
672 {
673 int i, j, s;
674 (void)j;
675 (void)s;
676 bubble *b;
677
678 for (i = 0; i < blimit; i++)
679 {
680 b = &binfo[i];
681 s = b->size;
682 /* clear */
683 /*
684 if ((b->y > 0) && (b->erased == 0))
685 {
686 erasebubble(b, s);
687 }
688 */
689 if ((b->y -= b->bstep) > 0)
690 {
691 double r = drand48();
692 if (r < 0.25) {
693 b->x -= 1;
694 } else if (r > 0.75) {
695 b->x += 1;
696 }
697 putbubble(b, bcolor);
698 } else {
699 if (drand48() < 0.25) {
700 new_bubble(b,0);
701 }
702 }
703 }
704 XFlush(Dpy);
705 }
706
707
708
709
710 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
711 Create a new fish. Placement along the y axis is random, as is the side
712 >from which the fish appears. Direction is determined from side. Increment
713 is also random.
714 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
715 void new_fish(fish *f0, int init) // init 0: position at edge, else position at random
716 {
717 int i, collide;
718 fish *f = f0;
719
720 f->type = drand48() * NUM_FISH;
721 fishtype *ft = &xfish[f->type][0][0];
722 for (i = 0, collide = 1; (i < 16) && (collide); i++)
723 {
724 f->y = (height - ft->h) * drand48();
725 if ((f->fstep = smooth * width / (8.0 * (1.0 + 8*drand48()))) == 0)
726 f->fstep = 1;
727 }
728 if (drand48() < 0.5)
729 {
730 f->direction = 0;
731 if (init)
732 f->x = ft->w + drand48()*width;
733 else
734 f->x = width;
735 }
736 else
737 {
738 f->direction = 1;
739 if (init)
740 f->x = drand48()*width-ft->w;
741 else
742 f->x = -ft->w;
743 }
744
745 f->frame = 0;
746 setanimtime(f);
747 P("newfish %d %d\n",(int)f->x,(int)f->y);
748 }
749
750 void remove_all_fishes()
751 {
752 int i;
753 for (i = 0; i < flimit; i++)
754 erasefish(&finfo[i]);
755 XFlush(Dpy);
756 }
757
758 void remove_all_bubbles()
759 {
760 int i;
761 for (i = 0; i < blimit; i++)
762 erasebubble(&binfo[i]);
763 XFlush(Dpy);
764 }
765
766 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
767 Move all the fish. Clearing old fish is accomplished by masking only the
768 exposed areas of the old fish. Random up-down factor can move fish 1/4 a
769 fish height in either direction, if no collisions are caused.
770 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
771 void move_fish()
772 {
773 int i, y, done = 0;
774 fish *f;
775
776 for (i = 0; i < flimit; i++)
777 {
778 f = &finfo[i];
779 fishtype *ft = &xfish[f->type][f->direction][f->frame];
780 if (f->direction == 0)
781 done = ((f->x -= speedfactor*f->fstep) < -ft->w);
782 else if (f->direction == 1)
783 done = ((f->x += speedfactor*f->fstep) > width);
784
785 if(0) // spontaneous change direction now and then
786 if (f->x > ft->w && f->x < width - 2*ft->w && drand48() < 0.001)
787 {
788 f->direction = 1-f->direction;
789 P("direction: %d\n",f->direction);
790 }
791 if (1)
792 {
793 if (!done)
794 {
795 double r = drand48();
796 if (r < 0.25)
797 y = f->fstep / 4;
798 else if (r > 0.75)
799 y = f->fstep / -4;
800 else
801 y = 0;
802
803 if (y)
804 f->y += y;
805 movefish(f);
806 }
807 else
808 {
809 erasefish(f);
810 new_fish(f,0);
811 }
812 }
813 }
814 XFlush(Dpy);
815 }
816
817 int do_move(void *dummy)
818 {
819 (void)dummy;
820
821 if (Done)
822 {
823 gtk_main_quit();
824 return FALSE;
825 }
826
827 #ifdef DOUBLE_BUFFER
828 if (UseXdbe)
829 {
830 XdbeSwapInfo swapInfo;
831 swapInfo.swap_window = wid1;
832 swapInfo.swap_action = BMETHOD;
833 XdbeSwapBuffers(Dpy, &swapInfo, 1);
834 if(xfishtank_trans)
835 XSetWindowBackground(Dpy,wid1,0);
836 }
837 else
838 {
839 XFlush(Dpy);
840 remove_all_bubbles();
841 remove_all_fishes();
842 }
843 #else
844 XFlush(Dpy);
845 remove_all_bubbles();
846 remove_all_fishes();
847 #endif
848 move_fish();
849 step_bubbles();
850 return TRUE;
851
852 }
853
854 int do_testfish(void *dummy)
855 {
856 (void)dummy;
857
858 static int x = 100;
859 static int y = 100;
860 int w = 64;
861 int h = 41;
862 w= 64;
863 h=59;
864 XClearArea(Dpy, wid, x,y,w,h,0);
865 x += 1;
866 y += 1;
867 if (x > 500)
868 {
869 x = 100;
870 y = 100;
871 }
872
873 GC gc = xfish[0][1][0].gc;
874 XSetClipOrigin(Dpy, gc, x, y);
875 XCopyArea(Dpy, xfish[0][1][0].pix, wid, gc,0,0,w,h,x,y);
876 XFlush(Dpy);
877 return TRUE;
878 }
879
880 void create_fishes(int n)
881 {
882 int i;
883 static int prev = 0;
884 flimit = n;
885 finfo = (fish *)realloc(finfo,sizeof(fish)*flimit);
886 for (i = prev; i < flimit; i++)
887 new_fish(&finfo[i],1);
888 prev = flimit;
889 }
890
891 void create_bubbles(int n)
892 {
893 int i;
894 static int prev = 0;
895 blimit = n;
896 binfo = (bubble *)realloc(binfo,sizeof(bubble)*blimit);
897 for (i = prev; i < blimit; i++)
898 new_bubble(&binfo[i],1);
899 prev = blimit;
900 }
901
902 void setspeed(float s)
903 {
904 speedfactor = s;
905 }
906
907 void setbcolor()
908 {
909 bcolor = AllocNamedColor(Dpy,bcolorstring,white)|0xff000000;
910 }
911
912 void setbgcolor()
913 {
914 if (inxscreensaver && !xfishtank_trans && bgcolorstring && strlen(bgcolorstring))
915 {
916 P("bgcolorstring '%s'\n",bgcolorstring);
917 bgcolor = AllocNamedColor(Dpy,bgcolorstring,black)|0xff000000;
918 XSetWindowBackground(Dpy, wid1, bgcolor);
919 XClearWindow(Dpy,wid1);
920 }
921 }
922
923
924 void Thanks()
925 {
926 if (HaltedByInterrupt)
927 printf("\nxfishtank: Caught signal %d\n",HaltedByInterrupt);
928 if (Dpy)
929 {
930 XClearWindow(Dpy,wid1);
931 XFlush(Dpy);
932 }
933 printf("\nThank you for using xfishtank\n");
934 exit(0);
935 }
936
937 void SigHandler(int signum)
938 {
939 HaltedByInterrupt = signum;
940 Done = 1;
941 }
942
943
944
945 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
946 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
947 int main(int argc, char *argv[])
948 {
949 // Circumvent wayland problems:before starting gtk: make sure that the
950 // gdk-x11 backend is used.
951
952 setenv("GDK_BACKEND","x11",1);
953 signal(SIGINT, SigHandler);
954 signal(SIGTERM, SigHandler);
955 signal(SIGHUP, SigHandler);
956
957 parse(argc, argv);
958 printf("xfishtank version %s\n",VERSION);
959 printf("Comments to: %s\n",PACKAGE_BUGREPORT);
960 printf("Url: %s\n",PACKAGE_URL);
961 srand48(time(NULL));
962 counter = 0;
963 gtk_init(&argc, &argv);
964 set_defaults();
965 ReadFlags();
966 Dpy = XOpenDisplay(display_name);
967 if(!Dpy)
968 {
969 char *name = display_name;
970 if (!display_name)
971 {
972 if(getenv("DISPLAY"))
973 name = getenv("DISPLAY");
974 }
975 printf("Cannot open display: '%s'\n",name);
976 Thanks();
977 exit(1);
978 }
979 screen = DefaultScreen(Dpy);
980
981 white = WhitePixel(Dpy, screen);
982 black = BlackPixel(Dpy, screen);
983
984 initialize();
985
986 setbcolor();
987 setbgcolor();
988
989 srand((unsigned) getpid());
990
991 create_bubbles(blimit);
992
993 create_fishes(flimit);
994
995 ui();
996
997 g_timeout_add_full(G_PRIORITY_DEFAULT, 30, do_move ,NULL, NULL);
998 g_timeout_add_full(G_PRIORITY_DEFAULT, 500, do_write_flags ,NULL, NULL);
999
1000 if(0)
1001 g_timeout_add_full(G_PRIORITY_DEFAULT, 40, do_testfish ,NULL, NULL);
1002
1003 if (nomenu)
1004 iconify();
1005 gtk_main();
1006
1007 Thanks();
1008
1009 return 0;
1010 }
1011
1012 void print_changelog()
1013 {
1014 #include "changelog.inc"
1015 }
1016
1017 #ifdef SELFREP
1018 static unsigned char tarfile[] = {
1019 #include "tarfile.inc"
1020 };
1021 void selfrep()
1022 {
1023 if(sizeof(tarfile) > 1000 && isatty(fileno(stdout)))
1024 {
1025 printf("Not sending tar file to terminal.\n");
1026 printf("Try redirecting to a file (e.g: xpenguins -selfrep > xpenguins.tar.gz),\n");
1027 printf("or use a pipe (e.g: xpenguins -selfrep | tar zxf -).\n");
1028 }
1029 else
1030 {
1031 ssize_t rc = mywrite(fileno(stdout),tarfile,sizeof(tarfile));
1032 if (rc < 0)
1033 fprintf(stderr,"xpenguins: Problems encountered during production of the tar ball.\n");
1034 }
1035 }
1036 #endif
1037
This page took 0.053406 seconds and 4 git commands to generate.