<style>
   div.gray {color:gray;}
</style>

# Snow issues

### Technical issues
#### C vs C++
Xsnow is written in C. However, because I needed a reliable hash function to maintain a cache
of bird surfaces, some C++ has been crept in. To store the bird surfaces, an 'unordered_map' is used
if available. Otherwise, 'map' is used, which is not implemented as a hash function but as a
binary tree. For housekeeping of the snow flakes, an '(unordered) set' is used. 
The WWW recommends to compile the main program with C++ when using a mix
of C and C++, so a very small main() is written in C++, which calls the original C main() function.
All sources have been adapted to compile using C++, so, if you are a C++ adept, you can do:

~~~~
   CC=g++ ./configure
   make
   make install
~~~~

#### Animation
The original xsnow-1.42 has an more or less ad-hoc timing mechanism. The program offers many flags to
tweak the speed of Santa, the speed of snow and the like. Xsnow-2.0 has a more straight forward timing
mechanism, in principle, every speed is defined in pixels/second. 

The updating and drawing of Santa are decoupled, so the refreshing rate of Santa does not affect His speed.

Fallen snow (bottom and on windows) is nice in xsnow-1.42, but after running for some time, the visualization
of fallen snow is not realistic. Therefore I use now a complete new strategy for the handling of fallen snow. 

The handling of wind is changed in xsnow-2.0: it is now more or less windy all the time, but the original
code from xsnow-1.42 is still used to experience more dramatic wind effects.
The algorithm for the sensibility to wind of Santa and snow has been changed: The velocities of Santa and snow
are changed by the wind.

In xsnow-1.42 an fixed-length array was used to hold the snow flakes. In xsnow-2.0, the flakes are in a double-linked
list. But now, the housekeeping is done with a C++ 'set' or 'unordered set'.
This makes it possible to add snow flakes at random times, for example when converting fallen
snow to snow flakes. Furthermore, the amount of snow is now determined by the rate the flakes
are produced and removed. The production rate is given in flakes per second per pixel (i.e. the width of the
window measured in pixels). This has the effect that, in a steady state situation, a larger window shows more 
flakes than a smaller one.

Inspired by [project wsnow](/wsnow/index.html), written in JavaScript, I searched for
an equivalent for setTimeout() in gdk/gtk. Indeed there is g\_timeout\_add\_full(), which
I overlooked or did not understand in the past. For 2.0.20 I rewrote all timed events using that function,
which led to a better readable and simpler code. No double linked list any more for snow.

Since I stumbled upon https://www.cairographics.org/Xlib/,
I decided to use only GTK/cairo to make the animations. 

The preferred way for xsnow to make it's drawings is to use a transparent, click-through window that
covers the whole screen. 

This requires a compositing window manager, which is not always available. In those cases painting is
done at the root-window (or window 'pcmanfm' when running LXDE). Using XdbeSwapBuffers and friends
a double buffer is used. When this is not desirable (for example when running xsnow together with xpenguins),
then you can specify the flag '-doublebuffer 0', at the cost of some flicker in the animations.
Also, when you specify '-xwininfo' or '-id &lt;window&gt;', painting in the chosen window is done using
double buffering, unless '-doublebuffer 0' is specified.

#### Deprecated: gdk_pixbuf_new_from_xpm_data()

gdk_pixbuf_new_from_xpm_data() was heavily used in xsnow. It seemed that this
function was deprecated, but now I cannot find the deprecation notice any more.

Any way, I wrote xpm2gdk() (see file src/xpm2cairo-gdk.c) which circumvents
the use of gdk_pixbuf_new_from_xpm_data().

#### Fallen snow

The contours of the fallen snow on windows and bottom of the screen are determined
by splines through randomly chosen points. Now and then, the splines are recalculated
using other random points.

#### Aurora

The aurora is simulated by drawing a number of vertical aurora lines next to and over 
each other.

The shape of the bottom of the aurora (the S-like curves you see in many aurora pictures)
is generated by creating a spline through (at the moment of writing) 8 equidistant points
with random y-values. Using this spline, a number larger than the width of
the requested aurora of equidistant points are interpolated. 

