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