1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /**
4  * \file effects.c "Special effects" other than compositor effects.
5  *
6  * Before we had a serious compositor, we supported swooping
7  * rectangles for minimising and so on.  These are still supported
8  * today, even when the compositor is enabled.  The file contains two
9  * parts:
10  *
11  *  1) A set of functions, each of which implements a special effect.
12  *     (Only the minimize function does anything interesting; we should
13  *      probably get rid of the rest.)
14  *
15  *  2) A set of functions for moving a highlighted wireframe box around
16  *     the screen, optionally with height and width shown in the middle.
17  *     This is used for moving and resizing when reduced_resources is set.
18  *
19  * There was formerly a system which allowed callers to drop in their
20  * own handlers for various things; it was never used (people who want
21  * their own handlers can just modify this file, after all) and it added
22  * a good deal of extra complexity, so it has been removed.  If you want it,
23  * it can be found in svn r3769.
24  *
25  * Once upon a time there were three different ways of drawing the box
26  * animation: window wireframe, window opaque, and root. People who had
27  * the shape extension theoretically had the choice of all three, and
28  * people who didn't weren't given the choice of the wireframe option.
29  * In practice, though, the opaque animation was never perfect, so it came
30  * down to the wireframe option for those who had the extension and
31  * the root option for those who didn't; there was actually no way of choosing
32  * any other option anyway.  Work on the opaque animation stopped in 2002;
33  * anyone who wants something like that these days will be using the
34  * compositor anyway.
35  *
36  * In svn r3769 this was made explicit.
37  */
38 
39 /*
40  * Copyright (C) 2001 Anders Carlsson, Havoc Pennington
41  *
42  * This program is free software; you can redistribute it and/or
43  * modify it under the terms of the GNU General Public License as
44  * published by the Free Software Foundation; either version 2 of the
45  * License, or (at your option) any later version.
46  *
47  * This program is distributed in the hope that it will be useful, but
48  * WITHOUT ANY WARRANTY; without even the implied warranty of
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
50  * General Public License for more details.
51  *
52  * You should have received a copy of the GNU General Public License
53  * along with this program; if not, write to the Free Software
54  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
55  * 02110-1301, USA.
56  */
57 
58 #include <config.h>
59 #include "effects.h"
60 #include "display-private.h"
61 #include "ui.h"
62 #include "window-private.h"
63 #include "prefs.h"
64 
65 #ifdef HAVE_SHAPE
66 #include <X11/extensions/shape.h>
67 #endif
68 
69 #define META_MINIMIZE_ANIMATION_LENGTH 0.25
70 #define META_SHADE_ANIMATION_LENGTH 0.2
71 
72 #include <string.h>
73 
74 typedef struct MetaEffect MetaEffect;
75 typedef struct MetaEffectPriv MetaEffectPriv;
76 
77 typedef struct
78 {
79   MetaScreen *screen;
80 
81   double millisecs_duration;
82   gint64 start_time;
83 
84 #ifdef HAVE_SHAPE
85   /** For wireframe window */
86   Window wireframe_xwindow;
87 #else
88   /** Rectangle to erase */
89   MetaRectangle last_rect;
90 
91   /** First time we've plotted anything in this animation? */
92   gboolean first_time;
93 
94   /** For wireframe drawn on root window */
95   GC gc;
96 #endif
97 
98   MetaRectangle start_rect;
99   MetaRectangle end_rect;
100 
101 } BoxAnimationContext;
102 
103 /**
104  * Information we need to know during a maximise or minimise effect.
105  */
106 typedef struct
107 {
108   /** This is the normal-size window. */
109   MetaRectangle window_rect;
110   /** This is the size of the window when it's an icon. */
111   MetaRectangle icon_rect;
112 } MetaMinimizeEffect, MetaUnminimizeEffect;
113 
114 struct MetaEffectPriv
115 {
116   MetaEffectFinished finished;
117   gpointer           finished_data;
118 };
119 
120 struct MetaEffect
121 {
122   /** The window the effect is applied to. */
123   MetaWindow *window;
124   /** Which effect is happening here. */
125   MetaEffectType type;
126   /** The effect handler can hang data here. */
127   gpointer info;
128 
129   union
130   {
131     MetaMinimizeEffect      minimize;
132     /* ... and theoretically anything else */
133   } u;
134 
135   MetaEffectPriv *priv;
136 };
137 
138 static void run_default_effect_handler (MetaEffect *effect);
139 static void run_handler (MetaEffect *effect);
140 static void effect_free (MetaEffect *effect);
141 
142 static MetaEffect *
143 create_effect (MetaEffectType      type,
144                MetaWindow         *window,
145                MetaEffectFinished  finished,
146                gpointer            finished_data);
147 
148 static void
149 draw_box_animation (MetaScreen     *screen,
150                     MetaRectangle  *initial_rect,
151                     MetaRectangle  *destination_rect,
152                     double          seconds_duration);
153 
154 /**
155  * Creates an effect.
156  *
157  */
158 static MetaEffect*
create_effect(MetaEffectType type,MetaWindow * window,MetaEffectFinished finished,gpointer finished_data)159 create_effect (MetaEffectType      type,
160                MetaWindow         *window,
161                MetaEffectFinished  finished,
162                gpointer            finished_data)
163 {
164     MetaEffect *effect = g_new (MetaEffect, 1);
165 
166     effect->type = type;
167     effect->window = window;
168     effect->priv = g_new (MetaEffectPriv, 1);
169     effect->priv->finished = finished;
170     effect->priv->finished_data = finished_data;
171 
172     return effect;
173 }
174 
175 /**
176  * Destroys an effect.  If the effect has a "finished" hook, it will be
177  * called before cleanup.
178  *
179  * \param effect  The effect.
180  */
181 static void
effect_free(MetaEffect * effect)182 effect_free (MetaEffect *effect)
183 {
184   if (effect->priv->finished)
185     effect->priv->finished (effect->priv->finished_data);
186 
187   g_free (effect->priv);
188   g_free (effect);
189 }
190 
191 void
meta_effect_run_focus(MetaWindow * window,MetaEffectFinished finished,gpointer data)192 meta_effect_run_focus (MetaWindow	    *window,
193 		       MetaEffectFinished   finished,
194 		       gpointer		    data)
195 {
196     MetaEffect *effect;
197 
198     g_return_if_fail (window != NULL);
199 
200     effect = create_effect (META_EFFECT_FOCUS, window, finished, data);
201 
202     run_handler (effect);
203 }
204 
205 void
meta_effect_run_minimize(MetaWindow * window,MetaRectangle * window_rect,MetaRectangle * icon_rect,MetaEffectFinished finished,gpointer data)206 meta_effect_run_minimize (MetaWindow         *window,
207                           MetaRectangle      *window_rect,
208                           MetaRectangle      *icon_rect,
209                           MetaEffectFinished  finished,
210                           gpointer            data)
211 {
212     MetaEffect *effect;
213 
214     g_return_if_fail (window != NULL);
215     g_return_if_fail (icon_rect != NULL);
216 
217     effect = create_effect (META_EFFECT_MINIMIZE, window, finished, data);
218 
219     effect->u.minimize.window_rect = *window_rect;
220     effect->u.minimize.icon_rect = *icon_rect;
221 
222     run_handler (effect);
223 }
224 
225 void
meta_effect_run_unminimize(MetaWindow * window,MetaRectangle * window_rect,MetaRectangle * icon_rect,MetaEffectFinished finished,gpointer data)226 meta_effect_run_unminimize (MetaWindow         *window,
227                             MetaRectangle      *window_rect,
228                             MetaRectangle      *icon_rect,
229                             MetaEffectFinished  finished,
230                             gpointer            data)
231 {
232     MetaEffect *effect;
233 
234     g_return_if_fail (window != NULL);
235     g_return_if_fail (icon_rect != NULL);
236 
237     effect = create_effect (META_EFFECT_UNMINIMIZE, window, finished, data);
238 
239     effect->u.minimize.window_rect = *window_rect;
240     effect->u.minimize.icon_rect = *icon_rect;
241 
242     run_handler (effect);
243 }
244 
245 void
meta_effect_run_close(MetaWindow * window,MetaEffectFinished finished,gpointer data)246 meta_effect_run_close (MetaWindow         *window,
247 		       MetaEffectFinished  finished,
248 		       gpointer            data)
249 {
250     MetaEffect *effect;
251 
252     g_return_if_fail (window != NULL);
253 
254     effect = create_effect (META_EFFECT_CLOSE, window,
255                             finished, data);
256 
257     run_handler (effect);
258 }
259 
260 /* old ugly minimization effect */
261 
262 #ifdef HAVE_SHAPE
263 static void
update_wireframe_window(MetaDisplay * display,Window xwindow,const MetaRectangle * rect)264 update_wireframe_window (MetaDisplay         *display,
265                          Window               xwindow,
266                          const MetaRectangle *rect)
267 {
268   XMoveResizeWindow (display->xdisplay,
269                      xwindow,
270                      rect->x, rect->y,
271                      rect->width, rect->height);
272 
273 #define OUTLINE_WIDTH 3
274 
275   if (rect->width > OUTLINE_WIDTH * 2 &&
276       rect->height > OUTLINE_WIDTH * 2)
277     {
278       XRectangle xrect;
279       Region inner_xregion;
280       Region outer_xregion;
281 
282       inner_xregion = XCreateRegion ();
283       outer_xregion = XCreateRegion ();
284 
285       xrect.x = 0;
286       xrect.y = 0;
287       xrect.width = rect->width;
288       xrect.height = rect->height;
289 
290       XUnionRectWithRegion (&xrect, outer_xregion, outer_xregion);
291 
292       xrect.x += OUTLINE_WIDTH;
293       xrect.y += OUTLINE_WIDTH;
294       xrect.width -= OUTLINE_WIDTH * 2;
295       xrect.height -= OUTLINE_WIDTH * 2;
296 
297       XUnionRectWithRegion (&xrect, inner_xregion, inner_xregion);
298 
299       XSubtractRegion (outer_xregion, inner_xregion, outer_xregion);
300 
301       XShapeCombineRegion (display->xdisplay, xwindow,
302                            ShapeBounding, 0, 0, outer_xregion, ShapeSet);
303 
304       XDestroyRegion (outer_xregion);
305       XDestroyRegion (inner_xregion);
306     }
307   else
308     {
309       /* Unset the shape */
310       XShapeCombineMask (display->xdisplay, xwindow,
311                          ShapeBounding, 0, 0, None, ShapeSet);
312     }
313 }
314 #endif
315 
316 static gboolean
effects_draw_box_animation_timeout(BoxAnimationContext * context)317 effects_draw_box_animation_timeout (BoxAnimationContext *context)
318 {
319   double elapsed;
320   gint64 current_time;
321   MetaRectangle draw_rect;
322   double fraction;
323 
324 #ifndef HAVE_SHAPE
325   if (!context->first_time)
326     {
327        /* Restore the previously drawn background */
328        XDrawRectangle (context->screen->display->xdisplay,
329                        context->screen->xroot,
330                        context->gc,
331                        context->last_rect.x, context->last_rect.y,
332                        context->last_rect.width, context->last_rect.height);
333     }
334   else
335     context->first_time = FALSE;
336 
337 #endif /* !HAVE_SHAPE */
338 
339   current_time = g_get_real_time ();
340 
341   /* We use milliseconds for all times */
342   elapsed = (current_time - context->start_time) / 1000.0;
343 
344   if (elapsed < 0)
345     {
346       /* Probably the system clock was set backwards? */
347       meta_warning ("System clock seemed to go backwards?\n");
348       elapsed = G_MAXDOUBLE; /* definitely done. */
349     }
350 
351   if (elapsed > context->millisecs_duration)
352     {
353       /* All done */
354 #ifdef HAVE_SHAPE
355         XDestroyWindow (context->screen->display->xdisplay,
356                           context->wireframe_xwindow);
357 #else
358         meta_display_ungrab (context->screen->display);
359         meta_ui_pop_delay_exposes (context->screen->ui);
360         XFreeGC (context->screen->display->xdisplay,
361                  context->gc);
362 #endif /* !HAVE_SHAPE */
363 
364       g_free (context);
365       return FALSE;
366     }
367 
368   g_assert (context->millisecs_duration > 0.0);
369   fraction = elapsed / context->millisecs_duration;
370 
371   draw_rect = context->start_rect;
372 
373   /* Now add a delta proportional to elapsed time. */
374   draw_rect.x += (context->end_rect.x - context->start_rect.x) * fraction;
375   draw_rect.y += (context->end_rect.y - context->start_rect.y) * fraction;
376   draw_rect.width += (context->end_rect.width - context->start_rect.width) * fraction;
377   draw_rect.height += (context->end_rect.height - context->start_rect.height) * fraction;
378 
379   /* don't confuse X or gdk-pixbuf with bogus rectangles */
380   if (draw_rect.width < 1)
381     draw_rect.width = 1;
382   if (draw_rect.height < 1)
383     draw_rect.height = 1;
384 
385 #ifdef HAVE_SHAPE
386   update_wireframe_window (context->screen->display,
387                            context->wireframe_xwindow,
388                            &draw_rect);
389 #else
390   context->last_rect = draw_rect;
391 
392   /* Draw the rectangle */
393   XDrawRectangle (context->screen->display->xdisplay,
394                   context->screen->xroot,
395                   context->gc,
396                   draw_rect.x, draw_rect.y,
397                   draw_rect.width, draw_rect.height);
398 
399 #endif /* !HAVE_SHAPE */
400 
401   /* kick changes onto the server */
402   XFlush (context->screen->display->xdisplay);
403 
404   return TRUE;
405 }
406 
407 void
draw_box_animation(MetaScreen * screen,MetaRectangle * initial_rect,MetaRectangle * destination_rect,double seconds_duration)408 draw_box_animation (MetaScreen     *screen,
409                     MetaRectangle  *initial_rect,
410                     MetaRectangle  *destination_rect,
411                     double          seconds_duration)
412 {
413   BoxAnimationContext *context;
414 
415 #ifdef HAVE_SHAPE
416   XSetWindowAttributes attrs;
417 #else
418   XGCValues gc_values;
419 #endif
420 
421   g_return_if_fail (seconds_duration > 0.0);
422 
423   if (g_getenv ("MARCO_DEBUG_EFFECTS"))
424     seconds_duration *= 10; /* slow things down */
425 
426   /* Create the animation context */
427   context = g_new0 (BoxAnimationContext, 1);
428 
429   context->screen = screen;
430 
431   context->millisecs_duration = seconds_duration * 1000.0;
432 
433   context->start_rect = *initial_rect;
434   context->end_rect = *destination_rect;
435 
436 #ifdef HAVE_SHAPE
437 
438   attrs.override_redirect = True;
439   attrs.background_pixel = BlackPixel (screen->display->xdisplay,
440                                        screen->number);
441 
442   context->wireframe_xwindow = XCreateWindow (screen->display->xdisplay,
443                                               screen->xroot,
444                                               initial_rect->x,
445                                               initial_rect->y,
446                                               initial_rect->width,
447                                               initial_rect->height,
448                                               0,
449                                               CopyFromParent,
450                                               CopyFromParent,
451                                               (Visual *)CopyFromParent,
452                                               CWOverrideRedirect | CWBackPixel,
453                                               &attrs);
454 
455   update_wireframe_window (screen->display,
456                            context->wireframe_xwindow,
457                            initial_rect);
458 
459   XMapWindow (screen->display->xdisplay,
460               context->wireframe_xwindow);
461 
462 #else /* !HAVE_SHAPE */
463 
464   context->first_time = TRUE;
465   gc_values.subwindow_mode = IncludeInferiors;
466   gc_values.function = GXinvert;
467 
468   context->gc = XCreateGC (screen->display->xdisplay,
469                            screen->xroot,
470                            GCSubwindowMode | GCFunction,
471                            &gc_values);
472 
473   /* Grab the X server to avoid screen dirt */
474   meta_display_grab (context->screen->display);
475   meta_ui_push_delay_exposes (context->screen->ui);
476 #endif
477 
478   /* Do this only after we get the pixbuf from the server,
479    * so that the animation doesn't get truncated.
480    */
481   context->start_time = g_get_real_time ();
482 
483   /* Add the timeout - a short one, could even use an idle,
484    * but this is maybe more CPU-friendly.
485    */
486   g_timeout_add (15,
487                  (GSourceFunc)effects_draw_box_animation_timeout,
488                  context);
489 
490   /* kick changes onto the server */
491   XFlush (context->screen->display->xdisplay);
492 }
493 
494 void
meta_effects_begin_wireframe(MetaScreen * screen,const MetaRectangle * rect,int width,int height)495 meta_effects_begin_wireframe (MetaScreen          *screen,
496                               const MetaRectangle *rect,
497                               int                  width,
498                               int                  height)
499 {
500   /* Grab the X server to avoid screen dirt */
501   meta_display_grab (screen->display);
502   meta_ui_push_delay_exposes (screen->ui);
503 
504   meta_effects_update_wireframe (screen,
505                                  NULL, -1, -1,
506                                  rect, width, height);
507 }
508 
509 static void
draw_xor_rect(MetaScreen * screen,const MetaRectangle * rect,int width,int height)510 draw_xor_rect (MetaScreen          *screen,
511                const MetaRectangle *rect,
512                int                  width,
513                int                  height)
514 {
515   /* The lines in the center can't overlap the rectangle or each
516    * other, or the XOR gets reversed. So we have to draw things
517    * a bit oddly.
518    */
519   XSegment segments[8];
520   MetaRectangle shrunk_rect;
521   int i;
522 
523 #define LINE_WIDTH META_WIREFRAME_XOR_LINE_WIDTH
524 
525   /* We don't want the wireframe going outside the window area.
526    * It makes it harder for the user to position windows and it exposes other
527    * annoying bugs.
528    */
529   shrunk_rect = *rect;
530 
531   shrunk_rect.x += LINE_WIDTH / 2 + LINE_WIDTH % 2;
532   shrunk_rect.y += LINE_WIDTH / 2 + LINE_WIDTH % 2;
533   shrunk_rect.width -= LINE_WIDTH + 2 * (LINE_WIDTH % 2);
534   shrunk_rect.height -= LINE_WIDTH + 2 * (LINE_WIDTH % 2);
535 
536   XDrawRectangle (screen->display->xdisplay,
537                   screen->xroot,
538                   screen->root_xor_gc,
539                   shrunk_rect.x, shrunk_rect.y,
540                   shrunk_rect.width, shrunk_rect.height);
541 
542   /* Don't put lines inside small rectangles where they won't fit */
543   if (shrunk_rect.width < (LINE_WIDTH * 4) ||
544       shrunk_rect.height < (LINE_WIDTH * 4))
545     return;
546 
547   if ((width >= 0) && (height >= 0))
548     {
549       XGCValues gc_values = { 0 };
550 
551       if (XGetGCValues (screen->display->xdisplay,
552                         screen->root_xor_gc,
553                         GCFont, &gc_values))
554         {
555           char *text;
556           int text_length;
557 
558           XFontStruct *font_struct;
559           int text_width, text_height;
560           int box_x, box_y;
561           int box_width, box_height;
562 
563           font_struct = XQueryFont (screen->display->xdisplay,
564                                     gc_values.font);
565 
566           if (font_struct != NULL)
567             {
568               text = g_strdup_printf ("%d x %d", width, height);
569               text_length = strlen (text);
570 
571               text_width = text_length * font_struct->max_bounds.width;
572               text_height = font_struct->max_bounds.descent +
573                             font_struct->max_bounds.ascent;
574 
575               box_width = text_width + 2 * LINE_WIDTH;
576               box_height = text_height + 2 * LINE_WIDTH;
577 
578               box_x = shrunk_rect.x + (shrunk_rect.width - box_width) / 2;
579               box_y = shrunk_rect.y + (shrunk_rect.height - box_height) / 2;
580 
581               if ((box_width < shrunk_rect.width) &&
582                   (box_height < shrunk_rect.height))
583                 {
584                   XFillRectangle (screen->display->xdisplay,
585                                   screen->xroot,
586                                   screen->root_xor_gc,
587                                   box_x, box_y,
588                                   box_width, box_height);
589                   XDrawString (screen->display->xdisplay,
590                                screen->xroot,
591                                screen->root_xor_gc,
592                                box_x + LINE_WIDTH,
593                                box_y + LINE_WIDTH + font_struct->max_bounds.ascent,
594                                text, text_length);
595                 }
596 
597               g_free (text);
598 
599               XFreeFontInfo (NULL, font_struct, 1);
600 
601               if ((box_width + LINE_WIDTH) >= (shrunk_rect.width / 3))
602                 return;
603 
604               if ((box_height + LINE_WIDTH) >= (shrunk_rect.height / 3))
605                 return;
606             }
607         }
608     }
609 
610   /* Two vertical lines at 1/3 and 2/3 */
611   segments[0].x1 = shrunk_rect.x + shrunk_rect.width / 3;
612   segments[0].y1 = shrunk_rect.y + LINE_WIDTH / 2 + LINE_WIDTH % 2;
613   segments[0].x2 = segments[0].x1;
614   segments[0].y2 = shrunk_rect.y + shrunk_rect.height - LINE_WIDTH / 2;
615 
616   segments[1] = segments[0];
617   segments[1].x1 = shrunk_rect.x + (shrunk_rect.width / 3) * 2;
618   segments[1].x2 = segments[1].x1;
619 
620   /* Now make two horizontal lines at 1/3 and 2/3, but not
621    * overlapping the verticals
622    */
623 
624   segments[2].x1 = shrunk_rect.x + LINE_WIDTH / 2 + LINE_WIDTH % 2;
625   segments[2].x2 = segments[0].x1 - LINE_WIDTH / 2;
626   segments[2].y1 = shrunk_rect.y + shrunk_rect.height / 3;
627   segments[2].y2 = segments[2].y1;
628 
629   segments[3] = segments[2];
630   segments[3].x1 = segments[2].x2 + LINE_WIDTH;
631   segments[3].x2 = segments[1].x1 - LINE_WIDTH / 2;
632 
633   segments[4] = segments[3];
634   segments[4].x1 = segments[3].x2 + LINE_WIDTH;
635   segments[4].x2 = shrunk_rect.x + shrunk_rect.width - LINE_WIDTH / 2;
636 
637   /* Second horizontal line is just like the first, but
638    * shifted down
639    */
640   i = 5;
641   while (i < 8)
642     {
643       segments[i] = segments[i - 3];
644       segments[i].y1 = shrunk_rect.y + (shrunk_rect.height / 3) * 2;
645       segments[i].y2 = segments[i].y1;
646       ++i;
647     }
648 
649   XDrawSegments (screen->display->xdisplay,
650                  screen->xroot,
651                  screen->root_xor_gc,
652                  segments,
653                  G_N_ELEMENTS (segments));
654 }
655 
656 void
meta_effects_update_wireframe(MetaScreen * screen,const MetaRectangle * old_rect,int old_width,int old_height,const MetaRectangle * new_rect,int new_width,int new_height)657 meta_effects_update_wireframe (MetaScreen          *screen,
658                                const MetaRectangle *old_rect,
659                                int                  old_width,
660                                int                  old_height,
661                                const MetaRectangle *new_rect,
662                                int                  new_width,
663                                int                  new_height)
664 {
665   if (old_rect)
666     draw_xor_rect (screen, old_rect, old_width, old_height);
667 
668   if (new_rect)
669     draw_xor_rect (screen, new_rect, new_width, new_height);
670 
671   XFlush (screen->display->xdisplay);
672 }
673 
674 void
meta_effects_end_wireframe(MetaScreen * screen,const MetaRectangle * old_rect,int old_width,int old_height)675 meta_effects_end_wireframe (MetaScreen          *screen,
676                             const MetaRectangle *old_rect,
677                             int                  old_width,
678                             int                  old_height)
679 {
680   meta_effects_update_wireframe (screen,
681                                  old_rect, old_width, old_height,
682                                  NULL, -1, -1);
683 
684   meta_display_ungrab (screen->display);
685   meta_ui_pop_delay_exposes (screen->ui);
686 }
687 
688 static void
run_default_effect_handler(MetaEffect * effect)689 run_default_effect_handler (MetaEffect *effect)
690 {
691     switch (effect->type)
692     {
693     case META_EFFECT_MINIMIZE:
694        draw_box_animation (effect->window->screen,
695                      &(effect->u.minimize.window_rect),
696                      &(effect->u.minimize.icon_rect),
697                      META_MINIMIZE_ANIMATION_LENGTH);
698        break;
699 
700     default:
701        break;
702     }
703 }
704 
705 static void
run_handler(MetaEffect * effect)706 run_handler (MetaEffect *effect)
707 {
708   if (meta_prefs_get_mate_animations ())
709     run_default_effect_handler (effect);
710 
711   effect_free (effect);
712 }
713