New upstream version 3.1.1
[xfishtank.git] / src / main.c
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..752b31f
--- /dev/null
@@ -0,0 +1,1037 @@
+/* -copyright-
+#-# Copyright © 2021 Eric Bina, Dave Black, TJ Phan, 
+#-#    Vincent Renardias, Willem Vermin
+#-# 
+#-# Permission is hereby granted, free of charge, to any person 
+#-# obtaining a copy of this software and associated documentation 
+#-# files (the “Software”), to deal in the Software without 
+#-# restriction, including without limitation the rights to use, 
+#-# copy, modify, merge, publish, distribute, sublicense, and/or 
+#-# sell copies of the Software, and to permit persons to whom 
+#-# the Software is furnished to do so, subject to the following 
+#-# conditions:
+#-# 
+#-# The above copyright notice and this permission notice shall 
+#-# be included in all copies or substantial portions of the Software.
+#-# 
+#-# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 
+#-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
+#-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+#-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
+#-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
+#-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+#-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
+#-# OTHER DEALINGS IN THE SOFTWARE.
+#-# 
+*/
+
+/*
+
+ *      Original Author Unknow.
+
+ *      8/10/88 - Ported from X10 to X11R3 by:
+
+ Jonathan Greenblatt (jonnyg@rover.umd.edu)
+
+ *      Cleaned up by Dave Lemke (lemke@sun.com)
+
+ *      Ported to monocrome by Jonathan Greenblatt (jonnyg@rover.umd.edu)
+
+ *     05/02/1996 Added TrueColor support by TJ Phan (phan@aur.alcatel.com)
+
+*/
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <gtk/gtk.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/xpm.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "xfishtank.h"
+#include "vroot.h"
+#include "bubbles.h"
+#include "debug.h"
+#include "transwindow.h"
+#include "utils.h"
+#include "fishes.h"
+#include "ixpm.h"
+#include "ui.h"
+
+
+#ifdef DOUBLE_BUFFER
+#include <X11/extensions/Xdbe.h>
+static int             UseXdbe;
+static XdbeSwapAction  BMETHOD;
+static int             XdbeAvailable = -42;
+#endif
+
+extern unsigned char *ReadBitmap();
+
+/* externals for pixmap and bimaps from xfishy.h */
+
+
+/* typedefs for bubble and fish structures, also caddr_t (not used in X.h) */
+typedef struct _bubble {
+   float x; 
+   float y; 
+   int   size;            // size of bubble
+   float bstep;           // increment in placement
+} bubble;
+
+typedef struct _fish {
+   float x;
+   float y;
+   int   direction;        // direction: 0: r->l
+   int   frame;            // animation frames: 0 .. NUM_FRAMES
+   int   type; 
+   float fstep;            // increment in placement
+   int   animtime;         // # drawings between change of frame
+} fish;
+
+typedef struct _fishtype {
+   Pixmap pix;
+   GC     gc;
+   int    w;
+   int    h;
+} fishtype;
+
+
+static int      binc[] = { 0, 64, 56, 48, 40, 32, 24, 16, 8 }; /* bubble increment and yes check tables */
+static int      DoubleBuf = 0;               /* Should we use double buffering */
+static int      width;                       /* width of initial window in pixels */
+static int      height;                      /* height of initial window in pixels */
+static int      screen;                      /* Default screen of this display */
+static int      inxscreensaver = 0;          /* are we running in xscreensaver? */
+static float    smooth = 0.02;               /* smoothness increment multiplier */
+static bubble  *binfo = NULL;                /* bubble info structures, allocated dynamically  */
+static fish    *finfo = NULL;                /* fish info structures, allocated dynamically */
+static Window   root_window;
+static fishtype xfish[NUM_FISH][2][NUM_FRAMES];        /* [type][left/right][frame] */
+
+static Pixmap xbubbles[9];              /* bubbles bitmaps (1 to 8, by size in pixels) */
+static Window wid;                      /* aquarium window */
+static Window wid1;
+static Window UserWindow = 0;
+static Pixel  white;
+static Pixel  black;
+static Pixel  bcolor;
+static Pixel  bgcolor;
+//static GC pgc;
+//static GC gc;
+static GC bgc;
+//static GC draw_gc;
+static int xfishtank_trans = 0;
+//static int rwidth[NUM_FISH];
+//static int rheight[NUM_FISH];
+static XdbeBackBuffer backbuf = 0;
+static int  WantXdbe = 1;
+static const int maxanimtime = 20;
+static int nomenu = 0;
+static int ForceRoot = 0;
+static int HaltedByInterrupt = 0;
+static int Done = 0;
+
+static Pixel AllocNamedColor(Display *display, char *colorName, Pixel dfltPix);
+static char *display_name=NULL;
+static int  do_move(void *dummy);
+static int  do_testfish(void *dummy);
+static int  do_write_flags(void *dummy);
+static void erasebubble(bubble *b);
+static void erasefish(fish *f);
+static void init_pixmap(void);
+static void init_signals(void);
+static void initialize(void);
+static void move_fish(void);
+static void movefish(fish *f);
+static void new_bubble(bubble *b, int init);
+static void new_fish(fish *f0, int init);
+static void parse(int argc, char **argv);
+static void putbubble(bubble *b, unsigned long c);
+static void remove_all_bubbles(void);
+static void remove_all_fishes(void);
+static void setanimtime(fish*f);
+static void step_bubbles(void);
+static void Usage(void);
+static void Thanks(void);
+static void SigHandler(int signum);
+static void print_changelog(void);
+static void selfrep(void);
+
+
+Display *Dpy = NULL;
+Window   xfishtankWin;
+int counter;
+int     blimit = 32;                 /* bubble limit */
+int     flimit = 10;                 /* fish limit */
+char   *bcolorstring  = NULL;
+char   *bgcolorstring = NULL;
+float   speedfactor;
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+   parse command line 
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void parse(int argc, char *argv[])
+{
+
+   int p = 0;
+
+#define CHECK do{if(++p >= argc){ Usage(); Thanks(); exit(1);}}while(0)
+   while (++p < argc)
+   {
+      P("argv[p] %s\n",argv[p]);
+      if (!strcmp(argv[p],"-b")) 
+      {
+        CHECK;
+        blimit = strtod(argv[p],NULL);
+      } 
+      else if (!strcmp(argv[p],"-f")) 
+      {
+        CHECK;
+        flimit = strtod(argv[p],NULL);
+      } 
+      else if (!strcmp(argv[p],"-display")) 
+      {
+        CHECK;
+        display_name = strdup(argv[p]);
+      } 
+      else if (!strcmp(argv[p],"-bc")) 
+      {
+        CHECK;
+        if(bcolorstring)
+           free(bcolorstring);
+        bcolorstring = strdup(argv[p]);
+      } 
+      else if (!strcmp(argv[p],"-bgc")) 
+      {
+        CHECK;
+        if(bgcolorstring)
+           free(bgcolorstring);
+        bgcolorstring = strdup(argv[p]);
+      } 
+      else if (!strcmp(argv[p],"-double")) 
+      {
+        CHECK;
+        WantXdbe = (strtod(argv[p],NULL)>0);
+      } 
+      else if (!strcmp(argv[p],"-speed")) 
+      {
+        CHECK;
+        speedfactor = (strtod(argv[p],NULL)/100.0);
+      } 
+      else if (!strcmp(argv[p],"-h")) 
+      {
+        Usage();
+        Thanks();
+      } 
+      else if (!strcmp(argv[p],"-defaults")) 
+      {
+        set_defaults();
+      } 
+      else if (!strcmp(argv[p],"-nomenu")) 
+      {
+        nomenu = 1;
+      } 
+      else if (!strcmp(argv[p],"-root")) 
+      {
+        ForceRoot = 1;
+      } 
+      else if (!strcmp(argv[p],"-window-id")) 
+      {
+        CHECK;
+        UserWindow = strtod(argv[p],NULL); 
+      } 
+      else if (!strcmp(argv[p],"-changelog")) 
+      {
+        print_changelog();
+        Thanks();
+      } 
+#ifdef SELFREP
+      else if (!strcmp(argv[p],"-selfrep")) 
+      {
+        selfrep();
+        exit(0);
+      } 
+#endif
+      else 
+      {
+        printf("Not understood: %s\n",argv[p]);
+        Usage();
+        Thanks();
+      }
+   }
+}
+
+void set_defaults()
+{
+   flimit        = 10;
+   blimit        = 32;
+   if(bcolorstring)
+      free(bcolorstring);
+   bcolorstring  = strdup("lightblue");
+   if(bgcolorstring)
+      free(bgcolorstring);
+   bgcolorstring = strdup("darkblue");
+   speedfactor   = 1.0;
+}
+
+void Usage()
+{
+   printf("\nUsage: xfishtank [options]\n\n");
+   printf("Options:\n");
+   printf("        -b n       number of bubbles (default 32)\n");
+   printf("        -f n       number of fish (default 10)\n");
+   printf("        -bc color  color of bubbles (default \"lightblue\")\n");
+   printf("        -double n  1: double buffering, 0: do not use (default: 1)\n");
+   printf("        -speed     speed of fishes (default: 100)\n");
+   printf("        -defaults  all options to default\n");
+   printf("        -nomenu    do not show menu\n");
+   printf("        -display   DISPLAY to draw on (default \"\")\n");
+   printf("        -root      use root-window or xscreensaver-provided window to draw in\n");
+   printf("        -window-id window to draw on\n");
+   printf("        -h         show this info\n");
+   printf("        -changelog show ChangeLog and exit\n");
+   printf("        -selfrep   output gzipped tarfile of the source and exit\n");
+   printf("\n");
+}
+
+int do_write_flags(void *dummy)
+{
+   if (FlagsChanged)
+   {
+      FlagsChanged = 0;
+      WriteFlags();
+   }
+   return TRUE;
+   (void)dummy;
+}
+
+
+void erasefish(fish *f)
+{
+   /*
+    * for something as small as a bubble, it was never worth the
+    * effort of using clipmasks to only turn of the bubble itself, so
+    * we just clear the whole rectangle.
+    */
+   //XClearArea(Dpy, wid, f->x, f->y, rwidth[f->type], rheight[f->type], False);
+   fishtype *ft = &xfish[f->type][f->direction][f->frame];
+   XClearArea(Dpy, wid, f->x, f->y, ft->w, ft->h, False);
+}
+
+
+/*
+ * This function can only be called if DoClipping is True.  It is used to
+ * move a clipmasked fish.  First the area under the fish is cleared,
+ * and then the new fish is masked in.
+ * The parameters x, y, amd d are from the old fish that is being
+ * erased before the new fish is drawn.
+ */
+void movefish(fish *f)
+{
+   fishtype *ft = &xfish[f->type][f->direction][f->frame];
+   XSetClipOrigin(Dpy, ft->gc, f->x, f->y);
+   XCopyArea(Dpy, ft->pix, wid,ft->gc,0,0, ft->w, ft->h, f->x, f->y);
+
+   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);
+   f->animtime --;
+   if (f->animtime <= 0)
+   {
+      setanimtime(f);
+      f->frame = (f->frame + 1)%NUM_FRAMES;
+   }
+}
+
+void setanimtime(fish*f)
+{
+   f->animtime = 0.75*(drand48()+0.333)*maxanimtime;
+}
+
+void erasebubble(bubble *b)
+{
+   XClearArea(Dpy, wid, b->x, b->y, b->size, b->size, 0);
+   P("erasebubble: %d %d %d\n",b->x, b->y, b->size);
+}
+
+
+void putbubble(bubble *b, unsigned long c)
+{
+   XGCValues gcv;
+
+   int s = b->size;
+   gcv.foreground = c;
+   gcv.clip_mask = xbubbles[s];
+   gcv.clip_x_origin = b->x;
+   gcv.clip_y_origin = b->y;
+   XChangeGC(Dpy, bgc, GCForeground | GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
+   XFillRectangle(Dpy, wid, bgc, b->x, b->y, s, s);
+   P("putbubble: %d %d\n", b->x, b->y);
+}
+
+
+/*
+   Allocate a color by name.
+   */
+Pixel AllocNamedColor(Display *display, char *colorName, Pixel dfltPix)
+{
+   Pixel  pix;
+   XColor exactcolor;
+   XColor scrncolor;
+
+   if (XAllocNamedColor(display,
+           DefaultColormap(display, DefaultScreen(Dpy)),
+           colorName,
+           &scrncolor,
+           &exactcolor))
+      pix = scrncolor.pixel;
+   else
+      pix = dfltPix;
+
+   return pix;
+}
+
+
+
+//static unsigned char bits[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+   Calibrate the pixmaps and bitmaps.  The right-fish data is coded in xfishy.h,
+   this is transformed to create the left-fish.  The eight bubbles are coded
+   in bubbles.h as a two dimensional array.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void init_pixmap()
+{
+
+   XWindowAttributes attr;
+   XGetWindowAttributes(Dpy, wid, &attr);
+
+   XpmAttributes attributes;
+   attributes.valuemask = (
+        XpmExactColors | XpmCloseness | XpmDepth);
+
+   attributes.exactColors = False;      // does not matter, true or false
+   attributes.closeness   = 40000;      // recommended in xpm manual
+   attributes.depth       = attr.depth; // essential
+
+   int k;
+   // debugging : print all fishes
+   if(0)
+      for (k=0; k<NUM_FISH*2; k++)
+      {
+        int w,h,c,m,i;
+        sscanf(fishes[k][0],"%d %d %d %d",&w,&h,&c,&m);
+        printf("k %d %d %d %d %d\n",k,w,h,c,m);
+        for (i=0; i<h+c+1; i++)
+           printf("k i: %d %d %s\n",k,i,fishes[k][i]);
+      }
+   //XGCValues gc_values;
+   //gc_values.function           = GXcopy;
+   //gc_values.graphics_exposures = False;
+   //gc_values.fill_style         = FillTiled;
+   //int gmask = GCFunction | GCFillStyle | GCGraphicsExposures;
+   //(void)gmask;
+   //(void)gc_values;
+   for (k = 0; k < NUM_FISH; k++) 
+   {
+      int i;
+      for (i=0; i<2; i++)
+      {
+        int j;
+        for(j=0; j<NUM_FRAMES; j++)
+        {
+           Pixmap pix;
+           fishtype *ft = &xfish[k][i][j];
+           iXpmCreatePixmapFromData(Dpy, wid, fishes[2*k+j], &ft->pix, &pix, &attributes, i);
+           ft->gc = XCreateGC(Dpy, wid, 0, NULL);
+           //XChangeGC(Dpy, draw_gc, gmask, &gc_values);
+           XSetClipMask(Dpy, ft->gc, pix);
+           XFreePixmap(Dpy,pix);
+           sscanf(fishes[2*k][0],"%d %d",&(ft->w),&(ft->h));
+        }
+      }
+   }
+
+   int i;
+   for (i = 1; i <= 8; i++) 
+      xbubbles[i] = XCreateBitmapFromData(Dpy, wid, (char *) xbBits[i], i, i);
+}
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+   Initialize signal so that SIGUSR1 causes secure mode to toggle.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void init_signals()
+{
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+   Variety of initialization calls, including getting the window up and running.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void initialize()
+{
+   XWindowAttributes winfo;
+   XSetWindowAttributes attr;
+   (void)attr;
+   XGCValues vals;
+   int i,j,k;
+
+   root_window = VirtualRootWindowOfScreen(DefaultScreenOfDisplay(Dpy));
+
+   XGetWindowAttributes(Dpy, root_window, &winfo);
+   width = winfo.width;
+   height = winfo.height;
+
+   DoubleBuf=0;
+
+   int x,y;
+   unsigned int w,h,b,depth;
+   Window root, searchWin;
+   GtkWidget   *gtkwin;
+   XGetGeometry(Dpy,root_window,&root,
+        &x, &y, &w, &h, &b, &depth);
+   gtkwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); 
+   gtk_window_set_title             (GTK_WINDOW(gtkwin),"Xfishtank-A");
+   gtk_window_set_skip_taskbar_hint (GTK_WINDOW(gtkwin),TRUE);
+   gtk_window_set_skip_pager_hint   (GTK_WINDOW(gtkwin),TRUE);
+
+   if (ForceRoot)
+   {
+      searchWin = DefaultRootWindow(Dpy);
+      // Are we started by xscreensaver?
+      if (getenv("XSCREENSAVER_WINDOW"))
+      {
+        searchWin = strtol(getenv("XSCREENSAVER_WINDOW"),NULL,0);
+        inxscreensaver = 1;
+      }
+   }
+   else if(UserWindow)
+   {
+      searchWin = UserWindow;
+   }
+   else
+   {
+      int HaveTrans = make_trans_window(gtkwin,
+           1 /*fullscreen*/,
+           1 /*sticky*/,
+           1 /* below*/,
+           1 /* dock*/ , 
+           NULL,
+           &searchWin);
+      if (HaveTrans)
+      {
+        xfishtank_trans = 1;
+      }
+      else
+      {
+        searchWin = root_window;
+        // Maybe, it is LXDE: find window with name pcmanfm
+        char *DesktopSession = NULL;
+        if (getenv("DESKTOP_SESSION"))
+        {
+           DesktopSession = strdup(getenv("DESKTOP_SESSION"));
+           char *a = DesktopSession;
+           while (*a) { *a = toupper(*a); a++; }
+           if (!strncmp(DesktopSession,"LXDE",4)) 
+           {
+              Window w = Window_With_Name(Dpy, root_window, "pcmanfm");
+              if(w)
+              {
+                 searchWin = w;
+                 printf("LXDE session found, using window pcmanfm\n");
+              }
+           }
+        }
+        if(DesktopSession)
+           free(DesktopSession);
+      }
+      if(xfishtank_trans)
+      {
+        XMoveWindow(Dpy,searchWin,0,0);
+        XSetWindowBackground(Dpy,searchWin,0);
+      }
+   }
+
+   P("searchWin: %#lx\n",searchWin);
+
+   wid = searchWin;
+
+   xfishtankWin = searchWin;
+
+
+   vals.foreground = vals.background = bcolor;
+   vals.graphics_exposures = False;
+   //gc  = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals);
+   //pgc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals);
+   bgc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals);
+   //draw_gc = XCreateGC(Dpy, wid, 0, NULL);
+   //XGCValues gc_values;
+   //gc_values.function           = GXcopy;
+   //gc_values.graphics_exposures = False;
+   //gc_values.fill_style         = FillTiled;
+   //XChangeGC(Dpy, draw_gc, GCFunction | GCFillStyle | GCGraphicsExposures, &gc_values);
+
+   for (i = 0; i < NUM_FISH; i++) 
+      for (j=0; j<2; j++)
+        for (k=0; k<NUM_FRAMES; k++)
+        {
+           fishtype *ft = &xfish[i][j][k];
+           ft->pix = 0;
+           ft->gc  = 0;
+           ft->w   = 42;
+           ft->h   = 42;
+        }
+
+   init_pixmap();
+   init_signals();
+
+   binfo = (bubble *) malloc(blimit * sizeof(bubble));
+   //finfo = (fish *) malloc(flimit * sizeof(fish));
+
+   wid1 = wid;
+#ifdef DOUBLE_BUFFER
+   int xdbemajor;
+   int xdbeminor;
+   if (XdbeAvailable == -42)
+      XdbeAvailable = XdbeQueryExtension(Dpy,&xdbemajor,&xdbeminor);
+#else
+   XdbeAvailable = 0;
+#endif
+   UseXdbe = XdbeAvailable && WantXdbe;
+
+#ifdef DOUBLE_BUFFER
+   //BMETHOD = XdbeBackground;
+   BMETHOD = XdbeBackground;
+   // BMETHOD = XdbeUndefined;
+   // BMETHOD = XdbeUntouched;
+   // BMETHOD = XdbeCopied;     // use this to check if dbe works; fishes and bubbles will not be erased
+
+   if (UseXdbe)
+   {
+      if (backbuf)
+        XdbeDeallocateBackBufferName(Dpy,backbuf);
+      backbuf = XdbeAllocateBackBufferName(Dpy, wid1, BMETHOD);
+      wid = backbuf;
+      printf("Using double buffer: %#lx window: %#lx\n",backbuf,wid1);
+   }
+   else
+      printf("Using window: %#lx\n",wid);
+#endif
+
+   if(xfishtank_trans)
+      printf("This is a new transparent window.\n");
+   else
+   {
+      if(UserWindow)
+        printf("This is an existing window.\n");
+      else
+        printf("This is an existing window, probably the root window.\n");
+   }
+   XClearWindow(Dpy,wid1);
+
+   // drawing on wid, XClearWindow, XSetWindowBackground on wid1
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+   Create a new bubble.  Placement along the x axis is random, as is the size of
+   the bubble.  Increment value is determined by speed.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void new_bubble(bubble *b, int init)  // init=0: postions at bottom, else random
+{
+   int s;
+
+   b->x = 5+(width-10) * drand48();
+   if (init)
+      b->y = (height / 16) * (16*drand48() + 1) - 1;
+   else
+      b->y = height - 1;
+   b->size = s = 1.0 + 8*drand48();
+   if ((b->bstep = smooth * height / (float) binc[s]) == 0)
+      b->bstep = 1;
+   P("newbubble %d %d\n",b->x, b->y);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+   Erase old bubbles, move and draw new bubbles.  Random left-right factor
+   can move bubble one size-unit in either direction.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void step_bubbles()
+{
+   int i, j, s;
+   (void)j;
+   (void)s;
+   bubble *b;
+
+   for (i = 0; i < blimit; i++) 
+   {
+      b = &binfo[i];
+      s = b->size;
+      /* clear */
+      /*
+        if ((b->y > 0) && (b->erased == 0)) 
+        {
+        erasebubble(b, s);
+        }
+        */
+      if ((b->y -= b->bstep) > 0) 
+      {
+        double r = drand48();
+        if (r < 0.25) {
+           b->x -= 1;
+        } else if (r > 0.75) {
+           b->x += 1;
+        }
+        putbubble(b, bcolor);
+      } else {
+        if (drand48() < 0.25) {
+           new_bubble(b,0);
+        }
+      }
+   }
+   XFlush(Dpy);
+}
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+   Create a new fish.   Placement along the y axis is random, as is the side
+   >from which the fish appears.  Direction is determined from side.  Increment
+   is also random.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void new_fish(fish *f0, int init)  // init 0: position at edge, else position at random
+{
+   int i, collide;
+   fish *f = f0;
+
+   f->type = drand48() * NUM_FISH;
+   fishtype *ft = &xfish[f->type][0][0];
+   for (i = 0, collide = 1; (i < 16) && (collide); i++) 
+   {
+      f->y = (height - ft->h) * drand48();
+      if ((f->fstep = smooth * width / (8.0 * (1.0 + 8*drand48()))) == 0) 
+        f->fstep = 1;
+   }
+   if (drand48() < 0.5) 
+   {
+      f->direction = 0;
+      if (init)
+        f->x = ft->w + drand48()*width;
+      else
+        f->x = width;
+   } 
+   else 
+   {
+      f->direction = 1;
+      if (init)
+        f->x = drand48()*width-ft->w;
+      else
+        f->x = -ft->w;
+   }
+
+   f->frame = 0;
+   setanimtime(f);
+   P("newfish %d %d\n",(int)f->x,(int)f->y);
+}
+
+void remove_all_fishes()
+{
+   int i;
+   for (i = 0; i < flimit; i++) 
+      erasefish(&finfo[i]);
+   XFlush(Dpy);
+}
+
+void remove_all_bubbles()
+{
+   int i;
+   for (i = 0; i < blimit; i++) 
+      erasebubble(&binfo[i]);
+   XFlush(Dpy);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+   Move all the fish.  Clearing old fish is accomplished by masking only the
+   exposed areas of the old fish.  Random up-down factor can move fish 1/4 a
+   fish height in either direction, if no collisions are caused.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void move_fish()
+{
+   int i, y, done = 0;
+   fish *f;
+
+   for (i = 0; i < flimit; i++) 
+   {
+      f = &finfo[i];
+      fishtype *ft = &xfish[f->type][f->direction][f->frame];
+      if (f->direction == 0) 
+        done = ((f->x -= speedfactor*f->fstep) < -ft->w);
+      else if (f->direction == 1) 
+        done = ((f->x += speedfactor*f->fstep) > width);
+
+      if(0) // spontaneous change direction now and then
+        if (f->x > ft->w && f->x < width - 2*ft->w && drand48() < 0.001) 
+        {
+           f->direction = 1-f->direction;
+           P("direction: %d\n",f->direction);
+        }
+      if (1) 
+      {
+        if (!done) 
+        {
+           double r = drand48();
+           if (r < 0.25) 
+              y = f->fstep / 4;
+           else if (r > 0.75) 
+              y = f->fstep / -4;
+           else 
+              y = 0;
+
+           if (y) 
+              f->y += y;
+           movefish(f);
+        } 
+        else
+        {
+           erasefish(f);
+           new_fish(f,0);
+        }
+      } 
+   }
+   XFlush(Dpy);
+}
+
+int do_move(void *dummy)
+{
+   (void)dummy;
+
+   if (Done)
+   {
+      gtk_main_quit();
+      return FALSE;
+   }
+
+#ifdef DOUBLE_BUFFER
+   if (UseXdbe)
+   {
+      XdbeSwapInfo swapInfo;
+      swapInfo.swap_window = wid1; 
+      swapInfo.swap_action = BMETHOD;
+      XdbeSwapBuffers(Dpy, &swapInfo, 1);
+      if(xfishtank_trans)
+        XSetWindowBackground(Dpy,wid1,0);
+   }
+   else
+   {
+      XFlush(Dpy); 
+      remove_all_bubbles();
+      remove_all_fishes();
+   }
+#else
+   XFlush(Dpy); 
+   remove_all_bubbles();
+   remove_all_fishes();
+#endif
+   move_fish();
+   step_bubbles();
+   return TRUE;
+
+}
+
+int do_testfish(void *dummy)
+{
+   (void)dummy;
+
+   static int x = 100;
+   static int y = 100;
+   int w = 64;
+   int h = 41;
+   w= 64;
+   h=59;
+   XClearArea(Dpy, wid, x,y,w,h,0);
+   x += 1;
+   y += 1;
+   if (x > 500)
+   {
+      x = 100;
+      y = 100;
+   }
+
+   GC gc = xfish[0][1][0].gc;
+   XSetClipOrigin(Dpy, gc, x, y);
+   XCopyArea(Dpy, xfish[0][1][0].pix, wid, gc,0,0,w,h,x,y);
+   XFlush(Dpy);
+   return TRUE;
+}
+
+void create_fishes(int n)
+{
+   int i;
+   static int prev = 0;
+   flimit = n;
+   finfo = (fish *)realloc(finfo,sizeof(fish)*flimit);
+   for (i = prev; i < flimit; i++)
+      new_fish(&finfo[i],1);
+   prev = flimit;
+}
+
+void create_bubbles(int n)
+{
+   int i;
+   static int prev = 0;
+   blimit = n;
+   binfo = (bubble *)realloc(binfo,sizeof(bubble)*blimit);
+   for (i = prev; i < blimit; i++)
+      new_bubble(&binfo[i],1);
+   prev = blimit;
+}
+
+void setspeed(float s)
+{
+   speedfactor = s;
+}
+
+void setbcolor()
+{
+   bcolor = AllocNamedColor(Dpy,bcolorstring,white)|0xff000000;
+}
+
+void setbgcolor()
+{
+   if (inxscreensaver && !xfishtank_trans && bgcolorstring && strlen(bgcolorstring))
+   {
+      P("bgcolorstring '%s'\n",bgcolorstring);
+      bgcolor = AllocNamedColor(Dpy,bgcolorstring,black)|0xff000000;
+      XSetWindowBackground(Dpy, wid1, bgcolor);
+      XClearWindow(Dpy,wid1);
+   }
+}
+
+
+void Thanks()
+{
+   if (HaltedByInterrupt)
+      printf("\nxfishtank: Caught signal %d\n",HaltedByInterrupt);
+   if (Dpy)
+   {
+      XClearWindow(Dpy,wid1);
+      XFlush(Dpy);
+   }
+   printf("\nThank you for using xfishtank\n");
+   exit(0);
+}
+
+void SigHandler(int signum)
+{
+   HaltedByInterrupt = signum;
+   Done = 1;
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+int main(int argc, char *argv[])
+{
+   // Circumvent wayland problems:before starting gtk: make sure that the 
+   // gdk-x11 backend is used.
+
+   setenv("GDK_BACKEND","x11",1);
+   signal(SIGINT,  SigHandler);
+   signal(SIGTERM, SigHandler);
+   signal(SIGHUP,  SigHandler);
+
+   parse(argc, argv);
+   printf("xfishtank version %s\n",VERSION);
+   printf("Comments to: %s\n",PACKAGE_BUGREPORT);
+   printf("Url: %s\n",PACKAGE_URL);
+   srand48(time(NULL));
+   counter = 0;
+   gtk_init(&argc, &argv);
+   set_defaults();
+   ReadFlags();
+   Dpy = XOpenDisplay(display_name);
+   if(!Dpy)
+   {
+      char *name = display_name;
+      if (!display_name)
+      {
+        if(getenv("DISPLAY"))
+           name = getenv("DISPLAY");
+      }
+      printf("Cannot open display: '%s'\n",name);
+      Thanks();
+      exit(1);
+   }
+   screen = DefaultScreen(Dpy);
+
+   white = WhitePixel(Dpy, screen);
+   black = BlackPixel(Dpy, screen);
+
+   initialize();
+
+   setbcolor();
+   setbgcolor();
+
+   srand((unsigned) getpid());
+
+   create_bubbles(blimit);
+
+   create_fishes(flimit);
+
+   ui();
+
+   g_timeout_add_full(G_PRIORITY_DEFAULT, 30,   do_move        ,NULL, NULL);
+   g_timeout_add_full(G_PRIORITY_DEFAULT, 500,  do_write_flags ,NULL, NULL);
+
+   if(0)
+      g_timeout_add_full(G_PRIORITY_DEFAULT, 40,   do_testfish ,NULL, NULL);
+
+   if (nomenu)
+      iconify();
+   gtk_main();
+
+   Thanks();
+
+   return 0;
+}
+
+void print_changelog()
+{
+#include "changelog.inc"
+}
+
+#ifdef SELFREP
+static unsigned char tarfile[] = {
+#include "tarfile.inc"
+};
+void selfrep()
+{
+   if(sizeof(tarfile) > 1000 && isatty(fileno(stdout)))
+   {
+      printf("Not sending tar file to terminal.\n");
+      printf("Try redirecting to a file (e.g: xpenguins -selfrep > xpenguins.tar.gz),\n");
+      printf("or use a pipe (e.g: xpenguins -selfrep | tar zxf -).\n"); 
+   }
+   else
+   {
+      ssize_t rc = mywrite(fileno(stdout),tarfile,sizeof(tarfile));
+      if (rc < 0)
+        fprintf(stderr,"xpenguins: Problems encountered during production of the tar ball.\n");
+   }
+}
+#endif
+
This page took 0.022171 seconds and 4 git commands to generate.