const char * const xdwCopyright= /*===========================================================================*/ " xdesktopwaves 1.3 - simulation of water waves on the X Windows desktop \n" " \n" " Copyright (C) 2004 Oliver Hamann (olha@users.sourceforge.net) \n" " \n" " This program is free software; you can redistribute it and/or modify \n" " it under the terms of the GNU General Public License as published by \n" " the Free Software Foundation; either version 2 of the License, or \n" " (at your option) any later version. \n" " \n" " This program is distributed in the hope that it will be useful, \n" " but WITHOUT ANY WARRANTY; without even the implied warranty of \n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \n" " GNU General Public License for more details. \n" " \n" " You should have received a copy of the GNU General Public License \n" " along with this program; if not, write to the Free Software \n" " Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \n"; /*===========================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include /*============================================================================*/ /*================================= Options ==================================*/ /*============================================================================*/ typedef enum { XDW_USE_ROOT, XDW_BACKDROP, XDW_WMBACKDROP, XDW_WINDOW } xdwWindowCreationType; typedef enum { XDW_OPAQUE, XDW_STIPPLED, XDW_SHAPED, XDW_WMOPACITY } xdwTransparencyType; /* Path/name of executable */ const char * xdwArg0="xdesktopwaves"; /* Option variables. */ int xdwOptVerb; const char * xdwOptDisplay; Bool xdwOptEnd; xdwWindowCreationType xdwOptWindowCreation; xdwTransparencyType xdwOptTransparency; int xdwOptTranspaParam; int xdwOptFrameRate; int xdwOptSimsPerFrame; int xdwOptEventsPerFrame; int xdwOptResDivX; int xdwOptResDivY; int xdwOptCellW; int xdwOptCellH; int xdwOptMaxColors; Bool xdwOptDoubleBuffer; int xdwOptNice; Bool xdwOptIdle; int xdwOptMaxOptimization; const char * xdwOptWaterColor; const char * xdwOptSkyColor; const char * xdwOptLightColor; Bool xdwOptWavesByMouse; Bool xdwOptWavesByWindows; int xdwOptRain; int xdwOptStorm; int xdwOptViscosity; int xdwOptSkyIntensity; int xdwOptLightIntensity; int xdwOptLightAltitude; int xdwOptLightAzimuth; /* Parameters for the quality option. */ const struct { int frameRate; int simsPerFrame; int eventsPerFrame; int resDivX; int resDivY; int cellWidth; int cellHeight; int maxColors; } xdwQualityParams[10]={ { 12, 2, 1, 6, 6, 3, 3, 8 }, { 14, 3, 1, 5, 5, 3, 3, 12 }, { 16, 4, 1, 4, 4, 3, 3, 16 }, { 18, 5, 1, 3, 3, 3, 3, 22 }, { 20, 5, 1, 2, 2, 3, 3, 32 }, { 20, 6, 1, 2, 2, 2, 2, 48 }, { 25, 5, 2, 1, 2, 4, 2, 64 }, { 20, 9, 2, 2, 2, 1, 1, 64 }, { 25, 8, 3, 1, 2, 2, 1, 96 }, { 25,12, 4, 1, 1, 1, 1, 128 } }; /* Color themes. */ const char * const xdwColorThemes[10][3] = { { "#406080", "#9696C0", "#FFFFFF" }, { "#1020C0", "#8080D0", "#FFFFFF" }, { "#000C18", "#122E20", "#FFFF80" }, { "#384840", "#90A0B0", "#FFFFE8" }, { "#808060", "#C0C080", "#FFF0F0" }, { "#000000", "#004030", "#FFFFFF" }, { "#78A020", "#80E098", "#F0FFF0" }, { "#504030", "#807050", "#FFFFE0" }, { "#800000", "#FF3060", "#C0FFFF" }, { "#F0F8FF", "#807050", "#586040" } }; static void xdwSetDefaultOptions(void) { /* Set default values for all the options. */ xdwOptVerb =0; xdwOptDisplay =NULL; xdwOptEnd =False; xdwOptWindowCreation =XDW_BACKDROP; xdwOptTransparency =XDW_SHAPED; xdwOptTranspaParam =0; xdwOptFrameRate =xdwQualityParams[5].frameRate; xdwOptSimsPerFrame =xdwQualityParams[5].simsPerFrame; xdwOptEventsPerFrame =xdwQualityParams[5].eventsPerFrame; xdwOptResDivX =xdwQualityParams[5].resDivX; xdwOptResDivY =xdwQualityParams[5].resDivY; xdwOptCellW =xdwQualityParams[5].cellWidth; xdwOptCellH =xdwQualityParams[5].cellHeight; xdwOptMaxColors =xdwQualityParams[5].maxColors; xdwOptDoubleBuffer =True; xdwOptNice =0; xdwOptIdle =True; xdwOptMaxOptimization=2; xdwOptWaterColor =xdwColorThemes[0][0]; xdwOptSkyColor =xdwColorThemes[0][1]; xdwOptLightColor =xdwColorThemes[0][2]; xdwOptWavesByMouse =True; xdwOptWavesByWindows =True; xdwOptRain =0; xdwOptStorm =0; xdwOptViscosity =3; xdwOptSkyIntensity =5; xdwOptLightIntensity =5; xdwOptLightAltitude =30; xdwOptLightAzimuth =-35; } static Bool xdwParseInt(const char * str, int minVal, int maxVal, int * pVal) { int sign, digit, val; /* Parse a decimal integer without allowing unexpected characters, and check range. */ if (*str=='-') { sign=-1; str++; } else if (*str=='+') { sign=1; str++; } else sign=1; if (!*str) return False; for (val=0; ; str++) { digit=*str; if (!digit) break; digit-='0'; if (digit<0 || digit>9) return False; val=val*10+digit; } val*=sign; if (valmaxVal) return False; *pVal=val; return True; } static void xdwParseOptions(int argc, char * argv[]) { const char * opt, * nm; char transpaStr[256]; int i, j; /* Keep name of executable. */ xdwArg0=argv[0]; /* Parse options. */ for (i=1; i]...\n" "Options:\n" " -h|-help -db|-doublebuffer\n" " -V|-version -ndb|-nodoublebuffer\n" " -v|-verbose -n|-nice \n" " -vv|-veryverbose -i|-idle\n" " -d|-display -ni|-noidle\n" " -e|-end -mo|-maxoptimization \n" " -r|-root -c|-colortheme <0-9>\n" " -b|-backdrop -wc|-watercolor \n" " -wmb|-wmbackdrop -sc|-skycolor \n" " -w|-window -lc|-lightcolor \n" " -o|-opaque -wm|-wavesbymouse\n" " -t|-stippled -nwm|-nowavesbymouse\n" " -s|-shaped -ww|-wavesbywindows\n" " -wmo|-wmopacity <0-100> -nww|-nowavesbywindows\n" " -q|-quality <0-9> -rn|-rain <0-10>\n" " -fr|-framerate -st|-storm <0-10>\n" " -sf|-simsperframe -vs|-viscosity <1-5>\n" " -ef|-eventsperframe -si|-skyintensity <1-10>\n" " -rd|-resdivision -li|-lightintensity <1-10>\n" " -cs|-cellsize -lal|-lightaltitude \n" " -mc|-maxcolors <2-128> -laz|-lightazimuth \n" "For details please refer to the manual page: xdesktopwaves(1)\n", xdwArg0 ); exit(0); } else if (!strcmp(nm,"V") || !strcmp(nm,"version")) { printf("\n%s\n",xdwCopyright); exit(0); } else if (!strcmp(nm,"v") || !strcmp(nm,"verbose")) { xdwOptVerb=1; } else if (!strcmp(nm,"vv") || !strcmp(nm,"veryverbose")) { xdwOptVerb=2; } else if (!strcmp(nm,"d") || !strcmp(nm,"display")) { if (i>=argc) goto L_MISSING_OPT_ARG; xdwOptDisplay=argv[i++]; } else if (!strcmp(nm,"e") || !strcmp(nm,"end")) { xdwOptEnd=True; } else if (!strcmp(nm,"r") || !strcmp(nm,"root")) { xdwOptWindowCreation=XDW_USE_ROOT; } else if (!strcmp(nm,"b") || !strcmp(nm,"backdrop")) { xdwOptWindowCreation=XDW_BACKDROP; } else if (!strcmp(nm,"wmb") || !strcmp(nm,"wmbackdrop")) { xdwOptWindowCreation=XDW_WMBACKDROP; } else if (!strcmp(nm,"w") || !strcmp(nm,"window")) { xdwOptWindowCreation=XDW_WINDOW; } else if (!strcmp(nm,"o") || !strcmp(nm,"opaque")) { xdwOptTransparency=XDW_OPAQUE; } else if (!strcmp(nm,"t") || !strcmp(nm,"stippled")) { xdwOptTransparency=XDW_STIPPLED; } else if (!strcmp(nm,"s") || !strcmp(nm,"shaped")) { xdwOptTransparency=XDW_SHAPED; } else if (!strcmp(nm,"wmo") || !strcmp(nm,"wmopacity")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],0,100,&j)) goto L_BAD_OPT_ARG; xdwOptTransparency=XDW_WMOPACITY; xdwOptTranspaParam=j; } else if (!strcmp(nm,"q") || !strcmp(nm,"quality")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],0,9,&j)) goto L_BAD_OPT_ARG; xdwOptFrameRate =xdwQualityParams[j].frameRate; xdwOptSimsPerFrame =xdwQualityParams[j].simsPerFrame; xdwOptEventsPerFrame=xdwQualityParams[j].eventsPerFrame; xdwOptResDivX =xdwQualityParams[j].resDivX; xdwOptResDivY =xdwQualityParams[j].resDivY; xdwOptCellW =xdwQualityParams[j].cellWidth; xdwOptCellH =xdwQualityParams[j].cellHeight; xdwOptMaxColors =xdwQualityParams[j].maxColors; } else if (!strcmp(nm,"fr") || !strcmp(nm,"framerate")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,1000,&j)) goto L_BAD_OPT_ARG; xdwOptFrameRate=j; } else if (!strcmp(nm,"sf") || !strcmp(nm,"simsperframe")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,1000,&j)) goto L_BAD_OPT_ARG; xdwOptSimsPerFrame=j; } else if (!strcmp(nm,"ef") || !strcmp(nm,"eventsperframe")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,1000,&j)) goto L_BAD_OPT_ARG; xdwOptEventsPerFrame=j; } else if (!strcmp(nm,"rd") || !strcmp(nm,"resdivision")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,256,&j)) goto L_BAD_OPT_ARG; xdwOptResDivX=j; if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,256,&j)) goto L_BAD_OPT_ARG; xdwOptResDivY=j; } else if (!strcmp(nm,"cs") || !strcmp(nm,"cellsize")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,256,&j)) goto L_BAD_OPT_ARG; xdwOptCellW=j; if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,256,&j)) goto L_BAD_OPT_ARG; xdwOptCellH=j; } else if (!strcmp(nm,"mc") || !strcmp(nm,"maxcolors")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],2,128,&j)) goto L_BAD_OPT_ARG; xdwOptMaxColors=j; } else if (!strcmp(nm,"db") || !strcmp(nm,"doublebuffer")) { xdwOptDoubleBuffer=True; } else if (!strcmp(nm,"ndb") || !strcmp(nm,"nodoublebuffer")) { xdwOptDoubleBuffer=False; } else if (!strcmp(nm,"n") || !strcmp(nm,"nice")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],-100,100,&j)) goto L_BAD_OPT_ARG; xdwOptNice=j; } else if (!strcmp(nm,"i") || !strcmp(nm,"idle")) { xdwOptIdle=True; } else if (!strcmp(nm,"ni") || !strcmp(nm,"noidle")) { xdwOptIdle=False; } else if (!strcmp(nm,"mo") || !strcmp(nm,"maxoptimization")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],0,100,&j)) goto L_BAD_OPT_ARG; xdwOptMaxOptimization=j; } else if (!strcmp(nm,"c") || !strcmp(nm,"colortheme")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],0,9,&j)) goto L_BAD_OPT_ARG; xdwOptWaterColor=xdwColorThemes[j][0]; xdwOptSkyColor =xdwColorThemes[j][1]; xdwOptLightColor=xdwColorThemes[j][2]; } else if (!strcmp(nm,"wc") || !strcmp(nm,"watercolor")) { if (i>=argc) goto L_MISSING_OPT_ARG; xdwOptWaterColor=argv[i++]; } else if (!strcmp(nm,"sc") || !strcmp(nm,"skycolor")) { if (i>=argc) goto L_MISSING_OPT_ARG; xdwOptSkyColor=argv[i++]; } else if (!strcmp(nm,"lc") || !strcmp(nm,"lightcolor")) { if (i>=argc) goto L_MISSING_OPT_ARG; xdwOptLightColor=argv[i++]; } else if (!strcmp(nm,"wm") || !strcmp(nm,"wavesbymouse")) { xdwOptWavesByMouse=True; } else if (!strcmp(nm,"nwm") || !strcmp(nm,"nowavesbymouse")) { xdwOptWavesByMouse=False; } else if (!strcmp(nm,"ww") || !strcmp(nm,"wavesbywindows")) { xdwOptWavesByWindows=True; } else if (!strcmp(nm,"nww") || !strcmp(nm,"nowavesbywindows")) { xdwOptWavesByWindows=False; } else if (!strcmp(nm,"rn") || !strcmp(nm,"rain")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],0,10,&j)) goto L_BAD_OPT_ARG; xdwOptRain=j; } else if (!strcmp(nm,"st") || !strcmp(nm,"storm")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],0,10,&j)) goto L_BAD_OPT_ARG; xdwOptStorm=j; } else if (!strcmp(nm,"vs") || !strcmp(nm,"viscosity")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,5,&j)) goto L_BAD_OPT_ARG; xdwOptViscosity=j; } else if (!strcmp(nm,"si") || !strcmp(nm,"skyintensity")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,10,&j)) goto L_BAD_OPT_ARG; xdwOptSkyIntensity=j; } else if (!strcmp(nm,"li") || !strcmp(nm,"lightintensity")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],1,10,&j)) goto L_BAD_OPT_ARG; xdwOptLightIntensity=j; } else if (!strcmp(nm,"lal") || !strcmp(nm,"lightaltitude")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],0,90,&j)) goto L_BAD_OPT_ARG; xdwOptLightAltitude=j; } else if (!strcmp(nm,"laz") || !strcmp(nm,"lightazimuth")) { if (i>=argc) goto L_MISSING_OPT_ARG; if (!xdwParseInt(argv[i++],-360,360,&j)) goto L_BAD_OPT_ARG; xdwOptLightAzimuth=j; } /* --- deprecated options --- */ else if (!strcmp(nm,"hc") || !strcmp(nm,"highlightcolor")) { fprintf(stderr,"%s: option %s is deprecated and should not be used any longer.\n",xdwArg0,opt); if (i>=argc) goto L_MISSING_OPT_ARG; xdwOptLightColor=argv[i++]; } else if (!strcmp(nm,"wr") || !strcmp(nm,"wavesbyrain")) { fprintf(stderr,"%s: option %s is deprecated and should not be used any longer.\n",xdwArg0,opt); if (!xdwOptRain) xdwOptRain=9; } else if (!strcmp(nm,"nwr") || !strcmp(nm,"nowavesbyrain")) { fprintf(stderr,"%s: option %s is deprecated and should not be used any longer.\n",xdwArg0,opt); xdwOptRain=0; } else if (!strcmp(nm,"ws") || !strcmp(nm,"wavesbystorm")) { fprintf(stderr,"%s: option %s is deprecated and should not be used any longer.\n",xdwArg0,opt); if (!xdwOptStorm) xdwOptStorm=9; } else if (!strcmp(nm,"nws") || !strcmp(nm,"nowavesbystorm")) { fprintf(stderr,"%s: option %s is deprecated and should not be used any longer.\n",xdwArg0,opt); xdwOptStorm=0; } /* --------------------------- */ else { fprintf(stderr,"%s: illegal option: %s\n",xdwArg0,opt); exit(1); } } /* Apply some rules on the options. */ if (xdwOptWindowCreation==XDW_USE_ROOT && xdwOptTransparency!=XDW_OPAQUE && xdwOptTransparency!=XDW_STIPPLED) { if (xdwOptVerb) printf("Forcing stippled mode for drawing to root window.\n"); xdwOptTransparency=XDW_STIPPLED; } if (xdwOptTransparency==XDW_SHAPED && !xdwOptDoubleBuffer) { if (xdwOptVerb) printf("Forcing double buffering for shaped mode.\n"); xdwOptDoubleBuffer=True; } if (xdwOptTransparency==XDW_STIPPLED && xdwOptDoubleBuffer) { if (xdwOptVerb) printf("Forcing single buffering for stippled mode.\n"); xdwOptDoubleBuffer=False; } /* Possibly report the resulting settings. */ if (xdwOptVerb>=1) printf( "Applied settings:\n" " display : %-16s max optimization: %d\n" " end : %-16s water color : \"%s\"\n" " window mode : %-16s sky color : \"%s\"\n" " transparency : %-16s light color : \"%s\"\n" " max frame rate : %-16d waves by mouse : %s\n" " sims per frame : %-16d waves by windows: %s\n" " events per frame: %-16d rain : %d\n" " resolution div : %-2d %-13d storm : %d\n" " cell size : %-2d %-13d viscosity : %d\n" " max color count : %-16d sky intensity : %d\n" " double buffering: %-16s light intensity : %d\n" " nice : %-16d light altitude : %d\n" " allow idle mode : %-16s light azimuth : %d\n", xdwOptDisplay ? xdwOptDisplay : "", xdwOptMaxOptimization, xdwOptEnd ? "yes" : "no", xdwOptWaterColor, xdwOptWindowCreation==XDW_USE_ROOT ? "root" : xdwOptWindowCreation==XDW_BACKDROP ? "backdrop" : xdwOptWindowCreation==XDW_WMBACKDROP ? "wm-backdrop" : "normal window", xdwOptSkyColor, xdwOptTransparency==XDW_OPAQUE ? "opaque" : xdwOptTransparency==XDW_STIPPLED ? "stippled" : xdwOptTransparency==XDW_SHAPED ? "shaped" : (sprintf(transpaStr,"opacity %d",xdwOptTranspaParam), transpaStr), xdwOptLightColor, xdwOptFrameRate, xdwOptWavesByMouse ? "yes" : "no", xdwOptSimsPerFrame, xdwOptWavesByWindows ? "yes" : "no", xdwOptEventsPerFrame, xdwOptRain, xdwOptResDivX, xdwOptResDivY, xdwOptStorm, xdwOptCellW, xdwOptCellH, xdwOptViscosity, xdwOptMaxColors, xdwOptSkyIntensity, xdwOptDoubleBuffer ? "yes" : "no", xdwOptLightIntensity, xdwOptNice, xdwOptLightAltitude, xdwOptIdle ? "yes" : "no", xdwOptLightAzimuth ); /* Ready. */ return; /* Error handling. */ L_MISSING_OPT_ARG: fprintf(stderr,"%s: missing argument for option %s\n",xdwArg0,opt); exit(1); L_BAD_OPT_ARG: fprintf(stderr,"%s: illegal argument for option %s\n",xdwArg0,opt); exit(1); } /*============================================================================*/ /*====================== Mathematical Helper Functions =======================*/ /*============================================================================*/ static int xdwSqrt32(unsigned x) { unsigned a, b, c; int i; /* Calculate the square root of x. */ a=0x8000; b=0x4000; c=0x40000000; for (i=14; x!=c && i>=0; i--, b>>=1) { if (xx1 < s->x2 && (!s->next || s->x2 < s->next->x1). */ } xdwRegion; xdwRegScan * xdwGarbageRegScans; /* List of garbage scan line segments for quick re-allocation. */ static xdwRegScan * xdwAllocRegScan() { xdwRegScan * s; if (xdwGarbageRegScans) { s=xdwGarbageRegScans; xdwGarbageRegScans=s->next; } else { s=(xdwRegScan*)malloc(sizeof(xdwRegScan)); } return s; } static void xdwFreeRegScanList(xdwRegScan * first) { xdwRegScan * last; if (first) { for (last=first; last->next; last=last->next); last->next=xdwGarbageRegScans; xdwGarbageRegScans=first; } } static void xdwInitRegion(xdwRegion * reg) { reg->y1=0; reg->y2=0; reg->rows=NULL; } static void xdwEmptyRegion(xdwRegion * reg) { xdwRegScan * * r, * * re; if (reg->rows) { r=reg->rows; re=r+(reg->y2-reg->y1); do { if (*r) xdwFreeRegScanList(*r); r++; } while (rrows); reg->rows=NULL; } reg->y1=0; reg->y2=0; } static int xdwCountRegionPixels(const xdwRegion * reg) { xdwRegScan * * r, * * re; xdwRegScan * s; int pixels; pixels=0; if (reg->rows) { r=reg->rows; re=r+(reg->y2-reg->y1); do { s=*r; if (s) { do { pixels+=s->x2-s->x1; s=s->next; } while (s); } r++; } while (r=x2 || y1>=y2) return; reg->y1=y1; reg->y2=y2; reg->rows=(xdwRegScan**)malloc((y2-y1)*sizeof(xdwRegScan*)); r=reg->rows; re=r+(y2-y1); do { s=xdwAllocRegScan(); s->next=NULL; s->x1=x1; s->x2=x2; *r=s; r++; } while (rrows) return; if (y1y1) y1=reg->y1; if (y2>reg->y2) y2=reg->y2; if (y1>=y2 || x1>=x2) { xdwEmptyRegion(reg); return; } r=reg->rows; if (y1>reg->y1) { re=r+(y1-reg->y1); do { if (*r) { xdwFreeRegScanList(*r); *r=NULL; } r++; } while (rx2>x1) { if (s->x1x1=x1; do { if (s->x2>x2) { if (s->x1x2=x2; ps=&s->next; s=*ps; if (!s) break; } do { *ps=s->next; s->next=xdwGarbageRegScans; xdwGarbageRegScans=s; s=*ps; } while (s); break; } ps=&s->next; s=*ps; } while (s); break; } *ps=s->next; s->next=xdwGarbageRegScans; xdwGarbageRegScans=s; s=*ps; } while (s); } r++; } while (ry2>y2) { re=r+(reg->y2-y2); do { if (*r) { xdwFreeRegScanList(*r); *r=NULL; } r++; } while (rrows) return; if (y1y1) y1=reg->y1; if (y2>reg->y2) y2=reg->y2; if (y1>=y2 || x1>=x2) return; r=reg->rows+(y1-reg->y1); re=r+(y2-y1); do { s=*r; if (s) { ps=r; do { if (s->x2>x1) { if (s->x1x2>x2) { s2=xdwAllocRegScan(); s2->next=s->next; s->next=s2; s2->x1=x2; s2->x2=s->x2; s->x2=x1; break; } s->x2=x1; ps=&s->next; s=*ps; if (!s) break; } do { if (s->x2>x2) { if (s->x1>=x2) break; s->x1=x2; break; } *ps=s->next; s->next=xdwGarbageRegScans; xdwGarbageRegScans=s; s=*ps; } while (s); break; } ps=&s->next; s=*ps; } while (s); } r++; } while (rrows || !reg2->rows) return; y1=reg2->y1; if (y1y1) y1=reg->y1; y2=reg2->y2; if (y2>reg->y2) y2=reg->y2; if (y1>=y2) return; r=reg->rows+(y1-reg->y1); re=r+(y2-y1); q=reg2->rows+(y1-reg2->y1); do { if ((s=*r)!=NULL && (t=*q)!=NULL) { for (ps=r;;) { if (s->x2<=t->x1) { ps=&s->next; s=*ps; if (s) continue; } else if (s->x1>=t->x2) { t=t->next; if (t) continue; } else if (s->x2<=t->x2) { if (s->x1>=t->x1) { *ps=s->next; s->next=xdwGarbageRegScans; xdwGarbageRegScans=s; s=*ps; if (s) continue; } else { s->x2=t->x1; ps=&s->next; s=*ps; if (s) continue; } } else if (s->x1>=t->x1) { s->x1=t->x2; t=t->next; if (t) continue; } else { s2=xdwAllocRegScan(); s2->next=s->next; s->next=s2; s2->x1=t->x2; s2->x2=s->x2; s->x2=t->x1; ps=&s->next; s=s2; t=t->next; if (t) continue; } break; } } r++; q++; } while (r=2) { if (xdwCPUHasCPUID ) printf("CPUID detected\n"); if (xdwCPUIsGenuineIntel) printf("Genuine Intel detected\n"); if (xdwCPUIsAuthenticAMD) printf("Authentic AMD detected\n"); if (xdwCPUHasMMX ) printf("MMX detected\n"); if (xdwCPUHasFXSR ) printf("FXSR detected\n"); if (xdwCPUHasSSE ) printf("SSE detected\n"); if (xdwCPUHasSSE2 ) printf("SSE2 detected\n"); } } /*============================================================================*/ /*========================== The Cellular Automata ===========================*/ /*============================================================================*/ int xdwCellCols, xdwCellRows; /* Number of columns and rows in the grid of cells. */ int xdwCellMemCols; /* Number of columns in memory. This can be a little bit greater than xdwCellCols. The additional cells are not used. Goal is to have the same 16-byte alignment for each row. */ short * xdwCells; /* The grid of cells. This data structure is a little bit awkward, for a better performance of the MMX and SSE2 variants of the algorithm. Each cell (c,r) with c = 0 to xdwCellCols-1 and r = 0 to xdwCellRows-1 has four states: level = xdwCells[(r*3+0)*xdwCellMemCols+c] velocity = xdwCells[(r*3+1)*xdwCellMemCols+c] covered = xdwCells[(r*3+2)*xdwCellMemCols+c]&1023 fading = xdwCells[(r*3+2)*xdwCellMemCols+c]>>10 "level" is the level of water or you can say the vertical position of the mass. "velocity" is the vertical velocity of the water (or mass). "covered" is the number of boats (windows and mouse) stacked on the cell. "fading" is a trick variable for fading from covered=0 to covered!=0 and back. It steps from 25 (got covered) down to 0 (ready with covering) and from -25 (got uncovered) up to 0 (ready with uncovering). The first column, the last column, the first row and the last two rows are making up the borders (the algorithms are easier with the additional border row at the end). They are always covered and should have level=0 and velocity=0. The lower-right corner of the upper-left cell lies in the center of the upper-left pixel of the canvas. And the upper-left corner of the right cell of the second-last row lies in the center of the lower-right pixel of the canvas. The second cell of each row is 16-byte aligned, more precise: each pointer (char*)&xdwCells[n*xdwCellMemCols+1] is a multiple of 16. */ short * xdwCellsMem; /* Pointer to the memory allocated for the cells. This may not be aligned correctly. */ const int xdwCellFadingTable[64]={ /* Table for the trick of fading from covered to uncovered and back. The values are added to the level of a faded cell on each time slice. The index is 32 plus the fading value which ranges from -25 to 25 (see description of xdwCells). */ 0, 0, 0, 0, 0, 0, 0, -36, -72, -108, -143, -178, -212, -246, -278, -310, -340, -369, -397, -424, -448, -472, -493, -513, -531, -546, -560, -572, -582, -590, -595, -598, 0, -450, -471, -485, -489, -484, -470, -445, -411, -367, -314, -253, -183, -107, -24, 61, 151, 243, 335, 425, 512, 594, 669, 737, 795, 843, 0, 0, 0, 0, 0, 0 }; static void xdwInitCells(int windowWidth, int windowHeight) { int col, row; /* Calculate number of columns and rows. */ xdwCellCols=((windowWidth-1)/xdwOptResDivX-1)/xdwOptCellW+3; xdwCellRows=((windowHeight-1)/xdwOptResDivY-1)/xdwOptCellH+4; /* Calculate number of columns in memory (=>alignment). */ xdwCellMemCols=(xdwCellCols+7)&~7; /* Allocate and clear memory for the cells. */ xdwCellsMem=malloc((xdwCellMemCols*xdwCellRows*3+7)*2); memset(xdwCellsMem,0,(xdwCellMemCols*xdwCellRows*3+7)*2); /* Have a pointer for the grid of cells (=>alignment). */ xdwCells=xdwCellsMem+((((short*)0)-xdwCellsMem-1)&7); /* Cover the borders. */ for (row=0; rowxdwCellCols-1) x2=xdwCellCols-1; if (x1>=x2) return False; y1=(2*y1-xdwOptResDivY)/(2*xdwOptCellH*xdwOptResDivY)+1; y2=(2*y2-xdwOptResDivY-1)/(2*xdwOptCellH*xdwOptResDivY)+2; if (y1<1) y1=1; if (y2>xdwCellRows-2) y2=xdwCellRows-2; if (y1>=y2) return False; do { p=xdwCells+(y1*3+2)*xdwCellMemCols+x1; pe=p+(x2-x1); do { c=*p; if ((c&0x3ff)==0) c+=25<<10; c+=deltaCovering; if ((c&0x3ff)==0) c-=25<<10; *p=c; p++; } while (p100) volume=100; x1=cx-5; y1=cy-5; x2=cx+6; y2=cy+6; if (x1<1) x1=1; if (y1<1) y1=1; if (x2>xdwCellCols-1) x2=xdwCellCols-1; if (y2>xdwCellRows-2) y2=xdwCellRows-2; changed=False; for (y=y1; y8 || (pc[x]&1023)!=0) continue; changed=True; v=pv[x]+drop[r]*volume; if (v<-0x8000) v=-0x8000; else if (v>0x7fff) v=0x7fff; pv[x]=(short)v; } } return changed; } static Bool xdwPutRaindrop(int x, int y, int volume) { return xdwPutRaindropOnCell( x/(xdwOptCellW*xdwOptResDivX)+1, y/(xdwOptCellH*xdwOptResDivY)+1, volume ); } static Bool xdwUpdateRain(int timeSlices) { static int rem=0; Bool changed; /* Create a number of raindrops per cell and time slice. A raindrop is just a modification of the velocity of some cells. */ if (xdwOptRain<=0) return False; rem+=xdwCellRows*xdwCellCols*timeSlices/100* xdwOptRain*xdwOptRain/100+10*xdwOptRain; changed=False; while (rem>0) { rem-=xdwRandom(2000,6000); changed|=xdwPutRaindropOnCell( xdwRandom(1,xdwCellCols-2), xdwRandom(1,xdwCellRows-3), xdwRandom(25,100) ); } return changed; } static void xdwUpdateStromRow(int row) { short * p, * pe; int v, n, m, k; /* For one row, perform one time slice of the storm simulation. The basic idea was to calculate the pressure (=acceleration of cells) from the product of airflow vector and water surface vector. With some pressure smoothing and "airflow-tear-offs", it's what this algorithm is nearly doing... */ if (xdwOptStorm<=0) return; p=xdwCells+row*3*xdwCellMemCols; pe=p+xdwCellCols-2; n=0; k=57+xdwOptStorm*xdwOptStorm*35/10; do { v=k; if (!p[2*xdwCellMemCols]) v=p[0]; p++; v-=p[0]; if ((unsigned)v>k) v=0; m=(n+7)>>3; n+=v; n-=m; v-=m; v+=p[xdwCellMemCols]; if (v<-0x8000) v=-0x8000; else if (v>0x7fff) v=0x7fff; p[xdwCellMemCols]=(short)v; } while (p>C2; \ v+=l1; \ l1=l2; \ l2<<=2; \ v-=l2; \ l3=p[0]; \ v+=l3; \ l3+=p[cols]>>2; \ v+=p[2*cols3]; \ c=p[2*cols]; \ if (c) { \ if ((c&0xfc00)!=0) { \ l3+=xdwCellFadingTable[(c>>10)+32]; \ if (c>0) { \ c-=1<<10; \ p[cols]=0; \ } \ else { \ c+=1<<10; \ } \ p[2*cols]=c; \ } \ else { \ l3=0; \ p[cols]=0; \ } \ } \ if (l3>0x7fff) l3=0x7fff; else if (l3<-0x8000) l3=-0x8000; \ p[0]=(short)l3; \ l2=p[cols3+1]; \ v+=l2; \ if (v>0x7fff) v=0x7fff; else if (v<-0x8000) v=-0x8000; \ p[4*cols]=(short)v; \ p++; \ } while(p=1) printf("Using MMX instruction set of 32-bit x86 CPU\n"); reported=True; } i=(((short*)0)-cells)&3; if (i) { xdwOperateCellsOpt0(cells,i); cells+=i; count-=i; } # define XDWOCO1(L,C1,C2) \ asm volatile ( \ " pushl %%ebp \n" \ " pushl %%edx \n" \ " pushl %%edi \n" \ " subl $200,%%esp \n" \ " fnsave (%%esp) \n" \ " movl %%edx,%%ebp \n" \ " pcmpeqw %%mm7,%%mm7 \n" \ " psrlw $" #C1 ",%%mm7 \n" \ " movq (%%edi,%%ecx),%%mm6 \n" \ " movzwl -2(%%edi,%%ecx),%%eax \n" \ " movd %%eax,%%mm5 \n" \ " movq %%mm6,%%mm0 \n" \ " psllq $16,%%mm0 \n" \ " paddsw %%mm0,%%mm5 \n" \ " psubsw %%mm6,%%mm5 \n" \ " jmp " #L "3 \n" \ " .balign 16 \n" \ #L "1: \n" \ " pxor %%mm4,%%mm4 \n" \ " movq %%mm4,(%%edi,%%ebx) \n" \ #L "2: \n" \ " psubsw %%mm2,%%mm3 \n" \ " movq %%mm4,(%%edi) \n" \ " movq %%mm3,(%%edi,%%ebx,4) \n" \ " addl $8,%%edi \n" \ #L "3: \n" \ " cmpl %%esi,%%edi \n" \ " jnc " #L "9 \n" \ " movq (%%edi,%%ebx,4),%%mm3 \n" \ " pxor %%mm2,%%mm2 \n" \ " pcmpeqb (%%edi,%%ebx,2),%%mm2 \n" \ " movq %%mm3,%%mm1 \n" \ " paddsw %%mm5,%%mm3 \n" \ " paddsw %%mm7,%%mm1 \n" \ " movq (%%edi),%%mm4 \n" \ " psrlq $16,%%mm5 \n" \ " psllw $1,%%mm2 \n" \ " psubsw %%mm5,%%mm3 \n" \ " packsswb %%mm2,%%mm2 \n" \ " movq (%%edi,%%ecx,2),%%mm0 \n" \ " psraw $" #C2 ",%%mm1 \n" \ " movq %%mm6,%%mm5 \n" \ " movd %%mm2,%%eax \n" \ " psubsw %%mm1,%%mm3 \n" \ " psubsw %%mm4,%%mm6 \n" \ " movq (%%edi,%%ebx),%%mm1 \n" \ " psubsw %%mm6,%%mm3 \n" \ " psubsw %%mm5,%%mm0 \n" \ " movq 8(%%edi,%%ecx),%%mm6 \n" \ " paddsw %%mm0,%%mm3 \n" \ " psrlq $48,%%mm5 \n" \ " movq %%mm6,%%mm0 \n" \ " psraw $2,%%mm1 \n" \ " psllq $16,%%mm0 \n" \ " psubsw %%mm6,%%mm5 \n" \ " paddsw %%mm1,%%mm4 \n" \ " movq %%mm5,%%mm2 \n" \ " cmpl $0xfefefefe,%%eax \n" \ " psllq $48,%%mm2 \n" \ " paddsw %%mm0,%%mm5 \n" \ " je " #L "2 \n" \ " cmpl $0x80808080,%%eax \n" \ " je " #L "1 \n" \ " pcmpeqd %%mm1,%%mm1 \n" \ " psrlq $48,%%mm1 \n" \ " movl $4,%%edx \n" \ #L "4: \n" \ " movzwl (%%edi,%%ebx,2),%%eax \n" \ " testl %%eax,%%eax \n" \ " je " #L "8 \n" \ " testl $0x0000fc00,%%eax \n" \ " je " #L "6 \n" \ " shrl $10,%%eax \n" \ " addl $32,%%eax \n" \ " andl $63,%%eax \n" \ " movd (%%ebp,%%eax,4),%%mm0 \n" \ " punpcklwd %%mm0,%%mm0 \n" \ " punpckldq %%mm0,%%mm0 \n" \ " pand %%mm1,%%mm0 \n" \ " paddsw %%mm0,%%mm4 \n" \ " testl $32,%%eax \n" \ " jne " #L "5 \n" \ " addw $0x0400,(%%edi,%%ebx,2) \n" \ " jmp " #L "8 \n" \ #L "5: \n" \ " subw $0x0400,(%%edi,%%ebx,2) \n" \ " jmp " #L "7 \n" \ #L "6: \n" \ " movq %%mm1,%%mm0 \n" \ " pandn %%mm4,%%mm0 \n" \ " movq %%mm0,%%mm4 \n" \ #L "7: \n" \ " movw $0,(%%edi,%%ebx) \n" \ #L "8: \n" \ " psllq $16,%%mm1 \n" \ " addl $2,%%edi \n" \ " decl %%edx \n" \ " jnz " #L "4 \n" \ " subl $8,%%edi \n" \ " jmp " #L "2 \n" \ #L "9: \n" \ " frstor (%%esp) \n" \ " addl $200,%%esp \n" \ " popl %%edi \n" \ " popl %%edx \n" \ " popl %%ebp \n" \ : \ : "D"(cells),"S"(cells+(count&~3)),"b"(2*xdwCellMemCols), \ "c"(3*2*xdwCellMemCols),"d"(xdwCellFadingTable) \ : "eax","memory","cc" \ ) switch(xdwOptViscosity) { case 1: XDWOCO1(XDWOCO1_1_, 7,10); break; case 2: XDWOCO1(XDWOCO1_2_, 8, 9); break; case 3: XDWOCO1(XDWOCO1_3_, 9, 8); break; case 4: XDWOCO1(XDWOCO1_4_,10, 7); break; case 5: XDWOCO1(XDWOCO1_5_,11, 6); break; } i=count&3; if (i) xdwOperateCellsOpt0(cells+count-i,i); return True; } #elif defined(__GNUC__) && defined(__x86_64__) /* Same as above, but for 64-bit x86 CPU. (Are there any 64-bit CPU's or assemblers supporting MMX but not SSE2? Probably no, but to be complete...) */ static Bool reported=False; int i; if (xdwCPUHasMMX) { if (!reported) { if (xdwOptVerb>=1) printf("Using MMX instruction set of 64-bit x86 CPU\n"); reported=True; } i=(((short*)0)-cells)&3; if (i) { xdwOperateCellsOpt0(cells,i); cells+=i; count-=i; } # define XDWOCO1(L,C1,C2) \ asm volatile ( \ " pushq %%rdi \n" \ " subq $200,%%rsp \n" \ " fnsave (%%rsp) \n" \ " pcmpeqw %%mm7,%%mm7 \n" \ " psrlw $" #C1 ",%%mm7 \n" \ " movq (%%rdi,%%rcx),%%mm6 \n" \ " movzwl -2(%%rdi,%%rcx),%%eax \n" \ " movd %%eax,%%mm5 \n" \ " movq %%mm6,%%mm0 \n" \ " psllq $16,%%mm0 \n" \ " paddsw %%mm0,%%mm5 \n" \ " psubsw %%mm6,%%mm5 \n" \ " jmp " #L "3 \n" \ " .balign 16 \n" \ #L "1: \n" \ " pxor %%mm4,%%mm4 \n" \ " movq %%mm4,(%%rdi,%%rbx) \n" \ #L "2: \n" \ " psubsw %%mm2,%%mm3 \n" \ " movq %%mm4,(%%rdi) \n" \ " movq %%mm3,(%%rdi,%%rbx,4) \n" \ " addq $8,%%rdi \n" \ #L "3: \n" \ " cmpq %%rsi,%%rdi \n" \ " jnc " #L "9 \n" \ " movq (%%rdi,%%rbx,4),%%mm3 \n" \ " pxor %%mm2,%%mm2 \n" \ " pcmpeqb (%%rdi,%%rbx,2),%%mm2 \n" \ " movq %%mm3,%%mm1 \n" \ " paddsw %%mm5,%%mm3 \n" \ " paddsw %%mm7,%%mm1 \n" \ " movq (%%rdi),%%mm4 \n" \ " psrlq $16,%%mm5 \n" \ " psllw $1,%%mm2 \n" \ " psubsw %%mm5,%%mm3 \n" \ " packsswb %%mm2,%%mm2 \n" \ " movq (%%rdi,%%rcx,2),%%mm0 \n" \ " psraw $" #C2 ",%%mm1 \n" \ " movq %%mm6,%%mm5 \n" \ " movd %%mm2,%%eax \n" \ " psubsw %%mm1,%%mm3 \n" \ " psubsw %%mm4,%%mm6 \n" \ " movq (%%rdi,%%rbx),%%mm1 \n" \ " psubsw %%mm6,%%mm3 \n" \ " psubsw %%mm5,%%mm0 \n" \ " movq 8(%%rdi,%%rcx),%%mm6 \n" \ " paddsw %%mm0,%%mm3 \n" \ " psrlq $48,%%mm5 \n" \ " movq %%mm6,%%mm0 \n" \ " psraw $2,%%mm1 \n" \ " psllq $16,%%mm0 \n" \ " psubsw %%mm6,%%mm5 \n" \ " paddsw %%mm1,%%mm4 \n" \ " movq %%mm5,%%mm2 \n" \ " cmpl $0xfefefefe,%%eax \n" \ " psllq $48,%%mm2 \n" \ " paddsw %%mm0,%%mm5 \n" \ " je " #L "2 \n" \ " cmpl $0x80808080,%%eax \n" \ " je " #L "1 \n" \ " pcmpeqd %%mm1,%%mm1 \n" \ " psrlq $48,%%mm1 \n" \ " movl $4,%%r8d \n" \ #L "4: \n" \ " movzwl (%%rdi,%%rbx,2),%%eax \n" \ " testl %%eax,%%eax \n" \ " je " #L "8 \n" \ " testl $0x0000fc00,%%eax \n" \ " je " #L "6 \n" \ " shrl $10,%%eax \n" \ " addl $32,%%eax \n" \ " andq $63,%%rax \n" \ " movd (%%rdx,%%rax,4),%%mm0 \n" \ " punpcklwd %%mm0,%%mm0 \n" \ " punpckldq %%mm0,%%mm0 \n" \ " pand %%mm1,%%mm0 \n" \ " paddsw %%mm0,%%mm4 \n" \ " testl $32,%%eax \n" \ " jne " #L "5 \n" \ " addw $0x0400,(%%rdi,%%rbx,2) \n" \ " jmp " #L "8 \n" \ #L "5: \n" \ " subw $0x0400,(%%rdi,%%rbx,2) \n" \ " jmp " #L "7 \n" \ #L "6: \n" \ " movq %%mm1,%%mm0 \n" \ " pandn %%mm4,%%mm0 \n" \ " movq %%mm0,%%mm4 \n" \ #L "7: \n" \ " movw $0,(%%rdi,%%rbx) \n" \ #L "8: \n" \ " psllq $16,%%mm1 \n" \ " addq $2,%%rdi \n" \ " decl %%r8d \n" \ " jnz " #L "4 \n" \ " subq $8,%%rdi \n" \ " jmp " #L "2 \n" \ #L "9: \n" \ " frstor (%%rsp) \n" \ " addq $200,%%rsp \n" \ " popq %%rdi \n" \ : \ : "D"(cells),"S"(cells+(count&~3)),"b"((long)2*xdwCellMemCols), \ "c"((long)3*2*xdwCellMemCols),"d"(xdwCellFadingTable) \ : "rax","r8","memory","cc" \ ) switch(xdwOptViscosity) { case 1: XDWOCO1(XDWOCO1_1_, 7,10); break; case 2: XDWOCO1(XDWOCO1_2_, 8, 9); break; case 3: XDWOCO1(XDWOCO1_3_, 9, 8); break; case 4: XDWOCO1(XDWOCO1_4_,10, 7); break; case 5: XDWOCO1(XDWOCO1_5_,11, 6); break; } i=count&3; if (i) xdwOperateCellsOpt0(cells+count-i,i); return True; } #endif return False; } static Bool xdwOperateCellsOpt2(short * cells, int count) { #if defined(XDW_MAX_OPTIMIZATION) && XDW_MAX_OPTIMIZATION < 2 return False; #elif defined(__GNUC__) && defined(__i386__) /* Optimization level 2 of the cellular automata using the SSE2 instruction set of a 32-bit x86 CPU, implemented as GNU-C inline assembler. It is assumed that the covering of a cell is not greater than 255 (it can be up to 1023 per definition). If it's greater, the algorithm is still correct but takes some extra time. */ static Bool reported=False; int i; if (xdwCPUHasFXSR && xdwCPUHasSSE2) { if (!reported) { if (xdwOptVerb>=1) printf("Using SSE2 instruction set of 32-bit x86 CPU\n"); reported=True; } i=(((short*)0)-cells)&7; if (i) { xdwOperateCellsOpt0(cells,i); cells+=i; count-=i; } # define XDWOCO2(L,C1,C2) \ asm volatile ( \ " pushl %%ebp \n" \ " pushl %%edx \n" \ " pushl %%edi \n" \ " movl %%esp,%%eax \n" \ " subl $512,%%esp \n" \ " andl $0xfffffff0,%%esp \n" \ " fxsave (%%esp) \n" \ " push %%eax \n" \ " movl %%edx,%%ebp \n" \ " pcmpeqw %%xmm7,%%xmm7 \n" \ " psrlw $" #C1 ",%%xmm7 \n" \ " movdqa (%%edi,%%ecx),%%xmm6 \n" \ " movzwl -2(%%edi,%%ecx),%%eax \n" \ " movd %%eax,%%xmm5 \n" \ " movdqa %%xmm6,%%xmm0 \n" \ " pslldq $16/8,%%xmm0 \n" \ " paddsw %%xmm0,%%xmm5 \n" \ " psubsw %%xmm6,%%xmm5 \n" \ " jmp " #L "3 \n" \ " .balign 16 \n" \ #L "1: \n" \ " pxor %%xmm4,%%xmm4 \n" \ " movdqa %%xmm4,(%%edi,%%ebx) \n" \ #L "2: \n" \ " psubsw %%xmm2,%%xmm3 \n" \ " movdqa %%xmm4,(%%edi) \n" \ " movdqa %%xmm3,(%%edi,%%ebx,4) \n" \ " addl $16,%%edi \n" \ #L "3: \n" \ " cmpl %%esi,%%edi \n" \ " jnc " #L "9 \n" \ " movdqa (%%edi,%%ebx,4),%%xmm3 \n" \ " pxor %%xmm2,%%xmm2 \n" \ " movdqa %%xmm3,%%xmm1 \n" \ " pcmpeqb (%%edi,%%ebx,2),%%xmm2 \n" \ " paddsw %%xmm5,%%xmm3 \n" \ " paddsw %%xmm7,%%xmm1 \n" \ " movdqa (%%edi),%%xmm4 \n" \ " psrldq $16/8,%%xmm5 \n" \ " movdqa (%%edi,%%ecx,2),%%xmm0 \n" \ " psubsw %%xmm5,%%xmm3 \n" \ " psraw $" #C2 ",%%xmm1 \n" \ " movdqa %%xmm6,%%xmm5 \n" \ " psubsw %%xmm1,%%xmm3 \n" \ " psubsw %%xmm4,%%xmm6 \n" \ " movdqa (%%edi,%%ebx),%%xmm1 \n" \ " psubsw %%xmm6,%%xmm3 \n" \ " pmovmskb %%xmm2,%%eax \n" \ " psubsw %%xmm5,%%xmm0 \n" \ " movdqa 16(%%edi,%%ecx),%%xmm6 \n" \ " paddsw %%xmm0,%%xmm3 \n" \ " psrldq $112/8,%%xmm5 \n" \ " movdqa %%xmm6,%%xmm0 \n" \ " psraw $2,%%xmm1 \n" \ " pslldq $16/8,%%xmm0 \n" \ " psubsw %%xmm6,%%xmm5 \n" \ " paddsw %%xmm1,%%xmm4 \n" \ " movdqa %%xmm5,%%xmm2 \n" \ " cmpl $0x0000ffff,%%eax \n" \ " pslldq $112/8,%%xmm2 \n" \ " paddsw %%xmm0,%%xmm5 \n" \ " je " #L "2 \n" \ " cmpl $0x0000aaaa,%%eax \n" \ " je " #L "1 \n" \ " pcmpeqd %%xmm1,%%xmm1 \n" \ " psrldq $112/8,%%xmm1 \n" \ " movl $8,%%edx \n" \ #L "4: \n" \ " movzwl (%%edi,%%ebx,2),%%eax \n" \ " testl %%eax,%%eax \n" \ " je " #L "8 \n" \ " testl $0x0000fc00,%%eax \n" \ " je " #L "6 \n" \ " shrl $10,%%eax \n" \ " addl $32,%%eax \n" \ " andl $63,%%eax \n" \ " movd (%%ebp,%%eax,4),%%xmm0 \n" \ " pshuflw $0,%%xmm0,%%xmm0 \n" \ " pshufd $0,%%xmm0,%%xmm0 \n" \ " pand %%xmm1,%%xmm0 \n" \ " paddsw %%xmm0,%%xmm4 \n" \ " testl $32,%%eax \n" \ " jne " #L "5 \n" \ " addw $0x0400,(%%edi,%%ebx,2) \n" \ " jmp " #L "8 \n" \ #L "5: \n" \ " subw $0x0400,(%%edi,%%ebx,2) \n" \ " jmp " #L "7 \n" \ #L "6: \n" \ " movdqa %%xmm1,%%xmm0 \n" \ " pandn %%xmm4,%%xmm0 \n" \ " movdqa %%xmm0,%%xmm4 \n" \ #L "7: \n" \ " movw $0,(%%edi,%%ebx) \n" \ #L "8: \n" \ " pslldq $16/8,%%xmm1 \n" \ " addl $2,%%edi \n" \ " decl %%edx \n" \ " jnz " #L "4 \n" \ " subl $16,%%edi \n" \ " jmp " #L "2 \n" \ #L "9: \n" \ " popl %%eax \n" \ " fxrstor (%%esp) \n" \ " movl %%eax,%%esp \n" \ " popl %%edi \n" \ " popl %%edx \n" \ " popl %%ebp \n" \ : \ : "D"(cells),"S"(cells+(count&~7)),"b"(2*xdwCellMemCols), \ "c"(3*2*xdwCellMemCols),"d"(xdwCellFadingTable) \ : "eax","memory","cc" \ ) switch(xdwOptViscosity) { case 1: XDWOCO2(XDWOCO2_1_, 7,10); break; case 2: XDWOCO2(XDWOCO2_2_, 8, 9); break; case 3: XDWOCO2(XDWOCO2_3_, 9, 8); break; case 4: XDWOCO2(XDWOCO2_4_,10, 7); break; case 5: XDWOCO2(XDWOCO2_5_,11, 6); break; } i=count&7; if (i) xdwOperateCellsOpt0(cells+count-i,i); return True; } #elif defined(__GNUC__) && defined(__x86_64__) /* Same as above, but for 64-bit x86 CPU. TODO: This is still just a slightly modified "copy" of the 32-bit mode implementation. Surely it could be improved a little bit, by making use of the additional registers xmm8-xmm15. */ static Bool reported=False; int i; if (xdwCPUHasFXSR && xdwCPUHasSSE2) { if (!reported) { if (xdwOptVerb>=1) printf("Using SSE2 instruction set of 64-bit x86 CPU\n"); reported=True; } i=(((short*)0)-cells)&7; if (i) { xdwOperateCellsOpt0(cells,i); cells+=i; count-=i; } # define XDWOCO2(L,C1,C2) \ asm volatile ( \ " pushq %%rdi \n" \ " movq %%rsp,%%rax \n" \ " subq $512,%%rsp \n" \ " andq $-16,%%rsp \n" \ " fxsave (%%rsp) \n" \ " pushq %%rax \n" \ " pcmpeqw %%xmm7,%%xmm7 \n" \ " psrlw $" #C1 ",%%xmm7 \n" \ " movdqa (%%rdi,%%rcx),%%xmm6 \n" \ " movzwl -2(%%rdi,%%rcx),%%eax \n" \ " movd %%eax,%%xmm5 \n" \ " movdqa %%xmm6,%%xmm0 \n" \ " pslldq $16/8,%%xmm0 \n" \ " paddsw %%xmm0,%%xmm5 \n" \ " psubsw %%xmm6,%%xmm5 \n" \ " jmp " #L "3 \n" \ " .balign 16 \n" \ #L "1: \n" \ " pxor %%xmm4,%%xmm4 \n" \ " movdqa %%xmm4,(%%rdi,%%rbx) \n" \ #L "2: \n" \ " psubsw %%xmm2,%%xmm3 \n" \ " movdqa %%xmm4,(%%rdi) \n" \ " movdqa %%xmm3,(%%rdi,%%rbx,4) \n" \ " addq $16,%%rdi \n" \ #L "3: \n" \ " cmpq %%rsi,%%rdi \n" \ " jnc " #L "9 \n" \ " movdqa (%%rdi,%%rbx,4),%%xmm3 \n" \ " pxor %%xmm2,%%xmm2 \n" \ " movdqa %%xmm3,%%xmm1 \n" \ " pcmpeqb (%%rdi,%%rbx,2),%%xmm2 \n" \ " paddsw %%xmm5,%%xmm3 \n" \ " paddsw %%xmm7,%%xmm1 \n" \ " movdqa (%%rdi),%%xmm4 \n" \ " psrldq $16/8,%%xmm5 \n" \ " movdqa (%%rdi,%%rcx,2),%%xmm0 \n" \ " psubsw %%xmm5,%%xmm3 \n" \ " psraw $" #C2 ",%%xmm1 \n" \ " movdqa %%xmm6,%%xmm5 \n" \ " psubsw %%xmm1,%%xmm3 \n" \ " psubsw %%xmm4,%%xmm6 \n" \ " movdqa (%%rdi,%%rbx),%%xmm1 \n" \ " psubsw %%xmm6,%%xmm3 \n" \ " pmovmskb %%xmm2,%%eax \n" \ " psubsw %%xmm5,%%xmm0 \n" \ " movdqa 16(%%rdi,%%rcx),%%xmm6 \n" \ " paddsw %%xmm0,%%xmm3 \n" \ " psrldq $112/8,%%xmm5 \n" \ " movdqa %%xmm6,%%xmm0 \n" \ " psraw $2,%%xmm1 \n" \ " pslldq $16/8,%%xmm0 \n" \ " psubsw %%xmm6,%%xmm5 \n" \ " paddsw %%xmm1,%%xmm4 \n" \ " movdqa %%xmm5,%%xmm2 \n" \ " cmpl $0x0000ffff,%%eax \n" \ " pslldq $112/8,%%xmm2 \n" \ " paddsw %%xmm0,%%xmm5 \n" \ " je " #L "2 \n" \ " cmpl $0x0000aaaa,%%eax \n" \ " je " #L "1 \n" \ " pcmpeqd %%xmm1,%%xmm1 \n" \ " psrldq $112/8,%%xmm1 \n" \ " movl $8,%%r8d \n" \ #L "4: \n" \ " movzwl (%%rdi,%%rbx,2),%%eax \n" \ " testl %%eax,%%eax \n" \ " je " #L "8 \n" \ " testl $0x0000fc00,%%eax \n" \ " je " #L "6 \n" \ " shrl $10,%%eax \n" \ " addl $32,%%eax \n" \ " andq $63,%%rax \n" \ " movd (%%rdx,%%rax,4),%%xmm0 \n" \ " pshuflw $0,%%xmm0,%%xmm0 \n" \ " pshufd $0,%%xmm0,%%xmm0 \n" \ " pand %%xmm1,%%xmm0 \n" \ " paddsw %%xmm0,%%xmm4 \n" \ " testl $32,%%eax \n" \ " jne " #L "5 \n" \ " addw $0x0400,(%%rdi,%%rbx,2) \n" \ " jmp " #L "8 \n" \ #L "5: \n" \ " subw $0x0400,(%%rdi,%%rbx,2) \n" \ " jmp " #L "7 \n" \ #L "6: \n" \ " movdqa %%xmm1,%%xmm0 \n" \ " pandn %%xmm4,%%xmm0 \n" \ " movdqa %%xmm0,%%xmm4 \n" \ #L "7: \n" \ " movw $0,(%%rdi,%%rbx) \n" \ #L "8: \n" \ " pslldq $16/8,%%xmm1 \n" \ " addq $2,%%rdi \n" \ " decl %%r8d \n" \ " jnz " #L "4 \n" \ " subq $16,%%rdi \n" \ " jmp " #L "2 \n" \ #L "9: \n" \ " popq %%rax \n" \ " fxrstor (%%rsp) \n" \ " movq %%rax,%%rsp \n" \ " popq %%rdi \n" \ : \ : "D"(cells),"S"(cells+(count&~7)),"b"((long)2*xdwCellMemCols), \ "c"((long)3*2*xdwCellMemCols),"d"(xdwCellFadingTable) \ : "rax","r8","memory","cc" \ ) switch(xdwOptViscosity) { case 1: XDWOCO2(XDWOCO2_1_, 7,10); break; case 2: XDWOCO2(XDWOCO2_2_, 8, 9); break; case 3: XDWOCO2(XDWOCO2_3_, 9, 8); break; case 4: XDWOCO2(XDWOCO2_4_,10, 7); break; case 5: XDWOCO2(XDWOCO2_5_,11, 6); break; } i=count&7; if (i) xdwOperateCellsOpt0(cells+count-i,i); return True; } #endif return False; } static void xdwUpdateCells(int timeSlices) { short * p; int i, n, t, r, c; /* For all time slices of one frame, push forward the cellular automata at all cells. Don't forget storm. After a row r has been operated for one time slice, row r-2 can be operated for the next time slice. That's good for cache utilization. */ n=xdwCellRows-2+(timeSlices-1)*2; for (i=0; i=0 && r0) xdwUpdateStromRow(r); } } } } /*============================================================================*/ /*================ Visualization (Canvas, Palette & Lighting =================*/ /*============================================================================*/ /* The "canvas" holds the resulting image of the water and it knows where the output window (or back buffer) is up-to-date. One pixel of the canvas is a rectangle of xdwOptResDivX * xdwOptResDivY pixels on the output window. The upper-left corner of the upper-left pixel of the canvas lies in the upper-left corner of the upper-left pixel of the output window. */ int xdwCanvasWidth, xdwCanvasHeight; /* Width and height of the canvas. xdwCanvasWidth must be n*xdwOptCellW+1 with an integral n, xdwCanvasHeight must be appropriate. */ int xdwCanvasVisibleW, xdwCanvasVisibleH; /* Size of the visible part of the canvas. This may be a little smaller than the canvas size.*/ signed char * xdwCanvas; /* The canvas (array of pixels). Bits 0 to 6 are the color as a palette index. Bit 7 means: output window (or back buffer) is not up-to-date here. */ int xdwTouchedCvRow1, xdwTouchedCvRow2; /* Minimum range of canvas rows, where something is not up-to-date. If everything is up-to-date, xdwTouchedCvRow1 must be >=xdwCanvasHeight, and xdwTouchedCvRow2 must be <=0. TODO: Even try to develop this kind of optimization for columns. */ typedef struct { int count; XRectangle rects[256]; } xdwRectSet; xdwRectSet * * xdwRectSets; /* This array reserves some memory for using XFillRectangles. xdwRectSets[i] is a set of rectangles to be drawn with color xdwPalette[i]. */ int xdwSkyColorIndex; /* Index of sky color. */ int xdwColorCount; /* Number of colors in the palette. */ unsigned long * xdwPalette; /* The color palette. The entries are X pixel values. Water color is at index 0, sky color is at index xdwSkyColorIndex, and light color is at index xdwColorCount-1. Other entries are fadings between those base colors. */ signed char * xdwLightingTable; /* Table for speeding up lighting. */ static void xdwInitPalette(Display * display) { Colormap cmap; XColor ec[3]; XColor c; int i, a, b, q, n; /* Allocate and fill the palette. */ xdwColorCount=xdwOptMaxColors; xdwPalette=malloc(sizeof(unsigned long)*xdwColorCount); xdwSkyColorIndex=xdwColorCount*3/4; cmap=DefaultColormap(display,DefaultScreen(display)); if (!XLookupColor(display,cmap,xdwOptWaterColor,&ec[0],&c)) { fprintf(stderr,"%s: no such color: %s\n",xdwArg0,xdwOptWaterColor); exit(1); } if (!XLookupColor(display,cmap,xdwOptSkyColor,&ec[1],&c)) { fprintf(stderr,"%s: no such color: %s\n",xdwArg0,xdwOptSkyColor); exit(1); } if (!XLookupColor(display,cmap,xdwOptLightColor,&ec[2],&c)) { fprintf(stderr,"%s: no such color: %s\n",xdwArg0,xdwOptLightColor); exit(1); } for (i=0; i=1) { printf("Palette has %d different colors (from %d)\n", xdwColorCount,xdwOptMaxColors); } } static void xdwInitLighting(void) { double lx, ly, lz, nx, ny, nz, r, c, k; int i, j, d1, d2; /* Direction where the light is. */ r=cos(xdwOptLightAltitude*(M_PI/180.0)); lx=r*sin(xdwOptLightAzimuth*(M_PI/180.0)); ly=-r*cos(xdwOptLightAzimuth*(M_PI/180.0)); lz=sin(xdwOptLightAltitude*(M_PI/180.0)); /* Prepare a table for that xdwUpdateCanvas(..) can perform faster. */ xdwLightingTable=malloc(sizeof(signed char)*0x40000); for (i=0; i<0x40000; i++) { /* Calculate norm vector of the water surface. */ d1=i>>9; if (d1&0x100) d1|=~0xff; d2=i&0x1ff; if (d2&0x100) d2|=~0xff; nx=d1-d2; ny=d1+d2; nz=128.0; r=sqrt(nx*nx+ny*ny+nz*nz); nx/=r; ny/=r; nz/=r; /* Reflection of the sky. */ k=xdwOptSkyIntensity*xdwOptSkyIntensity*(0.99-nz*nz)*0.2; if (k>1.0) k=1.0; if (k<0.0) k=0.0; c=k*xdwSkyColorIndex; /* Reflection of light source. */ k=(nx*lx+ny*ly+nz*lz)*nz*2.0-lz; if (k>0.0) { k=(0.51+xdwOptLightIntensity*0.043-sqrt(1.0001-k*k))/0.47; if (k>0.0) { if (k>1.0) k=1.0; c+=k*k*(xdwColorCount-1); } } /* Saturate and set table entry. */ j=(int)(c+0.5); if (j<0) j=0; else if (j>=xdwColorCount) j=xdwColorCount-1; xdwLightingTable[i]=j; } } static void xdwInitCanvas(int windowWidth, int windowHeight) { int i; /* Calculate sizes. */ xdwCanvasVisibleW=(windowWidth-1)/xdwOptResDivX+1; xdwCanvasVisibleH=(windowHeight-1)/xdwOptResDivY+1; xdwCanvasWidth=((xdwCanvasVisibleW-2)/xdwOptCellW+1)*xdwOptCellW+1; xdwCanvasHeight=((xdwCanvasVisibleH-2)/xdwOptCellH+1)*xdwOptCellH+1; /* Allocate the array of pixels, and mark all pixels as out-of-date. */ xdwCanvas=malloc(sizeof(signed char)*xdwCanvasWidth*xdwCanvasHeight); memset(xdwCanvas,0x80,xdwCanvasWidth*xdwCanvasHeight); /* Allocate and initialize memory for sets of rectangles. */ xdwRectSets=malloc(sizeof(xdwRectSet)*xdwColorCount); for (i=0; icount=0; } /* Nothing is up-to-date. */ xdwTouchedCvRow1=0; xdwTouchedCvRow2=xdwCanvasHeight; } static Bool xdwTouchCanvas(int x1, int y1, int x2, int y2) { signed char * p, * pe; /* Mark a rectangular region as out-of-date. The given coordinates are in window pixels. */ x1/=xdwOptResDivX; x2=(x2+xdwOptResDivX-1)/xdwOptResDivX; if (x1<0) x1=0; if (x2>xdwCanvasVisibleW) x2=xdwCanvasVisibleW; if (x1>=x2) return False; y1/=xdwOptResDivY; y2=(y2+xdwOptResDivY-1)/xdwOptResDivY; if (y1<0) y1=0; if (y2>xdwCanvasVisibleH) y2=xdwCanvasVisibleH; if (y1>=y2) return False; if (xdwTouchedCvRow1>y1) xdwTouchedCvRow1=y1; if (xdwTouchedCvRow2>5) ]; p++; if (*q!=c) break; q+=xdwOptCellW; } while(q=qe) return False; *q=c|0x80; q+=xdwOptCellW; if (q>=qe) return True; do { c=xdwLightingTable[ (((p[0]-p[cols3+1])&0x3fe0)<<4)+ (((p[1]-p[cols3])&0x3fe0)>>5) ]; p++; if (*q!=c) *q=c|0x80; q+=xdwOptCellW; } while(q>1; if (q[xdwCanvasWidth]!=c) q[xdwCanvasWidth]=c|0x80; q+=xdwOptCellW; } while(q>1; if (q[2*xdwCanvasWidth]!=c) q[2*xdwCanvasWidth]=c|0x80; c1=(c1+c+1)>>1; if (q[xdwCanvasWidth]!=c1) q[xdwCanvasWidth]=c1|0x80; c2=(c2+c+1)>>1; if (q[3*xdwCanvasWidth]!=c2) q[3*xdwCanvasWidth]=c2|0x80; q+=xdwOptCellW; } while(q>1); t=q+xdwCanvasWidth; for (i=1; i>1; if (*q!=c) *q=c|0x80; q+=2; } while(q>1; c1=(c1+c+1)>>1; if (*q!=c1) *q=c1|0x80; if (q[1]!=c) q[1]=c|0x80; c=c+c-c1; if (q[2]!=c) q[2]=c|0x80; q+=4; } while(q>1); for (i=1; i=0; y1-=xdwOptCellH) { prvChanged=curChanged; curChanged=xdwCvDoLighting(y1); if (!prvChanged && !curChanged) continue; if (xdwTouchedCvRow1>y1) xdwTouchedCvRow1=y1; if (xdwTouchedCvRow2=pxe) break; c=*px; c7=c&0x7f; rs=xdwRectSets[c7]; if (rs->count>=256) { (*pXFRCalls)++; (*pRects)+=rs->count; XSetForeground(display,gc,xdwPalette[c7]); XFillRectangles(display,drawable,gc,rs->rects,rs->count); rs->count=0; } r=rs->rects+rs->count; rs->count++; r->x=sx; r->y=sy; r->width=xdwOptResDivX; r->height=xdwOptResDivY; *px=c7; py=px+xdwCanvasWidth; px++; sx+=xdwOptResDivX; if (pxwidth+=xdwOptResDivX; sx+=xdwOptResDivX; px++; if (pyheight+=xdwOptResDivY; py+=xdwCanvasWidth; pxy=py+2; for (;;) { p=px; if (pheight+=xdwOptResDivY; } break; } p=px; do { *p=c7; p+=xdwCanvasWidth; } while (pwidth+=xdwOptResDivX; sx+=xdwOptResDivX; p=py; if (pwidth+=xdwOptResDivX; sx+=xdwOptResDivX; } break; } p=py; do { *p=c7; p++; } while (pheight+=xdwOptResDivY; } } else if (pxwidth+=xdwOptResDivX; } while (pxheight+=xdwOptResDivY; } while (py=0; c--) if ((rs=xdwRectSets[c])->count) { (*pXFRCalls)++; (*pRects)+=rs->count; XSetForeground(display,gc,xdwPalette[c]); XFillRectangles(display,drawable,gc,rs->rects,rs->count); rs->count=0; } xdwTouchedCvRow1=xdwCanvasHeight; xdwTouchedCvRow2=0; } /*============================================================================*/ /*================================== Times ===================================*/ /*============================================================================*/ unsigned xdwAbsTimeMS=0; /* Time in milliseconds (starts somewhere an may overflow). */ unsigned xdwProcTimeMS=0; /* Process busy time in milliseconds (starts somewhere an may overflow). */ static void xdwUpdateTimes(void) { static long tps=-1; static clock_t atcks=0, ptcks=0; clock_t d; struct tms tb; if (tps<=0) { tps=sysconf(_SC_CLK_TCK); if (tps<=0) { fprintf(stderr,"%s: sysconf(_SC_CLK_TCK) failed\n",xdwArg0); exit(1); } } d=times(&tb)-atcks; if (d) { atcks+=d; xdwAbsTimeMS+=(unsigned)( ((unsigned long)d)/((unsigned long)tps)*1000+ ((unsigned long)d)%((unsigned long)tps)*1000/(unsigned long)tps ); } d=tb.tms_utime+tb.tms_stime-ptcks; if (d) { ptcks+=d; xdwProcTimeMS+=(unsigned)( ((unsigned long)d)/((unsigned long)tps)*1000+ ((unsigned long)d)%((unsigned long)tps)*1000/(unsigned long)tps ); } } /*============================================================================*/ /*============================= Signal Handling ==============================*/ /*============================================================================*/ Bool xdwTerminationRequested=False; /* True if the program should terminate. */ Bool xdwFreeze=False; /* True if the program should freeze (just for making screenshots). Toggled by SIGUSR1. */ static void xdwSignalHandler(int signum) { /* Handle a signal... */ if (signum==SIGUSR1) xdwFreeze=!xdwFreeze; else xdwTerminationRequested=True; } static void xdwInitSignalHandling(void) { /* Install the signal handler. */ signal(SIGHUP ,xdwSignalHandler); signal(SIGINT ,xdwSignalHandler); signal(SIGQUIT,xdwSignalHandler); signal(SIGABRT,xdwSignalHandler); signal(SIGTERM,xdwSignalHandler); signal(SIGUSR1,xdwSignalHandler); } /*============================================================================*/ /*============================= X Error Handling =============================*/ /*============================================================================*/ int xdwXErrorAcceptance=0; /* Non-zero if X errors should be accepted/ignored, instead of terminating the program. */ int (*xdwOriginalXErrorHandler)(Display *, XErrorEvent *); /* Original X error handler. */ static int xdwXErrorHandler(Display * display, XErrorEvent * errorEvent) { char msg[256]; /* Own X error handler: fall back to the original handler, or accept the error. */ if (!xdwXErrorAcceptance) { return (*xdwOriginalXErrorHandler)(display,errorEvent); } if (xdwOptVerb>=2) { XGetErrorText(display,errorEvent->error_code,msg,sizeof(msg)); printf("XError accepted: %s\n",msg); } return 0; } static void xdwInitXErrorHandling(void) { /* Install X error handler. */ xdwOriginalXErrorHandler=XSetErrorHandler(xdwXErrorHandler); } static void xdwBeginAcceptXErrors(void) { /* Increase nesting of accepting X errors. */ xdwXErrorAcceptance++; } static void xdwEndAcceptXErrors(void) { /* Decrease nesting of accepting X errors. */ xdwXErrorAcceptance--; } /*============================================================================*/ /*==================== Detection of Virtual Root Window ======================*/ /*============================================================================*/ static Window xdwDetectVirtualRoot(Display * display, Window * pAboveWin, Bool * pIsNautilus) { XWindowAttributes attr; Window rwin, win, win2, win3, win4, win5; Window * cwins, * cwins2, * cwins3; Atom atom,type; int i, j, k, l, m, n, r, rwidth, rheight, frmt; unsigned long len, rem; unsigned char * data; char * str; /* This code tries to find a virtual root window created by the window manager. It's a horrible topic. I stole some ideas from Andreas Stolcke's vroot.h, and some from Robin Hogan's toon_root.c (xpenguins). */ /* Prepare some variables. */ *pAboveWin=None; *pIsNautilus=False; rwin=DefaultRootWindow(display); rwidth=DisplayWidth(display,DefaultScreen(display)); rheight=DisplayHeight(display,DefaultScreen(display)); /* The good(?) old standard(?): Maybe there is a child-of-root window which has a property named __SWM_VROOT, pointing to the virtual root window. This even works with KDE if "Allow Programs in Desktop Window" has been enabled in the desktop configuration. */ atom=XInternAtom(display,"__SWM_VROOT",False); XQueryTree(display,rwin,&win,&win2,&cwins,(unsigned*)&n); for (win=None, i=0; i=1) printf("Virtual Root detected: 0x%lX\n",win); return win; } /* Nautilus has a root window property pointing to the desktop window. But we have to go deeper within that desktop window... */ atom=XInternAtom(display,"NAUTILUS_DESKTOP_WINDOW_ID",False); r=XGetWindowProperty(display,rwin,atom,0,1,False,XA_WINDOW, &type,&frmt,&len,&rem,&data); win=(r==Success && type==XA_WINDOW && data) ? *(Window*)data : None; if (data) XFree(data); if (win) { for (win2=win;;) { XQueryTree(display,win2,&win4,&win5,&cwins,(unsigned*)&n); for (win3=None, i=n-1; i>=0; i--) { XGetWindowAttributes(display,cwins[i],&attr); if (attr.x==0 && attr.y==0 && attr.width==rwidth && attr.height==rheight && attr.map_state==IsViewable && attr.override_redirect==False) { win3=cwins[i]; break; } } if (cwins) XFree(cwins); if (win3==None) break; win2=win3; } if (xdwOptVerb>=1) { printf("Nautilus Desktop detected: 0x%lX, using 0x%lX\n",win,win2); } *pIsNautilus=True; return win2; } /* For old version of KDE, or if "Allow Programs in Desktop Window" has not been enabled in the KDE Desktop Configuration: There is a child-of-child-of-child-of-root window named "KDE Desktop". All windows in that path are full-size. But we have to go deeper within the desktop window, with large-size instead of full-size... */ XQueryTree(display,rwin,&win,&win2,&cwins,(unsigned*)&n); for (win=None, i=n-1; win==None && i>=0; i--) { XGetWindowAttributes(display,cwins[i],&attr); if (attr.x!=0 || attr.y!=0 || attr.width!=rwidth || attr.height!=rheight || attr.map_state!=IsViewable) continue; XQueryTree(display,cwins[i],&win2,&win3,&cwins2,(unsigned*)&m); for (j=m-1; win==None && j>=0; j--) { XGetWindowAttributes(display,cwins2[j],&attr); if (attr.x!=0 || attr.y!=0 || attr.width!=rwidth || attr.height!=rheight || attr.map_state!=IsViewable || attr.override_redirect!=False) continue; XQueryTree(display,cwins2[j],&win2,&win3,&cwins3,(unsigned*)&l); for (k=l-1; win==None && k>=0; k--) { XGetWindowAttributes(display,cwins3[k],&attr); if (attr.x!=0 || attr.y!=0 || attr.width!=rwidth || attr.height!=rheight || attr.map_state!=IsViewable || attr.override_redirect!=False) continue; if (XFetchName(display,cwins3[k],&str)) { if (strcmp(str,"KDE Desktop")==0) win=cwins3[k]; XFree(str); } } if (cwins3) XFree(cwins3); } if (cwins2) XFree(cwins2); } if (cwins) XFree(cwins); if (win) { for (win2=win;;) { XQueryTree(display,win2,&win4,&win5,&cwins,(unsigned*)&n); for (win3=None, i=n-1; i>=0; i--) { XGetWindowAttributes(display,cwins[i],&attr); if (attr.x>=0 && attr.y>=0 && attr.x+attr.width<=rwidth && attr.y+attr.height<=rheight && attr.width>=rwidth*3/4 && attr.height>=rheight*3/4 && attr.map_state==IsViewable && attr.override_redirect==False) { win3=cwins[i]; break; } } if (cwins) XFree(cwins); if (win3==None) break; win2=win3; } if (xdwOptVerb>=1) { printf("KDE Desktop detected: 0x%lX, using 0x%lX\n",win,win2); } return win2; } /* For CDE and possibly others: Try to detect another backdrop window on the real root and prepare for stacking our backdrop above the other backdrop. This should be safe, because it is checked that the other backdrop has no children, no button input and no other window behind. */ XQueryTree(display,rwin,&win2,&win3,&cwins,(unsigned*)&n); win=(n>=1?cwins[0]:None); if (cwins) XFree(cwins); if (win!=None && XFetchName(display,win,&str) && str) { if (strcmp(str,"xdesktopwaves")==0) win=None; XFree(str); } if (win!=None) { XGetWindowAttributes(display,win,&attr); if (attr.x==0 && attr.y==0 && attr.width==rwidth && attr.height==rheight && attr.border_width==0 && attr.map_state==IsViewable && attr.override_redirect==True && attr.class==InputOutput && !(attr.all_event_masks&ButtonPressMask)) { XQueryTree(display,win,&win2,&win3,&cwins,(unsigned*)&n); if (cwins) XFree(cwins); if (n==0) { if (xdwOptVerb>=1) printf("Other backdrop detected (CDE?)\n"); *pAboveWin=win; return rwin; } } } /* No special window found. Just return the real root window. */ return rwin; } /*============================================================================*/ /*============================== Mouse on Water ==============================*/ /*============================================================================*/ int xdwMouseX=-1, xdwMouseY=-1; /* Position of the mouse pointer, or -1 if the mouse does not cover the water. */ static Bool xdwChangeMouse(int mx, int my) { Bool b; /* Change the mouse position and adapt the covering. */ b=False; if (xdwOptWavesByMouse && (mx!=xdwMouseX || my!=xdwMouseY)) { if (xdwMouseX>=0) b|=xdwCoverCircle(xdwMouseX,xdwMouseY,6,-1); xdwMouseX=mx; xdwMouseY=my; if (xdwMouseX>=0) b|=xdwCoverCircle(xdwMouseX,xdwMouseY,6,1); } return b; } /*============================================================================*/ /*======== Windows on Water, Visibility, Event Selection, Backdropping =======*/ /*============================================================================*/ /* This ugly part has four jobs: - Interaction of windows and water. - Determine whether the output window is fully obscured by other windows. - Select events. - Update backdropping of the output window. To solve these, we are tracking a tree of windows. Our anchor to the tree is not the root window, but the output window (variable xdwWinPath). The windows w1=xdwWinPath, w2=w1->parent, w3=w2->parent... are making up the path from the output window up to the root window (possibly it's just one window). For each window in that path, we are tracking all their direct children. Several events have to be selected for keeping the tree up-to-date. And because XSelectInput has no and/or/xor option, the events from the other program parts are even selected here (e.g. mouse movements on the output window). Unfortunately the XVisibilityEvent would be useless here, because it does not respect child windows. Instead, the visibility is calculated from the mentioned windows, using xdwRegion. Finally, because we know the window lists in this program part, it is even a good place for updating the backdropping. */ typedef struct xdwWinNodeStruct { /* Data type for an element in the tree of windows. */ struct xdwWinNodeStruct * sister; /* Next element in the parent's list of children. */ struct xdwWinNodeStruct * parent; /* The parent (next element in path). */ struct xdwWinNodeStruct * childInPath; /* The child where the output window is (previous element in path). */ struct xdwWinNodeStruct * firstChild; /* List of all children. */ Window window; /* Id of the window. */ long eventMask, shapeEventMask; /* Selected events on the window. */ XWindowAttributes attr; /* Attributes of the window. */ XRectangle * shapeRects; int shapeRectCount; /* Array of rectangles, if it is a shaped window. */ unsigned isInPath : 1; /* Whether this window is member of the path. */ unsigned interaction : 1; /* Whether this window should interact with the water. */ unsigned attrUpToDate : 1; /* Whether the attributes are up-to-date. */ unsigned linkageUpToDate : 1; /* Whether the relationships to other windows are up-to-date. */ unsigned shapeUpToDate : 1; /* Whether the shape information is up-to-date. */ unsigned interactionUpToDate : 1; /* Whether the interaction information is up-to-date. */ } xdwWinNode; xdwWinNode * xdwWinPath; /* (refer to the description above) */ int xdwWinPathX,xdwWinPathY; /* Position of the output window relative to the root window. */ int xdwShapeEventBase, xdwShapeErrorBase; /* General information about the XShape extension. */ Atom XDESKTOPWAVES_WINDOW_INTERACTION; /* Property of client windows. It overrides the waves-by-windows option. */ xdwRegion xdwWindowShips; /* This region summarizes all windows which interact with the water. */ int xdwVisiblePixels; /* Number of visible pixels in the output window. */ Bool xdwWindowTrackingUpToDate; /* Whether everything here is up-to-date. */ static xdwWinNode * xdwCreateWinNode(Window window) { xdwWinNode * n; /* Create a new window node. */ n=malloc(sizeof(xdwWinNode)); memset(n,0,sizeof(xdwWinNode)); n->window=window; return n; } static void xdwDestroyWinTree(Display * display, xdwWinNode * tree) { xdwWinNode * n; /* Destroy a node and all its descendants */ if (tree) { while ((n=tree->firstChild)!=NULL) { tree->firstChild=n->sister; xdwDestroyWinTree(display,n); } if (tree->eventMask || tree->shapeEventMask) { xdwBeginAcceptXErrors(); if (tree->eventMask) { XSelectInput(display,tree->window,0); } if (tree->shapeEventMask) { XShapeSelectInput(display,tree->window,0); } XSync(display,False); xdwEndAcceptXErrors(); } if (tree->shapeRects) { XFree(tree->shapeRects); } free(tree); } } static void xdwDestroyWinAncestors(Display * display, xdwWinNode * node) { xdwWinNode * n, * parent; xdwWinNode * * pn; /* Destroy all nodes but the given node and its descendants */ if (node && node->parent) { parent=node->parent; node->parent=NULL; for (pn=&parent->firstChild, n=*pn; n; pn=&n->sister, n=*pn) { if (n==node) { *pn=n->sister; n->sister=NULL; break; } } while (parent->parent) parent=parent->parent; xdwDestroyWinTree(display,parent); } } static void xdwInitWindowTracking(Display * display, Window drawWin) { /* Query Shape extension. */ if (!XShapeQueryExtension(display,&xdwShapeEventBase,&xdwShapeErrorBase)) { fprintf(stderr,"%s: XShape extension not available\n",xdwArg0); exit(1); } /* Have an atom for the window interaction property. */ XDESKTOPWAVES_WINDOW_INTERACTION=XInternAtom( display,"XDESKTOPWAVES_WINDOW_INTERACTION",False ); /* Create a node for the output window. */ xdwWinPath=xdwCreateWinNode(drawWin); xdwWinPath->isInPath=1; /* Prepare some other variables. */ xdwWinPathX=0; xdwWinPathY=0; xdwInitRegion(&xdwWindowShips); xdwVisiblePixels=0; /* Not up-to-date. */ xdwWindowTrackingUpToDate=False; } static xdwWinNode * xdwSearchWinNode(Window window) { xdwWinNode * n; /* Search for a node by a given window id. */ n=xdwWinPath; while (n->parent) n=n->parent; while (n->window!=window) { if (n->firstChild) n=n->firstChild; else if (n->sister) n=n->sister; else { do { n=n->parent; } while (n && !n->sister); if (!n) break; n=n->sister; } } return n; } static void xdwOnSubstructureChanged(Window window) { xdwWinNode * n; /* Got a substructure notify event... */ n=xdwSearchWinNode(window); if (n) { n->linkageUpToDate=0; for (n=n->firstChild; n; n=n->sister) { n->attrUpToDate=0; /* We will not get any shape notify events, if the window is not shaped. Therefore: */ if (n->shapeRects && n->shapeRectCount<=1) n->shapeUpToDate=0; } xdwWindowTrackingUpToDate=False; } } static void xdwOnInteractionPropertyChanged(Window window) { xdwWinNode * n; /* Got a change event for the interaction property... */ n=xdwSearchWinNode(window); if (n) { n->interactionUpToDate=0; xdwWindowTrackingUpToDate=False; } } static void xdwOnShapeChanged(Window window) { xdwWinNode * n; /* Got a shape notify event... */ n=xdwSearchWinNode(window); if (n) { n->shapeUpToDate=0; xdwWindowTrackingUpToDate=False; } } static Bool xdwUpdateWinNode(Display * display, xdwWinNode * node) { xdwWinNode * n, * oldList; xdwWinNode * * pn, * * pn2; long eventMask, shapeEventMask; unsigned long len, rem; unsigned char * data; Window rwin, pwin; Window * cwins; Atom type; int i, ordering, r, frmt, cnt; /* Which events should be selected on the window? */ if (node->isInPath) { eventMask=SubstructureNotifyMask; shapeEventMask=0; if (!node->parent) eventMask|=PropertyChangeMask; if (!node->childInPath) { eventMask|=ExposureMask|PointerMotionMask|EnterWindowMask|LeaveWindowMask; } else if (!node->childInPath->childInPath && xdwOptTransparency==XDW_SHAPED) { eventMask|=PointerMotionMask|EnterWindowMask|LeaveWindowMask; } } else { eventMask=PropertyChangeMask; shapeEventMask=ShapeNotifyMask; } /* Update event selection. */ if (node->eventMask!=eventMask || node->shapeEventMask!=shapeEventMask) { xdwBeginAcceptXErrors(); if (node->eventMask!=eventMask) { XSelectInput(display,node->window,eventMask); node->eventMask=eventMask; } if (node->shapeEventMask!=shapeEventMask) { XShapeSelectInput(display,node->window,shapeEventMask); node->shapeEventMask=shapeEventMask; } XSync(display,False); xdwEndAcceptXErrors(); } /* Update attributes. */ if (!node->attrUpToDate) { xdwBeginAcceptXErrors(); r=XGetWindowAttributes(display,node->window,&node->attr); if (!r) memset(&node->attr,0,sizeof(node->attr)); node->attrUpToDate=1; xdwEndAcceptXErrors(); } /* Update shape information. */ if (!node->shapeUpToDate) { if (node->shapeRects) { XFree(node->shapeRects); node->shapeRects=NULL; node->shapeRectCount=0; } if (!node->isInPath) { xdwBeginAcceptXErrors(); node->shapeRects=XShapeGetRectangles( display,node->window,ShapeBounding,&node->shapeRectCount, &ordering ); xdwEndAcceptXErrors(); } node->shapeUpToDate=1; } /* Update interaction bit. */ if (!node->interactionUpToDate) { if (node->isInPath) { node->interaction=1; } else { xdwBeginAcceptXErrors(); data=NULL; r=XGetWindowProperty( display,node->window,XDESKTOPWAVES_WINDOW_INTERACTION, 0,1,False,XA_CARDINAL,&type,&frmt,&len,&rem,&data ); if (r==Success && type==XA_CARDINAL && data) { node->interaction = (*(unsigned*)data) ? 1 : 0; } else { node->interaction = xdwOptWavesByWindows ? 1 : 0; } if (data) XFree(data); xdwEndAcceptXErrors(); } node->interactionUpToDate=1; } /* Update relationships. */ if (!node->linkageUpToDate) { /* Query tree... */ xdwBeginAcceptXErrors(); r=XQueryTree(display,node->window,&rwin,&pwin,&cwins,(unsigned*)&cnt); if (!r) { rwin=None; pwin=None; cwins=NULL; cnt=0; } xdwEndAcceptXErrors(); /* Do not have any children if this is a window "out-of-path". */ if (!node->isInPath && cwins) { XFree(cwins); cwins=NULL; cnt=0; } /* Check parent relation. */ if ( (!node->parent && pwin!=None) || (node->parent && node->parent->window!=pwin) ) { if (node->isInPath) { if (node->parent) { xdwDestroyWinAncestors(display,node); node->parent=NULL; } if (pwin!=None) { node->parent=xdwCreateWinNode(pwin); node->parent->isInPath=1; node->parent->firstChild=node; node->parent->childInPath=node; node->sister=NULL; } } else { node->parent->linkageUpToDate=0; if (cwins) XFree(cwins); return False; } } /* Check child-in-path relation. */ if (node->childInPath) { for (i=0; ichildInPath->window; i++); if (i>=cnt) { node->childInPath->linkageUpToDate=0; if (cwins) XFree(cwins); return False; } } /* Update the list of children. */ oldList=node->firstChild; node->firstChild=NULL; pn=&node->firstChild; for (i=0; isister, n=*pn2) { if (n->window==cwins[i]) break; } if (n) { *pn2=n->sister; n->sister=NULL; } else { n=xdwCreateWinNode(cwins[i]); n->parent=node; n->linkageUpToDate=1; } *pn=n; pn=&n->sister; } while ((n=oldList)!=NULL) { oldList=n->sister; xdwDestroyWinTree(display,n); } if (cwins) XFree(cwins); node->linkageUpToDate=1; } return True; } static Bool xdwUpdateWindowTracking(Display * display) { xdwWinNode * pathElement, * child; xdwWinNode * node, * n; xdwRegion oldShips, noShips, visibility; xdwRegScan * s, * t; Bool coveringChanged; Window swins[2]; int x0, y0, y, y2, i; /* Be quick if everything is up-to-date here. */ if (xdwWindowTrackingUpToDate) return False; /* Update the tree... */ for (pathElement=xdwWinPath; pathElement;) { if (!xdwUpdateWinNode(display,pathElement)) { pathElement=xdwWinPath; } else { for (child=pathElement->firstChild; ; child=child->sister) { if (!child) { pathElement=pathElement->parent; break; } if (!xdwUpdateWinNode(display,child)) { pathElement=xdwWinPath; break; } } } } /* Calculate region of visible areas (visibility), calculate region of interacting windows (xdwWindowShips, but keep old variant in oldShips), and calculate the relative position of the output window (xdwWinPathX, xdwWinPathY). */ memcpy(&oldShips,&xdwWindowShips,sizeof(xdwRegion)); xdwInitRegion(&xdwWindowShips); xdwInitRegion(&visibility); xdwInitRegion(&noShips); xdwSetRegionRect( &noShips,0,0,xdwWinPath->attr.width,xdwWinPath->attr.height ); if (xdwWinPath->attr.map_state==IsViewable) { xdwSetRegionRect( &visibility,0,0,xdwWinPath->attr.width,xdwWinPath->attr.height ); } for (x0=0, y0=0, node=xdwWinPath; node; node=node->parent) { if (node->childInPath) { n=node->childInPath; x0-=n->attr.x; y0-=n->attr.y; n=n->sister; xdwRegAndRect( &visibility, x0, y0, x0+node->attr.width, y0+node->attr.height ); } else { n=node->firstChild; } for (; n; n=n->sister) { if (n->attr.map_state==IsViewable && n->attr.class!=InputOnly) { if (n->shapeRects) { for (i=0; ishapeRectCount; i++) { xdwRegAndNotRect( n->interaction ? &noShips : &visibility, x0+n->attr.x+n->shapeRects[i].x, y0+n->attr.y+n->shapeRects[i].y, x0+n->attr.x+n->shapeRects[i].x+n->shapeRects[i].width, y0+n->attr.y+n->shapeRects[i].y+n->shapeRects[i].height ); } } else { xdwRegAndNotRect( n->interaction ? &noShips : &visibility, x0+n->attr.x-n->attr.border_width, y0+n->attr.y-n->attr.border_width, x0+n->attr.x+n->attr.width+n->attr.border_width, y0+n->attr.y+n->attr.height+n->attr.border_width ); } } } } xdwSetRegionRect( &xdwWindowShips,0,0,xdwWinPath->attr.width,xdwWinPath->attr.height ); xdwRegAndNotReg(&xdwWindowShips,&noShips); xdwRegAndNotReg(&visibility,&xdwWindowShips); xdwWinPathX = -x0; xdwWinPathY = -y0; /* How many visible pixels do we have in the output window? */ xdwVisiblePixels=xdwCountRegionPixels(&visibility); /* Update covering by windows. */ coveringChanged=False; y=xdwWindowShips.y1; if (y>oldShips.y1) y=oldShips.y1; y2=xdwWindowShips.y2; if (y2=oldShips.y1 && y=xdwWindowShips.y1 && yx1x1)) { coveringChanged|=xdwCoverRect(s->x1,y,s->x2,y+1,-1); s=s->next; } else if (!s || s->x1!=t->x1 || s->x2!=t->x2) { coveringChanged|=xdwCoverRect(t->x1,y,t->x2,y+1,1); t=t->next; } else { s=s->next; t=t->next; } } } /* Get rid of temporary regions. */ xdwEmptyRegion(&visibility); xdwEmptyRegion(&noShips); xdwEmptyRegion(&oldShips); /* Backdropping: If there is a non-override-redirect window somewhere behind our backdrop window, bring our window behind it. */ if (xdwOptWindowCreation==XDW_BACKDROP && xdwWinPath->parent) { n=xdwWinPath->parent->firstChild; while (n!=xdwWinPath) { if (!n->attr.override_redirect) { swins[0]=n->window; swins[1]=xdwWinPath->window; xdwBeginAcceptXErrors(); XRestackWindows(display,swins,2); XSync(display,False); xdwEndAcceptXErrors(); break; } n=n->sister; } } /* Ready. */ xdwWindowTrackingUpToDate=True; return coveringChanged; } /*============================================================================*/ /*======================= Inter client communications ========================*/ /*============================================================================*/ Window xdwWindow; /* A created window for receiving client messages. */ Atom XDESKTOPWAVES_WINDOW_ID; /* A root window property for the window id. */ Atom XDESKTOPWAVES_COMMAND_MESSAGE; /* Client message type for receiving commands. */ Atom xdw_WM_PROTOCOLS; Atom xdw_WM_DELETE_WINDOW; /* Atoms for receiving deletion requests from the window manager. */ typedef struct xdwGhostStruct { /* Data type for a "ghost" on the water, generated by a client. */ struct xdwGhostStruct * next; /* Next elements in the list. */ int ghostId; /* Identification number of the ghost. */ int milliSecs; /* Remaining lifetime in milliseconds. */ Bool isCircle; /* Whether it's a circle or a rectangle. */ int x, y, width, height, radius; /* Coordinates... */ } xdwGhost; xdwGhost * xdwGhostList; /* List of ghosts. */ static void xdwInitCommunications(Display * display, Window ownWin) { /* Keep id of created window. */ xdwWindow=ownWin; /* Have some atoms. */ XDESKTOPWAVES_WINDOW_ID=XInternAtom( display,"XDESKTOPWAVES_WINDOW_ID",False ); XDESKTOPWAVES_COMMAND_MESSAGE=XInternAtom( display,"XDESKTOPWAVES_COMMAND_MESSAGE",False ); xdw_WM_PROTOCOLS=XInternAtom( display,"WM_PROTOCOLS",False ); xdw_WM_DELETE_WINDOW=XInternAtom( display,"WM_DELETE_WINDOW",False ); /* Set the window id property. Other instances will terminate after recognizing the change. */ XChangeProperty(display,DefaultRootWindow(display),XDESKTOPWAVES_WINDOW_ID, XA_WINDOW,32,PropModeReplace,(unsigned char*)&xdwWindow,1); /* Just for terminating old versions of xdesktopwaves. */ XDeleteProperty(display,DefaultRootWindow(display), XInternAtom(display,"XDESKTOPWAVES_INSTANCE_ID",False)); /* Finalize the property changes. */ XSync(display,False); } static void xdwCheckInstance(Display * display) { unsigned char * data; unsigned long len, bytesAfter; int format, ret; Atom type; /* See if there is a newer instance. If so, prepare for termination. */ data=NULL; ret=XGetWindowProperty( display,DefaultRootWindow(display),XDESKTOPWAVES_WINDOW_ID,0,1,False, XA_WINDOW,&type,&format,&len,&bytesAfter,&data ); if ( ret!=Success || type!=XA_WINDOW || format!=32 || bytesAfter!=0 || len!=1 || (*(Window*)data)!=xdwWindow ) { xdwTerminationRequested=True; } if (data) XFree(data); } static Bool xdwCoverByGhost(xdwGhost * g, int deltaCovering) { if (g->isCircle) { return xdwCoverCircle(g->x,g->y,g->radius,deltaCovering); } else { return xdwCoverRect(g->x,g->y,g->x+g->width,g->y+g->height,deltaCovering); } } static Bool xdwOnClientMessage(XClientMessageEvent * msg) { xdwGhost * g; Bool changed; int removeId,intensity; changed=False; if (msg->message_type==xdw_WM_PROTOCOLS) { if (msg->data.l[0]==xdw_WM_DELETE_WINDOW) { if (xdwOptVerb>=2) printf("got WM_DELETE_WINDOW\n"); xdwTerminationRequested=True; } } else if (msg->message_type==XDESKTOPWAVES_COMMAND_MESSAGE) { if (xdwOptVerb>=2) printf( "got command: %d %d %d %d %d %d %d %d %d %d\n", msg->data.s[0],msg->data.s[1],msg->data.s[2],msg->data.s[3], msg->data.s[4],msg->data.s[5],msg->data.s[6],msg->data.s[7], msg->data.s[8],msg->data.s[9] ); switch (msg->data.s[0]) { case 0: /* Ping */ break; case 1: /* Clear */ xdwClearCells(); changed=True; break; case 2: /* SetRain */ intensity=msg->data.s[1]; if (intensity<0) intensity=0; else if (intensity>10) intensity=10; xdwOptRain=intensity; break; case 3: /* SetStorm */ intensity=msg->data.s[1]; if (intensity<0) intensity=0; else if (intensity>10) intensity=10; xdwOptStorm=intensity; changed=True; /* (to get out of idle mode) */ break; case 4: /* PutRaindrop */ changed=xdwPutRaindrop( msg->data.s[1]-xdwWinPathX, msg->data.s[2]-xdwWinPathY, msg->data.s[3] ); break; case 5: /* PutGhostRect */ case 6: /* PutGhostCircle */ removeId=(((int)msg->data.s[3])<<16)|(unsigned short)msg->data.s[4]; g=NULL; if (removeId) { for (g=xdwGhostList; g; g=g->next) { if (g->ghostId==removeId) break; } } if (g) { changed|=xdwCoverByGhost(g,-1); } else { g=(xdwGhost*)malloc(sizeof(xdwGhost)); g->next=xdwGhostList; xdwGhostList=g; } g->ghostId=(((int)msg->data.s[1])<<16)|(unsigned short)msg->data.s[2]; g->milliSecs=msg->data.s[5]; g->x=msg->data.s[6]-xdwWinPathX; g->y=msg->data.s[7]-xdwWinPathY; if (msg->data.s[0]==5) { g->isCircle=False; g->width=msg->data.s[8]; g->height=msg->data.s[9]; } else { g->isCircle=True; g->radius=msg->data.s[8]; } changed|=xdwCoverByGhost(g,1); break; } } return changed; } static Bool xdwUpdateGhosts(int milliSecsGone) { xdwGhost * * ps; xdwGhost * s; Bool changed; if (milliSecsGone<0) milliSecsGone=0; changed=False; for (ps=&xdwGhostList, s=*ps; s; ) { s->milliSecs-=milliSecsGone; if (s->milliSecs<=0) { changed|=xdwCoverByGhost(s,-1); *ps=s->next; free(s); } else { ps=&s->next; } s=*ps; } return changed; } /*============================================================================*/ /*============================== Event Handling ==============================*/ /*============================================================================*/ static Bool xdwHandleEvent(Display * display, XEvent * event) { Bool coveringChanged; coveringChanged=False; switch (event->type) { case ClientMessage: coveringChanged=xdwOnClientMessage(&event->xclient); break; case PropertyNotify: if (event->xproperty.atom==XDESKTOPWAVES_WINDOW_ID) { if (xdwOptVerb>=2) printf( "PropertyNotify: window=0x%lX atom=XDESKTOPWAVES_WINDOW_ID\n", event->xproperty.window ); xdwCheckInstance(display); } else if (event->xproperty.atom==XDESKTOPWAVES_WINDOW_INTERACTION) { if (xdwOptVerb>=2) printf( "PropertyNotify: window=0x%lX atom=XDESKTOPWAVES_WINDOW_INTERACTION\n", event->xproperty.window ); xdwOnInteractionPropertyChanged(event->xproperty.window); } break; case Expose: if (xdwOptVerb>=2) printf( "Expose: x=%d y=%d w=%d h=%d\n", event->xexpose.x, event->xexpose.y, event->xexpose.width, event->xexpose.height ); /* TODO: If in double buffering mode, the exposed area should be copied from the back buffer to the window, instead of touching the canvas. (=> less flicker and less overhead) */ xdwTouchCanvas( event->xexpose.x, event->xexpose.y, event->xexpose.x+event->xexpose.width, event->xexpose.y+event->xexpose.height ); break; case MotionNotify: if (xdwOptVerb>=2) printf( "MotionNotify: x=%d y=%d\n", event->xmotion.x, event->xmotion.y ); coveringChanged=xdwChangeMouse(event->xmotion.x,event->xmotion.y); break; case EnterNotify: if (xdwOptVerb>=2) printf( "EnterNotify: x=%d y=%d\n", event->xcrossing.x, event->xcrossing.y ); coveringChanged=xdwChangeMouse(event->xcrossing.x,event->xcrossing.y); break; case LeaveNotify: if (xdwOptVerb>=2) printf("LeaveNotify\n"); coveringChanged=xdwChangeMouse(-1,-1); break; case CreateNotify: if (xdwOptVerb>=2) printf( "CreateNotify parent=0x%lX window=0x%lX\n", event->xcreatewindow.parent, event->xcreatewindow.window ); xdwOnSubstructureChanged(event->xcreatewindow.parent); break; case DestroyNotify: if (xdwOptVerb>=2) printf( "DestroyNotify window=0x%lX\n", event->xdestroywindow.window ); if (event->xdestroywindow.window==xdwWinPath->window) { xdwTerminationRequested=True; break; } xdwOnSubstructureChanged(event->xdestroywindow.event); break; case MapNotify: if (xdwOptVerb>=2) printf( "MapNotify window=0x%lX\n", event->xmap.window ); xdwOnSubstructureChanged(event->xmap.event); break; case UnmapNotify: if (xdwOptVerb>=2) printf( "UmapNotify window=0x%lX\n", event->xunmap.window ); xdwOnSubstructureChanged(event->xunmap.event); break; case ConfigureNotify: if (xdwOptVerb>=2) printf( "ConfigureNotify window=0x%lX\n", event->xconfigure.window ); xdwOnSubstructureChanged(event->xconfigure.event); break; case CirculateNotify: if (xdwOptVerb>=2) printf( "CirculateNotify window=0x%lX\n", event->xcirculate.window ); xdwOnSubstructureChanged(event->xcirculate.event); break; case ReparentNotify: if (xdwOptVerb>=2) printf( "ReparentNotify event=0x%lX window=0x%lX parent=0x%lX\n", event->xreparent.event, event->xreparent.window, event->xreparent.parent ); xdwOnSubstructureChanged(event->xreparent.event); break; default: if (event->type==xdwShapeEventBase+ShapeNotify) { if (xdwOptVerb>=2) printf( "ShapeNotify window=0x%lX\n", ((XShapeEvent*)event)->window ); xdwOnShapeChanged(((XShapeEvent*)event)->window); } break; } return coveringChanged; } /*============================================================================*/ /*=================================== main ===================================*/ /*============================================================================*/ int main(int argc, char * argv[]) { Display * display; Window rootWin, ownWin, drawWin, aboveWin; Pixmap pm, pm2, bufferPM; GC gc, drawGC, bufferGC; XGCValues gcv; XSetWindowAttributes xswa; XWindowAttributes xwa; XWindowChanges xwc; XSizeHints xsh; Bool isNautilus, idleByWaves, idleByVisibility, wasIdle; Atom atoms[8]; XEvent event; int width, height, calls, rects, loopRate, tsRem, frRem, timeSlices; int skippedTimeSlices, totalCalls, totalRects, sleepMS, reportMS; int totalFrames; unsigned card, prevAbsTimeMS, reportAbsTimeMS, reportProcTimeMS, t; /* Initialize handling of X errors. */ xdwInitXErrorHandling(); /* Prepare options. */ xdwSetDefaultOptions(); xdwParseOptions(argc,argv); /* Have a display. */ if (!(display=XOpenDisplay(xdwOptDisplay))) { fprintf(stderr,"%s: failed to open display %s\n",xdwArg0, XDisplayName(xdwOptDisplay)); exit(1); } /* Set process priority. */ if (xdwOptNice) { if (nice(xdwOptNice)==-1) { fprintf(stderr,"%s: nice(%d) failed: %s\n", xdwArg0,xdwOptNice,strerror(errno)); exit(1); } } /* Look for CPU features. */ xdwDetectCPUFeatures(); /* Which root window and size should be used? */ if (xdwOptWindowCreation==XDW_USE_ROOT || xdwOptWindowCreation==XDW_BACKDROP) { rootWin=xdwDetectVirtualRoot(display,&aboveWin,&isNautilus); XGetWindowAttributes(display,rootWin,&xwa); width=xwa.width; height=xwa.height; /* Nautilus: Make the backdrop window a little smaller. Otherwise xpenguins 2.2 (and maybe others) would take it as the virtual root! */ if (isNautilus && xdwOptWindowCreation==XDW_BACKDROP) height--; } else { rootWin=DefaultRootWindow(display); aboveWin=None; XGetWindowAttributes(display,rootWin,&xwa); width=xwa.width; height=xwa.height; } /* Initialize several data structures. */ xdwInitCells(width,height); xdwInitPalette(display); xdwInitLighting(); xdwInitCanvas(width,height); /* Have a window for drawing and an own window for receiving client messages (maybe it's just one window). */ if (xdwOptWindowCreation==XDW_USE_ROOT) { drawWin=rootWin; memset(&xswa,0,sizeof(xswa)); xswa.override_redirect=True; ownWin=XCreateWindow( display,DefaultRootWindow(display),-100,-100,1,1,0,CopyFromParent, InputOnly,CopyFromParent,CWOverrideRedirect,&xswa ); XStoreName(display,ownWin,"xdesktopwaves"); } else { memset(&xswa,0,sizeof(xswa)); if (xdwOptWindowCreation==XDW_BACKDROP) xswa.override_redirect=True; memset(&xsh,0,sizeof(xsh)); if (xdwOptWindowCreation==XDW_WINDOW) { xsh.flags =PMinSize|PMaxSize; xsh.x =width/16; xsh.y =height/16; xsh.width =width*7/8; xsh.height =height*7/8; xsh.min_width =width/16; xsh.min_height=height/16; xsh.max_width =width; xsh.max_height=height; } else { xsh.flags =PPosition|PSize|USPosition|USSize|PMinSize|PMaxSize; xsh.x =0; xsh.y =0; xsh.width =width; xsh.height =height; xsh.min_width =width; xsh.min_height=height; xsh.max_width =width; xsh.max_height=height; } drawWin=XCreateWindow( display,rootWin,xsh.x,xsh.y,xsh.width,xsh.height,0,CopyFromParent, InputOutput,CopyFromParent,CWOverrideRedirect,&xswa ); XSetWMProperties(display,drawWin,NULL,NULL,0,0,&xsh,0,NULL); XStoreName(display,drawWin,"xdesktopwaves"); atoms[0]=XInternAtom(display,"WM_DELETE_WINDOW",False); XChangeProperty( display,drawWin,XInternAtom(display,"WM_PROTOCOLS",False),XA_ATOM,32, PropModeReplace,(const unsigned char*)atoms,1 ); if (xdwOptWindowCreation==XDW_WMBACKDROP) { atoms[0]=XInternAtom(display,"_NET_WM_STATE_BELOW",False); atoms[1]=XInternAtom(display,"_NET_WM_STATE_FULLSCREEN",False); atoms[2]=XInternAtom(display,"_NET_WM_STATE_STICKY",False); atoms[3]=XInternAtom(display,"_NET_WM_STATE_SKIP_TASKBAR",False); atoms[4]=XInternAtom(display,"_NET_WM_STATE_SKIP_PAGER",False); XChangeProperty( display,drawWin,XInternAtom(display,"_NET_WM_STATE",False),XA_ATOM, 32,PropModeReplace,(const unsigned char*)atoms,5 ); card=-1; XChangeProperty( display,drawWin,XInternAtom(display,"_NET_WM_DESKTOP",False), XA_CARDINAL,32,PropModeReplace,(const unsigned char*)&card,1 ); } XMapWindow(display,drawWin); if (xdwOptWindowCreation==XDW_BACKDROP) { XLowerWindow(display,drawWin); if (aboveWin!=None) { memset(&xwc,0,sizeof(xwc)); xwc.sibling=aboveWin; xwc.stack_mode=Above; XConfigureWindow(display,drawWin,CWSibling|CWStackMode,&xwc); } } ownWin=drawWin; } /* Have a GC for the window. */ drawGC=XCreateGC(display,drawWin,0,&gcv); /* Possibly have Pixmap and GC for double buffering. */ if (xdwOptDoubleBuffer) { XGetWindowAttributes(display,drawWin,&xwa); bufferPM=XCreatePixmap(display,drawWin,width,height,xwa.depth); bufferGC=XCreateGC(display,bufferPM,0,&gcv); } else { bufferPM=None; bufferGC=None; } /* Prepare for transparency. */ switch (xdwOptTransparency) { case XDW_OPAQUE: if (xdwOptWindowCreation!=XDW_USE_ROOT) { XSetWindowBackground(display,drawWin,xdwPalette[0]); } break; case XDW_STIPPLED: pm=XCreateBitmapFromData(display,drawWin,"\252\125",2,2); XSetStipple(display,drawGC,pm); XSetFillStyle(display,drawGC,FillStippled); if (xdwOptWindowCreation!=XDW_USE_ROOT) { XSetWindowBackgroundPixmap(display,drawWin,ParentRelative); XClearWindow(display,drawWin); } break; case XDW_SHAPED: /* Very slow in drawing with large number of operations => double buffering recommended. Even, if the mask is not horizontal striped (e.g. checkered) everything is extremely slow. */ pm=XCreateBitmapFromData(display,drawWin,"\0\377",2,2); pm2=XCreatePixmap(display,drawWin,width,height,1); gcv.foreground = 1; gcv.background = 0; gc=XCreateGC(display,pm2,(GCForeground|GCBackground),&gcv); XSetStipple(display,gc,pm); XSetFillStyle(display,gc,FillOpaqueStippled); XFillRectangle(display,pm2,gc,0,0,width,height); XFreeGC(display,gc); XShapeCombineMask(display,drawWin,ShapeBounding,0,0,pm2,ShapeSet); XSetWindowBackground(display,drawWin,xdwPalette[0]); break; case XDW_WMOPACITY: if (xdwOptTranspaParam<100 && xdwOptWindowCreation!=XDW_USE_ROOT) { card=((unsigned)xdwOptTranspaParam)*42949672U; XChangeProperty( display,drawWin,XInternAtom(display,"_NET_WM_WINDOW_OPACITY",False), XA_CARDINAL,32,PropModeReplace,(unsigned char *)&card,1 ); } break; } /* Prepare for signals. */ xdwInitSignalHandling(); /* Prepare for tracking other windows covering our window. */ xdwInitWindowTracking(display,drawWin); /* Prepare communications: Be the only instance on the display - other instances will terminate. And maybe that's the only job we have to do... */ xdwInitCommunications(display,ownWin); if (xdwOptEnd) goto THE_END; /* Calculate rate of processing events. */ if (xdwOptEventsPerFrame=xdwOptSimsPerFrame) { frRem-=xdwOptSimsPerFrame; if (frRem>=xdwOptSimsPerFrame) frRem=0; totalFrames++; /* Update canvas. */ if (!idleByWaves && !idleByVisibility) xdwUpdateCanvas(); /* Are there any changes to be drawn? */ if (xdwTouchedCvRow1=1 && !wasIdle && !xdwFreeze) { printf("Frames/s=0 CPU=0%% MBit/s=0.0 (IDLE MODE)\n"); } t=1000000/xdwOptFrameRate; if (t<200000) t=200000; usleep(t); skippedTimeSlices= (xdwOptFrameRate*xdwOptSimsPerFrame*(t/1000-1000/loopRate)+500) /1000 ; if (skippedTimeSlices<0) skippedTimeSlices=0; } else { /* Non-idle mode: sleep for the best known delay (synchronization). */ if (sleepMS>0) usleep(sleepMS*1000); skippedTimeSlices=0; } /* Update times. */ prevAbsTimeMS=xdwAbsTimeMS; xdwUpdateTimes(); /* Synchronize on desired frame rate and report statistics. */ if (!idleByWaves && !idleByVisibility) { if (wasIdle) { reportAbsTimeMS=xdwAbsTimeMS; reportProcTimeMS=xdwProcTimeMS; totalFrames=0; totalCalls=0; totalRects=0; reportMS=250; } else { sleepMS+=(1000/loopRate-(int)(xdwAbsTimeMS-prevAbsTimeMS))/2; if (sleepMS<-100) sleepMS=-100; else if (sleepMS>1000) sleepMS=1000; t=xdwAbsTimeMS-reportAbsTimeMS; if (t>reportMS && totalFrames>0) { if (xdwOptVerb>=1) { printf( "Frames/s=%d CPU=%d%% MBit/s=%.1f XFillRectangles's/Frm=%d Rects/Frm=%d\n", (int)(totalFrames*1000.0/t+0.5), (int)((xdwProcTimeMS-reportProcTimeMS)*100.0/t+0.5), totalRects*0.069/t, totalCalls/totalFrames, totalRects/totalFrames ); } reportAbsTimeMS=xdwAbsTimeMS; reportProcTimeMS=xdwProcTimeMS; totalFrames=0; totalCalls=0; totalRects=0; reportMS=1000; } } } } THE_END: /* Termination: possibly redraw root window. */ if (xdwOptWindowCreation==XDW_USE_ROOT) { XClearArea(display,rootWin,0,0,width,height,True); } XCloseDisplay(display); if (xdwOptVerb>=1) printf("Exiting proper.\n"); return 0; }