* Note: all splines are computed following Steffen's method, see 
[this article](https://articles.adsabs.harvard.edu/full/seri/A+A../0239//0000443.000.html),
which has the advantage that overshoots cannot occur. 

Subsequently, the points are 'slanted' i.e: the x-values are modified using the corresponding
y-values and the points are rotated. Then, an array is produced which contains the 
x-y values, taking care that adjacent points differ 1 (one) in x-value. 

The number of points will
in general be larger than the width of the aurora, and the same x-value will occur more than
once. All x-values from the lowest to the highest will be present in the resulting array.
The code along with some comment is in aurora.c: create_aurora_base(). 

The height and the alpha values of the corona lines are determined using spline functions.

It is arranged that the top of the aurora shows a faint purple glow, and that the aurora
is green at the bottom fading out towards the top.

The start and the end of the aurora are blurred by applying a diminishing alpha value, 
as well as the bottom. 

During the simulation, the following parameters are gradually changed:

* the numbers defining the splines (shape, height, alpha)
* the overall alpha value of the aurora
* the rotation angle

Because computation of the aurora surface costs quite some CPU time, the construction
of this surface is done in a separate thread to prevent a choppy moving of
the other animations.

Using above computations, a reasonable aurora is obtained, but when the aurora
takes a turn to the left, a sharp boundary on the right side of the turn is
produced. Vice versa for a turn to the right. This can be dealt with using a 
global blur function, like in [here](https://www.cairographics.org/cookbook/blur.c) and
[here](https://github.com/cubicool/cairou/blob/master/src/cairou-gaussian-blur.c).
Alas, these algorithms are much to cpu-intensive. That is understandable: every pixel
of the surface is visited and acted upon.

So, I added some code to detect where the turning points are, and generate extra 
aurora lines with diminishing alpha to get rid of the sharp edges.

#### A note about splines

In the famous book 'Numerical Recipes in C' a spline generating function is given, along
with a function to interpolate using the spline. Alas, this code is still 
[licensed](https://assets.cambridge.org/97805218/80688/copyright/9780521880688_copyright_info.pdf)
(after all this years!).
Implementation using for example 'Einführing in die Numerische Mathematik &#8544; (Josef Stoer)'
is too complicated for me, and cheating by changing the NR code is way below Santa's standards.
So I am happily using the spline functions in the excellent
[GNU Scientific Library (libgsl)](https://www.gnu.org/software/gsl/doc/html/interp.html#d-interpolation-example-programs). 
As I understand, the NR license was one of the [reasons](https://en.wikipedia.org/wiki/Numerical_Recipes)
to create libgsl...

Btw: as far as I know, Numerical Recipes has no code for Steffen's algorithm used in xsnow.

#### A note about xinerama (multi-monitor setup) and tiling window managers

Placing the snow-window in a specific monitor, using Xinerama.h was not very difficult in
for example xfce. There, all monitors are part of the same workspace. But in a tiling
window manager, things are different. For example, in bspwm, one monitor shows desktop 0,
while the other monitor is on desktop 10. Xsnow wants to know in what desktop a window
is situated, and wants to know if a workspace is visible. The first one is easy and
was already solved, but the second one: which workspaces are visible: I found no easy
solution. Luckily, XineramaQueryScreens() gives the coordinates and sizes of the 
screens, and I solved the issue as follows: In each centre of the xinerama screens, place
a small window and ask, using xdo_get_desktop_for_window(), in which workspace the window 
is situated. That workspace must than be visible. 

#### A note about using X11: the xdo library: libxdo

I spent some time to get the snow-window in the correct location, serving at least the most used
desktop systems: gnome, cinnamon, kde(plasma), xfce, fvwm and some more. I could have saved
time if I was aware of [libxdo](https://github.com/jordansissel/xdotool) 
(part of the famous xdotool program). Surely, the 
creators of libxdo are much more experienced than I am. So, I am using now 
[libxdo](https://github.com/jordansissel/xdotool) wherever things get hairy. I am not sure 
if libxdo is general available outside of debian, so I incorporated the source in xsnow.

#### Multi language support

Xsnow now speaks some European languages, next to English. The translations are done
using the program 'trans' from the debian package 'translate-shell'. I made some
simple Python scripts to automate things. As to be expected: many translations
will not be optimal. You are encouraged to contact me if you want to do something about that.

#### Multithreading

As noted above, the aurora is computed in a separate thread. Inspired by the success,
multithreading is also implemented for the computing of fallen snow surfaces and the speed of the
birds. 

In all cases, multithreading is accomplished by starting a thread that only ends when
xsnow ends. Coordination is achieved by the use of semaphores.

The gain in speed is the largest in the aurora case, the aurora thread can be busy for 
30 to 70%, according to the program 'htop'. The other threads use only a few percents.

#### Self replication

Self replication is implemented by including an ascii-representation of a tar file, 
made by `make dist`. Xsnow is not really a self-replication program, because the 
ascii representation is not part of the tar file, but it works beautifully:

````
    xsnow -selfrep | tar zxf -
    cd xsnow-YYYxsnow-versionZZZ
    ./configure
    make
    # test:
    src/xsnow                       # to let it snow
    src/xsnow -selfrep | tar zxf -  # to create also here xsnow-YYYxsnow-versionZZZ
    cd xsnow-YYYxsnow-versionZZZ
    ./configure
    make
    #  etc.
````

### Window manager issues

* An in-depth story about [difficult to find good managers in general](https://www.youtube.com/watch?v=6gvTpZSGCp0).
Question: does this also apply for window managers?

* Here follows some information about the cooperation of xsnow and a few window managers, taken from the man page:

````
       In  general,  xsnow  works  better  when using a compositing window manager like xcompmgr,
       compton or picom.  However, with some window managers (FVWM for example), the xsnow-window
       is  transparent,  but  not  click-through.  Flags to be tried in this case include: -root,
       -doublebuffer, -xwininfo, -id.
       Here follow some window managers with their issues:

       Tiling window managers
              Here you need to float windows with class=Xsnow.

       AWESOME
              Without compositor: no issues.
              With compositor: no click-through  xsnow  window,  and  issues  with  multi-monitor
              setup.

       BSPWM  No issues if you add to your bspwmrc (the bspwm configuration file):
                  bspc rule -a Xsnow state=floating border=off

       CINNAMON
              No issues.

       DWM    No issues, except the "Below Windows" setting in the "settings" panel.

       ENLIGHTENMENT
              With one monitor: no issuses.
              With more montors: probems with showing in 'all monitors'

       FLUXBOX
              Without compositor: no issues.
              With compositor: no click-through xsnow window

       FVWM   Without compositor: no issues.
              With compositor: no click-through xsnow window

       GNOME on Xorg
              No issues.

       GNOME on Wayland
              Most windows don't catch snow.

       HERBSTLUFTWM
              No issues.

       I3     Without compositor: windows don't catch snow, use the next line in "config":
                  for_window [class="Xsnow"] floating enable;border none
              With compositor: unworkable.

       JVM    No issues.

       LXDE   With compositor: no issues.
              Without compositor: works with one monitor.
              Maybe you need to run with the flag -xwininfo

       LXQT   Without compositor: unworkable.  With compositor: no issues.

       MATE   No issues.

       OPENBOX
              No issues.

       PLASMA (KDE)
              No issues.

       SPECTRWM
              Various issues. In any case you need in spectrwm.conf:
                  quirk[Xsnow] = FLOAT

       TWM    Without compositor: no issues.
              With compositor: no click-through xsnow window and
              you need to tweak settings->lift snow on windows.

       WINDOW MAKER
              Without compositor: no issues.  With compositor: no click-through xsnow window

       XFCE   No issues when compositing is on, unworkable when compositing is off.
              See settings -> Window Manager Tweaks -> Compositor

       XMONAD No issues if you add to your xmonad.hs:
               import XMonad.Hooks.EwmhDesktops
               xmonad $ ewmh $ defaultConfig
               in the ManageHook section:
                  className = ? "Xsnow" --> doFloat
````


* Xsnow tries to find out if it can create a transparent click-through window. If successful,
  xsnow will create one. In general for this a running compositing window manager (xcompmgr, picom, compton)
  is needed. Alas, in some cases, the window created by xsnow is transparent, but not click-through.

  If not successful, xsnow will use the root window, except:

   * In <b>LXDE</b>, xsnow will snow on the window with the name 'pcmanfm'. If you have a 
   multi-monitor setup, changes are that the wrong window is chosen. Remedy: start xsnow like this: 
   `xsnow -xwininfo` and click on the desktop.

   * In <b>XFCE</b>, xsnow will snow on the window with the name 'Desktop'.

* If xsnow is runnig, but you don't see anything (this happens for example in the <b>LXQt</b> environment)
   * Try to start a compositing window manager first, like xcompmgr, compton or picom, like:

      ````
        xcompmgr &
      ````

* Xsnow can use <b>any window</b>, you can choose one (using `xsnow -xwininfo`), 
  or you can specify a window-id (using `xsnow -id window-id`). This feature does not always works perfect. 

* <b>Raspberry 64 bit</b>, and maybe others: xsnow is confused by the 'xcompmgr' daemon, which results
  in a black background. The reason: `xcompmgr` is started with the flag `-a`, this seems
  to be beneficial for <b>Openbox</b>, but is detrimental for xsnow and others. I hope to find a fix
  for this, for now I have this remedy (I don't know if this is detrimental for Openbox...):
   * On the command line, before starting xsnow:
````
       killall xcompmgr
       nohup xcompmgr -n &
```` 
   * Or: start xsnow with `xsnow -xwininfo` and click on the desktop for a less than perfect
     xsnow experience.
   * Or (permanent solution): edit `/usr/lib/raspi-config/cmstart.sh`, change the flag `-a` for `xcompmgr` 
     into `-n` and restart your system.
     I don't know if this is detrimental for Openbox...

* Xsnow by default starts on all <b>workspaces</b>. 
  In the `settings` tab, you can choose: run on all workspaces or run only in the current workspace.

  Among the [downloads](../downloads)
  I included shell scripts 'startondesktop' and 'startonalldesktops' (I should have called then 
  'startonworkspace' and 'startonworkspaces'...) that you can try to start
  xsnow on (a subset of) all workspaces if xsnow itself does not run in all workspaces on your
  system.

* The location of the top of the windows (we need that to figure out where snow should fall on),
  including the decorations, is a known issue on the web.
  I found a solution for this issue: see the code in wmctrl.c (from the xsnow tarball).
  And, by looking at the code of the programs wmctrl, xwininfo and xprop, I was able to incorporate
  the things I need in xsnow with respect to choosing a window, locate a window etc.

* It seems there is something wrong with the <b>Breeze theme</b>: xsnow works, but the buttons behave strangely.

#### Wayland
Before version 2.10, xsnow refused to run on Wayland (that is the proposed successor of X11 
and is the default on recent Debian-derived installations). I was prepared for major code 
changes in xsnow, but, after lots of googling, I found a solution, at least for xsnow:
simply set the environment variable GDK\_BACKEND=x11. So, before the gtk environment is 
initialized (using gtk\_init()),
xsnow sets (using setenv()) that variable to the desired value. Of course, the official GDK 
documentation has 
[something about the issue](https://developer.gnome.org/gdk3/stable/gdk3-General.html#gdk-set-allowed-backends) .
It could very well be that things work thanks to XWayland, I really don't know very much about X11 and Wayland.
In Wayland, applications have only a restricted control over the windows that are not owned by the
application. So, xsnow cannot snow in other windows. But for many of the windows the information
about the where-abouts are available to xsnow. For example, xsnow is snowing on the windows of firefox, 
gimp, gvim, xsnow, xterm, xclock and xcalc, but not on the windows of aislerioth, gedit, nautilus and gnome-terminal.   

#### Using screenshots
In the hope that there will become available an universal usable and fast screenshot utility in Wayland,
I did some experiments trying to locate windows in a screenshot. It works almost good enough and 
you can test it by clicking the 'Use screenshots' button in the settings pane.
In a Wayland environment this button is made inactive, but as soon as I find a workable
screenshot utility in Wayland, I will try to activate it. 

