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