1 /* -copyright-
2 #-#
3 #-# xsnow: let it snow on your desktop
4 #-# Copyright (C) 1984,1988,1990,1993-1995,2000-2001 Rick Jansen
5 #-# 	      2019,2020,2021 Willem Vermin
6 #-#
7 #-# This program is free software: you can redistribute it and/or modify
8 #-# it under the terms of the GNU General Public License as published by
9 #-# the Free Software Foundation, either version 3 of the License, or
10 #-# (at your option) any later version.
11 #-#
12 #-# This program is distributed in the hope that it will be useful,
13 #-# but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #-# GNU General Public License for more details.
16 #-#
17 #-# You should have received a copy of the GNU General Public License
18 #-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #-#
20  *
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <stdio.h>
26 #include <gtk/gtk.h>
27 #include <stdlib.h>
28 #include <math.h>
29 #include <X11/Xlib.h>
30 #include <X11/Intrinsic.h>
31 #include "fallensnow.h"
32 #include "utils.h"
33 #include "windows.h"
34 #include "flags.h"
35 #include "snow.h"
36 #include "Santa.h"
37 #include "blowoff.h"
38 #include "wind.h"
39 #include "debug.h"
40 
41 #define NOTACTIVE \
42    (Flags.BirdsOnly || !WorkspaceActive() || Flags.NoSnowFlakes)
43 
44 
45 static void   drawquartcircle(int n, short int *y);  // nb: dimension of y > n+1
46 static void   CreateSurfaceFromFallen(FallenSnow *f);
47 static void   EraseFallenPixel(FallenSnow *fsnow,int x);
48 static void   CreateDeshBottom(short int *desh, int w, int h);
49 static int    do_change_bottom(void *dummy);
50 static int    do_adjust_bottom(void *dummy);
51 static float  gaussf(float x, float mu, float sigma);
52 
fallensnow_init()53 void fallensnow_init()
54 {
55    InitFallenSnow();
56    add_to_mainloop(PRIORITY_DEFAULT, time_change_bottom, do_change_bottom);
57    add_to_mainloop(PRIORITY_DEFAULT, time_adjust_bottom, do_adjust_bottom);
58    P(" \n");
59 }
60 
UpdateFallenSnowAtBottom()61 void UpdateFallenSnowAtBottom()
62 {
63    FallenSnow *fsnow = FindFallen(global.FsnowFirst, 0);
64    if (fsnow)
65       fsnow->y = global.SnowWinHeight;
66 }
67 
fallensnow_draw(cairo_t * cr)68 void fallensnow_draw(cairo_t *cr)
69 {
70    if (NOTACTIVE)
71       return;
72    FallenSnow *fsnow = global.FsnowFirst;
73    while(fsnow)
74    {
75       if (HandleFallenSnow(fsnow))
76       {
77 	 P("fallensnow_draw %d %d\n",counter++,
78 	       cairo_image_surface_get_width(fsnow->surface));
79 	 P("fallensnow_draw: x:%d y:%d\n",fsnow->x,fsnow->y);
80 	 cairo_set_source_surface (cr, fsnow->surface, fsnow->x, fsnow->y-fsnow->h+1);
81 	 my_cairo_paint_with_alpha(cr,ALPHA);
82 	 fsnow->prevx = fsnow->x;
83 	 fsnow->prevy = fsnow->y-fsnow->h+1;
84 	 fsnow->prevw = cairo_image_surface_get_width(fsnow->surface);
85 	 fsnow->prevh = fsnow->h;
86       }
87       fsnow = fsnow->next;
88    }
89 }
90 
fallensnow_erase()91 void fallensnow_erase()
92 {
93    if (NOTACTIVE)
94       return;
95    FallenSnow *fsnow = global.FsnowFirst;
96    while(fsnow)
97    {
98       if (HandleFallenSnow(fsnow))
99       {
100 	 P("fallensnow_erase %d %d %d %d %d\n",counter++,
101 	       fsnow->prevx, fsnow->prevy, fsnow->prevw, fsnow->prevh);
102 
103 	 myXClearArea(global.display,global.SnowWin,
104 	       fsnow->prevx, fsnow->prevy, fsnow->prevw, fsnow->prevh, global.xxposures);
105       }
106       fsnow = fsnow->next;
107    }
108 
109 }
110 
fallensnow_ui()111 void fallensnow_ui()
112 {
113    UIDO(MaxWinSnowDepth   , InitFallenSnow(); ClearScreen(); );
114    UIDO(MaxScrSnowDepth   ,
115 	 SetMaxScreenSnowDepth();
116 	 InitFallenSnow();
117 	 ClearScreen();
118        );
119    UIDO(NoKeepSBot        , InitFallenSnow(); ClearScreen(); );
120    UIDO(NoKeepSWin        , InitFallenSnow(); ClearScreen(); );
121    UIDO(IgnoreTop         ,                                  );
122    UIDO(IgnoreBottom      ,                                  );
123 }
124 
do_fallen(void * d)125 int do_fallen(void *d)
126 {
127 
128    if (Flags.Done)
129       return FALSE;
130    if (NOTACTIVE)
131       return TRUE;
132 
133    FallenSnow *fsnow = global.FsnowFirst;
134    while(fsnow)
135    {
136       if (HandleFallenSnow(fsnow))
137 	 DrawFallen(fsnow);
138       fsnow = fsnow->next;
139    }
140    XFlush(global.display);
141    return TRUE;
142    (void)d;
143 }
144 
drawquartcircle(int n,short int * y)145 void drawquartcircle(int n, short int *y)  // nb: dimension of y > n+1
146 {
147    int i;
148    float n2 = n*n;
149    for(i=0; i<=n; i++)
150       y[i] = lrintf(sqrtf(n2 - i*i));
151 }
152 
153 // inspired by Mr. Gauss, and adapted for xsnow
gaussf(float x,float mu,float sigma)154 float gaussf(float x, float mu, float sigma)
155 {
156    float y = (x-mu)/sigma;
157    float y2 = y*y;
158    return expf(-y2);
159 }
160 
CreateDeshBottom(short int * desh,int w,int h)161 void CreateDeshBottom(short int *desh, int w, int h)
162 {
163 #if 0
164    const float twopi = 2*355.0/113.0;
165    int i;
166    {
167       float p   = drand48()*4+1;
168       int start = drand48()*w;
169       float a   = twopi/w;
170       for(i=0; i<w; i++)
171       {
172 	 desh[i] = h*(0.45f*cosf(p*(i+start)*a)+0.55f);
173       }
174       P("desh: %f %d\n",p,start);
175    }
176 #endif
177 
178    float *y = (float*)malloc(sizeof(float)*w);
179    int i,j;
180 
181    float base = 0.1;
182    for (i=0; i<w; i++)
183       y[i] = 0;
184    for (j=0; j<3; j++)
185    {
186       float sigma = (0.02+drand48()*0.2)*w;
187       float mu    = drand48()*w;
188       float z     = 1.0+drand48();
189       for (i=0; i<w; i++)
190 	 y[i] += z*gaussf(i,mu,sigma);
191       P("desh: %d %d %d %f\n",j,(int)sigma,(int)mu,z);
192    }
193 
194    float maxy = -1;
195    for (i=0; i<w; i++)
196       if(y[i]>maxy)
197 	 maxy=y[i];
198 
199    for(i=0; i<w; i++)
200    {
201       y[i] = y[i]/maxy;
202       if (y[i] < base)
203 	 y[i] = base;
204    }
205 
206    for (i=0; i<w; i++)
207       desh[i] = h*y[i];
208 
209 #if 0
210    FILE *ff = fopen("/tmp/desh","w");
211    for (i=0; i<w; i++)
212       fprintf(ff,"%d %d\n",i,desh[i]);
213    fclose(ff);
214 #endif
215 
216    free(y);
217 }
218 // insert a node at the start of the list
PushFallenSnow(FallenSnow ** first,WinInfo * win,int x,int y,int w,int h)219 void PushFallenSnow(FallenSnow **first, WinInfo *win, int x, int y, int w, int h)
220 {
221    FallenSnow *p = (FallenSnow *)malloc(sizeof(FallenSnow));
222    p->win        = *win;
223    p->x          = x;
224    p->y          = y;
225    p->w          = w;
226    p->h          = h;
227    p->prevx      = 0;
228    p->prevy      = 0;
229    p->prevw      = 10;
230    p->prevh      = 10;
231    p->w8         = ((w-1)/8+1)*8;
232    p->acth       = (short int *)malloc(sizeof(*(p->acth))*w);
233    p->desh       = (short int *)malloc(sizeof(*(p->desh))*w);
234    p->r          = (short int *)malloc(sizeof(*(p->r))*w);
235    p->pacth      = (short int *)malloc(sizeof(*(p->pacth))*w);
236    p->surface    = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,w,h);
237 
238    cairo_t *cr = cairo_create(p->surface);
239    cairo_set_source_rgba(cr,1,0,0,0.0);
240    cairo_rectangle(cr,0,0,w,h);
241    cairo_fill(cr);
242    cairo_destroy(cr);
243 
244    int l = 0,i;
245    for (i=0; i<w; i++)
246    {
247       p->acth[i] = 0; // specify l to get sawtooth effect
248       p->pacth[i] = 0;
249       p->desh[i] = h;
250       l++;
251       if (l > h)
252 	 l = 0;
253       p->r[i] = 0;  // no spikes
254       //p->r[i] = drand48()*2;     //0,1
255       //p->r[i] = drand48()*5-2;   // -2, -1, 0, 1, 2
256    }
257 
258    if (win->id == 0)
259       CreateDeshBottom(p->desh,w,h);
260 
261    if (w > h && win->id != 0)
262    {
263       drawquartcircle(h,&(p->desh[w-h-1]));
264       for(i=0; i<=h; i++)
265 	 p->desh[i] = p->desh[w-1-i];
266    }
267 
268    p->next  = *first;
269    *first   = p;
270 }
271 
272 // change bottom snow desired height
do_change_bottom(void * dummy)273 int do_change_bottom(void *dummy)
274 {
275    (void)dummy;
276    FallenSnow *fsnow = FindFallen(global.FsnowFirst, 0);
277    if (fsnow)
278       CreateDeshBottom(fsnow->desh, fsnow->w, fsnow->h);
279    return TRUE;
280 }
281 
do_adjust_bottom(void * dummy)282 int do_adjust_bottom(void *dummy)
283 {
284    (void)dummy;
285    FallenSnow *fsnow = FindFallen(global.FsnowFirst, 0);
286    if (fsnow)
287    {
288       int i;
289       int adjustments = 0;
290       for(i=0; i<fsnow->w; i++)
291       {
292 	 int d = fsnow->acth[i] - fsnow->desh[i];
293 	 if (d > 0)
294 	 {
295 	    int c = 1;
296 	    if (d > 10)
297 	       c=2;
298 	    adjustments++;
299 	    fsnow->acth[i] -= c;
300 	 }
301       }
302       P("adjustments: %d\n",adjustments);
303    }
304    return TRUE;
305 }
306 
307 // pop from list
PopFallenSnow(FallenSnow ** list)308 int PopFallenSnow(FallenSnow **list)
309 {
310    FallenSnow *next_node = NULL;
311 
312    if (*list == NULL)
313       return 0;
314 
315    next_node = (*list)->next;
316    FreeFallenSnow(*list);
317    *list = next_node;
318    return 1;
319 }
320 
321 // remove by id
RemoveFallenSnow(FallenSnow ** list,Window id)322 int RemoveFallenSnow(FallenSnow **list, Window id)
323 {
324    P("RemoveFallenSnow\n");
325    if (*list == NULL)
326       return 0;
327 
328    FallenSnow *fallen = *list;
329    if (fallen->win.id == id)
330    {
331       fallen = fallen->next;
332       FreeFallenSnow(*list);
333       *list = fallen;
334       return 1;
335    }
336 
337    FallenSnow *scratch = NULL;
338 
339    while (1)
340    {
341       if (fallen->next == NULL)
342 	 return 0;
343       scratch = fallen->next;
344       if (scratch->win.id == id)
345 	 break;
346       fallen = fallen->next;
347    }
348 
349    fallen->next = scratch->next;
350    FreeFallenSnow(scratch);
351 
352    return 1;
353 }
354 
FreeFallenSnow(FallenSnow * fallen)355 void FreeFallenSnow(FallenSnow *fallen)
356 {
357    free(fallen->acth);
358    free(fallen->desh);
359    free(fallen->r);
360    free(fallen->pacth);
361    cairo_surface_destroy(fallen->surface);
362    free(fallen);
363 }
364 
FindFallen(FallenSnow * first,Window id)365 FallenSnow *FindFallen(FallenSnow *first, Window id)
366 {
367    FallenSnow *fsnow = first;
368    while(fsnow)
369    {
370       if(fsnow->win.id == id)
371 	 return fsnow;
372       fsnow = fsnow->next;
373    }
374    return NULL;
375 }
376 // print list
PrintFallenSnow(FallenSnow * list)377 void PrintFallenSnow(FallenSnow *list)
378 {
379    FallenSnow *fallen = list;
380 
381    while (fallen != NULL) {
382       int sumact = 0;
383       int i;
384       for(i=0; i<fallen->w; i++)
385 	 sumact += fallen->acth[i];
386       printf("id:%#10lx ws:%4d x:%6d y:%6d w:%6d sty:%2d hid:%2d sum:%8d\n", fallen->win.id, fallen->win.ws,
387 	    fallen->x, fallen->y, fallen->w, fallen->win.sticky, fallen->win.hidden, sumact);
388       fallen = fallen->next;
389    }
390 }
391 
CleanFallenArea(FallenSnow * fsnow,int xstart,int w)392 void CleanFallenArea(FallenSnow *fsnow,int xstart,int w)
393 {
394    if(global.IsDouble)
395       return;
396    P("CleanFallenArea %d %d\n",counter++,global.IsDouble);
397    int x = fsnow->prevx;
398    int y = fsnow->prevy;
399    myXClearArea(global.display, global.SnowWin, x+xstart, y, w, fsnow->h+global.MaxSnowFlakeHeight, global.xxposures);
400 }
401 
402 // clean area for fallensnow with id
CleanFallen(Window id)403 void CleanFallen(Window id)
404 {
405    P("CleanFallen %#lx\n",id);
406    FallenSnow *fsnow = global.FsnowFirst;
407    // search the id
408    while(fsnow)
409    {
410       if(fsnow->win.id == id)
411       {
412 	 CleanFallenArea(fsnow,0,fsnow->w);
413 	 //myXClearArea(global.display,global.SnowWin,
414 	 //     fsnow->prevx, fsnow->prevy, fsnow->prevw, fsnow->prevh, global.xxposures);
415 	 break;
416       }
417       fsnow = fsnow->next;
418    }
419 }
420 
CreateSurfaceFromFallen(FallenSnow * f)421 void CreateSurfaceFromFallen(FallenSnow *f)
422 {
423    P("createsurface %#10lx %d %d %d %d %d %d\n",f->id,f->x,f->y,f->w,f->h,
424 	 cairo_image_surface_get_width(f->surface),
425 	 cairo_image_surface_get_height(f->surface));
426    int i;
427    GdkRGBA color;
428 
429    cairo_t *cr      = cairo_create(f->surface);
430    int h            = f->h;
431    int w            = f->w;
432    short int *acth  = f->acth;
433    short int *pacth = f->pacth;
434    short int *r     = f->r;
435 
436    cairo_set_antialias(cr,CAIRO_ANTIALIAS_NONE);
437    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
438 
439    cairo_set_source_rgba(cr,0,0,0,0);
440    cairo_set_line_width(cr,1);
441    for (i=0; i<w; i++) // remove
442    {
443       if (acth[i] < pacth[i])
444       {
445 	 cairo_move_to(cr,i,h-acth[i]+r[i]);
446 	 cairo_line_to(cr,i,0);
447 	 pacth[i] = acth[i];
448       }
449    }
450    cairo_stroke(cr);
451 
452    gdk_rgba_parse(&color,Flags.SnowColor);
453    cairo_set_source_rgb(cr,color.red, color.green, color.blue);
454    cairo_set_line_width(cr,1);
455    for (i=0; i<w; i++)  // add
456    {
457       if (acth[i] > pacth[i])
458       {
459 	 cairo_move_to(cr,i,h+2); // +2 because line_to to the same xy as move_to doesnt paint anything
460 	 cairo_line_to(cr,i,h-acth[i]+r[i]);
461 	 pacth[i] = acth[i];
462       }
463    }
464    cairo_stroke(cr);
465 
466    cairo_destroy(cr);
467 }
468 
DrawFallen(FallenSnow * fsnow)469 void DrawFallen(FallenSnow *fsnow)
470 {
471    if(fsnow->win.id == 0 || (!fsnow->win.hidden &&
472 	    (fsnow->win.ws == global.CWorkSpace || fsnow->win.sticky)))
473    {
474       // do not interfere with Santa
475       if(!Flags.NoSanta)
476       {
477 	 int in = XRectInRegion(global.SantaPlowRegion, fsnow->x, fsnow->y - fsnow->h,
478 	       fsnow->w, fsnow->h);
479 	 if (in == RectangleIn || in == RectanglePart)
480 	 {
481 	    // determine front of Santa in fsnow
482 	    int xfront = global.SantaX+global.SantaWidth - fsnow->x;
483 	    // determine back of Santa in fsnow, Santa can move backwards in strong wind
484 	    int xback = xfront - global.SantaWidth;
485 	    const int clearing = 3;
486 	    float vy = -1.5*global.ActualSantaSpeed;
487 	    if(vy > 0) vy = -vy;
488 	    if (vy < -100.0)
489 	       vy = -100;
490 	    if (global.ActualSantaSpeed > 0)
491 	       GenerateFlakesFromFallen(fsnow,xfront,clearing,vy);
492 	    CleanFallenArea(fsnow,xback-clearing,global.SantaWidth+2*clearing);
493 	    int i;
494 	    for (i=0; i<fsnow->w; i++)
495 	       if (i < xfront+clearing && i>=xback-clearing)
496 		  fsnow->acth[i] = 0;
497 	    XFlush(global.display);
498 	 }
499       }
500       CreateSurfaceFromFallen(fsnow);
501       // drawing is handled in fallensnow_draw
502    }
503 }
504 
GenerateFlakesFromFallen(FallenSnow * fsnow,int x,int w,float vy)505 void GenerateFlakesFromFallen(FallenSnow *fsnow, int x, int w, float vy)
506 {
507    P("GenerateFlakes %d %d %d %f\n",counter++,x,w,vy);
508    if (!Flags.BlowSnow || Flags.NoSnowFlakes)
509       return;
510    // animation of fallen fallen snow
511    // x-values x..x+w are transformed in flakes, vertical speed vy
512    int i;
513    int ifirst = x; if (ifirst < 0) ifirst = 0;
514    if (ifirst > fsnow->w) ifirst = fsnow->w;
515    int ilast  = x+w; if(ilast < 0) ilast = 0;
516    if (ilast > fsnow->w) ilast = fsnow->w;
517    P("ifirst ilast: %d %d %d %d\n",ifirst,ilast,w,w<global.MaxSnowFlakeWidth?w:global.MaxSnowFlakeWidth);
518    P("maxheight: %d maxw: %d\n",global.MaxSnowFlakeHeight,global.MaxSnowFlakeWidth);
519    //for (i=ifirst; i<ilast; i+=w<global.MaxSnowFlakeHeight?w:global.MaxSnowFlakeWidth)
520    for (i=ifirst; i<ilast; i+=1)
521    {
522       int j;
523       for(j=0; j<fsnow->acth[i]; j++)
524       {
525 	 int k, kmax = BlowOff();
526 	 for(k=0; k<kmax; k++)
527 	 {
528 	    float p = 0;
529 	    p = drand48();
530 	    // In X11, (global.Trans!=1) we want not too much
531 	    // generated flakes
532 	    // Otherwize, we go for more dramatic effects
533 	    // But, it appeared that, if global.Trans==1, too much snow
534 	    // is generated, choking the x server.
535 	    if (p < 0.15)
536 	    {
537 	       Snow *flake   = MakeFlake(-1);
538 	       flake->rx     = fsnow->x + i + 16*(drand48()-0.5);
539 	       flake->ry     = fsnow->y - j - 8;
540 	       if (Flags.NoWind)
541 		  flake->vx     = 0;
542 	       else
543 		  flake->vx      = global.NewWind/8;
544 	       flake->vy         = vy;
545 	       flake->cyclic     = 0;
546 	    }
547 	 }
548       }
549    }
550 }
551 
EraseFallenPixel(FallenSnow * fsnow,int x)552 void EraseFallenPixel(FallenSnow *fsnow, int x)
553 {
554    if(fsnow->acth[x] > 0)
555    {
556       if(!global.IsDouble)
557       {
558 	 int x1 = fsnow->x + x;
559 	 int y1 = fsnow->y - fsnow->acth[x];
560 	 myXClearArea(global.display, global.SnowWin, x1 , y1, 1, 1, global.xxposures);
561       }
562       fsnow->acth[x]--;
563    }
564 }
565 
InitFallenSnow()566 void InitFallenSnow()
567 {
568    while (global.FsnowFirst)
569       PopFallenSnow(&global.FsnowFirst);
570    // create fallensnow on bottom of screen:
571    WinInfo *NullWindow = (WinInfo *)malloc(sizeof(WinInfo));
572    memset(NullWindow,0,sizeof(WinInfo));
573 
574    PushFallenSnow(&global.FsnowFirst, NullWindow, 0, global.SnowWinHeight, global.SnowWinWidth, global.MaxScrSnowDepth);
575    free(NullWindow);
576 }
577 
578 // removes some fallen snow from fsnow, w pixels. If fallensnowheight < h: no removal
579 // also add snowflakes
UpdateFallenSnowWithWind(FallenSnow * fsnow,int w,int h)580 void UpdateFallenSnowWithWind(FallenSnow *fsnow, int w, int h)
581 {
582    int i;
583    int x = randint(fsnow->w - w);
584    for(i=x; i<x+w; i++)
585       if(fsnow->acth[i] > h)
586       {
587 	 // animation of blown off snow
588 	 if (!Flags.NoWind && global.Wind != 0 && drand48() > 0.5)
589 	 {
590 	    int j, jmax = BlowOff();
591 	    for (j=0; j< jmax; j++)
592 	    {
593 	       Snow *flake       = MakeFlake(0);
594 	       flake->rx         = fsnow->x + i;
595 	       flake->ry         = fsnow->y - fsnow->acth[i] - drand48()*8;
596 	       flake->vx         = fsignf(global.NewWind)*global.WindMax;
597 	       flake->vy         = -5;
598 	       flake->cyclic     = (fsnow->win.id == 0); // not cyclic for Windows, cyclic for bottom
599 	       P("%d:\n",counter++);
600 	    }
601 	    EraseFallenPixel(fsnow,i);
602 	 }
603       }
604 }
605 
SetMaxScreenSnowDepth()606 void SetMaxScreenSnowDepth()
607 {
608    global.MaxScrSnowDepth = Flags.MaxScrSnowDepth;
609    if (global.MaxScrSnowDepth> (global.SnowWinHeight-SNOWFREE)) {
610       printf("** Maximum snow depth set to %d\n", global.SnowWinHeight-SNOWFREE);
611       global.MaxScrSnowDepth = global.SnowWinHeight-SNOWFREE;
612    }
613 }
614 
615 
UpdateFallenSnowPartial(FallenSnow * fsnow,int x,int w)616 void UpdateFallenSnowPartial(FallenSnow *fsnow, int x, int w)
617 {
618    if (NOTACTIVE)
619       return;
620    P("update ...\n");
621    if(!HandleFallenSnow(fsnow)) return;
622    int imin = x;
623    if(imin < 0) imin = 0;
624    int imax = x + w;
625    if (imax > fsnow->w) imax = fsnow->w;
626    int i, k;
627    k = 0;
628    short int *old;
629    // old will contain the acth values, corresponding with x-1..x+w (including)
630    old = (short int *)malloc(sizeof(*old)*(w+2));
631    for (i=imin-1; i<=imax; i++)
632    {
633       if (i < 0)
634 	 old[k++] = fsnow->acth[0];
635       else if (i>=fsnow->w)
636 	 old[k++] = fsnow->acth[fsnow->w-1];
637       else
638 	 old[k++] = fsnow->acth[i];
639    }
640 
641    int add;
642    if (fsnow->acth[imin] < fsnow->desh[imin]/4)
643       add = 4;
644    else if(fsnow->acth[imin] < fsnow->desh[imin]/2)
645       add = 2;
646    else
647       add = 1;
648    k = 1;  // old[1] corresponds with acth[0]
649    for (i=imin; i<imax; i++)
650    {
651       if ((fsnow->desh[i] > old[k]) &&
652 	    (old[k-1] >= old[k] || old[k+1] >= old[k]))
653 	 fsnow->acth[i] = add + (old[k-1] + old[k+1])/2;
654       k++;
655    }
656    // old will contain the new acth values, corresponding with x-1..x+w (including)
657    k = 0;
658    for (i=imin-1; i<=imax; i++)
659    {
660       if (i < 0)
661 	 old[k++] = fsnow->acth[0];
662       else if (i>=fsnow->w)
663 	 old[k++] = fsnow->acth[fsnow->w-1];
664       else
665 	 old[k++] = fsnow->acth[i];
666    }
667    // and now some smoothing
668    k = 1;
669    for (i=imin; i<imax; i++)
670    {
671       int j;
672       int sum=0;
673       for (j=k-1; j<=k+1; j++)
674 	 sum += old[j];
675       fsnow->acth[i] = sum/3;
676       k++;
677    }
678    free(old);
679 }
680 
HandleFallenSnow(FallenSnow * fsnow)681 int HandleFallenSnow(FallenSnow *fsnow)
682 {
683    if (fsnow->win.id == 0)
684       return !Flags.NoKeepSBot;
685    if (fsnow->win.hidden)
686       return 0;
687    if (!fsnow->win.sticky)
688    {
689       if (fsnow->win.ws != global.CWorkSpace)
690 	 return 0;
691    }
692    return !Flags.NoKeepSWin;
693 }
694 
695 
696