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 #include <stdio.h>
23 #include <gtk/gtk.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include <X11/Intrinsic.h>
27 #include <X11/xpm.h>
28 #include "Santa.h"
29 #include "pixmaps.h"
30 #include "debug.h"
31 #include "windows.h"
32 #include "flags.h"
33 #include "utils.h"
34 #include "wind.h"
35 #include "ixpm.h"
36 #include "moon.h"
37
38 #define NOTACTIVE \
39 (Flags.BirdsOnly || !WorkspaceActive())
40 static int do_usanta(void *);
41 //static void InitSantaPixmaps(void);
42 static void init_Santa_surfaces(void);
43 static Region RegionCreateRectangle(int x, int y, int w, int h);
44 static void ResetSanta(void);
45 static void SetSantaSizeSpeed(void);
46 static void setSantaRegions(void);
47
48 static int CurrentSanta;
49 static Pixmap SantaMaskPixmap[PIXINANIMATION];
50 static Pixmap SantaPixmap[PIXINANIMATION];
51 static Region SantaRegion = 0;
52 static float SantaSpeed;
53 static float SantaXr;
54 static float SantaYr;
55 static int SantaYStep;
56 static int OldSantaX = 0; // the x value of Santa when he was last drawn
57 static int OldSantaY = 0; // the y value of Santa when he was last drawn
58 static const float LocalScale = 0.6;
59 static int MoonSeeking = 1;
60
61 static cairo_surface_t *Santa_surfaces[MAXSANTA+1][2][PIXINANIMATION];
62
63 /* Speed for each Santa in pixels/second*/
64 static float Speed[] = {SANTASPEED0, /* Santa 0 */
65 SANTASPEED1, /* Santa 1 */
66 SANTASPEED2, /* Santa 2 */
67 SANTASPEED3, /* Santa 3 */
68 SANTASPEED4, /* Santa 4 */
69 };
70
Santa_ui()71 void Santa_ui()
72 {
73 UIDO(SantaSize, SetSantaSizeSpeed(););
74 UIDO(Rudolf, SetSantaSizeSpeed(););
75 UIDO(NoSanta, );
76 UIDO(SantaSpeedFactor, SetSantaSizeSpeed(););
77
78 static int prev = 100;
79 if(ScaleChanged(&prev))
80 {
81 P("%d Santa_scale \n",global.counter);
82 SetSantaSizeSpeed();
83 }
84 }
85
Santa_draw(cairo_t * cr)86 int Santa_draw(cairo_t *cr)
87 {
88 if (Flags.NoSanta)
89 return TRUE;
90 P("Santa_draw %d\n",global.counter++);
91 cairo_surface_t *surface;
92 surface = Santa_surfaces[Flags.SantaSize][Flags.Rudolf][CurrentSanta];
93 cairo_set_source_surface (cr, surface, global.SantaX, global.SantaY);
94 my_cairo_paint_with_alpha(cr,ALPHA);
95 OldSantaX = global.SantaX;
96 OldSantaY = global.SantaY;
97 return TRUE;
98 }
99
Santa_erase(cairo_t * cr)100 void Santa_erase(cairo_t *cr)
101 {
102 P("Santa_erase %d %d\n",OldSantaX,OldSantaY);
103 (void)cr;
104 myXClearArea(global.display, global.SnowWin,
105 OldSantaX, OldSantaY,
106 global.SantaWidth+1,global.SantaHeight,
107 global.xxposures);
108 }
109
Santa_init()110 void Santa_init()
111 {
112 P("Santa_init\n");
113 int i;
114 for (i=0; i<PIXINANIMATION; i++)
115 {
116 SantaPixmap[i] = 0;
117 SantaMaskPixmap[i] = 0;
118 }
119 int j,k;
120 for (i=0; i<MAXSANTA+1; i++)
121 for(j=0; j<2; j++)
122 for (k=0; k<PIXINANIMATION; k++)
123 Santa_surfaces[i][j][k] = NULL;
124
125 SantaRegion = XCreateRegion();
126 global.SantaPlowRegion = XCreateRegion();
127 SetSantaSizeSpeed();
128 ResetSanta();
129 add_to_mainloop(PRIORITY_HIGH, time_usanta, do_usanta);
130 }
131
init_Santa_surfaces()132 void init_Santa_surfaces()
133 {
134 GdkPixbuf *pixbuf;
135 int i,j,k;
136 for(i=0; i<MAXSANTA+1; i++)
137 for (j=0; j<2; j++)
138 for (k=0; k<PIXINANIMATION; k++)
139 {
140 int w,h;
141 sscanf(Santas[i][j][k][0],"%d %d",&w,&h);
142 w *= 0.01*Flags.Scale*LocalScale*global.WindowScale;
143 h *= 0.01*Flags.Scale*LocalScale*global.WindowScale;
144 P("%d init_Santa_surfaces %d %d %d %d %d\n",global.counter++,i,j,k,w,h);
145 pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)Santas[i][j][k]);
146 if(w < 1) w = 1;
147 if(h < 1) h = 1;
148 if (w == 1 && h == 1) h = 2;
149 GdkPixbuf *pixbufscaled = gdk_pixbuf_scale_simple(pixbuf,w,h,GDK_INTERP_HYPER);
150 if(Santa_surfaces[i][j][k])
151 cairo_surface_destroy(Santa_surfaces[i][j][k]);
152
153 Santa_surfaces[i][j][k] = gdk_cairo_surface_create_from_pixbuf (pixbufscaled, 0, NULL);
154 g_clear_object(&pixbuf);
155 g_clear_object(&pixbufscaled);
156 }
157 int ok = 1;
158 char *path[PIXINANIMATION];
159 const char *filenames[] =
160 {
161 "xsnow/pixmaps/santa1.xpm",
162 "xsnow/pixmaps/santa2.xpm",
163 "xsnow/pixmaps/santa3.xpm",
164 "xsnow/pixmaps/santa4.xpm",
165 };
166 for (i=0; i<PIXINANIMATION; i++)
167 {
168 path[i] = NULL;
169 FILE *f = HomeOpen(filenames[i],"r",&path[i]);
170 if(!f){ ok = 0; if (path[i]) free(path[i]); break; }
171 fclose(f);
172 }
173 if (ok)
174 {
175 printf("Using external Santa: %s.\n",path[0]);
176 if (!Flags.NoMenu)
177 printf("Disabling menu.\n");
178 Flags.NoMenu = 1;
179 int rc,i;
180 char **santaxpm;
181 for (i=0; i<PIXINANIMATION; i++)
182 {
183 rc = XpmReadFileToData(path[i],&santaxpm);
184 if(rc == XpmSuccess)
185 {
186 int w,h;
187 sscanf(santaxpm[0],"%d %d",&w,&h);
188 w *= 0.01*Flags.Scale*LocalScale*global.WindowScale;
189 h *= 0.01*Flags.Scale*LocalScale*global.WindowScale;
190 if(w < 1) w = 1;
191 if(h < 1) h = 1;
192 if (w == 1 && h == 1) h = 2;
193 pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)santaxpm);
194 GdkPixbuf *pixbufscaled = gdk_pixbuf_scale_simple(pixbuf,w,h,GDK_INTERP_HYPER);
195 cairo_surface_destroy( Santa_surfaces[0][0][i]);
196 Santa_surfaces[0][0][i] = gdk_cairo_surface_create_from_pixbuf(pixbufscaled,0,NULL);
197 XpmFree(santaxpm);
198 g_clear_object(&pixbuf);
199 g_clear_object(&pixbufscaled);
200 }
201 else
202 {
203 printf("Invalid external xpm for Santa given: %s\n",path[i]);
204 exit(1);
205 }
206 free(path[i]);
207 }
208 Flags.SantaSize = 0;
209 Flags.Rudolf = 0;
210 }
211 setSantaRegions();
212 }
213
SetSantaSizeSpeed()214 void SetSantaSizeSpeed()
215 {
216 SantaSpeed = Speed[Flags.SantaSize];
217 if (Flags.SantaSpeedFactor < 10)
218 SantaSpeed = 0.1*SantaSpeed;
219 else
220 SantaSpeed = 0.01*Flags.SantaSpeedFactor*SantaSpeed;
221 global.ActualSantaSpeed = SantaSpeed;
222 init_Santa_surfaces();
223 cairo_surface_t *surface = Santa_surfaces[Flags.SantaSize][Flags.Rudolf][CurrentSanta];
224 global.SantaWidth = cairo_image_surface_get_width(surface);
225 global.SantaHeight = cairo_image_surface_get_height(surface);
226 setSantaRegions();
227 }
228
229
230 // update santa's coordinates and speed
do_usanta(void * d)231 int do_usanta(void *d)
232 {
233 (void) d;
234 P("do_usanta %d\n",counter++);
235 if (Flags.Done)
236 return FALSE;
237 #define RETURN do { return TRUE ; } while(0)
238 if (NOTACTIVE)
239 RETURN;
240 if(Flags.NoSanta)
241 RETURN;
242 double yspeed;
243 static int yspeeddir = 0;
244 static double sdt = 0;
245 static double dtt = 0;
246
247 int oldx = global.SantaX;
248 int oldy = global.SantaY;
249
250 double dt = time_usanta;
251
252 double santayrmin = 0;
253 double santayrmax = global.SnowWinHeight*0.33;
254
255 global.ActualSantaSpeed += dt*(SANTASENS*global.NewWind+SantaSpeed - global.ActualSantaSpeed);
256 if (global.ActualSantaSpeed>3*SantaSpeed)
257 global.ActualSantaSpeed = 3*SantaSpeed;
258 else if (global.ActualSantaSpeed < -2*SantaSpeed)
259 global.ActualSantaSpeed = -2*SantaSpeed;
260
261 SantaXr += dt*global.ActualSantaSpeed;
262 P("SantaXr: %ld global.SnowWinWidth: %d\n",lrint(SantaXr),global.SnowWinWidth);
263 if (SantaXr >= global.SnowWinWidth)
264 {
265 ResetSanta();
266 oldx = global.SantaX;
267 oldy = global.SantaY;
268 }
269 if (SantaXr < -global.SantaWidth-global.ActualSantaSpeed)
270 SantaXr = -global.SantaWidth - global.ActualSantaSpeed;
271 global.SantaX = lrintf(SantaXr);
272 dtt += dt;
273 if (dtt > 0.1 && fabs(global.ActualSantaSpeed) > 3)
274 {
275 dtt = 0;
276 CurrentSanta++;
277 if (CurrentSanta >= PIXINANIMATION) CurrentSanta = 0;
278 }
279
280 yspeed = global.ActualSantaSpeed/4;
281 sdt += dt;
282 if (sdt > 2.0*50.0/SantaSpeed || sdt > 2.0)
283 {
284 // time to change yspeed
285 sdt = 0;
286 yspeeddir = randint(3)-1; // -1, 0, 1
287 if (SantaYr < santayrmin + 20)
288 yspeeddir = 2;
289
290 if (SantaYr > santayrmax - 20)
291 yspeeddir = -2;
292 int mooncy = global.moonY+Flags.MoonSize/2;
293 if (MoonSeeking &&
294 Flags.Moon &&
295 global.SantaX+global.SantaWidth < global.moonX+Flags.MoonSize &&
296 global.SantaX+global.SantaWidth > global.moonX-300) // Santa likes to hover the moon
297 {
298 int dy = global.SantaY+global.SantaHeight/2 - mooncy;
299 if (dy < 0)
300 yspeeddir = 1;
301 else
302 yspeeddir = -1;
303 if (dy < -global.moonR/2) // todo
304 yspeeddir = 3;
305 else if (dy > Flags.MoonSize/2)
306 yspeeddir = -3;
307 P("moon seeking %f %f %d %f\n",SantaYr, moonY, yspeeddir,SantaSpeed);
308 }
309 }
310
311 SantaYr += dt*yspeed*yspeeddir;
312 if (SantaYr < santayrmin)
313 SantaYr = 0;
314
315 if (SantaYr > santayrmax)
316 SantaYr = santayrmax;
317
318 global.SantaY = lrintf(SantaYr);
319 XOffsetRegion(SantaRegion, global.SantaX - oldx, global.SantaY - oldy);
320 XOffsetRegion(global.SantaPlowRegion, global.SantaX - oldx, global.SantaY - oldy);
321
322 RETURN;
323 }
324
ResetSanta()325 void ResetSanta()
326 {
327 global.SantaX = -global.SantaWidth - global.ActualSantaSpeed;
328 SantaXr = global.SantaX;
329 global.SantaY = randint(global.SnowWinHeight / 3)+40;
330 // sometimes Santa is moon seeking, sometimes not
331 MoonSeeking = drand48() > 0.5;
332 P("MoonSeeking: %d\n",MoonSeeking);
333 if (MoonSeeking && Flags.Moon && global.moonX < 400)
334 {
335 P("moon seeking at start\n");
336 global.SantaY = randint(Flags.MoonSize + 40)+global.moonY-20;
337 }
338 else
339 global.SantaY = randint(global.SnowWinHeight / 3)+40;
340 SantaYr = global.SantaY;
341 SantaYStep = 1;
342 CurrentSanta = 0;
343 SetSantaSizeSpeed();
344 }
345
setSantaRegions()346 void setSantaRegions()
347 {
348 P("setSantaRegions %d %d %d %d\n",global.SantaX,global.SantaY,global.SantaWidth,global.SantaHeight);
349 if(SantaRegion)
350 XDestroyRegion(SantaRegion);
351 SantaRegion = RegionCreateRectangle(
352 global.SantaX,global.SantaY,global.SantaWidth,global.SantaHeight);
353
354 if(global.SantaPlowRegion)
355 XDestroyRegion(global.SantaPlowRegion);
356 global.SantaPlowRegion = RegionCreateRectangle(
357 global.SantaX + global.SantaWidth, global.SantaY, 1, global.SantaHeight);
358 }
359
RegionCreateRectangle(int x,int y,int w,int h)360 Region RegionCreateRectangle(int x, int y, int w, int h)
361 {
362 XPoint p[5];
363 p[0].x = x; p[0].y = y;
364 p[1].x = x+w; p[1].y = y;
365 p[2].x = x+w; p[2].y = y+h;
366 p[3].x = x; p[3].y = y+h;
367 p[4].x = x; p[4].y = y;
368 return XPolygonRegion(p, 5, EvenOddRule);
369 }
370