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 /*
23    And in a vocoded voice it sounds:
24    Xsnow zwei-tausend
25    Xsnow two-thousand
26    Xsnow deux mille
27    Xsnow dos mil
28    etc.
29    */
30 
31 /*
32  * contains main_c(), the actual main program, written in C.
33  * main_c() is to be called from main(), written in C++,
34  * see mainstub.cpp and mainstub.h
35  */
36 
37 #define dosync 0  /* synchronise X-server. Change to 1 will detoriate the performance
38 		     but allow for better analysis
39 		     */
40 
41 /*
42  * Reals dealing with time are declared as double.
43  * Other reals as float
44  */
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48 #include <X11/Intrinsic.h>
49 #include <X11/extensions/Xdbe.h>
50 #include <ctype.h>
51 #include <gtk/gtk.h>
52 #include <cairo-xlib.h>
53 #include <stdlib.h>
54 #include <math.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <string.h>
58 
59 #include "birds.h"
60 #include "clocks.h"
61 #include "docs.h"
62 #include "dsimple.h"
63 #include "fallensnow.h"
64 #include "flags.h"
65 #include "mainstub.h"
66 #include "meteo.h"
67 #include "moon.h"
68 #include "Santa.h"
69 #include "scenery.h"
70 #include "snow.h"
71 #include "transwindow.h"
72 #include "ui.h"
73 #include "utils.h"
74 #include "version.h"
75 #include "wind.h"
76 #include "windows.h"
77 #include "wmctrl.h"
78 #include "xsnow.h"
79 #include "stars.h"
80 #include "blowoff.h"
81 #include "debug.h"
82 #include "treesnow.h"
83 #include "loadmeasure.h"
84 #include "selfrep.h"
85 
86 #include "vroot.h"
87 
88 #ifdef DEBUG
89 #undef DEBUG
90 #endif
91 //#define DEBUG
92 
93 #define BMETHOD XdbeBackground
94 //#define BMETHOD XdbeUndefined
95 //#define BMETHOD XdbeUntouched
96 //#define BMETHOD XdbeCopied       // to see if double buffering is actually used
97 
98 struct _global global;
99 
100 int              SnowWinChanged = 1;
101 cairo_t         *CairoDC = NULL;
102 cairo_surface_t *CairoSurface = NULL;
103 
104 
105 
106 // miscellaneous
107 char       Copyright[] = "\nXsnow\nCopyright 1984,1988,1990,1993-1995,2000-2001 by Rick Jansen, all rights reserved, 2019,2020 also by Willem Vermin\n";
108 
109 static char **Argv;
110 static int Argc;
111 
112 // timing stuff
113 //static double       TotSleepTime = 0;
114 
115 // windows stuff
116 static int          DoRestart      = 0;
117 static guint        draw_all_id    = 0;
118 static guint        drawit_id      = 0;
119 static int          Xorig;
120 static int          Yorig;
121 static GtkWidget   *TransA         = NULL;
122 static char        *SnowWinName    = NULL;
123 
124 /* Colo(u)rs */
125 static const char *BlackColor  = "black";
126 static Pixel       BlackPix;
127 
128 /* Forward decls */
129 static void   HandleCpuFactor(void);
130 static void   RestartDisplay(void);
131 static void   SigHandler(int signum);
132 static int    XsnowErrors(Display *dpy, XErrorEvent *err);
133 static void   drawit(cairo_t *cr);
134 static void   restart_do_draw_all(void);
135 static void   set_below_above(void);
136 static void   DoAllWorkspaces(void);
137 static void   X11WindowById(Window *xwin, char **xwinname);
138 static int    HandleX11Cairo(void);
139 static int    StartWindow(void);
140 static void   SetWindowScale(void);
141 static void   movewindow(void);
142 
143 
144 // callbacks
145 static int do_displaychanged(void *);
146 static int do_draw_all(gpointer widget);
147 static int do_event(void *);
148 static int do_show_range_etc(void *);
149 static int do_testing(void *);
150 static int do_ui_check(void *);
151 static int do_stopafter(void *);
152 static int do_show_desktop_type(void *);
153 static int do_display_dimensions(void *);
154 static int do_drawit(void*);
155 static gboolean     on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data);
156 
157 /**********************************************************************************************/
158 
159 /* About some technicalities of this program
160 
161  * The following animations exist:
162  * ===============================
163  *   - snow
164  *   - treesnow
165  *   - blowoff
166  *   - Santa
167  *   - scenery (trees etc)
168  *   - stars
169  *   - moon + halo
170  *   - meteorites
171  *   - birds
172  *
173  * Selection of window to draw in
174  * ==============================
175  *
176  * All drawing is done using the gtk-cairo library, using the window SnowWin.
177  *
178  * For the definition of SnowWin, the following possibilities exist:
179  *
180  * A: SnowWin is a transparent, click-through window covering the
181  *    whole screen.
182  * B: SnowWin is set to the root window, or a window with the name pcmanfm (LXDE desktop).
183  * C: SnowWin is set to the desired window (flags -id or -xwininfo).
184  *
185  *
186  * If the user specifies the flag -id or -xwininfo, case C is chosen
187  *    (make_trans_window() in transwindow.c).
188  * else if a transparent, click-through window can be created, case A is chosen.
189  * else case B is chosen.
190  *
191  * In cases B and C, usage is made of cairo_xlib_surface_create() to tell cairo
192  * in which window to paint.
193  *
194  * Double buffering
195  * ================
196  *
197  * In case A, double buffering is provided by the cairo library.
198  * In the other cases, double buffering is accomplished by XdbeSwapBuffers().
199  *
200  * If cases B and C, if double buffering is not possible, or not wanted
201  *   (flag -doublebuffer), an attempt is made to erase and draw with minimal flicker.
202  *
203  * Double buffer switches
204  * ======================
205  *
206  * Trans:     1: case A
207  * UseDouble: 1: case B or C using XdbeSwapBuffers() and friends.
208  * IsDouble:  1: using double buffering
209  *
210  * The following scenario's exist:
211  *
212  * Scenario Trans   UseDouble   IsDouble
213  *   I        1         0          1            case A
214  *   II       0         1          1            case B or C, using XdbeSwapBuffers and friends
215  *   III      0         0          0            case B or C, not using double buffering
216  *
217  * Note:
218  * In scenario III it is necessary to explicitly erase moving objects before
219  * drawing.
220  * One could use XlearWindow() to erase everything, and then draw everything again,
221  * but this results in severe flicker.
222  *
223  * In scenario's I and II, it is necessary to repaint everything, also not moving objects.
224  *
225  */
226 /*
227  * Globals
228  * =======
229  *
230  * Many gobal variables are used. If a global is only used in one file only, it is declared
231  * there as static.
232  * Globals that are used in more than one file, are members of the struct 'global', defined in xsnow.h
233  *
234  * Types
235  * =====
236  *
237  * All types are defined in xsnow.h
238  *
239  */
240 
241 /**********************************************************************************************/
242 
main_c(int argc,char * argv[])243 int main_c(int argc, char *argv[])
244 {
245    P("This is xsnow\n");
246    signal(SIGINT,  SigHandler);
247    signal(SIGTERM, SigHandler);
248    signal(SIGHUP,  SigHandler);
249    srand48((long int)(wallcl()*1.0e6));
250 
251 
252    memset((void *)&global,0,sizeof(global));
253 
254    global.counter            = 0;
255    global.cpufactor          = 1.0;
256    global.WindowScale        = 1.0;
257 
258    global.MaxSnowFlakeHeight = 0;
259    global.MaxSnowFlakeWidth  = 0;
260    global.FlakeCount         = 0;  /* # active flakes */
261    global.FluffCount         = 0;
262 
263    global.SnowWin            = 0;
264    global.DesktopSession     = NULL;
265    global.CWorkSpace         = 0;
266    global.WindowsChanged     = 0;
267 
268    global.FsnowFirst         = NULL;
269    global.MaxScrSnowDepth    = 0;
270    global.RemoveFluff        = 0;
271 
272    global.SnowOnTrees        = NULL;
273    global.OnTrees            = 0;
274 
275    global.moonX              = 1000;
276    global.moonY              = 80;
277 
278    global.Wind               = 0;
279    global.Direction          = 0;
280    global.WindMax            = 100.0;
281    global.NewWind = 0;
282 
283    global.HaltedByInterrupt  = 0;
284    global.Message[0]         = 0;
285 
286    global.SantaPlowRegion    = 0;
287 
288    int i;
289    // make a copy of all flags, before gtk_init() maybe removes some.
290    // we need this at a restart of the program.
291 
292    Argv = (char**) malloc((argc+1)*sizeof(char**));
293    for (i=0; i<argc; i++)
294       Argv[i] = strdup(argv[i]);
295    Argv[argc] = NULL;
296    Argc = argc;
297 
298    InitFlags();
299    // we search for flags that only produce output to stdout,
300    // to enable to run in a non-X environment, in which case
301    // gtk_init() would fail.
302    for (i=0; i<argc; i++)
303    {
304       char *arg = argv[i];
305       if(!strcmp(arg, "-h") || !strcmp(arg, "-help"))
306       {
307 	 docs_usage(0);
308 	 return 0;
309       }
310       if(!strcmp(arg, "-H") || !strcmp(arg, "-manpage"))
311       {
312 	 docs_usage(1);
313 	 return 0;
314       }
315       if (!strcmp(arg, "-v") || !strcmp(arg, "-version"))
316       {
317 	 PrintVersion();
318 	 return 0;
319       }
320       else if (!strcmp(arg, "-changelog"))
321       {
322 	 docs_changelog();
323 	 return 0;
324       }
325 #ifdef SELFREP
326       else if (!strcmp(arg, "-selfrep"))
327       {
328 	 selfrep();
329 	 return 0;
330       }
331 #endif
332    }
333    printf("Xsnow running in GTK version: %s\n",ui_gtk_version());
334 
335    // Circumvent wayland problems:before starting gtk: make sure that the
336    // gdk-x11 backend is used.
337 
338    setenv("GDK_BACKEND","x11",1);
339    if (getenv("WAYLAND_DISPLAY")&&getenv("WAYLAND_DISPLAY")[0])
340    {
341       printf("Detected Wayland desktop\n");
342       global.IsWayland = 1;
343    }
344    else
345       global.IsWayland = 0;
346 
347    gtk_init(&argc, &argv);
348    int rc = HandleFlags(argc, argv);
349    switch(rc)
350    {
351       case -1:   // wrong flag
352 	 PrintVersion();
353 	 Thanks();
354 	 return 1;
355 	 break;
356       case 1:    // manpage or help
357 	 //         Ok, this cannot happen, is already caught above
358 	 return 0;
359 	 break;
360       default:  // ditto
361 	 PrintVersion();
362 	 break;
363    }
364    if (!Flags.NoConfig)
365       WriteFlags();
366 
367    if(Flags.CheckGtk && !ui_checkgtk() && !Flags.NoMenu)
368    {
369       printf("Xsnow needs gtk version >= %s, found version %s\n",ui_gtk_required(),ui_gtk_version());
370 
371       if(!ui_run_nomenu())
372       {
373 	 Thanks();
374 	 return 0;
375       }
376       else
377       {
378 	 Flags.NoMenu = 1;
379 	 printf("Continuing with flag '-nomenu'\n");
380       }
381    }
382 
383    global.display = XOpenDisplay(Flags.DisplayName);
384    XSynchronize(global.display,dosync);
385    XSetErrorHandler(XsnowErrors);
386    int screen = DefaultScreen(global.display);
387    global.Black = BlackPixel(global.display, screen);
388    global.White = WhitePixel(global.display, screen);
389 
390 
391    { // if somebody messed up colors in .xsnowrc:
392       if (!ValidColor(Flags.TreeColor))
393 	 Flags.TreeColor = strdup(DefaultFlags.TreeColor);
394       if (!ValidColor(Flags.SnowColor))
395 	 Flags.SnowColor = strdup(DefaultFlags.SnowColor);
396       if (!ValidColor(Flags.BirdsColor))
397 	 Flags.BirdsColor = strdup(DefaultFlags.BirdsColor);
398       WriteFlags();
399    }
400 
401 
402    InitSnowOnTrees();
403 
404    if (global.display == NULL) {
405       if (Flags.DisplayName == NULL) Flags.DisplayName = getenv("DISPLAY");
406       (void) fprintf(stderr, "%s: cannot connect to X server %s\n", argv[0],
407 	    Flags.DisplayName ? Flags.DisplayName : "(default)");
408       exit(1);
409    }
410    //
411    // define:
412    // - snow in:
413    //           ------------------------
414    //           |  * *           *     |
415    //           |     *  *             |
416    //           |               *      |
417    //           ------------------------
418    // - snow on:
419    //               *****  *     ****
420    //           *  ****** **** *******
421    //           ------------------------
422    //           |                      |
423    //           |                      |
424    //           |                      |
425    //           ------------------------
426    //
427    // Find window to snow in:
428    //
429    // if(switches.Desktop): we have a desktop and we will snow on the windows in it
430    //   else we have a normal window and will not snow on other windows
431    // if(switches.Trans): drawing to the desktop is as follows:
432    //   - all colours are made opaque by or-ing the colors with 0xff000000
433    //   - clearing is done writing the same image, but with color black (0x00000000)
434    //   else
435    //   - we will use XClearArea to erase flakes and the like. This works well
436    //     on fvwm-like desktops (desktop == Rootwindow) with exposures set to 0
437    //     It works more or less in for example KDE, but exposures must be set to 1
438    //     which severely stresses plasma shell (or nautilus-desktop in Gnome,
439    //     but we do not use XClearArea in Gnome).
440    //
441    // And then, we have 'snowing below' and 'snowing above', which respectively
442    // means: snow behind all windows and snow before all windows.
443 
444 
445 
446    if (!StartWindow())
447    {
448       return 1;
449    }
450 
451    Flags.Done = 0;
452    windows_init();
453    moon_init();
454    Santa_init();
455    birds_init();
456    scenery_init();
457    snow_init();
458    meteo_init();
459    wind_init();
460    stars_init();
461    fallensnow_init();
462    blowoff_init();
463    treesnow_init();
464    loadmeasure_init();
465 
466    add_to_mainloop(PRIORITY_DEFAULT, time_displaychanged,     do_displaychanged     );
467    add_to_mainloop(PRIORITY_DEFAULT, time_event,              do_event              );
468    add_to_mainloop(PRIORITY_DEFAULT, time_testing,            do_testing            );
469    add_to_mainloop(PRIORITY_DEFAULT, time_display_dimensions, do_display_dimensions );
470    add_to_mainloop(PRIORITY_HIGH,    time_ui_check,           do_ui_check           );
471    add_to_mainloop(PRIORITY_DEFAULT, time_show_range_etc,     do_show_range_etc     );
472 
473    if (Flags.StopAfter > 0)
474       add_to_mainloop(PRIORITY_DEFAULT, Flags.StopAfter, do_stopafter);
475 
476    HandleCpuFactor();
477 
478 #define DOIT_I(x,d,v) OldFlags.x = Flags.x;
479 #define DOIT_L(x,d,v) DOIT_I(x,d,v);
480 #define DOIT_S(x,d,v) OldFlags.x = strdup(Flags.x);
481    DOITALL;
482 #include "undefall.inc"
483 
484 
485    OldFlags.FullScreen = !Flags.FullScreen;
486 
487    BlackPix = AllocNamedColor(BlackColor, global.Black);
488 
489    // events
490    if(global.Desktop)
491    {
492       XSelectInput(global.display, global.Rootwindow, StructureNotifyMask| SubstructureNotifyMask);
493    }
494    else
495       XSelectInput(global.display, global.SnowWin, StructureNotifyMask|SubstructureNotifyMask);
496 
497    ClearScreen();   // without this, no snow, scenery etc. in KDE; this seems to be a vintage comment
498 
499    if(!Flags.NoMenu && !global.XscreensaverMode)
500    {
501       ui();
502       ui_gray_erase(global.Trans);
503       ui_set_sticky(Flags.AllWorkspaces);
504       add_to_mainloop(PRIORITY_DEFAULT, time_desktop_type, do_show_desktop_type);
505    }
506 
507    // main loop
508    gtk_main();
509 
510 
511    if (SnowWinName) free(SnowWinName);
512 
513    XClearWindow(global.display, global.SnowWin);
514    XFlush(global.display);
515    XCloseDisplay(global.display); //also frees the GC's, pixmaps and other resources on display
516 
517    if (DoRestart)
518    {
519       sleep(0);
520       printf("Xsnow restarting: %s\n",Argv[0]);
521       fflush(NULL);
522       execvp(Argv[0],Argv);
523    }
524    else
525       Thanks();
526    return 0;
527 }		/* End of snowing */
528 
529 
530 
set_below_above()531 void set_below_above()
532 {
533    P("%d set_below_above\n",global.counter++);
534    XWindowChanges changes;
535    // to be sure: we do it in gtk mode and window mode
536    if (Flags.BelowAll)
537    {
538       if(TransA)setbelow(GTK_WINDOW(TransA));
539       changes.stack_mode = Below;
540       if(global.SnowWin)XConfigureWindow(global.display,global.SnowWin,CWStackMode,&changes);
541    }
542    else
543    {
544       if(TransA)setabove(GTK_WINDOW(TransA));
545       changes.stack_mode = Above;
546       if(global.SnowWin)XConfigureWindow(global.display,global.SnowWin,CWStackMode,&changes);
547    }
548 }
549 
X11WindowById(Window * xwin,char ** xwinname)550 void X11WindowById(Window *xwin, char **xwinname)
551 {
552    *xwin = 0;
553    // user supplied window id:
554    if (Flags.WindowId)
555    {
556       *xwin = Flags.WindowId;
557       return;
558    }
559    if (Flags.XWinInfoHandling)
560    {
561       // user ask to point to a window
562       printf("Click on a window ...\n");
563       *xwin = XWinInfo(xwinname);
564       if (*xwin == 0)
565       {
566 	 fprintf(stderr,"XWinInfo failed\n");
567 	 exit(1);
568       }
569    }
570    return;
571 }
572 
StartWindow()573 int StartWindow()
574 {
575    int X11cairo     = 0;
576 
577    global.Trans     = 0;
578    global.xxposures = 0;
579    global.Desktop   = 0;
580    global.UseDouble = 0;
581    global.IsDouble  = 0;
582    global.XscreensaverMode = 0;
583 
584    global.Rootwindow = DefaultRootWindow(global.display);
585    Window xwin;
586    // see if user chooses window
587    X11WindowById(&xwin, NULL);
588    if (xwin)
589    {
590       P("StartWindow xwin%#lx\n",xwin);
591       global.SnowWin           = xwin;
592       X11cairo = 1;
593    }
594    else if(Flags.ForceRoot)
595    {
596       // user specified to run on root window
597       printf("Trying to snow in root window\n");
598       global.SnowWin = global.Rootwindow;
599       if (getenv("XSCREENSAVER_WINDOW"))
600       {
601 	 global.XscreensaverMode  = 1;
602 	 global.SnowWin    = strtol(getenv("XSCREENSAVER_WINDOW"),NULL,0);
603 	 global.Rootwindow = global.SnowWin;
604       }
605       X11cairo = 1;
606    }
607    else
608    {
609       // default behaviour
610       // try to create a transparent clickthrough window
611       GtkWidget *gtkwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
612       int rc = make_trans_window(gtkwin,
613 	    1,                   // full screen
614 	    Flags.AllWorkspaces, // sticky
615 	    Flags.BelowAll,      // below
616 	    1,                   // dock
617 	    NULL,                // gdk_window
618 	    &xwin                // x11_window
619 	    );
620       if (rc)
621       {
622 	 global.Trans            = 1;
623 	 global.IsDouble         = 1;
624 	 global.Desktop          = 1;
625 	 TransA                  = gtkwin;
626 	 GtkWidget *drawing_area = gtk_drawing_area_new();
627 	 gtk_container_add(GTK_CONTAINER(TransA), drawing_area);
628 	 g_signal_connect(TransA, "draw", G_CALLBACK(on_draw_event), NULL);
629 	 set_below_above();
630 	 global.SnowWin = xwin;
631 	 printf("Using transparent window\n");
632       }
633       else
634       {
635 	 global.Desktop  = 1;
636 	 X11cairo = 1;
637 	 // use rootwindow, pcmanfm or Desktop:
638 	 printf("Cannot create transparent window\n");
639 	 if (global.DesktopSession == NULL)
640 	 {
641 	    char *desktopsession = NULL;
642 	    const char *desktops[] = {
643 	       "DESKTOP_SESSION",
644 	       "XDG_SESSION_DESKTOP",
645 	       "XDG_CURRENT_DESKTOP",
646 	       "GDMSESSION",
647 	       NULL
648 	    };
649 
650 	    int i;
651 	    for (i=0; desktops[i]; i++)
652 	    {
653 	       desktopsession = getenv(desktops[i]);
654 	       if (desktopsession)
655 		  break;
656 	    }
657 	    if (desktopsession)
658 	       printf("Detected desktop session: %s\n",desktopsession);
659 	    else
660 	    {
661 	       printf("Could not determine desktop session\n");
662 	       desktopsession = (char *)"unknown_desktop_session";
663 	    }
664 
665 	    global.DesktopSession = strdup(desktopsession);
666 
667 	    if (!strcasecmp(global.DesktopSession,"enlightenment"))
668 	       printf("NOTE: xsnow will probably run, but some glitches are to be expected.\n");
669 	    else if(!strcasecmp(global.DesktopSession,"twm"))
670 	       printf("NOTE: you probably need to tweak 'Lift snow on windows' in the 'settings' panel.\n");
671 	 }
672 	 // if envvar DESKTOP_SESSION == LXDE, search for window with name pcmanfm
673 	 if (global.DesktopSession != NULL &&
674 	       !strncmp(global.DesktopSession,"LXDE",4) &&
675 	       (xwin = FindWindowWithName(global.display,"pcmanfm")))
676 	 {
677 	    printf("LXDE session found, using window 'pcmanfm'.\n");
678 	    P("lxdefound: %d %#lx\n",lxdefound,*xwin);
679 	 }
680 	 else if ((xwin = FindWindowWithName(global.display,"Desktop")))
681 	 {
682 	    printf("Using window 'Desktop'.\n");
683 	 }
684 	 else
685 	 {
686 	    printf("Using root window\n");
687 	    xwin = global.Rootwindow;
688 	 }
689 	 global.SnowWin = xwin;
690       }
691    }
692 
693    if(X11cairo)
694    {
695       HandleX11Cairo();
696       drawit_id = add_to_mainloop1(PRIORITY_HIGH, time_draw_all, do_drawit, CairoDC);
697    }
698 
699    global.IsDouble = global.Trans || global.UseDouble; // just to be sure ...
700 
701    XTextProperty x;
702    if (XGetWMName(global.display,xwin,&x))
703       SnowWinName = strdup((char *)x.value);
704    else
705       SnowWinName = strdup("no name");
706    XFree(x.value);
707    InitDisplayDimensions();
708    printf("Snowing in %#lx: %s %d+%d %dx%d\n",global.SnowWin,SnowWinName,global.SnowWinX,global.SnowWinY,global.SnowWinWidth,global.SnowWinHeight);
709 
710    Xorig = global.SnowWinX;
711    Yorig = global.SnowWinY;
712 
713    movewindow();
714 
715    SetWindowScale();
716    if (global.XscreensaverMode && !Flags.BlackBackground)
717       SetBackground();
718 
719    return TRUE;
720 }
721 
HandleX11Cairo()722 int HandleX11Cairo()
723 {
724    XWindowAttributes attr;
725    XGetWindowAttributes(global.display,global.SnowWin,&attr);
726    int w = attr.width;
727    int h = attr.height;
728    Visual *visual = DefaultVisual(global.display,DefaultScreen(global.display));
729    int rcv;
730    P("double: %d\n",Flags.UseDouble);
731 #ifdef XDBE_AVAILABLE
732    int dodouble = Flags.UseDouble;
733 #else
734    int dodouble = 0;
735 #endif
736 #ifdef XDBE_AVAILABLE
737    if(dodouble)
738    {
739       static Drawable backBuf = 0;
740       if(backBuf)
741 	 XdbeDeallocateBackBufferName(global.display,backBuf);
742       backBuf = XdbeAllocateBackBufferName(global.display,global.SnowWin,BMETHOD);
743       if (CairoSurface)
744 	 cairo_surface_destroy(CairoSurface);
745       CairoSurface = cairo_xlib_surface_create(global.display, backBuf, visual, w, h);
746       global.UseDouble = 1;
747       global.IsDouble  = 1;
748       printf("Using double buffer: %#lx.\n",backBuf);
749       rcv = TRUE;
750    }
751 #endif
752    if(!dodouble)
753    {
754       CairoSurface = cairo_xlib_surface_create(global.display, global.SnowWin, visual, w, h);
755       printf("NOT using double buffering:");
756       if (Flags.UseDouble)
757 	 printf(" because double buffering is not available on this system\n");
758       else
759 	 printf(" on your request.\n");
760       rcv = FALSE;
761    }
762 
763    if (CairoDC)
764       cairo_destroy(CairoDC);
765 
766    CairoDC = cairo_create(CairoSurface);
767    cairo_xlib_surface_set_size(CairoSurface,w,h);
768    return rcv;
769 }
770 
DoAllWorkspaces()771 void DoAllWorkspaces()
772 {
773    if(Flags.AllWorkspaces)
774    {
775       P("stick\n");
776       if (global.Trans)
777       {
778 	 gtk_window_stick(GTK_WINDOW(TransA));
779       }
780    }
781    else
782    {
783       P("unstick\n");
784       if (global.Trans)
785       {
786 	 gtk_window_unstick(GTK_WINDOW(TransA));
787       }
788    }
789    ui_set_sticky(Flags.AllWorkspaces);
790 }
791 
792 // here we are handling the buttons in ui
793 // Ok, this is a long list, and could be implemented more efficient.
794 // But, do_ui_check is called not too frequently, so....
795 // Note: if changes != 0, the settings will be written to .xsnowrc
796 //
do_ui_check(void * d)797 int do_ui_check(void *d)
798 {
799    if (Flags.Done)
800       gtk_main_quit();
801 
802    if (Flags.NoMenu)
803       return TRUE;
804 
805    Santa_ui();
806    scenery_ui();
807    birds_ui();
808    snow_ui();
809    meteo_ui();
810    wind_ui();
811    stars_ui();
812    fallensnow_ui();
813    blowoff_ui();
814    treesnow_ui();
815    moon_ui();
816    ui_ui();
817 
818    UIDO (CpuLoad             , HandleCpuFactor();               );
819    UIDO (Transparency        ,                                  );
820    UIDO (Scale               ,                                  );
821    UIDO (OffsetS             , DisplayDimensions();             );
822    UIDO (OffsetY             , UpdateFallenSnowRegions();       );
823    UIDO (NoFluffy            , ClearScreen();                   );
824    UIDO (AllWorkspaces       , DoAllWorkspaces();               );
825    UIDO (BelowAll            , set_below_above();               );
826    UIDOS(BackgroundFile      ,                                  );
827    UIDO (BlackBackground     ,                                  );
828 
829    if (Flags.Changes > 0)
830    {
831       WriteFlags();
832       P("-----------Changes: %d\n",Flags.Changes);
833    }
834    Flags.Changes = 0;
835    return TRUE;
836    (void)d;
837 }
838 
839 
movewindow()840 void movewindow()
841 {
842    if(!global.Desktop)
843       return;
844    XMoveWindow(global.display,global.SnowWin,0,0);
845    //InitDisplayDimensions();
846    P("movewindow: Orig: %d %d %d %d\n",Xorig,Yorig,global.SnowWinWidth,global.SnowWinHeight);
847 }
848 
do_displaychanged(void * d)849 int do_displaychanged(void *d)
850 {
851    // if we are snowing in the desktop, we check if the size has changed,
852    // this can happen after changing of the displays settings
853    // If the size has been changed, we restart the program
854    (void)d;
855    if (Flags.Done)
856       return FALSE;
857    P("Trans: %d xxposures: %d Desktop: %d\n",
858 	 global.Trans,
859 	 global.xxposures, global.Desktop);
860 
861    if (!global.Desktop)
862       return TRUE;
863    {
864       unsigned int w,h;
865       Display* display = XOpenDisplay(Flags.DisplayName);
866       Screen* screen   = DefaultScreenOfDisplay(display);
867       w = WidthOfScreen(screen);
868       h = HeightOfScreen(screen);
869       P("width height: %d %d\n",w,h);
870       if(global.Wroot != w || global.Hroot != h)
871       {
872 	 DoRestart = 1;
873 	 Flags.Done = 1;
874 	 printf("Restart due to change of display settings...\n");
875       }
876       XCloseDisplay(display);
877       return TRUE;
878    }
879 }
880 
do_event(void * d)881 int do_event(void *d)
882 {
883    (void)d;
884    P("do_event %d\n",counter++);
885    if (Flags.Done)
886       return FALSE;
887    XEvent ev;
888    XFlush(global.display);
889    while (XPending(global.display))
890    {
891       XNextEvent(global.display, &ev);
892       {
893 	 if (ev.type == ConfigureNotify || ev.type == MapNotify
894 	       || ev.type == UnmapNotify)
895 	 {
896 	    global.WindowsChanged++;
897 	    P("WindowsChanged %d %d\n",counter++,global.WindowsChanged);
898 	 }
899 	 switch (ev.type)
900 	 {
901 	    case ConfigureNotify:
902 	       P("ConfigureNotify: ev=%ld win=%#lx geo=(%d,%d,%d,%d) bw=%d root: %d\n",
903 		     ev.xconfigure.event,
904 		     ev.xconfigure.window,
905 		     ev.xconfigure.x,
906 		     ev.xconfigure.y,
907 		     ev.xconfigure.width,
908 		     ev.xconfigure.height,
909 		     ev.xconfigure.border_width,
910 		     (global.SnowWin == ev.xconfigure.event)
911 		);
912 	       if (ev.xconfigure.window == global.SnowWin)
913 	       {
914 		  SnowWinChanged = 1;
915 	       }
916 	       break;
917 	 }
918       }
919    }
920    return TRUE;
921 }
922 
RestartDisplay()923 void RestartDisplay()
924 {
925    P("Restartdisplay: %d W: %d H: %d\n",counter++,global.SnowWinWidth,global.SnowWinHeight);
926    InitFallenSnow();
927    init_stars();
928    EraseTrees();
929    if(!Flags.NoKeepSnowOnTrees && !Flags.NoTrees)
930    {
931       reinit_treesnow_region();
932    }
933    if(!Flags.NoTrees)
934    {
935       cairo_region_destroy(global.TreeRegion);
936       global.TreeRegion = cairo_region_create();
937    }
938    if(!global.IsDouble)
939       ClearScreen();
940 }
941 
942 
943 
do_show_range_etc(void * d)944 int do_show_range_etc(void *d)
945 {
946    if (Flags.Done)
947       return FALSE;
948 
949    if (Flags.NoMenu)
950       return TRUE;
951    ui_show_range_etc();
952    return TRUE;
953    (void)d;
954 }
955 
do_show_desktop_type(void * d)956 int do_show_desktop_type(void *d)
957 {
958    P("do_show_desktop_type %d\n",counter++);
959    if (Flags.NoMenu)
960       return TRUE;
961    char *s;
962    if (global.IsWayland)
963       s = (char *)"Wayland (Expect some slugginess)";
964    else if (global.IsCompiz)
965       s = (char *)"Compiz";
966    else
967       s = (char *)"Probably X11";
968    char t[128];
969    snprintf(t,64,"%s. Snow window: %#lx",s,global.SnowWin);
970    ui_show_desktop_type(t);
971    return TRUE;
972    (void)d;
973 }
974 
975 
do_testing(void * d)976 int do_testing(void *d)
977 {
978    (void)d;
979    if (Flags.Done)
980       return FALSE;
981 
982    return TRUE;
983    global.counter++;
984    XWindowAttributes attr;
985    XGetWindowAttributes(global.display,global.SnowWin,&attr);
986 
987    P("%d wxh %d %d %d %d    %d %d %d %d \n",counter,SnowWinX,SnowWinY,global.SnowWinWidth,global.SnowWinHeight,attr.x,attr.y,attr.width,attr.height);
988 
989    return TRUE;
990 }
991 
992 
SigHandler(int signum)993 void SigHandler(int signum)
994 {
995    global.HaltedByInterrupt = signum;
996    Flags.Done        = 1;
997 }
998 
XsnowErrors(Display * dpy,XErrorEvent * err)999 int XsnowErrors(Display *dpy, XErrorEvent *err)
1000 {
1001    static int count   = 0;
1002    const int countmax = 1000;
1003    char msg[1024];
1004    XGetErrorText(dpy, err->error_code, msg,sizeof(msg));
1005    if(Flags.Noisy)
1006       I("%d %s\n",global.counter++,msg);
1007 
1008    if (++count > countmax)
1009    {
1010       snprintf(global.Message,sizeof(global.Message),"More than %d errors, I quit!",countmax);
1011       Flags.Done = 1;
1012    }
1013    return 0;
1014 }
1015 
1016 
1017 
1018 // the draw callback
on_draw_event(GtkWidget * widget,cairo_t * cr,gpointer user_data)1019 gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data)
1020 {
1021    P("Just to check who this is: %p %p\n",(void *)widget,(void *)TransA);
1022    drawit(cr);
1023    return FALSE;
1024    (void)widget;
1025    (void)user_data;
1026 }
1027 
do_drawit(void * cr)1028 int do_drawit(void *cr)
1029 {
1030    P("do_drawit %d %p\n",counter++,cr);
1031    drawit((cairo_t*)cr);
1032    return TRUE;
1033 }
1034 
drawit(cairo_t * cr)1035 void drawit(cairo_t *cr)
1036 {
1037    P("drawit %d %p\n",counter++,(void *)cr);
1038 
1039    if (Flags.Done)
1040       return;
1041 
1042    if(global.UseDouble)
1043    {
1044       XdbeSwapInfo swapInfo;
1045       swapInfo.swap_window = global.SnowWin;
1046       swapInfo.swap_action = BMETHOD;
1047       XdbeSwapBuffers(global.display,&swapInfo,1);
1048    }
1049    else if(!global.IsDouble)
1050    {
1051       XFlush(global.display);
1052       moon_erase (0);
1053       Santa_erase(cr);
1054       stars_erase(); // not really necessary
1055       birds_erase(0);
1056       snow_erase(1);
1057       XFlush(global.display);
1058    }
1059 
1060    int skipit = Flags.BirdsOnly || !WorkspaceActive();
1061 
1062    if (!skipit)
1063    {
1064       stars_draw(cr);
1065       meteo_draw(cr);
1066 
1067       P("moon\n");
1068       moon_draw(cr);
1069       scenery_draw(cr);
1070    }
1071 
1072    P("birds %d\n",counter++);
1073    birds_draw(cr);
1074 
1075    if (skipit)
1076       return;
1077 
1078    fallensnow_draw(cr);
1079 
1080    if(!Flags.FollowSanta || !Flags.ShowBirds) // if Flags.FollowSanta: drawing of Santa is done in birds_draw()
1081       Santa_draw(cr);
1082 
1083    treesnow_draw(cr);
1084 
1085    snow_draw(cr);
1086 
1087    XFlush(global.display);
1088 }
1089 
SetWindowScale()1090 void SetWindowScale()
1091 {
1092    // assuming a standard screen of 1024x576, we suggest to use the scalefactor
1093    // WindowScale
1094    float x = global.SnowWinWidth / 1000.0;
1095    float y = global.SnowWinHeight / 576.0;
1096    if (x < y)
1097       global.WindowScale = x;
1098    else
1099       global.WindowScale = y;
1100    P("WindowScale: %f\n",global.WindowScale);
1101 }
1102 
do_display_dimensions(void * d)1103 int do_display_dimensions(void *d)
1104 {
1105    (void)d;
1106    if (Flags.Done)
1107       return FALSE;
1108    if (!SnowWinChanged)
1109       return TRUE;
1110    SnowWinChanged = 0;
1111    static int prevw = 0, prevh = 0;
1112    P("%d do_display_dimensions %d %d\n",counter++,global.SnowWinWidth,global.SnowWinHeight);
1113    DisplayDimensions();
1114    if (prevw != global.SnowWinWidth || prevh != global.SnowWinHeight)
1115    {
1116       // if(global.X11cairo) // let op
1117       if (!global.Trans)
1118       {
1119 	 HandleX11Cairo();
1120 	 RestartDisplay();
1121       }
1122       prevw = global.SnowWinWidth;
1123       prevh = global.SnowWinHeight;
1124       SetWindowScale();
1125    }
1126    return TRUE;
1127 }
1128 
do_draw_all(gpointer widget)1129 int do_draw_all(gpointer widget)
1130 {
1131    if (Flags.Done)
1132       return FALSE;
1133    P("do_draw_all %d %p\n",counter++,(void *)widget);
1134 
1135    // this will result in a call off on_draw_event():
1136    gtk_widget_queue_draw(GTK_WIDGET(widget));
1137    return TRUE;
1138 }
1139 
1140 
1141 // handle callbacks for things whose timings depend on cpufactor
HandleCpuFactor()1142 void HandleCpuFactor()
1143 {
1144    static guint fallen_id=0;
1145 
1146    // re-add things whose timing is dependent on cpufactor
1147    if (Flags.CpuLoad <= 0)
1148       global.cpufactor = 1;
1149    else
1150       global.cpufactor = 100.0/Flags.CpuLoad;
1151 
1152    if (fallen_id)
1153       g_source_remove(fallen_id);
1154 
1155    fallen_id = add_to_mainloop(PRIORITY_DEFAULT, time_fallen, do_fallen);
1156    P("handlecpufactor %f %f %d\n",oldcpufactor,cpufactor,counter++);
1157    add_to_mainloop(PRIORITY_HIGH, time_init_snow , do_initsnow);  // remove flakes
1158 
1159    restart_do_draw_all();
1160 }
1161 
restart_do_draw_all()1162 void restart_do_draw_all()
1163 {
1164    if (global.Trans)
1165    {
1166       if (draw_all_id)
1167 	 g_source_remove(draw_all_id);
1168       draw_all_id = add_to_mainloop1(PRIORITY_HIGH, time_draw_all, do_draw_all, TransA);
1169       P("started do_draw_all %d %p %f\n",draw_all_id, (void *)TransA, time_draw_all);
1170    }
1171    else
1172    {
1173       if (drawit_id)
1174 	 g_source_remove(drawit_id);
1175       drawit_id = add_to_mainloop1(PRIORITY_HIGH, time_draw_all, do_drawit, CairoDC);
1176       P("started do_drawit %d %p %f\n",drawit_id, (void *)CairoDC, time_draw_all);
1177    }
1178 }
1179 
1180 
do_stopafter(void * d)1181 int do_stopafter(void *d)
1182 {
1183    Flags.Done = 1;
1184    printf("Halting because of flag -stopafter\n");
1185    return FALSE;
1186    (void)d;
1187 }
1188