1 /* -*-c-*- */
2 /*
3  * FvwmAnimate! Animation module for fvwm
4  *
5  * Copyright (c) 1997 Frank Scheelen <scheelen@worldonline.nl>
6  * Copyright (c) 1996 Alfredo Kengi Kojima (kojima@inf.ufrgs.br)
7  * Copyright (c) 1996 Kaj Groner <kajg@mindspring.com>
8  *   Added .steprc parsing and twisty iconify.
9  *
10  * Copyright (c) 1998 Dan Espen <dane@mk.bellcore.com>
11  *   Changed to run under fvwm.  Lots of changes made.
12  *   This used to only animate iconify on M_CONFIGURE_WINDOW.
13  *   This module no longer reads M_CONFIGURE_WINDOW.
14  *   I added args to M_ICONIFY so iconification takes one message.
15  *   The arg parsing is completely redone using library functions.
16  *   I also added args to M_DEICONIFY to eliminate the need to read the
17  *   window size.
18  *   Added AnimateResizeLines animation effect.
19  *   Changed option "resize" to "effect", (resize still works).
20  *   Changed effect "zoom" to "frame", (zoom still works).
21  *   Added myfprintf double parens debugging trick.
22  *   Changed so that commands can be sent to this module while it is
23  *   running.
24  *   Changed so that this module creates its own built in menu.
25  *   Added Stop, Save commands.
26  *   Changed so this this module uses FvwmForm for complete control on all
27  *   settings.
28  *   Anything can request an animation thru "sendtomodule".
29  *
30  * This program is free software; you can redistribute it and/or modify
31  * it under the terms of the GNU General Public License as published by
32  * the Free Software Foundation; either version 2 of the License, or
33  * (at your option) any later version.
34  *
35  * This program is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38  * GNU General Public License for more details.
39  *
40  * You should have received a copy of the GNU General Public License
41  * along with this program; if not, see: <http://www.gnu.org/licenses/>
42  */
43 
44 #include "config.h"
45 
46 #include <stdio.h>
47 #include <math.h>
48 #include <unistd.h>
49 #include <ctype.h>
50 #include <fcntl.h>                      /* for O_WRONLY */
51 #include <sys/times.h>
52 #include "libs/ftime.h"
53 #include <limits.h>
54 #include "libs/fvwmsignal.h"
55 #include "libs/Module.h"
56 #include "libs/fvwmlib.h"
57 #include "libs/Picture.h"
58 #include "libs/PictureGraphics.h"
59 #include "libs/PictureUtils.h"
60 #include "libs/FRenderInit.h"
61 #include "libs/Grab.h"
62 #include "libs/Graphics.h"
63 #include "libs/Parse.h"
64 #include "libs/Strings.h"
65 #include "FvwmAnimate.h"
66 
67 #define AS_PI 3.14159265358979323846
68 
69 static Display *dpy;
70 GC gc;
71 static ModuleArgs* module;
72 static int Channel[2];
73 static XColor xcol;
74 static unsigned long color;             /* color for animation */
75 static Pixmap pixmap = None;            /* pixmap for weirdness */
76 static XGCValues gcv;
77 static int animate_none = 0;            /* count bypassed animations */
78 static Bool stop_recvd = False;         /* got stop command */
79 static Bool running = False;            /* whether we are initiialized or not */
80 static Bool custom_recvd = False;       /* got custom command */
81 
82 #define MAX_SAVED_STATES 25
83 static Bool play_state = True;          /* current state: pause or play */
84 static unsigned int num_saved_play_states = 0;
85 static Bool saved_play_states[MAX_SAVED_STATES];
86 static char *mname;
87 
88 static struct
89 {
90     int screen;
91     Window root;
92     int MyDisplayWidth;
93     int MyDisplayHeight;
94     int Vx;
95     int Vy;
96     int CurrentDesk;
97 } Scr;
98 
99 /* here is the old double parens trick. */
100 /* #define DEBUG */
101 #ifdef DEBUG
102 #define myfprintf(X) \
103   fprintf X;\
104   fflush (stderr);
105 #else
106 #define myfprintf(X)
107 #endif
108 
109 
110 
111 /* #define DEBUG_ANIMATION */
112 #ifdef DEBUG_ANIMATION
113 #define myaprintf(X) \
114   fprintf X;\
115   fflush (stderr);
116 #else
117 #define myaprintf(X)
118 #endif
119 
120 /* Macros for creating and sending commands:
121    CMD1X - module->name
122    CMD10 - module->name, CatString3("*",module->name,0)
123    CMD11 - module->name,module->name */
124 #define CMD1X(TEXT) \
125   sprintf(cmd,TEXT,module->name);\
126   SendText(Channel,cmd,0);
127 #define CMD10(TEXT) \
128   do { \
129 	  char *x; \
130 	  xasprintf(&x, "*%s", module->name); \
131 	  sprintf(cmd,TEXT,module->name, x);\
132 	  SendText(Channel,cmd,0); \
133 	  free(x); \
134   } while (0);
135 #define CMD11(TEXT) \
136   sprintf(cmd,TEXT,module->name,module->name);\
137   SendText(Channel,cmd,0);
138 
139 static void Loop(void);
140 static void ParseOptions(void);
141 static void ParseConfigLine(char *);
142 static void CreateDrawGC(void);
143 static void DefineMe(void);
144 static void SaveConfig(void);
145 static void StopCmd(void);
146 static void AnimateResizeZoom(int, int, int, int, int, int, int, int);
147 static void AnimateResizeLines(int, int, int, int, int, int, int, int);
148 static void AnimateResizeFlip(int, int, int, int, int, int, int, int);
149 static void AnimateResizeTurn(int, int, int, int, int, int, int, int);
150 static void AnimateResizeRandom(int, int, int, int, int, int, int, int);
151 static void AnimateResizeZoom3D(int, int, int, int, int, int, int, int);
152 static void AnimateResizeNone(int, int, int, int, int, int, int, int);
153 static void AnimateResizeTwist(int, int, int, int, int, int, int, int);
154 static void DefineForm(void);
155 
156 static RETSIGTYPE HandleTerminate(int sig);
157 
158 struct ASAnimate Animate = { NULL, NULL, ANIM_ITERATIONS, ANIM_DELAY,
159 			     ANIM_TWIST, ANIM_WIDTH,
160 			     AnimateResizeTwist, ANIM_TIME };
161 
162 /* We now have so many effects, that I feel the need for a table. */
163 typedef struct AnimateEffects {
164   char *name;
165   char *alias;
166   void (*function)(int, int, int, int, int, int, int, int);
167   char *button;                 /* used to set custom form */
168 } ae;
169 /* Note None and Random must be the first 2 entries. */
170 struct AnimateEffects effects[] = {
171   {"None", 0, AnimateResizeNone, NULL},
172   {"Random", 0, AnimateResizeRandom, NULL},
173   {"Flip", 0, AnimateResizeFlip, NULL},
174   {"Frame", "Zoom", AnimateResizeZoom, NULL},
175   {"Frame3D", "Zoom3D", AnimateResizeZoom3D, NULL},
176   {"Lines", 0, AnimateResizeLines, NULL},
177   {"Turn", 0, AnimateResizeTurn, NULL},
178   {"Twist", 0, AnimateResizeTwist, NULL}
179 };
180 #define NUM_EFFECTS sizeof(effects) / sizeof(struct AnimateEffects)
181 
is_animation_visible(int x,int y,int w,int h,int fx,int fy,int fw,int fh)182 static Bool is_animation_visible(
183     int x, int y, int w, int h, int fx, int fy, int fw, int fh)
184 {
185     Bool is_start_visible = True;
186     Bool is_end_visible = True;
187 
188     if (x >= Scr.MyDisplayWidth || x + w < 0 ||
189 	y >= Scr.MyDisplayWidth || y + h < 0)
190     {
191 	is_start_visible = False;
192     }
193     if (fx >= Scr.MyDisplayWidth || fx + fw < 0 ||
194 	fy >= Scr.MyDisplayWidth || fy + fh < 0)
195     {
196 	is_end_visible = False;
197     }
198     return (is_start_visible || is_end_visible);
199 }
200 
201 /*
202  * This makes a twisty iconify/deiconify animation for a window, similar to
203  * MacOS.  Parameters specify the position and the size of the initial
204  * window and the final window
205  */
AnimateResizeTwist(int x,int y,int w,int h,int fx,int fy,int fw,int fh)206 static void AnimateResizeTwist(
207     int x, int y, int w, int h, int fx, int fy, int fw, int fh)
208 {
209     float cx, cy, cw, ch;
210     float xstep, ystep, wstep, hstep;
211     XPoint points[5];
212     float angle, angle_finite, a, d;
213 
214     if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh))
215       return;
216     x += w/2;
217     y += h/2;
218     fx += fw/2;
219     fy += fh/2;
220 
221     xstep = (float)(fx-x)/Animate.iterations;
222     ystep = (float)(fy-y)/Animate.iterations;
223     wstep = (float)(fw-w)/Animate.iterations;
224     hstep = (float)(fh-h)/Animate.iterations;
225 
226     cx = (float)x;
227     cy = (float)y;
228     cw = (float)w;
229     ch = (float)h;
230     a = atan(ch/cw);
231     d = sqrt((cw/2)*(cw/2)+(ch/2)*(ch/2));
232 
233     angle_finite = 2*AS_PI*Animate.twist;
234     MyXGrabServer(dpy);
235     XInstallColormap(dpy, Pcmap);
236     for (angle=0;; angle+=(float)(2*AS_PI*Animate.twist/Animate.iterations)) {
237 	if (angle > angle_finite)
238 	    angle = angle_finite;
239 	points[0].x = cx+cos(angle-a)*d;
240 	points[0].y = cy+sin(angle-a)*d;
241 	points[1].x = cx+cos(angle+a)*d;
242 	points[1].y = cy+sin(angle+a)*d;
243 	points[2].x = cx+cos(angle-a+AS_PI)*d;
244 	points[2].y = cy+sin(angle-a+AS_PI)*d;
245 	points[3].x = cx+cos(angle+a+AS_PI)*d;
246 	points[3].y = cy+sin(angle+a+AS_PI)*d;
247 	points[4].x = cx+cos(angle-a)*d;
248 	points[4].y = cy+sin(angle-a)*d;
249 	XDrawLines(dpy, Scr.root, gc, points, 5, CoordModeOrigin);
250 	XFlush(dpy);
251 	usleep(Animate.delay*1000);
252 	XDrawLines(dpy, Scr.root, gc, points, 5, CoordModeOrigin);
253 	cx+=xstep;
254 	cy+=ystep;
255 	cw+=wstep;
256 	ch+=hstep;
257 	a = atan(ch/cw);
258 	d = sqrt((cw/2)*(cw/2)+(ch/2)*(ch/2));
259 	if (angle >= angle_finite)
260 	    break;
261     }
262     MyXUngrabServer(dpy);
263 }
264 
265 /*
266  * Add even more 3D feel to AfterStep by doing a flipping iconify.
267  * Parameters specify the position and the size of the initial and the
268  * final window.
269  *
270  * Idea: how about texture mapped, user definable free 3D movement
271  * during a resize? That should get X on its knees all right! :)
272  */
AnimateResizeFlip(int x,int y,int w,int h,int fx,int fy,int fw,int fh)273 void AnimateResizeFlip(
274     int x, int y, int w, int h, int fx, int fy, int fw, int fh)
275 {
276   float cx, cy, cw, ch;
277   float xstep, ystep, wstep, hstep;
278   XPoint points[5];
279 
280   float distortx;
281   float distortch;
282   float midy;
283 
284   float angle, angle_finite;
285 
286     if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh))
287       return;
288 
289   xstep = (float) (fx - x) / Animate.iterations;
290   ystep = (float) (fy - y) / Animate.iterations;
291   wstep = (float) (fw - w) / Animate.iterations;
292   hstep = (float) (fh - h) / Animate.iterations;
293 
294   cx = (float) x;
295   cy = (float) y;
296   cw = (float) w;
297   ch = (float) h;
298 
299   angle_finite = 2 * AS_PI * Animate.twist;
300   MyXGrabServer(dpy);
301   XInstallColormap(dpy, Pcmap);
302   for (angle = 0; ;
303        angle += (float) (2 * AS_PI * Animate.twist / Animate.iterations)) {
304     if (angle > angle_finite)
305       angle = angle_finite;
306 
307     distortx = (cw / 10) - ((cw / 5) * sin(angle));
308     distortch = (ch / 2) * cos(angle);
309     midy = cy + (ch / 2);
310 
311     points[0].x = cx + distortx;
312     points[0].y = midy - distortch;
313     points[1].x = cx + cw - distortx;
314     points[1].y = points[0].y;
315     points[2].x = cx + cw + distortx;
316     points[2].y = midy + distortch;
317     points[3].x = cx - distortx;
318     points[3].y = points[2].y;
319     points[4].x = points[0].x;
320     points[4].y = points[0].y;
321 
322     XDrawLines(dpy, Scr.root, gc, points, 5, CoordModeOrigin);
323     XFlush(dpy);
324     usleep(Animate.delay * 1000);
325     XDrawLines(dpy, Scr.root, gc, points, 5, CoordModeOrigin);
326     cx += xstep;
327     cy += ystep;
328     cw += wstep;
329     ch += hstep;
330     if (angle >= angle_finite)
331       break;
332   }
333   MyXUngrabServer(dpy);
334 }
335 
336 
337 /*
338  * And another one, this time around the Y-axis.
339  */
AnimateResizeTurn(int x,int y,int w,int h,int fx,int fy,int fw,int fh)340 void AnimateResizeTurn(
341     int x, int y, int w, int h, int fx, int fy, int fw, int fh)
342 {
343     float cx, cy, cw, ch;
344     float xstep, ystep, wstep, hstep;
345     XPoint points[5];
346 
347     float distorty;
348     float distortcw;
349     float midx;
350 
351     float angle, angle_finite;
352 
353     if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh))
354       return;
355 
356     xstep = (float) (fx - x) / Animate.iterations;
357     ystep = (float) (fy - y) / Animate.iterations;
358     wstep = (float) (fw - w) / Animate.iterations;
359     hstep = (float) (fh - h) / Animate.iterations;
360 
361     cx = (float) x;
362     cy = (float) y;
363     cw = (float) w;
364     ch = (float) h;
365 
366     angle_finite = 2 * AS_PI * Animate.twist;
367     MyXGrabServer(dpy);
368     XInstallColormap(dpy, Pcmap);
369     for (angle = 0; ;
370 	 angle += (float) (2 * AS_PI * Animate.twist / Animate.iterations)) {
371 	if (angle > angle_finite)
372 	    angle = angle_finite;
373 
374 	distorty = (ch / 10) - ((ch / 5) * sin(angle));
375 	distortcw = (cw / 2) * cos(angle);
376 	midx = cx + (cw / 2);
377 
378 	points[0].x = midx - distortcw;
379 	points[0].y = cy + distorty;
380 	points[1].x = midx + distortcw;
381 	points[1].y = cy - distorty;
382 	points[2].x = points[1].x;
383 	points[2].y = cy + ch + distorty;
384 	points[3].x = points[0].x;
385 	points[3].y = cy + ch - distorty;
386 	points[4].x = points[0].x;
387 	points[4].y = points[0].y;
388 
389 	XDrawLines(dpy, Scr.root, gc, points, 5, CoordModeOrigin);
390 	XFlush(dpy);
391 	usleep(Animate.delay * 1000);
392 	XDrawLines(dpy, Scr.root, gc, points, 5, CoordModeOrigin);
393 	cx += xstep;
394 	cy += ystep;
395 	cw += wstep;
396 	ch += hstep;
397 	if (angle >= angle_finite)
398 	    break;
399     }
400     MyXUngrabServer(dpy);
401 }
402 
403 /*
404  * This makes a zooming iconify/deiconify animation for a window, like most
405  * any other icon animation out there.  Parameters specify the position and
406  * the size of the initial window and the final window
407  */
AnimateResizeZoom(int x,int y,int w,int h,int fx,int fy,int fw,int fh)408 static void AnimateResizeZoom(int x, int y, int w, int h,
409 			       int fx, int fy, int fw, int fh)
410 {
411     float cx, cy, cw, ch;
412     float xstep, ystep, wstep, hstep;
413     int i;
414 
415     if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh))
416       return;
417 
418     xstep = (float)(fx-x)/Animate.iterations;
419     ystep = (float)(fy-y)/Animate.iterations;
420     wstep = (float)(fw-w)/Animate.iterations;
421     hstep = (float)(fh-h)/Animate.iterations;
422 
423     cx = (float)x;
424     cy = (float)y;
425     cw = (float)w;
426     ch = (float)h;
427     MyXGrabServer(dpy);
428     XInstallColormap(dpy, Pcmap);
429     for (i=0; i<Animate.iterations; i++) {
430 	XDrawRectangle(dpy, Scr.root, gc, (int)cx, (int)cy, (int)cw, (int)ch);
431 	XFlush(dpy);
432 	usleep(Animate.delay*1000);
433 	XDrawRectangle(dpy, Scr.root, gc, (int)cx, (int)cy, (int)cw, (int)ch);
434 	cx+=xstep;
435 	cy+=ystep;
436 	cw+=wstep;
437 	ch+=hstep;
438     }
439     MyXUngrabServer(dpy);
440 }
441 
442 /*
443  * The effect of this is similar to AnimateResizeZoom but this time we
444  * add lines to create a 3D effect.  The gotcha is that we have to do
445  * something different depending on the direction we are zooming in.
446  *
447  * Andy Parker <parker_andy@hotmail.com>
448  */
AnimateResizeZoom3D(int x,int y,int w,int h,int fx,int fy,int fw,int fh)449 void AnimateResizeZoom3D(
450     int x, int y, int w, int h, int fx, int fy, int fw, int fh)
451 {
452     float cx, cy, cw, ch;
453     float xstep, ystep, wstep, hstep, srca, dsta;
454     int i;
455 
456     if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh))
457       return;
458 
459     xstep = (float) (fx - x) / Animate.iterations;
460     ystep = (float) (fy - y) / Animate.iterations;
461     wstep = (float) (fw - w) / Animate.iterations;
462     hstep = (float) (fh - h) / Animate.iterations;
463     dsta = (float) (fw + fh);
464     srca = (float) (w + h);
465 
466     cx = (float) x;
467 
468     cy = (float) y;
469     cw = (float) w;
470     ch = (float) h;
471     MyXGrabServer(dpy);
472     XInstallColormap(dpy, Pcmap);
473 
474     if (dsta <= srca)
475   /* We are going from a Window to an Icon */
476     {
477 	for (i = 0; i < Animate.iterations; i++) {
478 	    XDrawRectangle(dpy, Scr.root, gc, (int) cx, (int) cy, (int) cw,
479 			   (int) ch);
480 	    XDrawRectangle(dpy, Scr.root, gc, (int) fx, (int) fy, (int) fw,
481 			   (int) fh);
482 	    XDrawLine(dpy, Scr.root, gc, (int) cx, (int) cy, fx, fy);
483 	    XDrawLine(dpy, Scr.root, gc, ((int) cx + (int) cw), (int) cy,
484 			    (fx + fw), fy);
485 	    XDrawLine(dpy, Scr.root, gc, ((int) cx + (int) cw),
486 			    ((int) cy + (int) ch), (fx + fw), (fy + fh));
487 	    XDrawLine(dpy, Scr.root, gc, (int) cx, ((int) cy + (int) ch), fx,
488 			    (fy + fh));
489 	    XFlush(dpy);
490 	    usleep(Animate.delay * 1000);
491 	    XDrawRectangle(dpy, Scr.root, gc, (int) cx, (int) cy, (int) cw,
492 			   (int) ch);
493 	    XDrawRectangle(dpy, Scr.root, gc, (int) fx, (int) fy, (int) fw,
494 			   (int) fh);
495 	    XDrawLine(dpy, Scr.root, gc, (int) cx, (int) cy, fx, fy);
496 	    XDrawLine(dpy, Scr.root, gc, ((int) cx + (int) cw), (int) cy,
497 			    (fx + fw), fy);
498 	    XDrawLine(dpy, Scr.root, gc, ((int) cx + (int) cw),
499 			    ((int) cy + (int) ch), (fx + fw), (fy + fh));
500 	    XDrawLine(dpy, Scr.root, gc, (int) cx, ((int) cy + (int) ch), fx,
501 			    (fy + fh));
502 	    cx += xstep;
503 	    cy += ystep;
504 	    cw += wstep;
505 	    ch += hstep;
506 	}
507     }
508     if (dsta > srca) {
509 /* We are going from an Icon to a Window */
510 	for (i = 0; i < Animate.iterations; i++) {
511 	    XDrawRectangle(dpy, Scr.root, gc, (int) cx, (int) cy, (int) cw,
512 			   (int) ch);
513 	    XDrawRectangle(dpy, Scr.root, gc, x, y, w, h);
514 	    XDrawLine(dpy, Scr.root, gc, (int) cx, (int) cy, x, y);
515 	    XDrawLine(dpy, Scr.root, gc, ((int) cx + (int) cw), (int) cy,
516 			    (x + w), y);
517 	    XDrawLine(dpy, Scr.root, gc, ((int) cx + (int) cw), ((int) cy +
518 					    (int) ch), (x + w), (y + h));
519 	    XDrawLine(dpy, Scr.root, gc, (int) cx, ((int) cy + (int) ch), x,
520 			    (y + h));
521 	    XFlush(dpy);
522 	    usleep(Animate.delay * 1000);
523 	    XDrawRectangle(dpy, Scr.root, gc, (int) cx, (int) cy, (int) cw,
524 			   (int) ch);
525 	    XDrawRectangle(dpy, Scr.root, gc, x, y, w, h);
526 	    XDrawLine(dpy, Scr.root, gc, (int) cx, (int) cy, x, y);
527 	    XDrawLine(dpy, Scr.root, gc, ((int) cx + (int) cw), (int) cy,
528 			    (x + w), y);
529 	    XDrawLine(dpy, Scr.root, gc, ((int) cx + (int) cw),
530 			    ((int) cy + (int) ch), (x + w), (y + h));
531 	    XDrawLine(dpy, Scr.root, gc, (int) cx, ((int) cy + (int) ch), x,
532 			    (y + h));
533 	    cx += xstep;
534 	    cy += ystep;
535 	    cw += wstep;
536 	    ch += hstep;
537 	}
538     }
539     MyXUngrabServer(dpy);
540 }
541 
542 /*
543  * This picks one of the animations and calls it.
544  */
AnimateResizeRandom(int x,int y,int w,int h,int fx,int fy,int fw,int fh)545 void AnimateResizeRandom(
546     int x, int y, int w, int h, int fx, int fy, int fw, int fh)
547 {
548   if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh))
549     return;
550 
551   /* Note, first 2 effects "None" and "Random" should never be chosen */
552   effects[(rand() + (x * y + w * h + fx)) % (NUM_EFFECTS - 2) + 2].function
553     (x, y, w, h, fx, fy, fw, fh);
554 }
555 
556 /*
557  * This animation creates 4 lines from each corner of the initial window,
558  * to each corner of the final window.
559  *
560  * Parameters specify the position and the size of the initial window and
561  * the final window.
562  *
563  * Starting with x/y  w/h, need to draw sets  of 4 line segs from initial
564  * to final window.
565  *
566  * The variable "ants" controls whether each  iteration is drawn and then
567  * erased vs. draw all the segments and then come back and erase them.
568  *
569  * Currently I  have this  hardcoded as the  later.   The word  "ants" is
570  * used, because if  "ants" is set  to 0 and the  number of iterations is
571  * high, it looks a little like ants crawling across the screen.
572  */
AnimateResizeLines(int x,int y,int w,int h,int fx,int fy,int fw,int fh)573 static void AnimateResizeLines(int x, int y, int w, int h,
574 			       int fx, int fy, int fw, int fh) {
575   int i, j;
576   int ants = 1, ant_ctr;
577   typedef struct {
578     XSegment seg[4];                   /* draw 4 unconnected lines */
579     XSegment incr[4];                  /* x/y increments */
580   } Endpoints;
581   Endpoints ends[2];
582 
583   if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh))
584     return;
585 
586   /* define the array occurances */
587 #define UR seg[0]
588 #define UL seg[1]
589 #define LR seg[2]
590 #define LL seg[3]
591 #define BEG ends[0]
592 #define INC ends[1]
593 
594   if (ants == 1) {                      /* if draw then erase */
595     MyXGrabServer(dpy);                   /* grab for whole animation */
596     XInstallColormap(dpy, Pcmap);
597   }
598   for (ant_ctr=0;ant_ctr<=ants;ant_ctr++) {
599     /*  Put args into arrays: */
600     BEG.UR.x1 = x;                      /* upper left */
601     BEG.UR.y1 = y;
602     /* Temporarily put width and height in Lower Left slot.
603      Changed to Lower Left x/y later. */
604     BEG.LL.x1 = w;
605     BEG.LL.y1 = h;
606 
607   /* Use final positions to calc increments. */
608     INC.UR.x1 = fx;
609     INC.UR.y1 = fy;
610     INC.LL.x1 = fw;
611     INC.LL.y1 = fh;
612 
613   /* The lines look a little better if they start and end a little in
614      from the edges.  Allowing tuning might be overkill. */
615     for (i=0;i<2;i++) {                   /* for begin and endpoints */
616       if (ends[i].LL.x1 > 40) {           /* if width > 40 */
617 	ends[i].LL.x1 -=16;               /* reduce width a little */
618 	ends[i].UR.x1 += 8;               /* move in a little */
619       }
620       if (ends[i].LL.y1 > 40) {           /* if height > 40 */
621 	ends[i].LL.y1 -=16;               /* reduce height a little */
622 	ends[i].UR.y1 += 8;               /* move down a little */
623       }
624       /* Upper Left, Use x from Upper Right + width */
625       ends[i].UL.x1 = ends[i].UR.x1 + ends[i].LL.x1;
626       ends[i].UL.y1 = ends[i].UR.y1;                /* copy y */
627       /* Lower Right, Use y from Upper Right + height */
628       ends[i].LR.x1 = ends[i].UR.x1;                /* copy x */
629       ends[i].LR.y1 = ends[i].UR.y1 + ends[i].LL.y1;
630       /* Now width and height have been used, change LL to endpoints. */
631       ends[i].LL.x1 += ends[i].UR.x1;
632       ends[i].LL.y1 += ends[i].UR.y1;
633     }
634     /* Now put the increment in the end x/y slots */
635     for (i=0;i<4;i++) {                   /* for each of 4 line segs */
636       INC.seg[i].x2 = (INC.seg[i].x1 - BEG.seg[i].x1)/Animate.iterations;
637       INC.seg[i].y2 = (INC.seg[i].y1 - BEG.seg[i].y1)/Animate.iterations;
638     }
639     for (i=0; i<Animate.iterations; i++) {
640       for (j=0;j<4;j++) {                 /* all 4 line segs */
641 	BEG.seg[j].x2 = BEG.seg[j].x1 + INC.seg[j].x2; /* calc end points */
642 	BEG.seg[j].y2 = BEG.seg[j].y1 + INC.seg[j].y2; /* calc end points */
643       }
644       myaprintf((stderr,
645 		 "Lines %dx%d-%dx%d, %dx%d-%dx%d, %dx%d-%dx%d, %dx%d-%dx%d,"
646 		 "ant_ctr %d\n",
647 		 BEG.UR.x1, BEG.UR.y1, BEG.UR.x2, BEG.UR.y2,
648 		 BEG.UL.x1, BEG.UL.y1, BEG.UL.x2, BEG.UL.y2,
649 		 BEG.LR.x1, BEG.LR.y1, BEG.LR.x2, BEG.LR.y2,
650 		 BEG.LL.x1, BEG.LL.y1, BEG.LL.x2, BEG.LL.y2, ant_ctr));
651       if (ants==0) {
652 	MyXGrabServer(dpy);
653 	XInstallColormap(dpy, Pcmap);
654       }
655       XDrawSegments(dpy, Scr.root, gc, BEG.seg, 4);
656       XFlush(dpy);
657       if (ant_ctr == 0) {               /* only pause on draw cycle */
658 	usleep(Animate.delay*1000);
659       }
660       if (ants==0) {
661 	XDrawSegments(dpy, Scr.root, gc, BEG.seg, 4);
662 	MyXUngrabServer(dpy);
663       }
664       for (j=0;j<4;j++) {                 /* all 4 lines segs */
665 	BEG.seg[j].x1 += INC.seg[j].x2;   /* calc new starting point */
666 	BEG.seg[j].y1 += INC.seg[j].y2;   /* calc new starting point */
667       } /* end 4 lines segs */
668     } /* end iterations */
669   } /* end for ants */
670   if (ants == 1) {                      /* if draw then erase */
671     MyXUngrabServer(dpy);                 /* end grab for whole animation */
672     myaprintf((stderr,"Did ungrab\n"));
673   }
674   XFlush(dpy);
675   myaprintf((stderr,"Done animating\n"));
676 }
677 
678 /*
679  * Animate None  is set on during  configuration.  When set on, it causes
680  * this  module to exit  after some  number of  animation events.  (If it
681  * just exited immediately, you couldn't use  this module to turn it back
682  * on.)
683  */
AnimateResizeNone(int x,int y,int w,int h,int fx,int fy,int fw,int fh)684 static void AnimateResizeNone(
685     int x, int y, int w, int h, int fx, int fy, int fw, int fh)
686 {
687   (void)x;
688   (void)y;
689   (void)w;
690   (void)h;
691   (void)fx;
692   (void)fy;
693   (void)fw;
694   (void)fh;
695 
696   if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh))
697     return;
698 
699   ++animate_none;
700   return;
701 }
702 #if 0
703 /*
704  * This makes a animation that looks like that light effect
705  * when you turn off an old TV.
706  * Used for window destruction
707  *
708  * This was commented out in Afterstep, I don't know why yet.  dje.
709  */
710 static void AnimateClose(int x, int y, int w, int h)
711 {
712     int i, step;
713 
714     if (!is_animation_visible(x, y, w, h, fx, fy, fw, fh)
715       return;
716 
717     if (h>4) {
718 	step = h*4/Animate.iterations;
719 	if (step==0) {
720 	    step = 2;
721 	}
722 	for (i=h; i>=2; i-=step) {
723 	    XDrawRectangle(dpy, Scr.root, gc, x, y, w, i);
724 	    XFlush(dpy);
725 	    usleep(ANIM_DELAY2*600);
726 	    XDrawRectangle(dpy, Scr.root, gc, x, y, w, i);
727 	    y+=step/2;
728 	}
729     }
730     if (w<2) return;
731     step = w*4/Animate.iterations;
732     if (step==0) {
733 	step = 2;
734     }
735     for (i=w; i>=0; i-=step) {
736 	XDrawRectangle(dpy, Scr.root, gc, x, y, i, 2);
737 	XFlush(dpy);
738 	usleep(ANIM_DELAY2*1000);
739 	XDrawRectangle(dpy, Scr.root, gc, x, y, i, 2);
740 	x+=step/2;
741     }
742     usleep(100000);
743     XFlush(dpy);
744 }
745 #endif
746 
747 void
DeadPipe(int arg)748 DeadPipe(int arg) {
749   myfprintf((stderr,"Dead Pipe, arg %d\n",arg));
750   exit(0);
751   SIGNAL_RETURN;
752 }
753 
754 
755 static RETSIGTYPE
HandleTerminate(int sig)756 HandleTerminate(int sig) {
757   fvwmSetTerminate(sig);
758   SIGNAL_RETURN;
759 }
760 
761 
main(int argc,char ** argv)762 int main(int argc, char **argv) {
763   char cmd[200];                        /* really big area for a command */
764 
765   /* The new arg parsing function replaces the "manual" parsing */
766   module = ParseModuleArgs(argc,argv,1);
767   if (module==NULL)
768   {
769     fprintf(stderr,"FvwmAnimate Version "VERSION" should only be executed by fvwm!\n");
770     exit(1);
771   }
772 
773   xasprintf(&mname, "*%s", module->name);
774 
775 #ifdef HAVE_SIGACTION
776   {
777     struct sigaction  sigact;
778 
779     sigemptyset(&sigact.sa_mask);
780     sigaddset(&sigact.sa_mask, SIGTERM);
781     sigaddset(&sigact.sa_mask, SIGINT);
782     sigaddset(&sigact.sa_mask, SIGPIPE);
783 #ifdef SA_INTERRUPT
784     sigact.sa_flags = SA_INTERRUPT; /* to interrupt ReadFvwmPacket() */
785 #else
786     sigact.sa_flags = 0;
787 #endif
788     sigact.sa_handler = HandleTerminate;
789 
790     sigaction(SIGTERM, &sigact, NULL);
791     sigaction(SIGINT,  &sigact, NULL);
792     sigaction(SIGPIPE, &sigact, NULL);
793   }
794 #else
795 #ifdef USE_BSD_SIGNALS
796   fvwmSetSignalMask( sigmask(SIGTERM) |
797 		     sigmask(SIGINT) |
798 		     sigmask(SIGPIPE) );
799 #endif
800   signal(SIGTERM, HandleTerminate);
801   signal(SIGINT,  HandleTerminate);
802   signal(SIGPIPE, HandleTerminate);     /* Dead pipe == fvwm died */
803 #ifdef HAVE_SIGINTERRUPT
804   siginterrupt(SIGTERM, True);
805   siginterrupt(SIGINT,  True);
806   siginterrupt(SIGPIPE, True);
807 #endif
808 #endif
809 
810   Channel[0] = module->to_fvwm;
811   Channel[1] = module->from_fvwm;
812 
813   dpy = XOpenDisplay("");
814   if (dpy==NULL) {
815     fprintf(stderr,"%s: can not open display\n",module->name);
816     exit(1);
817   }
818   /* FvwmAnimate must use the root visuals so do not call
819    * PictureInitCMap but PictureInitCMapRoot. Color Limit is not needed */
820   PictureInitCMapRoot(dpy, False, NULL, False, False);
821   FRenderInit(dpy);
822   Scr.root = DefaultRootWindow(dpy);
823   Scr.screen = DefaultScreen(dpy);
824 
825   sprintf(cmd,"read .%s Quiet",module->name); /* read quiet modules config */
826   SendText(Channel,cmd,0);
827   ParseOptions();                       /* get cmds fvwm has parsed */
828 
829   SetMessageMask(Channel, M_ICONIFY|M_DEICONIFY|M_STRING|M_SENDCONFIG
830 		 |M_CONFIG_INFO);        /* tell fvwm about our mask */
831   CreateDrawGC();                       /* create initial GC if necc. */
832   SendText(Channel,"Nop",0);
833   DefineMe();
834   running = True;                       /* out of initialization phase */
835   SendFinishedStartupNotification(Channel); /* tell fvwm we're running */
836   SetSyncMask(Channel,M_ICONIFY|M_DEICONIFY|M_STRING); /* lock on send mask */
837   SetNoGrabMask(Channel,M_ICONIFY|M_DEICONIFY|M_STRING); /* ignore during recapture */
838   Loop();                               /* start running */
839   return 0;
840 }
841 
842 /*
843  * Wait for some event like iconify, deiconify and stuff.
844  */
Loop(void)845 static void Loop(void) {
846   FvwmPacket* packet;
847   char cmd[200];
848 
849   myfprintf((stderr,"Starting event loop\n"));
850   while ( !isTerminated ) {
851     if ( (packet = ReadFvwmPacket(Channel[1])) == NULL )
852 	break;                    /* fvwm is gone */
853 
854       switch (packet->type) {
855       case M_NEW_PAGE:
856 	  Scr.Vx = packet->body[0];
857 	  Scr.Vy = packet->body[1];
858 	  Scr.CurrentDesk = packet->body[2];
859 	  break;
860       case M_NEW_DESK:
861 	  Scr.CurrentDesk = packet->body[0];
862 	  break;
863       case M_DEICONIFY:
864 	if (play_state == False)
865 	{
866 		break;
867 	}
868 	if (packet->size < 15            /* If not all info needed, */
869 	    || packet->body[5] == 0) {   /* or a "noicon" icon */
870 	  break;                      /* don't animate it */
871 	}
872 	Animate.resize((int)packet->body[3],     /* t->icon_x_loc */
873 		       (int)packet->body[4],     /* t->icon_y_loc */
874 		       (int)packet->body[5],     /* t->icon_p_width */
875 		       (int)packet->body[6],     /* t->icon_p_height */
876 		       (int)packet->body[7],     /* t->frame_x */
877 		       (int)packet->body[8],     /* t->frame_y */
878 		       (int)packet->body[9],     /* t->frame_width */
879 		       (int)packet->body[10]);   /* t->frame_height */
880 	myaprintf((stderr,
881 		   "DE_Iconify, args %d+%d+%dx%d %d+%d+%dx%d.\n",
882 		   (int)packet->body[3],     /* t->icon_x_loc */
883 		   (int)packet->body[4],     /* t->icon_y_loc */
884 		   (int)packet->body[5],     /* t->icon_p_width */
885 		   (int)packet->body[6],     /* t->icon_p_height */
886 		   (int)packet->body[7],     /* t->frame_x */
887 		   (int)packet->body[8],     /* t->frame_y */
888 		   (int)packet->body[9],     /* t->frame_width */
889 		   (int)packet->body[10]     /* t->frame_height */
890 	));
891 	break;
892       case M_ICONIFY:
893 	if (play_state == False)
894 	{
895 		break;
896 	}
897 	/* In Afterstep, this logic waited for M_CONFIGURE_WINDOW
898 	     before animating.  To this time, I don't know why.
899 	     (One is sent right after the other.)
900 	  */
901 	if (packet->size < 15            /* if not enough info */
902 	    || (int)packet->body[3] == -10000    /* or a transient window */
903 	    || (int)packet->body[5] == 0) {   /* or a "noicon" icon */
904 	  break;                    /* don't animate it */
905 	}
906 	Animate.resize((int)packet->body[7],     /* t->frame_x */
907 		       (int)packet->body[8],     /* t->frame_y */
908 		       (int)packet->body[9],     /* t->frame_width */
909 		       (int)packet->body[10],    /* t->frame_height */
910 		       (int)packet->body[3],     /* t->icon_x_loc */
911 		       (int)packet->body[4],     /* t->icon_y_loc */
912 		       (int)packet->body[5],     /* t->icon_p_width */
913 		       (int)packet->body[6]);    /* t->icon_p_height */
914 	myaprintf((stderr,
915 		   "Iconify, args %d+%d+%dx%d %d+%d+%dx%d. Took %d\n",
916 		   (int)packet->body[7],     /* t->frame_x */
917 		   (int)packet->body[8],     /* t->frame_y */
918 		   (int)packet->body[9],     /* t->frame_width */
919 		   (int)packet->body[10],    /* t->frame_height */
920 		   (int)packet->body[3],     /* t->icon_x_loc */
921 		   (int)packet->body[4],     /* t->icon_y_loc */
922 		   (int)packet->body[5],     /* t->icon_p_width */
923 		   (int)packet->body[6]
924 	));
925 	break;
926       case M_STRING:
927 	{
928 		char *token;
929 		char *line = (char *)&packet->body[3];
930 
931 		line = GetNextToken(line, &token);
932 		if (!token)
933 		{
934 			break;
935 		}
936 
937 		if (strcasecmp(token, "animate") == 0)
938 		{
939 			/* This message lets anything create an animation. Eg:
940 			   SendToModule FvwmAnimate \
941 			     animate 1 1 10 10 100 100 100 100
942 			 */
943 			int locs[8];
944 			int matched;
945 			matched = sscanf(
946 				line, "%5d %5d %5d %5d %5d %5d %5d %5d",
947 				&locs[0], &locs[1], &locs[2], &locs[3],
948 				&locs[4], &locs[5], &locs[6], &locs[7]);
949 			if (matched == 8 && play_state == True)
950 			{
951 				Animate.resize(
952 					locs[0], locs[1], locs[2], locs[3],
953 					locs[4], locs[5], locs[6], locs[7]);
954 				myaprintf((stderr,
955 					"animate %d+%d+%dx%d %d+%d+%dx%d\n",
956 					locs[0], locs[1], locs[2], locs[3],
957 					locs[4], locs[5], locs[6], locs[7]));
958 			}
959 			free(token);
960 			break;
961 		}
962 		while (token)
963 		{
964 			if (strcasecmp(token, "reset") == 0)
965 			{
966 				play_state = True;
967 				num_saved_play_states = 0;
968 			}
969 			else if (strcasecmp(token, "play") == 0)
970 			{
971 				play_state = True;
972 			}
973 			else if (strcasecmp(token, "pause") == 0)
974 			{
975 				play_state = False;
976 			}
977 			else if (strcasecmp(token, "push") == 0)
978 			{
979 				if (num_saved_play_states < MAX_SAVED_STATES)
980 				{
981 					saved_play_states[num_saved_play_states]
982 						= play_state;
983 				}
984 				else
985 				{
986 					fprintf(
987 						stderr,	"FvwmAnimate: Too many "
988 						"nested push commands;\n\tthe "
989 						"current state can not be "
990 						"restored later using pop\n");
991 				}
992 				num_saved_play_states++;
993 			}
994 			else if (strcasecmp(token, "pop") == 0)
995 			{
996 				num_saved_play_states--;
997 				if (num_saved_play_states < MAX_SAVED_STATES)
998 				{
999 					play_state = saved_play_states[
1000 						num_saved_play_states];
1001 				}
1002 			}
1003 			free(token);
1004 			line = GetNextToken(line, &token);
1005 		}
1006 		break;
1007 	}
1008       case M_CONFIG_INFO:
1009 	myfprintf((stderr,"Got command: %s\n", (char *)&packet->body[3]));
1010 	ParseConfigLine((char *)&packet->body[3]);
1011 	break;
1012       } /* end switch header */
1013 
1014     myfprintf((stderr,"Sending unlock\n"));
1015     if (packet->type != M_CONFIG_INFO)
1016       SendUnlockNotification(Channel);  /* fvwm can continue now! */
1017     if ((Animate.resize == AnimateResizeNone  /* If no animation desired */
1018 	&& animate_none >= 1)           /* and 1 animation(s) */
1019 	|| stop_recvd) {                /* or stop cmd */
1020       /* This still isn't perfect, if the user turns off animation,
1021 	 they would expect the menu to change on the spot.
1022 	 On the otherhand, the menu shouldn't change if the module is
1023 	 still running.
1024 	 This logic is dependent on fvwm sending iconify messages
1025 	 to make this module exit.  Currently it is sending messages
1026 	 even when "Style NoIcon" is on for everything.
1027       */
1028       StopCmd();                        /* update menu */
1029       myfprintf((stderr,"Exiting, animate none count %d, stop recvd %c\n",
1030 		 animate_none, stop_recvd ? 'y' : 'n'));
1031       break;                            /* and stop */
1032     } /* end stopping */
1033     /* The execution of the custom command has to be delayed,
1034        because we first had to send the UNLOCK response.
1035     */
1036     if (custom_recvd) {
1037       custom_recvd = False;
1038       DefineForm();
1039       CMD1X("Module FvwmForm Form%s");
1040     }
1041   } /* end while */
1042 }
1043 
1044 /*
1045  *
1046  * This routine is responsible for reading and parsing the config file
1047  * Used FvwmEvent as a model.
1048  *
1049  */
1050 
1051 static const char *table[]= {
1052   "Color",
1053 #define Color_arg 0
1054   "Custom",
1055 #define Custom_arg 1
1056   "Delay",
1057 #define Delay_arg 2
1058   "Effect",
1059 #define Effect_arg 3
1060   "Iterations",
1061 #define Iterations_arg 4
1062   "Pixmap",
1063 #define Pixmap_arg 5
1064   "Resize",
1065 #define Resize_arg 6
1066   "Save",
1067 #define Save_arg 7
1068   "Stop",
1069 #define Stop_arg 8
1070   "Time",
1071 #define Time_arg 9
1072   "Twist",
1073 #define Twist_arg 10
1074   "Width"
1075 #define Width_arg 11
1076 };      /* Keep list in alphabetic order, using binary search! */
ParseOptions(void)1077 static void ParseOptions(void) {
1078   char *buf;
1079 
1080   Scr.MyDisplayWidth = DisplayWidth(dpy, Scr.screen);
1081   Scr.MyDisplayHeight = DisplayHeight(dpy, Scr.screen);
1082   Scr.Vx = 0;
1083   Scr.Vy = 0;
1084   Scr.CurrentDesk = 0;
1085 
1086   myfprintf((stderr,"Reading options\n"));
1087   InitGetConfigLine(Channel, mname);
1088   while (GetConfigLine(Channel,&buf), buf != NULL) {
1089     ParseConfigLine(buf);
1090   } /* end config lines */
1091 } /* end function */
1092 
1093 
ParseConfigLine(char * buf)1094 void ParseConfigLine(char *buf) {
1095   char **e, *p, *q;
1096   unsigned seed;
1097   long curtime;
1098   unsigned i;
1099 
1100   if (buf[strlen(buf)-1] == '\n') {     /* if line ends with newline */
1101     buf[strlen(buf)-1] = '\0';  /* strip off \n */
1102   }
1103   /* capture the ImagePath setting, don't worry about ColorLimit */
1104   if (strncasecmp(buf, "ImagePath",9)==0) {
1105     PictureSetImagePath(&buf[9]);
1106   }
1107   /* Search for MyName (normally *FvwmAnimate) */
1108   else if (strncasecmp(buf, mname, module->namelen+1) == 0) {/* If its for me */
1109     myfprintf((stderr,"Found line for me: %s\n", buf));
1110     p = buf+module->namelen+1;              /* starting point */
1111     q = NULL;
1112     if ((e = FindToken(p,table,char *))) { /* config option ? */
1113       if ((strcasecmp(*e,"Stop") != 0)
1114 	  && (strcasecmp(*e,"Custom") != 0)
1115 	  && (strcasecmp(*e,"Save") != 0)) { /* no arg commands */
1116 	p+=strlen(*e);          /* skip matched token */
1117 	p = GetNextSimpleOption( p, &q );
1118 	if (!q) {                       /* If arg not found */
1119 	  fprintf(stderr,"%s: %s%s needs a parameter\n",
1120 		  module->name, module->name,*e);
1121 	  return;
1122 	}
1123       }
1124 
1125       switch (e - (char**)table) {
1126       case Stop_arg:                    /* Stop command */
1127 	if (running) {                  /* if not a stored command */
1128 	  stop_recvd = True;            /* remember to stop */
1129 	}
1130 	break;
1131       case Save_arg:                    /* Save command */
1132 	SaveConfig();
1133 	break;
1134       case Custom_arg:                  /* Custom command */
1135 	if (running) {                  /* if not a stored command */
1136 	  custom_recvd = True;          /* remember someone asked */
1137 	}
1138 	break;
1139       case Color_arg:                   /* Color */
1140 	if (Animate.color) {
1141 	  free(Animate.color);          /* release storage holding color name */
1142 	  Animate.color = 0;            /* show its gone */
1143 	}
1144 	if ((strcasecmp(q,"None") != 0) /* If not color "none"  */
1145 	    && (strcasecmp(q,"Black^White") != 0)
1146 	    && (strcasecmp(q,"White^Black") != 0)) {
1147 	  Animate.color = fxstrdup(q); /* make copy of name */
1148 	}
1149 	/* override the pixmap option */
1150 	if (Animate.pixmap) {
1151 	  free(Animate.pixmap);
1152 	  Animate.pixmap = 0;
1153 	}
1154 	if (pixmap) {
1155 	  XFreePixmap(dpy, pixmap);
1156 	  pixmap = None;
1157 	}
1158 	CreateDrawGC();                /* update GC */
1159 	break;
1160       case Pixmap_arg:
1161 	if (Animate.pixmap) {
1162 	  free(Animate.pixmap);        /* release storage holding pixmap name */
1163 	  Animate.pixmap = 0;          /* show its gone */
1164 	}
1165 	if (strcasecmp(q,"None") != 0) { /* If not pixmap "none"  */
1166 	  Animate.pixmap = fxstrdup(q); /* make copy of name */
1167 	}
1168 	if (pixmap) {
1169 	  XFreePixmap(dpy, pixmap);
1170 	  pixmap = None;
1171 	}
1172 	CreateDrawGC();                 /* update GC */
1173 	break;
1174       case Delay_arg:                   /* Delay */
1175 	Animate.delay = atoi(q);
1176 	break;
1177       case Iterations_arg:              /* Iterations */
1178 	Animate.iterations = atoi(q);
1179 	/* Silently fix up iterations less than 1. */
1180 	if (Animate.iterations <= 0) {
1181 	  Animate.iterations = 1;
1182 	}
1183 	break;
1184       case Effect_arg:                /* Effect */
1185       case Resize_arg:                /* -or - Resize */
1186 	for (i=0; i < NUM_EFFECTS; i++) {
1187 	  if (strcasecmp(q, effects[i].name)==0
1188 	      || (effects[i].alias
1189 		  && strcasecmp(q, effects[i].alias)==0)) {
1190 	    Animate.resize = effects[i].function;
1191 	    break;
1192 	  } /* end match */
1193 	} /* end all effects */
1194 	if (i > NUM_EFFECTS) {          /* If not found */
1195 	  fprintf(stderr, "%s: Unknown Effect '%s'\n", module->name, q);
1196 	}
1197 	/* Logically, you would only reset these when you got a command
1198 	   of None, or Random, but it doesn't really matter. */
1199 	animate_none = 0;
1200 	curtime = time(NULL);
1201 	seed = (unsigned) curtime % INT_MAX;
1202 	srand(seed);
1203 	break;
1204       case Time_arg:                  /* Time in milliseconds */
1205 	Animate.time = (clock_t)atoi(q);
1206 	break;
1207       case Twist_arg:                 /* Twist */
1208 	Animate.twist = atof(q);
1209 	break;
1210       case Width_arg:                 /* Width */
1211 	Animate.width = atoi(q);
1212 	/* Silently fix up width less than 0. */
1213 	if (Animate.width < 0) {
1214 	  Animate.width = 0;
1215 	}
1216 	CreateDrawGC();                 /* update GC */
1217 	break;
1218       default:
1219 	fprintf(stderr,"%s: unknown action %s\n",module->name,*e);
1220 	break;
1221       }
1222     } else {                        /* Match Myname, but a space */
1223       fprintf(stderr,"%s: unknown command: %s\n",module->name,buf);
1224     }
1225     if(q) {                             /* if parsed an arg */
1226       free(q);                          /* free its memory */
1227     }
1228   } /* config line for me */
1229 } /* end function */
1230 
1231 /* create GC for drawing the various animations */
1232 /* Called during start-up, and whenever the color or line width changes. */
CreateDrawGC(void)1233 static void CreateDrawGC(void) {
1234 
1235 	myfprintf((stderr,"Creating GC\n"));
1236 	if (gc != NULL)
1237 	{
1238 		XFreeGC(dpy,gc);                /* free old GC */
1239 	}
1240 	/* From builtins.c: */
1241 	color = (PictureBlackPixel() ^ PictureWhitePixel());
1242 	pixmap = None;
1243 	gcv.function = GXxor;                 /* default is to xor the lines */
1244 	if (Animate.pixmap)
1245 	{       /* if pixmap called for */
1246 		FvwmPicture *picture;
1247 		FvwmPictureAttributes fpa;
1248 
1249 		fpa.mask = FPAM_NO_COLOR_LIMIT;
1250 		picture = PGetFvwmPicture(
1251 			dpy, RootWindow(dpy,Scr.screen), 0, Animate.pixmap, fpa);
1252 		if (!picture)
1253 			fprintf(stderr, "%s: Could not load pixmap '%s'\n",
1254 				module->name, Animate.pixmap);
1255 		else {
1256 			pixmap = XCreatePixmap(
1257 				dpy, RootWindow(dpy, Scr.screen), picture->width,
1258 				picture->height, Pdepth);
1259 			PGraphicsCopyPixmaps(
1260 				dpy, picture->picture, None, None,
1261 				picture->depth, pixmap, None,
1262 				0, 0, picture->width, picture->height, 0, 0);
1263 			PDestroyFvwmPicture(dpy, picture);
1264 		}
1265 	}
1266 	else if (Animate.color)
1267 	{       /* if color called for */
1268 		if (XParseColor(dpy, Pcmap,Animate.color, &xcol))
1269 		{
1270 			if (PictureAllocColor(dpy, Pcmap, &xcol, True))
1271 			{
1272 				color = xcol.pixel;
1273 				/* free it now, only interested in the pixel */
1274 				PictureFreeColors(
1275 					dpy, Pcmap, &xcol.pixel, 1, 0, True);
1276                                 /* gcv.function=GXequiv;  Afterstep used this. */
1277 			}
1278 			else
1279 			{
1280 				fprintf(stderr,
1281 					"%s: could not allocate color '%s'\n",
1282 					module->name,Animate.color);
1283 			}
1284 		}
1285 		else
1286 		{
1287 			fprintf(stderr,"%s: could not parse color '%s'\n",
1288 				module->name,Animate.color);
1289 		}
1290 	}
1291 	gcv.line_width = Animate.width;
1292 	gcv.foreground = color;
1293 	myfprintf((stderr,"Color is %ld\n",gcv.foreground));
1294 	gcv.subwindow_mode = IncludeInferiors;
1295 	if (pixmap)
1296 	{
1297 		gcv.tile = pixmap;
1298 		gcv.fill_style = FillTiled;
1299 	}
1300 	gc=fvwmlib_XCreateGC(
1301 		dpy, Scr.root, GCFunction | GCForeground | GCLineWidth
1302 		| GCSubwindowMode | (pixmap ? GCFillStyle | GCTile : 0), &gcv);
1303 }
1304 
1305 /*
1306  * Send commands to fvwm to define this modules menus.
1307  *
1308  * When I first wrote this, I thought it might be a good idea to call the
1309  * menu  "FvwmAnimate", just like  the  module name.   To my surprise,  I
1310  * found that fvwm treats menus just like functions.  In fact I could no
1311  * longer start FvwmAnimate  because it kept  finding the menu instead of
1312  * the  function.   This  probably should   be fixed, but  for  now,  the
1313  * generated menu is  called  "MenuFvwmAnimate", or  "Menu<ModuleAlias>".
1314  * dje, 10/11/98.
1315  */
DefineMe(void)1316 static void DefineMe(void) {
1317   char cmd[200];                        /* really big area for a command */
1318   myfprintf((stderr,"defining menu\n"));
1319 
1320   CMD1X("DestroyMenu Menu%s");
1321   CMD1X("DestroyMenu MenuIterations%s");
1322   CMD1X("DestroyMenu MenuEffects%s");
1323   CMD1X("DestroyMenu MenuWidth%s");
1324   CMD1X("DestroyMenu MenuTwist%s");
1325   CMD1X("DestroyMenu MenuDelay%s");
1326   CMD1X("DestroyMenu MenuColor%s");
1327 
1328   CMD1X("AddToMenu Menu%s \"Animation Main Menu\" Title");
1329   CMD11("AddToMenu Menu%s \"&E. Effects\" Popup MenuEffects%s");
1330   CMD11("AddToMenu Menu%s \"&I. Iterations\" Popup MenuIterations%s");
1331   CMD11("AddToMenu Menu%s \"&T. Twists\" Popup MenuTwist%s");
1332   CMD11("AddToMenu Menu%s \"&L. Line Width\" Popup MenuWidth%s");
1333   CMD11("AddToMenu Menu%s \"&D. Delays\" Popup MenuDelay%s");
1334   CMD11("AddToMenu Menu%s \"&X. Color for XOR\" Popup MenuColor%s");
1335   CMD10("AddToMenu Menu%s \"&C. Custom Settings\" %sCustom");
1336   CMD10("AddToMenu Menu%s \"&S. Save Config\" %sSave");
1337   CMD10("AddToMenu Menu%s \"&Z. Stop Animation\" %sStop");
1338   CMD11("AddToMenu Menu%s \"&R. Restart Animation\" FuncRestart%s");
1339 
1340   /* Define function for stopping and restarting Animation. */
1341   CMD1X("DestroyFunc FuncRestart%s");
1342   CMD10("AddToFunc FuncRestart%s \"I\" %sStop");
1343   CMD11("AddToFunc FuncRestart%s \"I\" Module FvwmAnimate %s");
1344 
1345   /* Define the sub menus. */
1346   CMD1X("AddToMenu MenuIterations%s \"Iterations\" Title");
1347   CMD10("AddToMenu MenuIterations%s \"&1. Iterations 1\" %sIterations 1");
1348   CMD10("AddToMenu MenuIterations%s \"&2. Iterations 2\" %sIterations 2");
1349   CMD10("AddToMenu MenuIterations%s \"&3. Iterations 4\" %sIterations 4");
1350   CMD10("AddToMenu MenuIterations%s \"&4. Iterations 8\" %sIterations 8");
1351   CMD10("AddToMenu MenuIterations%s \"&5. Iterations 16\" %sIterations 16");
1352   CMD10("AddToMenu MenuIterations%s \"&6. Iterations 32\" %sIterations 32");
1353 
1354   CMD1X("AddToMenu MenuWidth%s \"Line Width\" Title");
1355   CMD10("AddToMenu MenuWidth%s \"&1. Line Width 0 (fast)\" %sWidth 0");
1356   CMD10("AddToMenu MenuWidth%s \"&2. Line Width 1\" %sWidth 1");
1357   CMD10("AddToMenu MenuWidth%s \"&3. Line Width 2\" %sWidth 2");
1358   CMD10("AddToMenu MenuWidth%s \"&4. Line Width 4\" %sWidth 4");
1359   CMD10("AddToMenu MenuWidth%s \"&5. Line Width 6\" %sWidth 6");
1360   CMD10("AddToMenu MenuWidth%s \"&6. Line Width 8\" %sWidth 8");
1361 
1362   CMD1X("AddToMenu MenuTwist%s \"Twists (Twist, Turn, Flip only)\" Title");
1363   CMD10("AddToMenu MenuTwist%s \"&1. Twist .25\" %sTwist .25");
1364   CMD10("AddToMenu MenuTwist%s \"&2. Twist .50\" %sTwist .50");
1365   CMD10("AddToMenu MenuTwist%s \"&3. Twist 1\" %sTwist 1");
1366   CMD10("AddToMenu MenuTwist%s \"&4. Twist 2\" %sTwist 2");
1367 
1368   CMD1X("AddToMenu MenuDelay%s \"Delays\" Title");
1369   CMD10("AddToMenu MenuDelay%s \"&1. Delay 1/1000 sec\" %sDelay 1");
1370   CMD10("AddToMenu MenuDelay%s \"&2. Delay 1/100 sec\" %sDelay 10");
1371   CMD10("AddToMenu MenuDelay%s \"&3. Delay 1/10 sec\" %sDelay 100");
1372 
1373   /* Same as the colors at the front of the colorlimiting table */
1374   CMD1X("AddToMenu MenuColor%s \"Colors\" Title");
1375   CMD10("AddToMenu MenuColor%s \"&1. Color Black^White\" %sColor None");
1376   CMD10("AddToMenu MenuColor%s \"&2. Color White\" %sColor white");
1377   CMD10("AddToMenu MenuColor%s \"&3. Color Black\" %sColor black");
1378   CMD10("AddToMenu MenuColor%s \"&4. Color Grey\" %sColor grey");
1379   CMD10("AddToMenu MenuColor%s \"&5. Color Green\" %sColor green");
1380   CMD10("AddToMenu MenuColor%s \"&6. Color Blue\" %sColor blue");
1381   CMD10("AddToMenu MenuColor%s \"&7. Color Red\" %sColor red");
1382   CMD10("AddToMenu MenuColor%s \"&8. Color Cyan\" %sColor cyan");
1383   CMD10("AddToMenu MenuColor%s \"&9. Color Yellow\" %sColor yellow");
1384   CMD10("AddToMenu MenuColor%s \"&A. Color Magenta\" %sColor magenta");
1385   CMD10("AddToMenu MenuColor%s \"&B. Color DodgerBlue\" %sColor DodgerBlue");
1386   CMD10("AddToMenu MenuColor%s \"&C. Color SteelBlue\" %sColor SteelBlue");
1387   CMD10("AddToMenu MenuColor%s \"&D. Color Chartreuse\" %sColor chartreuse");
1388   CMD10("AddToMenu MenuColor%s \"&E. Color Wheat\" %sColor wheat");
1389   CMD10("AddToMenu MenuColor%s \"&F. Color Turquoise\" %sColor turquoise");
1390 
1391   CMD1X("AddToMenu MenuEffects%s \"Effects\" Title");
1392   CMD10("AddToMenu MenuEffects%s \"&1. Effect Random\" %sEffect Random");
1393   CMD10("AddToMenu MenuEffects%s \"&2. Effect Flip\" %sEffect Flip");
1394   CMD10("AddToMenu MenuEffects%s \"&3. Effect Frame\" %sEffect Frame");
1395   CMD10("AddToMenu MenuEffects%s \"&4. Effect Frame3D\" %sEffect Frame3D");
1396   CMD10("AddToMenu MenuEffects%s \"&5. Effect Lines\" %sEffect Lines");
1397   CMD10("AddToMenu MenuEffects%s \"&6. Effect Turn\" %sEffect Turn");
1398   CMD10("AddToMenu MenuEffects%s \"&7. Effect Twist\" %sEffect Twist");
1399   CMD10("AddToMenu MenuEffects%s \"&N. Effect None\" %sEffect None");
1400 
1401   /* Still to be done:
1402      Use of FvwmForms for Help.  (Need to fix line spacing in FvwmForms first).
1403   */
1404 }
1405 
1406 /* Write the current config into a file. */
SaveConfig(void)1407 static void SaveConfig(void) {
1408   FILE *config_file;
1409   unsigned i;
1410   char filename[100];                   /* more than enough room */
1411   char msg[200];                        /* even more room for msg */
1412   /* Need to use logic to create fully qualified file name same as in
1413      read.c, right now, this logic only works well if fvwm is started
1414      from the users home directory.
1415   */
1416   sprintf(filename,"%s/.%s",getenv("FVWM_USERDIR"),module->name);
1417   config_file = fopen(filename,"w");
1418   if (config_file == NULL) {
1419     sprintf(msg,
1420 	    "%s: Open config file <%s> for write failed. \
1421 Save not done! Error\n",
1422 	    module->name, filename);
1423     perror(msg);
1424     return;
1425   }
1426   fprintf(config_file,"# This file was created by %s\n\n",module->name);
1427   for (i=0; i < NUM_EFFECTS; i++) {
1428     if (effects[i].function == Animate.resize) {
1429       fprintf(config_file,"*%s: Effect %s\n",module->name,effects[i].name);
1430       break;
1431     } /* found match */
1432   } /* all possible effects */
1433   fprintf(config_file,"*%s: Iterations %d\n",module->name,Animate.iterations);
1434   fprintf(config_file,"*%s: Width %d\n",module->name,Animate.width);
1435   fprintf(config_file,"*%s: Twist %f\n",module->name,Animate.twist);
1436   fprintf(config_file,"*%s: Delay %d\n",module->name,Animate.delay);
1437   if (Animate.color) {
1438     fprintf(config_file,"*%s: Color %s\n",module->name,Animate.color);
1439   }
1440   /* Note, add "time" if that ever works. dje. 10/14/98 */
1441   fclose(config_file);
1442 }
1443 
1444 /* Stop is different than KillModule in that it gives this module a chance to
1445    alter the builtin menu before it exits.
1446 */
StopCmd(void)1447 static void StopCmd(void) {
1448   char cmd[200];
1449   myfprintf((stderr,"%s: Defining startup menu in preparation for stop\n",
1450 	     module->name));
1451   CMD1X("DestroyMenu Menu%s");
1452   CMD11("AddToMenu Menu%s \"%s\" Title");
1453   CMD11("AddToMenu Menu%s \"&0. Start FvwmAnimate\" Module %s");
1454 }
1455 
DefineForm(void)1456 static void DefineForm(void) {
1457   unsigned i;
1458   char cmd[200];
1459   myfprintf((stderr,"Defining form Form%s\n", module->name));
1460   CMD1X("DestroyModuleConfig Form%s*");
1461   CMD1X("*Form%sWarpPointer");
1462   CMD1X("*Form%sLine         center");
1463   CMD11("*Form%sText         \"Custom settings for %s\"");
1464   CMD1X("*Form%sLine         left");
1465   CMD1X("*Form%sText         \"\"");
1466   CMD1X("*Form%sLine         left");
1467   CMD1X("*Form%sText         \"Effect:\"");
1468   CMD1X("*Form%sSelection    meth single");
1469   for (i=0; i < NUM_EFFECTS; i++) {    /* for all effects */
1470     effects[i].button="off";           /* init the button setting */
1471     if (Animate.resize == effects[i].function) { /* compare to curr setting */
1472       effects[i].button="on";          /* turn on one button */
1473     } /* end if curr setting */
1474   } /* end all buttons */
1475   /* Macro for a command with one var */
1476 #define CMD1V(TEXT,VAR) \
1477   sprintf(cmd,TEXT,module->name,VAR);\
1478   SendText(Channel,cmd,0);
1479 
1480   /* There is a cleaner way (using the array) for this...dje */
1481   CMD1V("*Form%sChoice RANDOM RANDOM %s \"Random\"",effects[1].button);
1482   CMD1V("*Form%sChoice FLIP FLIP %s \"Flip\"",effects[2].button);
1483   CMD1V("*Form%sChoice FRAME FRAME %s \"Frame\"",effects[3].button);
1484   CMD1V("*Form%sChoice FRAME3D FRAME3D %s \"Frame3d\"",effects[4].button);
1485   CMD1V("*Form%sChoice LINES LINES %s \"Lines\"",effects[5].button);
1486   CMD1V("*Form%sChoice TURN TURN %s \"Turn\"",effects[6].button);
1487   CMD1V("*Form%sChoice TWIST TWIST %s \"Twist\"",effects[7].button);
1488   CMD1X("*Form%sLine         left");
1489   CMD1X("*Form%sText         \"\"");
1490   CMD1X("*Form%sLine         left");
1491   CMD1X("*Form%sText         \"Iterations:\"");
1492   CMD1V("*Form%sInput        Iterations 5 \"%d\"",Animate.iterations);
1493   CMD1X("*Form%sText         \"Twists:\"");
1494   CMD1V("*Form%sInput        Twists 10 \"%f\"",Animate.twist);
1495   CMD1X("*Form%sText         \"Linewidth:\"");
1496   CMD1V("*Form%sInput        Linewidth 3 \"%d\"",Animate.width);
1497   CMD1X("*Form%sText         \"Delays:\"");
1498   CMD1V("*Form%sInput        Delays 5 \"%d\"",Animate.delay);
1499   CMD1X("*Form%sLine         left");
1500   CMD1X("*Form%sText         \"\"");
1501   CMD1X("*Form%sLine         left");
1502   CMD1X("*Form%sText         \"Color:\"");
1503   CMD1V("*Form%sInput        Color 20 \"%s\"",
1504 	Animate.color ? Animate.color : "Black^White");
1505   CMD1X("*Form%sLine         left");
1506   CMD1X("*Form%sText         \"\"");
1507   /*
1508     F1 - Apply, F2 - Apply and Save, F3 - Reset, F4 - Dismiss
1509   */
1510 
1511   CMD1X("*Form%sLine         expand");
1512   CMD1X("*Form%sButton       continue \"F1 - Apply\" F1");
1513   CMD11("*Form%sCommand      *%sIterations $(Iterations)");
1514   CMD11("*Form%sCommand      *%sTwist $(Twists)");
1515   CMD11("*Form%sCommand      *%sWidth $(Linewidth)");
1516   CMD11("*Form%sCommand      *%sDelay $(Delays)");
1517   CMD11("*Form%sCommand      *%sColor $(Color)");
1518   CMD11("*Form%sCommand      *%sEffect $(RANDOM?Random)\
1519 $(FLIP?Flip)\
1520 $(FRAME?Frame)\
1521 $(FRAME3D?Frame3d)\
1522 $(LINES?Lines)\
1523 $(TURN?Turn)\
1524 $(TWIST?Twist)");
1525 
1526   CMD1X("*Form%sButton       continue \"F2 - Apply & Save\" F2");
1527   CMD11("*Form%sCommand      *%sIterations $(Iterations)");
1528   CMD11("*Form%sCommand      *%sTwist $(Twists)");
1529   CMD11("*Form%sCommand      *%sWidth $(Linewidth)");
1530   CMD11("*Form%sCommand      *%sDelay $(Delays)");
1531   CMD11("*Form%sCommand      *%sColor $(Color)");
1532   CMD11("*Form%sCommand      *%sEffect $(RANDOM?Random)\
1533 $(FLIP?Flip)\
1534 $(FRAME?Frame)\
1535 $(FRAME3D?Frame3d)\
1536 $(LINES?Lines)\
1537 $(TURN?Turn)\
1538 $(TWIST?Twist)");
1539   CMD11("*Form%sCommand      *%sSave");
1540 
1541   CMD1X("*Form%sButton       restart   \"F3 - Reset\" F3");
1542 
1543   CMD1X("*Form%sButton       quit \"F4 - Dismiss\" F4");
1544   CMD1X("*Form%sCommand Nop");
1545 }
1546