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