1 /**
2  * Animation plugin for compiz/beryl
3  *
4  * animation.c
5  *
6  * Copyright : (C) 2006 Erkin Bahceci
7  * E-mail    : erkinbah@gmail.com
8  *
9  * Based on Wobbly and Minimize plugins by
10  *           : David Reveman
11  * E-mail    : davidr@novell.com>
12  *
13  * Airplane added by : Carlo Palma
14  * E-mail            : carlopalma@salug.it
15  * Based on code originally written by Mark J. Kilgard
16  *
17  * Beam-Up added by : Florencio Guimaraes
18  * E-mail           : florencio@nexcorp.com.br
19  *
20  * Fold and Skewer added by : Tomasz Kolodziejski
21  * E-mail                   : tkolodziejski@gmail.com
22  *
23  * Hexagon tessellator added by : Mike Slegeir
24  * E-mail                       : mikeslegeir@mail.utexas.edu>
25  *
26  * Particle system added by : (C) 2006 Dennis Kasprzyk
27  * E-mail                   : onestone@beryl-project.org
28  *
29  * This program is free software; you can redistribute it and/or
30  * modify it under the terms of the GNU General Public License
31  * as published by the Free Software Foundation; either version 2
32  * of the License, or (at your option) any later version.
33  *
34  * This program is distributed in the hope that it will be useful,
35  * but WITHOUT ANY WARRANTY; without even the implied warranty of
36  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37  * GNU General Public License for more details.
38  *
39  * You should have received a copy of the GNU General Public License
40  * along with this program; if not, write to the Free Software
41  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
42  **/
43 
44 /*
45  * TODO:
46  *
47  * - Custom bounding box update function for Airplane
48  *
49  * - Auto direction option: Close in opposite direction of opening
50  * - Proper side surface normals for lighting
51  * - decoration shadows
52  *   - shadow quad generation
53  *   - shadow texture coords (from clip tex. matrices)
54  *   - draw shadows
55  *   - fade in shadows
56  *
57  * - Voronoi tessellation
58  * - Brick tessellation
59  * - Triangle tessellation
60  * - Hexagonal tessellation
61  *
62  * Effects:
63  * - Circular action for tornado type fx
64  * - Tornado 3D (especially for minimize)
65  * - Helix 3D (hor. strips descend while they rotate and fade in)
66  * - Glass breaking 3D
67  *   - Gaussian distr. points (for gradually increasing polygon size
68  *                           starting from center or near mouse pointer)
69  *   - Drawing cracks
70  *   - Gradual cracking
71  *
72  * - fix slowness during transparent cube with <100 opacity
73  * - fix occasional wrong side color in some windows
74  * - fix on top windows and panels
75  *   (These two only matter for viewing during Rotate Cube.
76  *    All windows should be painted with depth test on
77  *    like 3d-plugin does)
78  * - play better with rotate (fix cube face drawn on top of polygons
79  *   after 45 deg. rotation)
80  *
81  */
82 
83 #include <GL/glu.h>
84 #include "animation-internal.h"
85 
86 
87 #define EXTENSION_INCREMENT 4
88 
89 #define FAKE_ICON_SIZE 4
90 
91 int animDisplayPrivateIndex;
92 int animFunctionsPrivateIndex;
93 CompMetadata animMetadata;
94 
95 static int switcherPostWait = 0;
96 
97 
98 char *eventNames[AnimEventNum] =
99 {"Open", "Close", "Minimize", "Shade", "Focus"};
100 
101 int chosenEffectOptionIds[AnimEventNum] =
102 {
103     ANIM_SCREEN_OPTION_OPEN_EFFECTS,
104     ANIM_SCREEN_OPTION_CLOSE_EFFECTS,
105     ANIM_SCREEN_OPTION_MINIMIZE_EFFECTS,
106     ANIM_SCREEN_OPTION_SHADE_EFFECTS,
107     ANIM_SCREEN_OPTION_FOCUS_EFFECTS
108 };
109 
110 int randomEffectOptionIds[AnimEventNum] =
111 {
112     ANIM_SCREEN_OPTION_OPEN_RANDOM_EFFECTS,
113     ANIM_SCREEN_OPTION_CLOSE_RANDOM_EFFECTS,
114     ANIM_SCREEN_OPTION_MINIMIZE_RANDOM_EFFECTS,
115     ANIM_SCREEN_OPTION_SHADE_RANDOM_EFFECTS,
116     -1
117 };
118 
119 int customOptionOptionIds[AnimEventNum] =
120 {
121     ANIM_SCREEN_OPTION_OPEN_OPTIONS,
122     ANIM_SCREEN_OPTION_CLOSE_OPTIONS,
123     ANIM_SCREEN_OPTION_MINIMIZE_OPTIONS,
124     ANIM_SCREEN_OPTION_SHADE_OPTIONS,
125     ANIM_SCREEN_OPTION_FOCUS_OPTIONS
126 };
127 
128 int matchOptionIds[AnimEventNum] =
129 {
130     ANIM_SCREEN_OPTION_OPEN_MATCHES,
131     ANIM_SCREEN_OPTION_CLOSE_MATCHES,
132     ANIM_SCREEN_OPTION_MINIMIZE_MATCHES,
133     ANIM_SCREEN_OPTION_SHADE_MATCHES,
134     ANIM_SCREEN_OPTION_FOCUS_MATCHES
135 };
136 
137 int durationOptionIds[AnimEventNum] =
138 {
139     ANIM_SCREEN_OPTION_OPEN_DURATIONS,
140     ANIM_SCREEN_OPTION_CLOSE_DURATIONS,
141     ANIM_SCREEN_OPTION_MINIMIZE_DURATIONS,
142     ANIM_SCREEN_OPTION_SHADE_DURATIONS,
143     ANIM_SCREEN_OPTION_FOCUS_DURATIONS
144 };
145 
146 
147 // Bind each effect in the list of chosen effects for every event, to the
148 // corresponding animation effect (i.e. effect with that name) if it is
149 // provided by a plugin, otherwise set it to None.
150 static void
updateEventEffects(CompScreen * s,AnimEvent e,Bool forRandom)151 updateEventEffects (CompScreen *s,
152 		    AnimEvent e,
153 		    Bool forRandom)
154 {
155     ANIM_SCREEN (s);
156 
157     CompListValue *listVal;
158     EffectSet *effectSet;
159     if (forRandom)
160     {
161 	listVal = &as->opt[randomEffectOptionIds[e]].value.list;
162 	effectSet = &as->randomEffects[e];
163     }
164     else
165     {
166 	listVal = &as->opt[chosenEffectOptionIds[e]].value.list;
167 	effectSet = &as->eventEffects[e];
168     }
169     int n = listVal->nValue;
170 
171     if (effectSet->effects)
172 	free (effectSet->effects);
173     effectSet->effects = calloc (n, sizeof (AnimEffect));
174     if (!effectSet->effects)
175     {
176 	compLogMessage ("animation", CompLogLevelError,
177 			"Not enough memory");
178 	return;
179     }
180     effectSet->n = n;
181 
182     int nEventEffectsAllowed = as->nEventEffectsAllowed[e];
183     const AnimEffect *eventEffectsAllowed = as->eventEffectsAllowed[e];
184 
185     int r;
186     for (r = 0; r < n; r++) // for each row
187     {
188 	const char *animName = listVal->value[r].s;
189 
190 	// Find the animation effect with matching name
191 	effectSet->effects[r] = AnimEffectNone;
192 	int i;
193 	for (i = 0; i < nEventEffectsAllowed; i++)
194 	{
195 	    if (0 == strcasecmp (animName, eventEffectsAllowed[i]->name))
196 	    {
197 		effectSet->effects[r] = eventEffectsAllowed[i];
198 		break;
199 	    }
200 	}
201     }
202 }
203 
204 static void
updateAllEventEffects(CompScreen * s)205 updateAllEventEffects (CompScreen *s)
206 {
207     AnimEvent e;
208     for (e = 0; e < AnimEventNum; e++) // for each anim event
209 	updateEventEffects (s, e, FALSE);
210     for (e = 0; e < AnimEventNum - 1; e++) // for each anim event except focus
211 	updateEventEffects (s, e, TRUE);
212 }
213 
214 // Free everything related to effects
215 static void
freeAllEffects(AnimScreen * as)216 freeAllEffects (AnimScreen *as)
217 {
218     AnimEvent e;
219     for (e = 0; e < AnimEventNum; e++)
220     {
221 	if (as->randomEffects[e].effects)
222 	    free (as->randomEffects[e].effects);
223 	if (as->eventEffectsAllowed[e])
224 	    free (as->eventEffectsAllowed[e]);
225 	if (as->eventEffects[e].n > 0 && as->eventEffects[e].effects)
226 	    free (as->eventEffects[e].effects);
227     }
228 }
229 
230 // Extension functions
231 
232 static void
animAddExtension(CompScreen * s,ExtensionPluginInfo * extensionPluginInfo)233 animAddExtension (CompScreen *s,
234 		  ExtensionPluginInfo *extensionPluginInfo)
235 {
236     ANIM_SCREEN (s);
237 
238     // Make sure there is enough space for extension plugins
239     if (as->nExtensionPlugins == as->maxExtensionPlugins)
240     {
241 	ExtensionPluginInfo **newExtensionPlugins =
242 	    realloc (as->extensionPlugins,
243 		     (as->maxExtensionPlugins + EXTENSION_INCREMENT) *
244 		     sizeof (ExtensionPluginInfo *));
245 	if (!newExtensionPlugins)
246 	{
247 	    compLogMessage ("animation", CompLogLevelError,
248 			    "Not enough memory");
249 	    return;
250 	}
251 	as->extensionPlugins = newExtensionPlugins;
252 	as->maxExtensionPlugins += EXTENSION_INCREMENT;
253     }
254 
255     as->extensionPlugins[as->nExtensionPlugins] = extensionPluginInfo;
256     as->nExtensionPlugins++;
257 
258     unsigned int nPluginEffects = extensionPluginInfo->nEffects;
259 
260     // Make sure there is enough space for event effects
261     AnimEvent e;
262     for (e = 0; e < AnimEventNum; e++) // for each anim event
263     {
264 	if (as->maxEventEffectsAllowed[e] <
265 	    as->nEventEffectsAllowed[e] + nPluginEffects)
266 	{
267 	    int newNum = as->nEventEffectsAllowed[e] + nPluginEffects;
268 	    AnimEffect *newEventEfffects =
269 		realloc (as->eventEffectsAllowed[e],
270 			 newNum * sizeof (AnimEffect));
271 	    if (!newEventEfffects)
272 	    {
273 		compLogMessage ("animation", CompLogLevelError,
274 				"Not enough memory");
275 		return;
276 	    }
277 	    as->eventEffectsAllowed[e] = newEventEfffects;
278 	    as->maxEventEffectsAllowed[e] = newNum;
279 	}
280     }
281 
282     Bool eventEffectsNeedUpdate[AnimEventNum] =
283 	{FALSE, FALSE, FALSE, FALSE, FALSE};
284 
285     // Put this plugin's effects into as->eventEffects and
286     // as->eventEffectsAllowed
287     int j;
288     for (j = 0; j < nPluginEffects; j++)
289     {
290 	const AnimEffect effect = extensionPluginInfo->effects[j];
291 
292 	// Update allowed effects for each event
293 	for (e = 0; e < AnimEventNum; e++)
294 	{
295 	    if (effect->usedForEvents[e])
296 	    {
297 		as->eventEffectsAllowed[e][as->nEventEffectsAllowed[e]++] =
298 		    effect;
299 		eventEffectsNeedUpdate[e] = TRUE;
300 	    }
301 	}
302     }
303     for (e = 0; e < AnimEventNum; e++)
304 	if (eventEffectsNeedUpdate[e])
305 	{
306 	    updateEventEffects (s, e, FALSE);
307 	    if (e != AnimEventFocus)
308 		updateEventEffects (s, e, TRUE);
309 	}
310 }
311 
312 static void
animRemoveExtension(CompScreen * s,ExtensionPluginInfo * extensionPluginInfo)313 animRemoveExtension (CompScreen *s,
314 		     ExtensionPluginInfo *extensionPluginInfo)
315 {
316     ANIM_SCREEN (s);
317     char *pluginName = NULL;
318     int pluginNameLen = 0;
319 
320     if (extensionPluginInfo->nEffects > 0)
321     {
322 	pluginName = extensionPluginInfo->effects[0]->name;
323 	pluginNameLen = strchr (pluginName, ':') - pluginName;
324     }
325 
326     // Stop all ongoing animations
327     CompWindow *w;
328     for (w = s->windows; w; w = w->next)
329     {
330 	ANIM_WINDOW (w);
331 	if (aw->com.curAnimEffect != AnimEffectNone)
332 	    postAnimationCleanup (w);
333     }
334 
335     int p;
336     for (p = 0; p < as->nExtensionPlugins; p++)
337     {
338 	// Find the matching one
339 	if (as->extensionPlugins[p] == extensionPluginInfo)
340 	    break;
341     }
342     if (p == as->nExtensionPlugins)
343 	return; // couldn't find that extension plugin
344 
345     // Remove extensionPlugins[p] (shift following plugins)
346     as->nExtensionPlugins--;
347     if (as->nExtensionPlugins > 0)
348 	memmove (&as->extensionPlugins[p],
349 		 &as->extensionPlugins[p + 1],
350 		 (as->nExtensionPlugins - p) *
351 		 sizeof (ExtensionPluginInfo *));
352 
353     AnimEvent e;
354     for (e = 0; e < AnimEventNum; e++)
355     {
356 	AnimEffect *eventEffectsAllowed = as->eventEffectsAllowed[e];
357 	int n = as->nEventEffectsAllowed[e];
358 
359 	// nUpto: number of event effects upto the removed plugin
360 	int nUpto;
361 	for (nUpto = 0; nUpto < n; nUpto++)
362 	{
363 	    // if plugin name matches
364 	    if (0 == strncmp (pluginName, eventEffectsAllowed[nUpto]->name,
365 			      pluginNameLen))
366 		break;
367 	}
368 	// nUptoNext: number of event effects upto the next plugin
369 	int nUptoNext;
370 	for (nUptoNext = nUpto; nUptoNext < n; nUptoNext++)
371 	{
372 	    // if plugin name doesn't match
373 	    if (0 != strncmp (pluginName, eventEffectsAllowed[nUpto]->name,
374 			      pluginNameLen))
375 		break;
376 	}
377 	if (nUpto < nUptoNext)
378 	{
379 	    // Remove event effects for plugin p (Shift following effects)
380 	    if (nUptoNext < n)
381 		memmove (&eventEffectsAllowed[nUpto],
382 			 &eventEffectsAllowed[nUptoNext],
383 			 (nUptoNext - nUpto) * sizeof (AnimEffect));
384 	    as->nEventEffectsAllowed[e] -= nUptoNext - nUpto;
385 
386 	    // Update event effects to complete removal
387 	    updateEventEffects (s, e, FALSE);
388 	    if (e != AnimEventFocus)
389 		updateEventEffects (s, e, TRUE);
390 	}
391     }
392 }
393 
394 // End of extension functions
395 
396 
397 Bool
defaultAnimInit(CompWindow * w)398 defaultAnimInit (CompWindow * w)
399 {
400     ANIM_SCREEN(w->screen);
401     ANIM_WINDOW(w);
402 
403     // store window opacity
404     aw->com.storedOpacity = w->paint.opacity;
405 
406     aw->com.timestep =
407     	(w->screen->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
408 	 as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);
409 
410     return TRUE;
411 }
412 
413 Bool
animZoomToIcon(CompWindow * w)414 animZoomToIcon (CompWindow *w)
415 {
416     ANIM_WINDOW(w);
417 
418     if (aw->com.curAnimEffect->properties.zoomToIconFunc)
419 	return aw->com.curAnimEffect->properties.zoomToIconFunc (w);
420 
421     return FALSE;
422 }
423 
424 static Bool
defaultMinimizeAnimInit(CompWindow * w)425 defaultMinimizeAnimInit (CompWindow * w)
426 {
427     ANIM_WINDOW(w);
428 
429     if (animZoomToIcon (w))
430     {
431 	aw->com.animTotalTime /= ZOOM_PERCEIVED_T;
432 	aw->com.animRemainingTime = aw->com.animTotalTime;
433 	aw->com.usingTransform = TRUE;
434     }
435     return defaultAnimInit (w);
436 }
437 
438 static Bool
animWithTransformInit(CompWindow * w)439 animWithTransformInit (CompWindow * w)
440 {
441     ANIM_WINDOW(w);
442 
443     aw->com.usingTransform = TRUE;
444 
445     return defaultMinimizeAnimInit (w);
446 }
447 
448 static inline Bool
returnTrue(CompWindow * w)449 returnTrue (CompWindow *w)
450 {
451     return TRUE;
452 }
453 
454 // Assumes events in the metadata are in
455 // [Open, Close, Minimize, Focus, Shade] order
456 // and effects among those are in alphabetical order
457 // but with "(Event) None" first and "(Event) Random" last.
458 static AnimEffect
getMatchingAnimSelection(CompWindow * w,AnimEvent e,int * duration)459 getMatchingAnimSelection (CompWindow *w,
460 			  AnimEvent e,
461 			  int *duration)
462 {
463     ANIM_SCREEN(w->screen);
464     ANIM_WINDOW(w);
465 
466     EffectSet *eventEffects;
467     CompOptionValue *valMatch;
468     CompOptionValue *valDuration;
469     CompOptionValue *valCustomOptions;
470 
471     eventEffects = &as->eventEffects[e];
472     valMatch = &as->opt[matchOptionIds[e]].value;
473     valDuration = &as->opt[durationOptionIds[e]].value;
474     valCustomOptions = &as->opt[customOptionOptionIds[e]].value;
475 
476     int nRows = valMatch->list.nValue;
477     if (nRows != eventEffects->n ||
478 	nRows != valDuration->list.nValue ||
479 	nRows != valCustomOptions->list.nValue)
480     {
481 	compLogMessage ("animation", CompLogLevelError,
482 			"Animation settings mismatch in \"Animation "
483 			"Selection\" list for %s event.", eventNames[e]);
484 	return AnimEffectNone;
485     }
486 
487     // Find the first row that matches this window for this event
488     int i;
489     for (i = 0; i < nRows; i++)
490     {
491 	if (!matchEval (&valMatch->list.value[i].match, w))
492 	    continue;
493 
494 	aw->prevAnimSelectionRow = aw->curAnimSelectionRow;
495 	aw->curAnimSelectionRow = i;
496 
497 	if (duration)
498 	    *duration = valDuration->list.value[i].i;
499 
500 	return eventEffects->effects[i];
501     }
502 
503     return AnimEffectNone;
504 }
505 
506 static inline AnimEffect
animGetAnimEffect(AnimScreen * as,AnimEffect effect,AnimEvent animEvent)507 animGetAnimEffect (AnimScreen *as,
508 		   AnimEffect effect,
509 		   AnimEvent animEvent)
510 {
511     Bool allRandom = as->opt[ANIM_SCREEN_OPTION_ALL_RANDOM].value.b;
512     AnimEffect *randomEffects = as->randomEffects[animEvent].effects;
513     unsigned int nRandomEffects = as->randomEffects[animEvent].n;
514 
515     if ((effect == AnimEffectRandom) || allRandom)
516     {
517 	if (nRandomEffects == 0) // no random animation selected, assume "all"
518 	{
519 	    // exclude None and Random
520 	    randomEffects = as->eventEffectsAllowed[animEvent] + 2;
521 	    nRandomEffects = as->nEventEffectsAllowed[animEvent] - 2;
522 	}
523 	unsigned int index;
524 	index = (unsigned int)(nRandomEffects * (double)rand() / RAND_MAX);
525 	return randomEffects[index];
526     }
527     else
528 	return effect;
529 }
530 
531 // Converts animation direction string to an integer direction
532 // (up, down, left, or right)
getActualAnimDirection(CompWindow * w,AnimDirection dir,Bool openDir)533 AnimDirection getActualAnimDirection (CompWindow * w,
534 				      AnimDirection dir,
535 				      Bool openDir)
536 {
537     ANIM_WINDOW(w);
538 
539     if (dir == AnimDirectionRandom)
540     {
541 	dir = rand() % 4;
542     }
543     else if (dir == AnimDirectionAuto)
544     {
545 	// away from icon
546 	int centerX = BORDER_X(w) + BORDER_W(w) / 2;
547 	int centerY = BORDER_Y(w) + BORDER_H(w) / 2;
548 	float relDiffX = ((float)centerX - aw->com.icon.x) / BORDER_W(w);
549 	float relDiffY = ((float)centerY - aw->com.icon.y) / BORDER_H(w);
550 
551 	if (openDir)
552 	{
553 	    if (aw->com.curWindowEvent == WindowEventMinimize ||
554 		aw->com.curWindowEvent == WindowEventUnminimize)
555 		// min/unmin. should always result in +/- y direction
556 		dir = aw->com.icon.y < w->screen->height - aw->com.icon.y ?
557 		    AnimDirectionDown : AnimDirectionUp;
558 	    else if (fabs(relDiffY) > fabs(relDiffX))
559 		dir = relDiffY > 0 ? AnimDirectionDown : AnimDirectionUp;
560 	    else
561 		dir = relDiffX > 0 ? AnimDirectionRight : AnimDirectionLeft;
562 	}
563 	else
564 	{
565 	    if (aw->com.curWindowEvent == WindowEventMinimize ||
566 		aw->com.curWindowEvent == WindowEventUnminimize)
567 		// min/unmin. should always result in +/- y direction
568 		dir = aw->com.icon.y < w->screen->height - aw->com.icon.y ?
569 		    AnimDirectionUp : AnimDirectionDown;
570 	    else if (fabs(relDiffY) > fabs(relDiffX))
571 		dir = relDiffY > 0 ? AnimDirectionUp : AnimDirectionDown;
572 	    else
573 		dir = relDiffX > 0 ? AnimDirectionLeft : AnimDirectionRight;
574 	}
575     }
576     return dir;
577 }
578 
579 float
defaultAnimProgress(CompWindow * w)580 defaultAnimProgress (CompWindow *w)
581 {
582     ANIM_WINDOW (w);
583 
584     float forwardProgress =
585 	1 - aw->com.animRemainingTime / (aw->com.animTotalTime - aw->com.timestep);
586     forwardProgress = MIN(forwardProgress, 1);
587     forwardProgress = MAX(forwardProgress, 0);
588 
589     if (aw->com.curWindowEvent == WindowEventOpen ||
590 	aw->com.curWindowEvent == WindowEventUnminimize ||
591 	aw->com.curWindowEvent == WindowEventUnshade ||
592 	aw->com.curWindowEvent == WindowEventFocus)
593 	forwardProgress = 1 - forwardProgress;
594 
595     return forwardProgress;
596 }
597 
598 float
sigmoidAnimProgress(CompWindow * w)599 sigmoidAnimProgress (CompWindow *w)
600 {
601     ANIM_WINDOW (w);
602 
603     float forwardProgress =
604 	1 - aw->com.animRemainingTime / (aw->com.animTotalTime - aw->com.timestep);
605     forwardProgress = MIN(forwardProgress, 1);
606     forwardProgress = MAX(forwardProgress, 0);
607 
608     // Apply sigmoid and normalize
609     forwardProgress =
610 	(sigmoid(forwardProgress) - sigmoid(0)) /
611 	(sigmoid(1) - sigmoid(0));
612 
613     if (aw->com.curWindowEvent == WindowEventOpen ||
614 	aw->com.curWindowEvent == WindowEventUnminimize ||
615 	aw->com.curWindowEvent == WindowEventUnshade ||
616 	aw->com.curWindowEvent == WindowEventFocus)
617 	forwardProgress = 1 - forwardProgress;
618 
619     return forwardProgress;
620 }
621 
622 // Gives some acceleration (when closing a window)
623 // or deceleration (when opening a window)
624 // Applies a sigmoid with slope s,
625 // where minx and maxx are the
626 // starting and ending points on the sigmoid
decelerateProgressCustom(float progress,float minx,float maxx)627 float decelerateProgressCustom(float progress, float minx, float maxx)
628 {
629     float x = 1 - progress;
630     float s = 8;
631 
632     return (1 -
633 	    ((sigmoid2(minx + (x * (maxx - minx)), s) - sigmoid2(minx, s)) /
634 	     (sigmoid2(maxx, s) - sigmoid2(minx, s))));
635 }
636 
decelerateProgress(float progress)637 float decelerateProgress(float progress)
638 {
639     return decelerateProgressCustom(progress, 0.5, 0.75);
640 }
641 
642 float
getProgressAndCenter(CompWindow * w,Point * center)643 getProgressAndCenter (CompWindow *w,
644 		      Point *center)
645 {
646     float forwardProgress = 0;
647 
648     ANIM_WINDOW (w);
649 
650     if (center)
651 	center->x = WIN_X (w) + WIN_W (w) / 2.0;
652 
653     if (animZoomToIcon (w))
654     {
655 	float dummy;
656 	fxZoomAnimProgress (w, &forwardProgress, &dummy, TRUE);
657 
658 	if (center)
659 	    getZoomCenterScale (w, center, NULL);
660     }
661     else
662     {
663 	forwardProgress = defaultAnimProgress (w);
664 
665 	if (center)
666 	{
667 	    if (aw->com.curWindowEvent == WindowEventShade ||
668 		aw->com.curWindowEvent == WindowEventUnshade)
669 	    {
670 		float origCenterY = WIN_Y (w) + WIN_H (w) / 2.0;
671 		center->y =
672 		    (1 - forwardProgress) * origCenterY +
673 		    forwardProgress * (WIN_Y (w) + aw->com.model->topHeight);
674 	    }
675 	    else // i.e. (un)minimizing without zooming
676 	    {
677 		    center->y = WIN_Y (w) + WIN_H (w) / 2.0;
678 	    }
679 	}
680     }
681     return forwardProgress;
682 }
683 
684 void
defaultAnimStep(CompWindow * w,float time)685 defaultAnimStep (CompWindow *w, float time)
686 {
687     int steps;
688 
689     ANIM_SCREEN (w->screen);
690     ANIM_WINDOW (w);
691 
692     float timestep =
693     	(w->screen->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
694 	 as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);
695 
696     aw->com.timestep = timestep;
697 
698     aw->remainderSteps += time / timestep;
699     steps = floor(aw->remainderSteps);
700     aw->remainderSteps -= steps;
701 
702     steps = MAX(1, steps);
703 
704     aw->com.animRemainingTime -= timestep * steps;
705 
706     // avoid sub-zero values
707     aw->com.animRemainingTime = MAX(aw->com.animRemainingTime, 0);
708 
709     matrixGetIdentity (&aw->com.transform);
710     if (animZoomToIcon (w))
711     {
712 	applyZoomTransform (w);
713     }
714 }
715 
716 void
defaultUpdateWindowTransform(CompWindow * w,CompTransform * wTransform)717 defaultUpdateWindowTransform (CompWindow *w,
718 			      CompTransform *wTransform)
719 {
720     ANIM_WINDOW(w);
721 
722     if (aw->com.usingTransform)
723     {
724 	if (aw->com.curAnimEffect->properties.modelAnimIs3D)
725 	{
726 	    // center for perspective correction
727 	    Point center;
728 	    getProgressAndCenter (w, &center);
729 
730 	    ANIM_SCREEN (w->screen);
731 	    CompTransform skewTransform;
732 	    matrixGetIdentity (&skewTransform);
733 	    applyPerspectiveSkew (as->output, &skewTransform, &center);
734 	    applyTransform (wTransform, &aw->com.transform);
735 	    applyTransform (wTransform, &skewTransform);
736 	}
737 	else
738 	{
739 	    applyTransform (wTransform, &aw->com.transform);
740 	}
741     }
742 }
743 
744 // Apply transform to wTransform
745 inline void
applyTransform(CompTransform * wTransform,CompTransform * transform)746 applyTransform (CompTransform *wTransform,
747 		CompTransform *transform)
748 {
749     matrixMultiply (wTransform, wTransform, transform);
750 }
751 
752 static void
copyResetBB(AnimWindow * aw)753 copyResetBB (AnimWindow *aw)
754 {
755     memcpy (&aw->lastBB, &aw->BB, sizeof (Box));
756     aw->BB.x1 = aw->BB.y1 = MAXSHORT;
757     aw->BB.x2 = aw->BB.y2 = MINSHORT;
758 }
759 
760 void
expandBoxWithBox(Box * target,Box * source)761 expandBoxWithBox (Box *target, Box *source)
762 {
763     if (source->x1 < target->x1)
764 	target->x1 = source->x1;
765     if (source->x2 > target->x2)
766 	target->x2 = source->x2;
767     if (source->y1 < target->y1)
768 	target->y1 = source->y1;
769     if (source->y2 > target->y2)
770 	target->y2 = source->y2;
771 }
772 
773 void
expandBoxWithPoint(Box * target,float fx,float fy)774 expandBoxWithPoint (Box *target, float fx, float fy)
775 {
776     short x = MAX (MIN (fx, MAXSHORT - 1), MINSHORT);
777     short y = MAX (MIN (fy, MAXSHORT - 1), MINSHORT);
778 
779     if (target->x1 == MAXSHORT)
780     {
781 	target->x1 = x;
782 	target->y1 = y;
783 	target->x2 = x + 1;
784 	target->y2 = y + 1;
785 	return;
786     }
787     if (x < target->x1)
788 	target->x1 = x;
789     else if (x > target->x2)
790 	target->x2 = x;
791 
792     if (y < target->y1)
793 	target->y1 = y;
794     else if (y > target->y2)
795 	target->y2 = y;
796 }
797 
798 // This will work for zoom-like 2D transforms,
799 // but not for glide-like 3D transforms.
800 static void
expandBoxWithPoint2DTransform(CompScreen * s,Box * target,CompVector * coords,CompTransform * transformMat)801 expandBoxWithPoint2DTransform (CompScreen *s,
802 			       Box *target,
803 			       CompVector *coords,
804 			       CompTransform *transformMat)
805 {
806     CompVector coordsTransformed;
807 
808     matrixMultiplyVector (&coordsTransformed, coords, transformMat);
809     expandBoxWithPoint (target, coordsTransformed.x, coordsTransformed.y);
810 }
811 
812 // Either points or objects should be non-NULL.
813 static Bool
expandBoxWithPoints3DTransform(CompOutput * output,CompScreen * s,const CompTransform * transform,Box * targetBox,const float * points,Object * objects,int nPoints)814 expandBoxWithPoints3DTransform (CompOutput          *output,
815 				CompScreen          *s,
816 				const CompTransform *transform,
817 				Box                 *targetBox,
818 				const float         *points,
819 				Object              *objects,
820 				int                 nPoints)
821 {
822     GLdouble dModel[16];
823     GLdouble dProjection[16];
824     GLdouble x, y, z;
825     int i;
826     for (i = 0; i < 16; i++)
827     {
828 	dModel[i] = transform->m[i];
829 	dProjection[i] = s->projection[i];
830     }
831     GLint viewport[4] =
832 	{output->region.extents.x1,
833 	 output->region.extents.y1,
834 	 output->width,
835 	 output->height};
836 
837     if (points) // use points
838     {
839 	for (; nPoints; nPoints--, points += 3)
840 	{
841 	    if (!gluProject (points[0], points[1], points[2],
842 			     dModel, dProjection, viewport,
843 			     &x, &y, &z))
844 		return FALSE;
845 
846 	    expandBoxWithPoint (targetBox, x + 0.5, (s->height - y) + 0.5);
847 	}
848     }
849     else // use objects
850     {
851 	Object *object = objects;
852 	for (; nPoints; nPoints--, object++)
853 	{
854 	    if (!gluProject (object->position.x,
855 			     object->position.y,
856 			     object->position.z,
857 			     dModel, dProjection, viewport,
858 			     &x, &y, &z))
859 		return FALSE;
860 
861 	    expandBoxWithPoint (targetBox, x + 0.5, (s->height - y) + 0.5);
862 	}
863     }
864     return TRUE;
865 }
866 
867 static void
modelUpdateBB(CompOutput * output,CompWindow * w,Box * BB)868 modelUpdateBB (CompOutput *output,
869 	       CompWindow * w,
870 	       Box *BB)
871 {
872     int i;
873 
874     ANIM_WINDOW (w);
875 
876     Model *model = aw->com.model;
877     if (!model)
878 	return;
879 
880     Object *object = model->objects;
881 
882     if (aw->com.usingTransform)
883     {
884 	if (aw->com.curAnimEffect->properties.modelAnimIs3D)
885 	{
886 	    CompTransform wTransform;
887 
888 	    // center for perspective correction
889 	    Point center;
890 	    getProgressAndCenter (w, &center);
891 
892 	    CompTransform fullTransform;
893 	    memcpy (fullTransform.m, aw->com.transform.m, sizeof (float) * 16);
894 	    applyPerspectiveSkew (output, &fullTransform, &center);
895 
896 	    prepareTransform (w->screen, output, &wTransform, &fullTransform);
897 
898 	    expandBoxWithPoints3DTransform (output,
899 					    w->screen,
900 					    &wTransform,
901 					    BB,
902 					    NULL,
903 					    model->objects,
904 					    model->numObjects);
905 	}
906 	else
907 	{
908 	    Object *object = model->objects;
909 	    for (i = 0; i < model->numObjects; i++, object++)
910 	    {
911 		CompVector coords;
912 
913 		coords.x = object->position.x;
914 		coords.y = object->position.y;
915 		coords.z = 0;
916 		coords.w = 1;
917 
918 		expandBoxWithPoint2DTransform (w->screen,
919 					       BB,
920 					       &coords,
921 					       &aw->com.transform);
922 	    }
923   	}
924     }
925     else
926     {
927 	for (i = 0; i < model->numObjects; i++, object++)
928 	{
929 	    expandBoxWithPoint (BB,
930 				object->position.x + 0.5,
931 				object->position.y + 0.5);
932 	}
933     }
934 }
935 
936 void
updateBBWindow(CompOutput * output,CompWindow * w,Box * BB)937 updateBBWindow (CompOutput *output,
938 		CompWindow * w,
939 		Box *BB)
940 {
941     Box windowBox = {WIN_X(w), WIN_X(w) + WIN_W(w),
942 		     WIN_Y(w), WIN_Y(w) + WIN_H(w)};
943     expandBoxWithBox (BB, &windowBox);
944 }
945 
946 void
updateBBScreen(CompOutput * output,CompWindow * w,Box * BB)947 updateBBScreen (CompOutput *output,
948 		CompWindow * w,
949 		Box *BB)
950 {
951     Box screenBox = {0, w->screen->width,
952 		     0, w->screen->height};
953     expandBoxWithBox (BB, &screenBox);
954 }
955 
956 void
prepareTransform(CompScreen * s,CompOutput * output,CompTransform * resultTransform,CompTransform * transform)957 prepareTransform (CompScreen *s,
958 		  CompOutput *output,
959 		  CompTransform *resultTransform,
960 		  CompTransform *transform)
961 {
962     CompTransform sTransform;
963 
964     matrixGetIdentity (&sTransform);
965     transformToScreenSpace (s, output,
966 			    -DEFAULT_Z_CAMERA, &sTransform);
967 
968     matrixMultiply (resultTransform, &sTransform, transform);
969 }
970 
971 void
compTransformUpdateBB(CompOutput * output,CompWindow * w,Box * BB)972 compTransformUpdateBB (CompOutput *output,
973 		       CompWindow *w,
974 		       Box *BB)
975 {
976     ANIM_WINDOW(w);
977     CompScreen *s = w->screen;
978     CompTransform wTransform;
979 
980     prepareTransform (s, output, &wTransform, &aw->com.transform);
981 
982     float corners[4*3] = {WIN_X(w), WIN_Y(w), 0,
983 			  WIN_X(w) + WIN_W(w), WIN_Y(w), 0,
984 			  WIN_X(w), WIN_Y(w) + WIN_H(w), 0,
985 			  WIN_X(w) + WIN_W(w), WIN_Y(w) + WIN_H(w), 0};
986 
987     expandBoxWithPoints3DTransform (output,
988 				    s,
989 				    &wTransform,
990 				    BB,
991 				    corners,
992 				    NULL,
993 				    4);
994 }
995 
996 // Damage the union of window's bounding box
997 // before and after animStepFunc does its job
998 static void
damageBoundingBox(CompWindow * w)999 damageBoundingBox (CompWindow * w)
1000 {
1001     ANIM_WINDOW(w);
1002 
1003     if (aw->BB.x1 == MAXSHORT) // unintialized BB
1004 	return;
1005 
1006     // Find union of BB and lastBB
1007     Region regionToDamage = XCreateRegion();
1008     if (!regionToDamage)
1009 	return;
1010 
1011     XRectangle rect;
1012 
1013     BoxPtr BB = &aw->BB;
1014     BoxPtr lastBB = &aw->lastBB;
1015 
1016     // Have a 1 pixel margin to prevent occasional 1 pixel line artifact
1017     rect.x = BB->x1 - 1;
1018     rect.y = BB->y1 - 1;
1019     rect.width  = BB->x2 - BB->x1 + 2;
1020     rect.height = BB->y2 - BB->y1 + 2;
1021     XUnionRectWithRegion (&rect, &emptyRegion, regionToDamage);
1022 
1023     rect.x = lastBB->x1 - 1;
1024     rect.y = lastBB->y1 - 1;
1025     rect.width  = lastBB->x2 - lastBB->x1 + 2;
1026     rect.height = lastBB->y2 - lastBB->y1 + 2;
1027     XUnionRectWithRegion (&rect, regionToDamage, regionToDamage);
1028 
1029     damageScreenRegion (w->screen, regionToDamage);
1030 
1031     XDestroyRegion (regionToDamage);
1032 }
1033 
getMousePointerXY(CompScreen * s,short * x,short * y)1034 Bool getMousePointerXY(CompScreen * s, short *x, short *y)
1035 {
1036     Window w1, w2;
1037     int xp, yp, xj, yj;
1038     unsigned int m;
1039 
1040     if (XQueryPointer
1041 	(s->display->display, s->root, &w1, &w2, &xj, &yj, &xp, &yp, &m))
1042     {
1043 	*x = xp;
1044 	*y = yp;
1045 	return TRUE;
1046     }
1047     return FALSE;
1048 }
1049 
animGetWindowState(CompWindow * w)1050 static int animGetWindowState(CompWindow * w)
1051 {
1052     Atom actual;
1053     int result, format;
1054     unsigned long n, left;
1055     unsigned char *data;
1056     int retval = WithdrawnState;
1057 
1058     result = XGetWindowProperty(w->screen->display->display, w->id,
1059 				w->screen->display->wmStateAtom, 0L,
1060 				1L, FALSE,
1061 				w->screen->display->wmStateAtom,
1062 				&actual, &format, &n, &left, &data);
1063 
1064     if (result == Success && data)
1065     {
1066 	if (n)
1067     	    memcpy(&retval, data, sizeof(int));
1068 
1069 	XFree((void *)data);
1070     }
1071 
1072     return retval;
1073 }
1074 
1075 static Bool
animSetScreenOptions(CompPlugin * plugin,CompScreen * screen,const char * name,CompOptionValue * value)1076 animSetScreenOptions(CompPlugin *plugin,
1077 		     CompScreen * screen,
1078 		     const char *name,
1079 		     CompOptionValue * value)
1080 {
1081     CompOption *o;
1082     int index;
1083 
1084     ANIM_SCREEN(screen);
1085 
1086     o = compFindOption(as->opt, NUM_OPTIONS(as), name, &index);
1087     if (!o)
1088 	return FALSE;
1089 
1090     switch (index)
1091     {
1092     case ANIM_SCREEN_OPTION_OPEN_MATCHES:
1093     case ANIM_SCREEN_OPTION_CLOSE_MATCHES:
1094     case ANIM_SCREEN_OPTION_MINIMIZE_MATCHES:
1095     case ANIM_SCREEN_OPTION_SHADE_MATCHES:
1096     case ANIM_SCREEN_OPTION_FOCUS_MATCHES:
1097 	if (compSetOptionList(o, value))
1098 	{
1099 	    int i;
1100 	    for (i = 0; i < o->value.list.nValue; i++)
1101 		matchUpdate (screen->display, &o->value.list.value[i].match);
1102 	    return TRUE;
1103 	}
1104 	break;
1105     case ANIM_SCREEN_OPTION_OPEN_OPTIONS:
1106 	if (compSetOptionList(o, value))
1107 	{
1108 	    updateOptionSets (screen, AnimEventOpen);
1109 	    return TRUE;
1110 	}
1111 	break;
1112     case ANIM_SCREEN_OPTION_CLOSE_OPTIONS:
1113 	if (compSetOptionList(o, value))
1114 	{
1115 	    updateOptionSets (screen, AnimEventClose);
1116 	    return TRUE;
1117 	}
1118 	break;
1119     case ANIM_SCREEN_OPTION_MINIMIZE_OPTIONS:
1120 	if (compSetOptionList(o, value))
1121 	{
1122 	    updateOptionSets (screen, AnimEventMinimize);
1123 	    return TRUE;
1124 	}
1125 	break;
1126     case ANIM_SCREEN_OPTION_SHADE_OPTIONS:
1127 	if (compSetOptionList(o, value))
1128 	{
1129 	    updateOptionSets (screen, AnimEventShade);
1130 	    return TRUE;
1131 	}
1132 	break;
1133     case ANIM_SCREEN_OPTION_FOCUS_OPTIONS:
1134 	if (compSetOptionList(o, value))
1135 	{
1136 	    updateOptionSets (screen, AnimEventFocus);
1137 	    return TRUE;
1138 	}
1139 	break;
1140     case ANIM_SCREEN_OPTION_OPEN_EFFECTS:
1141 	if (compSetOptionList(o, value))
1142 	{
1143 	    updateEventEffects (screen, AnimEventOpen, FALSE);
1144 	    return TRUE;
1145 	}
1146 	break;
1147     case ANIM_SCREEN_OPTION_CLOSE_EFFECTS:
1148 	if (compSetOptionList(o, value))
1149 	{
1150 	    updateEventEffects (screen, AnimEventClose, FALSE);
1151 	    return TRUE;
1152 	}
1153 	break;
1154     case ANIM_SCREEN_OPTION_MINIMIZE_EFFECTS:
1155 	if (compSetOptionList(o, value))
1156 	{
1157 	    updateEventEffects (screen, AnimEventMinimize, FALSE);
1158 	    return TRUE;
1159 	}
1160 	break;
1161     case ANIM_SCREEN_OPTION_SHADE_EFFECTS:
1162 	if (compSetOptionList(o, value))
1163 	{
1164 	    updateEventEffects (screen, AnimEventShade, FALSE);
1165 	    return TRUE;
1166 	}
1167 	break;
1168     case ANIM_SCREEN_OPTION_FOCUS_EFFECTS:
1169 	if (compSetOptionList(o, value))
1170 	{
1171 	    updateEventEffects (screen, AnimEventFocus, FALSE);
1172 	    return TRUE;
1173 	}
1174 	break;
1175     case ANIM_SCREEN_OPTION_OPEN_RANDOM_EFFECTS:
1176 	if (compSetOptionList(o, value))
1177 	{
1178 	    updateEventEffects (screen, AnimEventOpen, TRUE);
1179 	    return TRUE;
1180 	}
1181 	break;
1182     case ANIM_SCREEN_OPTION_CLOSE_RANDOM_EFFECTS:
1183 	if (compSetOptionList(o, value))
1184 	{
1185 	    updateEventEffects (screen, AnimEventClose, TRUE);
1186 	    return TRUE;
1187 	}
1188 	break;
1189     case ANIM_SCREEN_OPTION_MINIMIZE_RANDOM_EFFECTS:
1190 	if (compSetOptionList(o, value))
1191 	{
1192 	    updateEventEffects (screen, AnimEventMinimize, TRUE);
1193 	    return TRUE;
1194 	}
1195 	break;
1196     case ANIM_SCREEN_OPTION_SHADE_RANDOM_EFFECTS:
1197 	if (compSetOptionList(o, value))
1198 	{
1199 	    updateEventEffects (screen, AnimEventShade, TRUE);
1200 	    return TRUE;
1201 	}
1202 	break;
1203     default:
1204 	return compSetScreenOption (screen, o, value);
1205 	break;
1206     }
1207 
1208     return FALSE;
1209 }
1210 
1211 static const CompMetadataOptionInfo animScreenOptionInfo[] = {
1212     // Event settings
1213     { "open_effects", "list", "<type>string</type>", 0, 0 },
1214     { "open_durations", "list", "<type>int</type><min>50</min>", 0, 0 },
1215     { "open_matches", "list", "<type>match</type>", 0, 0 },
1216     { "open_options", "list", "<type>string</type>", 0, 0 },
1217     { "open_random_effects", "list", "<type>string</type>", 0, 0 },
1218     { "close_effects", "list", "<type>string</type>", 0, 0 },
1219     { "close_durations", "list", "<type>int</type><min>50</min>", 0, 0 },
1220     { "close_matches", "list", "<type>match</type>", 0, 0 },
1221     { "close_options", "list", "<type>string</type>", 0, 0 },
1222     { "close_random_effects", "list", "<type>string</type>", 0, 0 },
1223     { "minimize_effects", "list", "<type>string</type>", 0, 0 },
1224     { "minimize_durations", "list", "<type>int</type><min>50</min>", 0, 0 },
1225     { "minimize_matches", "list", "<type>match</type>", 0, 0 },
1226     { "minimize_options", "list", "<type>string</type>", 0, 0 },
1227     { "minimize_random_effects", "list", "<type>string</type>", 0, 0 },
1228     { "shade_effects", "list", "<type>string</type>", 0, 0 },
1229     { "shade_durations", "list", "<type>int</type><min>50</min>", 0, 0 },
1230     { "shade_matches", "list", "<type>match</type>", 0, 0 },
1231     { "shade_options", "list", "<type>string</type>", 0, 0 },
1232     { "shade_random_effects", "list", "<type>string</type>", 0, 0 },
1233     { "focus_effects", "list", "<type>string</type>", 0, 0 },
1234     { "focus_durations", "list", "<type>int</type><min>50</min>", 0, 0 },
1235     { "focus_matches", "list", "<type>match</type>", 0, 0 },
1236     { "focus_options", "list", "<type>string</type>", 0, 0 },
1237     // Misc. settings
1238     { "all_random", "bool", 0, 0, 0 },
1239     { "time_step", "int", "<min>1</min>", 0, 0 },
1240     // Effect settings
1241     { "curved_fold_amp_mult", "float", "<min>-1.5</min><max>2.0</max>", 0, 0 },
1242     { "curved_fold_zoom_to_taskbar", "bool", 0, 0, 0 },
1243     { "dodge_gap_ratio", "float", "<min>0.0</min><max>1.0</max>", 0, 0 },
1244     { "dream_zoom_to_taskbar", "bool", 0, 0, 0 },
1245     { "glide1_away_position", "float", 0, 0, 0 },
1246     { "glide1_away_angle", "float", 0, 0, 0 },
1247     { "glide1_zoom_to_taskbar", "bool", 0, 0, 0 },
1248     { "glide2_away_position", "float", 0, 0, 0 },
1249     { "glide2_away_angle", "float", 0, 0, 0 },
1250     { "glide2_zoom_to_taskbar", "bool", 0, 0, 0 },
1251     { "horizontal_folds_amp_mult", "float", "<min>-1.0</min><max>3.0</max>", 0, 0 },
1252     { "horizontal_folds_num_folds", "int", "<min>1</min>", 0, 0 },
1253     { "horizontal_folds_zoom_to_taskbar", "bool", 0, 0, 0 },
1254     { "magic_lamp_moving_end", "bool", 0, 0, 0 },
1255     { "magic_lamp_grid_res", "int", "<min>4</min>", 0, 0 },
1256     { "magic_lamp_max_waves", "int", "<min>3</min>", 0, 0 },
1257     { "magic_lamp_amp_min", "float", "<min>200</min>", 0, 0 },
1258     { "magic_lamp_amp_max", "float", "<min>200</min>", 0, 0 },
1259     { "magic_lamp_open_start_width", "int", "<min>0</min>", 0, 0 },
1260     { "rollup_fixed_interior", "bool", 0, 0, 0 },
1261     { "sidekick_num_rotations", "float", "<min>0</min>", 0, 0 },
1262     { "sidekick_springiness", "float", "<min>0</min><max>1</max>", 0, 0 },
1263     { "sidekick_zoom_from_center", "int", RESTOSTRING (0, LAST_ZOOM_FROM_CENTER), 0, 0 },
1264     { "vacuum_moving_end", "bool", 0, 0, 0 },
1265     { "vacuum_grid_res", "int", "<min>4</min>", 0, 0 },
1266     { "vacuum_open_start_width", "int", "<min>0</min>", 0, 0 },
1267     { "wave_width", "float", "<min>0</min>", 0, 0 },
1268     { "wave_amp_mult", "float", "<min>-20.0</min><max>20.0</max>", 0, 0 },
1269     { "zoom_from_center", "int", RESTOSTRING (0, LAST_ZOOM_FROM_CENTER), 0, 0 },
1270     { "zoom_springiness", "float", "<min>0</min><max>1</max>", 0, 0 }
1271 };
1272 
1273 static CompOption *
animGetScreenOptions(CompPlugin * plugin,CompScreen * screen,int * count)1274 animGetScreenOptions(CompPlugin *plugin, CompScreen * screen, int *count)
1275 {
1276     ANIM_SCREEN(screen);
1277 
1278     *count = NUM_OPTIONS(as);
1279     return as->opt;
1280 }
1281 
1282 static void
objectInit(Object * object,float positionX,float positionY,float gridPositionX,float gridPositionY)1283 objectInit(Object * object,
1284 	   float positionX, float positionY,
1285 	   float gridPositionX, float gridPositionY)
1286 {
1287     object->gridPosition.x = gridPositionX;
1288     object->gridPosition.y = gridPositionY;
1289 
1290     object->position.x = positionX;
1291     object->position.y = positionY;
1292 
1293     object->offsetTexCoordForQuadBefore.x = 0;
1294     object->offsetTexCoordForQuadBefore.y = 0;
1295     object->offsetTexCoordForQuadAfter.x = 0;
1296     object->offsetTexCoordForQuadAfter.y = 0;
1297 }
1298 
1299 void
modelInitObjects(Model * model,int x,int y,int width,int height)1300 modelInitObjects(Model * model, int x, int y, int width, int height)
1301 {
1302     int gridX, gridY;
1303     int nGridCellsX, nGridCellsY;
1304     float x0, y0;
1305 
1306     x0 = model->scaleOrigin.x;
1307     y0 = model->scaleOrigin.y;
1308 
1309     // number of grid cells in x direction
1310     nGridCellsX = model->gridWidth - 1;
1311 
1312     if (model->forWindowEvent == WindowEventShade ||
1313 	model->forWindowEvent == WindowEventUnshade)
1314     {
1315 	// number of grid cells in y direction
1316 	nGridCellsY = model->gridHeight - 3;	// One allocated for top, one for bottom
1317 
1318 	float winContentsHeight =
1319 	    height - model->topHeight - model->bottomHeight;
1320 
1321 	//Top
1322 	float objectY = y + (0 - y0) * model->scale.y + y0;
1323 
1324 	for (gridX = 0; gridX < model->gridWidth; gridX++)
1325 	{
1326 	    objectInit(&model->objects[gridX],
1327 		       x + ((gridX * width / nGridCellsX) - x0) *
1328 		       model->scale.x + x0, objectY,
1329 		       (float)gridX / nGridCellsX, 0);
1330 	}
1331 
1332 	// Window contents
1333 	for (gridY = 1; gridY < model->gridHeight - 1; gridY++)
1334 	{
1335 	    float inWinY =
1336 		(gridY - 1) * winContentsHeight / nGridCellsY +
1337 		model->topHeight;
1338 	    float gridPosY = inWinY / height;
1339 
1340 	    objectY = y + (inWinY - y0) * model->scale.y + y0;
1341 
1342 	    for (gridX = 0; gridX < model->gridWidth; gridX++)
1343 	    {
1344 		objectInit(&model->objects[gridY * model->gridWidth + gridX],
1345 			   x + ((gridX * width / nGridCellsX) - x0) *
1346 			   model->scale.x + x0,
1347 			   objectY, (float)gridX / nGridCellsX, gridPosY);
1348 	    }
1349 	}
1350 
1351 	// Bottom (gridY is model->gridHeight-1 now)
1352 	objectY = y + (height - y0) * model->scale.y + y0;
1353 
1354 	for (gridX = 0; gridX < model->gridWidth; gridX++)
1355 	{
1356 	    objectInit(&model->objects[gridY * model->gridWidth + gridX],
1357 		       x + ((gridX * width / nGridCellsX) - x0) *
1358 		       model->scale.x + x0, objectY,
1359 		       (float)gridX / nGridCellsX, 1);
1360 	}
1361     }
1362     else
1363     {
1364 	int objIndex = 0;
1365 
1366 	// number of grid cells in y direction
1367 	nGridCellsY = model->gridHeight - 1;
1368 
1369 	for (gridY = 0; gridY < model->gridHeight; gridY++)
1370 	{
1371 	    float objectY =
1372 		y + ((gridY * height / nGridCellsY) -
1373 		     y0) * model->scale.y + y0;
1374 	    for (gridX = 0; gridX < model->gridWidth; gridX++)
1375 	    {
1376 		objectInit(&model->objects[objIndex],
1377 			   x + ((gridX * width / nGridCellsX) - x0) *
1378 			   model->scale.x + x0,
1379 			   objectY,
1380 			   (float)gridX / nGridCellsX,
1381 			   (float)gridY / nGridCellsY);
1382 		objIndex++;
1383 	    }
1384 	}
1385     }
1386 }
1387 
1388 static void
modelMove(Model * model,float tx,float ty)1389 modelMove (Model *model,
1390 	   float tx,
1391 	   float ty)
1392 {
1393     Object *object = model->objects;
1394     int i;
1395     for (i = 0; i < model->numObjects; i++, object++)
1396     {
1397 	object->position.x += tx;
1398 	object->position.y += ty;
1399     }
1400 }
1401 
createModel(CompWindow * w,WindowEvent forWindowEvent,AnimEffect forAnimEffect,int gridWidth,int gridHeight)1402 static Model *createModel(CompWindow * w,
1403 			  WindowEvent forWindowEvent,
1404 			  AnimEffect forAnimEffect, int gridWidth,
1405 			  int gridHeight)
1406 {
1407     int x = WIN_X(w);
1408     int y = WIN_Y(w);
1409     int width = WIN_W(w);
1410     int height = WIN_H(w);
1411 
1412     Model *model;
1413 
1414     model = calloc(1, sizeof(Model));
1415     if (!model)
1416     {
1417 	compLogMessage ("animation", CompLogLevelError,
1418 			"Not enough memory");
1419 	return 0;
1420     }
1421 
1422     model->gridWidth = gridWidth;
1423     model->gridHeight = gridHeight;
1424     model->numObjects = gridWidth * gridHeight;
1425     model->objects = calloc(model->numObjects, sizeof(Object));
1426     if (!model->objects)
1427     {
1428 	compLogMessage ("animation", CompLogLevelError,
1429 			"Not enough memory");
1430 	free(model);
1431 	return 0;
1432     }
1433 
1434     // Store win. size to check later
1435     model->winWidth = width;
1436     model->winHeight = height;
1437 
1438     // For shading
1439     model->forWindowEvent = forWindowEvent;
1440     model->topHeight = w->output.top;
1441     model->bottomHeight = w->output.bottom;
1442 
1443     model->scale.x = 1.0f;
1444     model->scale.y = 1.0f;
1445 
1446     model->scaleOrigin.x = 0.0f;
1447     model->scaleOrigin.y = 0.0f;
1448 
1449     modelInitObjects(model, x, y, width, height);
1450 
1451     return model;
1452 }
1453 
1454 static void
animFreeModel(AnimWindow * aw)1455 animFreeModel(AnimWindow *aw)
1456 {
1457     if (!aw->com.model)
1458 	return;
1459 
1460     if (aw->com.model->objects)
1461 	free(aw->com.model->objects);
1462     free(aw->com.model);
1463     aw->com.model = NULL;
1464 }
1465 
1466 static Bool
animEnsureModel(CompWindow * w)1467 animEnsureModel(CompWindow * w)
1468 {
1469     ANIM_WINDOW(w);
1470 
1471     WindowEvent forWindowEvent = aw->com.curWindowEvent;
1472     AnimEffect forAnimEffect = aw->com.curAnimEffect;
1473 
1474     int gridWidth = 2;
1475     int gridHeight = 2;
1476 
1477     if (forAnimEffect->properties.initGridFunc)
1478 	forAnimEffect->properties.initGridFunc (w, &gridWidth, &gridHeight);
1479 
1480     Bool isShadeUnshadeEvent =
1481 	(forWindowEvent == WindowEventShade ||
1482 	 forWindowEvent == WindowEventUnshade);
1483 
1484     Bool wasShadeUnshadeEvent = aw->com.model &&
1485 	(aw->com.model->forWindowEvent == WindowEventShade ||
1486 	 aw->com.model->forWindowEvent == WindowEventUnshade);
1487 
1488     if (!aw->com.model ||
1489 	gridWidth != aw->com.model->gridWidth ||
1490 	gridHeight != aw->com.model->gridHeight ||
1491 	(isShadeUnshadeEvent != wasShadeUnshadeEvent) ||
1492 	aw->com.model->winWidth != WIN_W(w) || aw->com.model->winHeight != WIN_H(w))
1493     {
1494 	animFreeModel(aw);
1495 	aw->com.model = createModel(w, forWindowEvent, forAnimEffect,
1496 				gridWidth, gridHeight);
1497 	if (!aw->com.model)
1498 	    return FALSE;
1499     }
1500 
1501     return TRUE;
1502 }
1503 
cleanUpParentChildChainItem(AnimScreen * as,AnimWindow * aw)1504 static void cleanUpParentChildChainItem(AnimScreen *as, AnimWindow *aw)
1505 {
1506     if (aw->winThisIsPaintedBefore && !aw->winThisIsPaintedBefore->destroyed)
1507     {
1508 	AnimWindow *aw2 =
1509 	    GET_ANIM_WINDOW(aw->winThisIsPaintedBefore, as);
1510 	if (aw2)
1511 	    aw2->winToBePaintedBeforeThis = NULL;
1512     }
1513     aw->winThisIsPaintedBefore = NULL;
1514     aw->moreToBePaintedPrev = NULL;
1515     aw->moreToBePaintedNext = NULL;
1516     aw->isDodgeSubject = FALSE;
1517     aw->skipPostPrepareScreen = FALSE;
1518 }
1519 
1520 // Update this window's dodgers so that they no longer point
1521 // to this window as their subject
1522 static void
clearDodgersSubject(AnimScreen * as,CompWindow * w)1523 clearDodgersSubject (AnimScreen *as, CompWindow *w)
1524 {
1525     CompWindow *dw;
1526     AnimWindow *adw;
1527 
1528     ANIM_WINDOW (w);
1529 
1530     for (dw = aw->dodgeChainStart; dw; dw = adw->dodgeChainNext)
1531     {
1532 	adw = GET_ANIM_WINDOW(dw, as);
1533 	if (!adw)
1534 	    break;
1535 	if (adw->dodgeSubjectWin == w)
1536 	    adw->dodgeSubjectWin = NULL;
1537     }
1538 }
1539 
1540 // Remove this dodger window from the dodge chain
1541 static void
removeFromDodgeChain(AnimScreen * as,CompWindow * dw)1542 removeFromDodgeChain (AnimScreen *as, CompWindow *dw)
1543 {
1544     AnimWindow *adw = GET_ANIM_WINDOW(dw, as);
1545     if (!adw)
1546 	return;
1547 
1548     if (adw->dodgeSubjectWin)
1549     {
1550 	AnimWindow *awSubject = GET_ANIM_WINDOW (adw->dodgeSubjectWin, as);
1551 
1552 	// if dw is the starting window in the dodge chain
1553 	if (awSubject && awSubject->dodgeChainStart == dw)
1554 	{
1555 	    if (adw->dodgeChainNext)
1556 	    {
1557 		// Subject is being raised and there is a next dodger.
1558 		awSubject->dodgeChainStart = adw->dodgeChainNext;
1559 	    }
1560 	    else
1561 	    {
1562 		// Subject is being lowered or there is no next dodger.
1563 		// In either case, we can point chain start to prev. in chain,
1564 		// which can be NULL.
1565 		awSubject->dodgeChainStart = adw->dodgeChainPrev;
1566 	    }
1567 	}
1568     }
1569 
1570     if (adw->dodgeChainNext)
1571     {
1572 	AnimWindow *awNext = GET_ANIM_WINDOW (adw->dodgeChainNext, as);
1573 
1574 	// Point adw->next's prev. to adw->prev.
1575 	if (awNext)
1576 	    awNext->dodgeChainPrev = adw->dodgeChainPrev;
1577     }
1578 
1579     if (adw->dodgeChainPrev)
1580     {
1581 	AnimWindow *awPrev = GET_ANIM_WINDOW (adw->dodgeChainPrev, as);
1582 
1583 	// Point adw->prev.'s next to adw->next
1584 	if (awPrev)
1585 	    awPrev->dodgeChainNext = adw->dodgeChainNext;
1586     }
1587 }
1588 
postAnimationCleanupCustom(CompWindow * w,Bool closing,Bool finishing,Bool clearMatchingRow)1589 static void postAnimationCleanupCustom (CompWindow * w,
1590 					Bool closing,
1591 					Bool finishing,
1592 					Bool clearMatchingRow)
1593 {
1594     ANIM_WINDOW(w);
1595     ANIM_SCREEN(w->screen);
1596 
1597     if (// make sure window shadows (which are not drawn by polygon engine)
1598 	// are damaged
1599 	(aw->com.curAnimEffect &&
1600 	 aw->com.curAnimEffect != AnimEffectNone &&
1601 	 aw->com.curAnimEffect != AnimEffectRandom &&
1602 	 aw->com.curAnimEffect->properties.addCustomGeometryFunc &&
1603 	 (aw->com.curWindowEvent == WindowEventOpen ||
1604 	  aw->com.curWindowEvent == WindowEventUnminimize ||
1605 	  aw->com.curWindowEvent == WindowEventUnshade ||
1606 	  aw->com.curWindowEvent == WindowEventFocus)) ||
1607 	// make sure the window gets fully damaged with
1608 	// effects that possibly have models that don't cover
1609 	// the whole window (like in magic lamp with menus)
1610 	aw->com.curAnimEffect == AnimEffectMagicLamp ||
1611 	aw->com.curAnimEffect == AnimEffectVacuum ||
1612 	// make sure dodging windows get one last damage
1613 	aw->com.curAnimEffect == AnimEffectDodge ||
1614 	// make sure non-animated closing windows get a damage
1615 	(aw->com.curWindowEvent == WindowEventClose &&
1616 	 aw->com.curAnimEffect == AnimEffectNone))
1617     {
1618 	updateBBWindow (NULL, w, &aw->BB);
1619     }
1620     // Clear winPassingThrough of each window
1621     // that this one was passing through
1622     // during focus effect
1623     if (aw->com.curAnimEffect == AnimEffectFocusFade)
1624     {
1625 	CompWindow *w2;
1626 	for (w2 = w->screen->windows; w2; w2 = w2->next)
1627 	{
1628 	    AnimWindow *aw2;
1629 
1630 	    aw2 = GET_ANIM_WINDOW(w2, as);
1631 	    if (aw2->winPassingThrough == w)
1632 		aw2->winPassingThrough = NULL;
1633 	}
1634     }
1635 
1636     if (aw->com.curAnimEffect == AnimEffectFocusFade ||
1637 	aw->com.curAnimEffect == AnimEffectDodge)
1638     {
1639 	as->walkerAnimCount--;
1640     }
1641 
1642     if (aw->com.curAnimEffect &&
1643 	aw->com.curAnimEffect != AnimEffectNone &&
1644 	aw->com.curAnimEffect != AnimEffectRandom &&
1645 	aw->com.curAnimEffect->properties.cleanupFunc)
1646 	aw->com.curAnimEffect->properties.cleanupFunc (w);
1647 
1648     if (aw->isDodgeSubject)
1649 	clearDodgersSubject (as, w);
1650     else if (aw->com.curAnimEffect == AnimEffectDodge)
1651 	removeFromDodgeChain (as, w);
1652 
1653     aw->com.curWindowEvent = WindowEventNone;
1654     aw->com.curAnimEffect = AnimEffectNone;
1655     aw->com.animOverrideProgressDir = 0;
1656     aw->com.usingTransform = FALSE;
1657 
1658     aw->magicLampWaveCount = 0;
1659 
1660     if (aw->magicLampWaves)
1661     {
1662 	free (aw->magicLampWaves);
1663 	aw->magicLampWaves = 0;
1664     }
1665 
1666     if (aw->BB.x1 != MAXSHORT)
1667     {
1668 	// damage BB
1669 	damageBoundingBox (w);
1670     }
1671     aw->BB.x1 = aw->BB.y1 = MAXSHORT;
1672     aw->BB.x2 = aw->BB.y2 = MINSHORT;
1673 
1674     Bool thereIsUnfinishedChainElem = FALSE;
1675 
1676     // Look for still playing windows in parent-child chain
1677     CompWindow *wCur = aw->moreToBePaintedNext;
1678     while (wCur)
1679     {
1680 	AnimWindow *awCur = GET_ANIM_WINDOW(wCur, as);
1681 
1682 	if (awCur->com.animRemainingTime > 0)
1683 	{
1684 	    thereIsUnfinishedChainElem = TRUE;
1685 	    break;
1686 	}
1687 	wCur = awCur->moreToBePaintedNext;
1688     }
1689     if (!thereIsUnfinishedChainElem)
1690     {
1691 	wCur = aw->moreToBePaintedPrev;
1692 	while (wCur)
1693 	{
1694 	    AnimWindow *awCur = GET_ANIM_WINDOW(wCur, as);
1695 
1696 	    if (awCur->com.animRemainingTime > 0)
1697 	    {
1698 		thereIsUnfinishedChainElem = TRUE;
1699 		break;
1700 	    }
1701 	    wCur = awCur->moreToBePaintedPrev;
1702 	}
1703     }
1704     if (closing || finishing || !thereIsUnfinishedChainElem)
1705     {
1706 	// Finish off all windows in parent-child chain
1707 	CompWindow *wCur = aw->moreToBePaintedNext;
1708 	while (wCur)
1709 	{
1710 	    AnimWindow *awCur = GET_ANIM_WINDOW(wCur, as);
1711 	    if (awCur->isDodgeSubject)
1712 		clearDodgersSubject (as, wCur);
1713 	    wCur = awCur->moreToBePaintedNext;
1714 	    cleanUpParentChildChainItem(as, awCur);
1715 	}
1716 	wCur = w;
1717 	while (wCur)
1718 	{
1719 	    AnimWindow *awCur = GET_ANIM_WINDOW(wCur, as);
1720 	    if (awCur->isDodgeSubject)
1721 		clearDodgersSubject (as, wCur);
1722 	    wCur = awCur->moreToBePaintedPrev;
1723 	    cleanUpParentChildChainItem(as, awCur);
1724 	}
1725     }
1726 
1727     aw->state = aw->newState;
1728 
1729     if (clearMatchingRow)
1730 	aw->curAnimSelectionRow = -1;
1731 
1732     if (aw->com.drawRegion)
1733 	XDestroyRegion(aw->com.drawRegion);
1734     aw->com.drawRegion = NULL;
1735     aw->com.useDrawRegion = FALSE;
1736 
1737     aw->animInitialized = FALSE;
1738     aw->remainderSteps = 0;
1739     aw->com.animRemainingTime = 0;
1740 
1741     // Reset dodge parameters
1742     aw->dodgeMaxAmount = 0;
1743     if (!(aw->moreToBePaintedPrev ||
1744 	  aw->moreToBePaintedNext))
1745     {
1746 	aw->isDodgeSubject = FALSE;
1747 	aw->skipPostPrepareScreen = FALSE;
1748     }
1749 
1750     if (aw->restackInfo)
1751     {
1752 	free(aw->restackInfo);
1753 	aw->restackInfo = NULL;
1754     }
1755 
1756     if (!finishing)
1757     {
1758 	aw->ignoreDamage = TRUE;
1759 	while (aw->unmapCnt)
1760 	{
1761 	    unmapWindow(w);
1762 	    aw->unmapCnt--;
1763 	}
1764 	aw->ignoreDamage = FALSE;
1765     }
1766     while (aw->destroyCnt)
1767     {
1768 	destroyWindow(w);
1769 	aw->destroyCnt--;
1770     }
1771 }
1772 
postAnimationCleanup(CompWindow * w)1773 void postAnimationCleanup (CompWindow * w)
1774 {
1775     postAnimationCleanupCustom (w, FALSE, FALSE, TRUE);
1776 }
1777 
1778 static void
postAnimationCleanupPrev(CompWindow * w,Bool closing,Bool clearMatchingRow)1779 postAnimationCleanupPrev (CompWindow * w,
1780 			  Bool closing,
1781 			  Bool clearMatchingRow)
1782 {
1783     ANIM_WINDOW(w);
1784 
1785     int curAnimSelectionRow = aw->curAnimSelectionRow;
1786     // Use previous event's anim selection row
1787     aw->curAnimSelectionRow = aw->prevAnimSelectionRow;
1788 
1789     postAnimationCleanupCustom (w, closing, FALSE, clearMatchingRow);
1790 
1791     // Restore current event's anim selection row
1792     aw->curAnimSelectionRow = curAnimSelectionRow;
1793 }
1794 
1795 static void
animActivateEvent(CompScreen * s,Bool activating)1796 animActivateEvent (CompScreen *s,
1797 		   Bool       activating)
1798 {
1799     ANIM_SCREEN(s);
1800 
1801     if (activating)
1802     {
1803 	if (as->animInProgress)
1804 	    return;
1805     }
1806     as->animInProgress = activating;
1807 
1808     CompOption o[2];
1809 
1810     o[0].type = CompOptionTypeInt;
1811     o[0].name = "root";
1812     o[0].value.i = s->root;
1813 
1814     o[1].type = CompOptionTypeBool;
1815     o[1].name = "active";
1816     o[1].value.b = activating;
1817 
1818     (*s->display->handleCompizEvent) (s->display, "animation", "activate", o, 2);
1819 }
1820 
1821 static const PluginEventInfo watchedPlugins[] =
1822 {
1823     {"switcher", "activate"},
1824     {"staticswitcher", "activate"},
1825     {"ring", "activate"},
1826     {"shift", "activate"},
1827     {"stackswitch", "activate"},
1828     {"scale", "activate"},
1829     // the above ones are the switchers
1830     {"group", "tabChangeActivate"},
1831     {"fadedesktop", "activate"},
1832 };
1833 
1834 static Bool
otherPluginsActive(AnimScreen * as)1835 otherPluginsActive(AnimScreen *as)
1836 {
1837     int i;
1838     for (i = 0; i < NUM_WATCHED_PLUGINS; i++)
1839 	if (as->pluginActive[i])
1840 	    return TRUE;
1841     return FALSE;
1842 }
1843 
1844 static inline Bool
isWinVisible(CompWindow * w)1845 isWinVisible(CompWindow *w)
1846 {
1847     return (!w->destroyed &&
1848 	    !(!w->shaded &&
1849 	      (w->attrib.map_state != IsViewable)));
1850 }
1851 
1852 static inline void
getHostedOnWin(AnimScreen * as,CompWindow * w,CompWindow * wHost)1853 getHostedOnWin (AnimScreen *as,
1854 		CompWindow *w,
1855 		CompWindow *wHost)
1856 {
1857     ANIM_WINDOW(w);
1858     AnimWindow *awHost = GET_ANIM_WINDOW(wHost, as);
1859     awHost->winToBePaintedBeforeThis = w;
1860     aw->winThisIsPaintedBefore = wHost;
1861 }
1862 
1863 static void
initiateFocusAnimation(CompWindow * w)1864 initiateFocusAnimation(CompWindow *w)
1865 {
1866     CompScreen *s = w->screen;
1867     ANIM_SCREEN(s);
1868     ANIM_WINDOW(w);
1869     int duration = 200;
1870 
1871     if (aw->com.curWindowEvent != WindowEventNone || otherPluginsActive(as))
1872 	return;
1873 
1874     // Check the "switcher post-wait" counter that effectively prevents
1875     // focus animation to be initiated when the zoom option value is low
1876     // in Switcher.
1877     if (switcherPostWait)
1878 	return;
1879 
1880     AnimEffect chosenEffect =
1881 	getMatchingAnimSelection (w, AnimEventFocus, &duration);
1882 
1883     if (chosenEffect != AnimEffectNone &&
1884 	// On unminimization, focus event is fired first.
1885 	// When this happens and minimize is in progress,
1886 	// don't prevent rewinding of minimize when unminimize is fired
1887 	// right after this focus event.
1888 	aw->com.curWindowEvent != WindowEventMinimize)
1889     {
1890 	CompWindow *wStart = NULL;
1891 	CompWindow *wEnd = NULL;
1892 	CompWindow *wOldAbove = NULL;
1893 
1894 	RestackInfo *restackInfo = aw->restackInfo;
1895 	Bool raised = TRUE;
1896 
1897 	if (restackInfo)
1898 	{
1899 	    wStart = restackInfo->wStart;
1900 	    wEnd = restackInfo->wEnd;
1901 	    wOldAbove = restackInfo->wOldAbove;
1902 	    raised = restackInfo->raised;
1903 	}
1904 
1905 	// FOCUS event!
1906 
1907 	aw->com.curWindowEvent = WindowEventFocus;
1908 	aw->com.curAnimEffect = chosenEffect;
1909 
1910 	if (chosenEffect == AnimEffectFocusFade ||
1911 	    chosenEffect == AnimEffectDodge)
1912 	{
1913 	    as->walkerAnimCount++;
1914 
1915 	    // Find union region of all windows that will be
1916 	    // faded through by w. If the region is empty, don't
1917 	    // run focus fade effect.
1918 
1919 	    Region fadeRegion = XCreateRegion();
1920 	    Region thisAndSubjectIntersection = XCreateRegion();
1921 	    Region thisWinRegion = XCreateRegion();
1922 	    Region subjectWinRegion = XCreateRegion();
1923 	    XRectangle rect;
1924 
1925 	    int numDodgingWins = 0;
1926 
1927 	    // Compute subject win. region
1928 	    rect.x = BORDER_X(w);
1929 	    rect.y = BORDER_Y(w);
1930 	    rect.width = BORDER_W(w);
1931 	    rect.height = BORDER_H(w);
1932 	    XUnionRectWithRegion(&rect, &emptyRegion, subjectWinRegion);
1933 
1934 	    CompWindow *dw; // Dodge or Focus fade candidate window
1935 	    for (dw = wStart; dw && dw != wEnd->next; dw = dw->next)
1936 	    {
1937 		if (!isWinVisible(dw) ||
1938 		    dw->wmType & CompWindowTypeDockMask)
1939 		    continue;
1940 
1941 		AnimWindow *adw = GET_ANIM_WINDOW(dw, as);
1942 
1943 		// Skip windows that have been restacked
1944 		if (dw != wEnd && adw->restackInfo)
1945 		    continue;
1946 
1947 		// Skip subject window for focus fade
1948 		if (w == dw && chosenEffect == AnimEffectFocusFade)
1949 		    continue;
1950 
1951 		Bool nonMatching = FALSE;
1952 		if (chosenEffect == AnimEffectDodge &&
1953 		    getMatchingAnimSelection (dw, AnimEventFocus, NULL) !=
1954 		    chosenEffect)
1955 		    nonMatching = TRUE;
1956 
1957 		// Compute intersection of this (dw) with subject
1958 		rect.x = BORDER_X(dw);
1959 		rect.y = BORDER_Y(dw);
1960 		rect.width = BORDER_W(dw);
1961 		rect.height = BORDER_H(dw);
1962 		XUnionRectWithRegion(&rect, &emptyRegion, thisWinRegion);
1963 		XIntersectRegion(subjectWinRegion, thisWinRegion,
1964 				 thisAndSubjectIntersection);
1965 		XUnionRegion(fadeRegion, thisAndSubjectIntersection,
1966 			     fadeRegion);
1967 
1968 		if (chosenEffect == AnimEffectFocusFade)
1969 		{
1970 		    adw->winPassingThrough = w;
1971 		}
1972 		else if (chosenEffect == AnimEffectDodge &&
1973 			 !XEmptyRegion(thisAndSubjectIntersection) &&
1974 			 (adw->com.curAnimEffect == AnimEffectNone ||
1975 			  (adw->com.curAnimEffect == AnimEffectDodge)) &&
1976 			 dw->id != w->id) // don't let the subject dodge itself
1977 		{
1978 		    // Mark this window for dodge
1979 
1980 		    numDodgingWins++;
1981 		    adw->dodgeOrder = numDodgingWins;
1982 		    if (nonMatching) // Use neg. values for non-matching windows
1983 			adw->dodgeOrder *= -1;
1984 		}
1985 	    }
1986 
1987 	    if (XEmptyRegion(fadeRegion))
1988 	    {
1989 		// empty intersection -> won't be drawn (will end prematurely)
1990 		duration = 0;
1991 	    }
1992 	    if ((chosenEffect == AnimEffectFocusFade ||
1993 		 chosenEffect == AnimEffectDodge) && wOldAbove)
1994 	    {
1995 		// Store this window in the next window
1996 		// so that this is drawn before that,
1997 		// i.e. in its old place
1998 		getHostedOnWin(as, w, wOldAbove);
1999 	    }
2000 
2001 	    if (chosenEffect == AnimEffectDodge)
2002 	    {
2003 		float maxTransformTotalProgress = 0;
2004 		float dodgeMaxStartProgress =
2005 		    numDodgingWins *
2006 		    animGetF (w, ANIM_SCREEN_OPTION_DODGE_GAP_RATIO) *
2007 		    duration / 1000.0f;
2008 
2009 		CompWindow *wDodgeChainLastVisited = NULL;
2010 
2011 		animActivateEvent(s, TRUE);
2012 
2013 		aw->isDodgeSubject = TRUE;
2014 		aw->dodgeChainStart = NULL;
2015 
2016 		for (dw = wStart; dw && dw != wEnd->next; dw = dw->next)
2017 		{
2018 		    AnimWindow *adw = GET_ANIM_WINDOW(dw, as);
2019 
2020 		    // Skip non-dodgers
2021 		    if (adw->dodgeOrder == 0)
2022 			continue;
2023 
2024 		    // Initiate dodge for this window
2025 
2026 		    Bool stationaryDodger = FALSE;
2027 		    if (adw->dodgeOrder < 0)
2028 		    {
2029 			adw->dodgeOrder *= -1; // Make it positive again
2030 			stationaryDodger = TRUE;
2031 		    }
2032 		    if (adw->com.curAnimEffect != AnimEffectDodge)
2033 		    {
2034 			adw->com.curAnimEffect = AnimEffectDodge;
2035 			as->walkerAnimCount++;
2036 		    }
2037 		    adw->dodgeSubjectWin = w;
2038 
2039 		    // Slight change in dodge movement start
2040 		    // to reflect stacking order of dodgy windows
2041 		    if (raised)
2042 			adw->com.transformStartProgress =
2043 			    dodgeMaxStartProgress *
2044 			    (adw->dodgeOrder - 1) / numDodgingWins;
2045 		    else
2046 			adw->com.transformStartProgress =
2047 			    dodgeMaxStartProgress *
2048 			    (1 - (float)adw->dodgeOrder / numDodgingWins);
2049 
2050 		    float transformTotalProgress =
2051 			1 + adw->com.transformStartProgress;
2052 
2053 		    if (maxTransformTotalProgress < transformTotalProgress)
2054 			maxTransformTotalProgress = transformTotalProgress;
2055 
2056 		    // normalize
2057 		    adw->com.transformStartProgress /=
2058 			transformTotalProgress;
2059 
2060 		    if (stationaryDodger)
2061 		    {
2062 			adw->com.transformStartProgress = 0;
2063 			transformTotalProgress = 0;
2064 		    }
2065 
2066 		    adw->com.animTotalTime =
2067 			transformTotalProgress * duration;
2068 		    adw->com.animRemainingTime = adw->com.animTotalTime;
2069 
2070 		    // Put window on dodge chain
2071 
2072 		    // if dodge chain was started before
2073 		    if (wDodgeChainLastVisited)
2074 		    {
2075 			AnimWindow *awDodgeChainLastVisited =
2076 			    GET_ANIM_WINDOW(wDodgeChainLastVisited, as);
2077 			if (raised)
2078 			    awDodgeChainLastVisited->dodgeChainNext = dw;
2079 			else
2080 			    awDodgeChainLastVisited->dodgeChainPrev = dw;
2081 		    }
2082 		    else if (raised) // mark chain start
2083 		    {
2084 			aw->dodgeChainStart = dw;
2085 		    }
2086 		    if (raised)
2087 		    {
2088 			adw->dodgeChainPrev = wDodgeChainLastVisited;
2089 			adw->dodgeChainNext = NULL;
2090 		    }
2091 		    else
2092 		    {
2093 			adw->dodgeChainPrev = NULL;
2094 			adw->dodgeChainNext = wDodgeChainLastVisited;
2095 		    }
2096 
2097 		    // Find direction (left, right, up, down)
2098 		    // that minimizes dodge amount
2099 
2100 		    // Dodge amount (dodge shadows as well)
2101 
2102 		    int dodgeAmount[4];
2103 
2104 		    int i;
2105 		    for (i = 0; i < 4; i++)
2106 			dodgeAmount[i] = DODGE_AMOUNT(w, dw, i);
2107 
2108 		    int amountMin = abs(dodgeAmount[0]);
2109 		    int iMin = 0;
2110 		    for (i=1; i<4; i++)
2111 		    {
2112 			int absAmount = abs(dodgeAmount[i]);
2113 			if (absAmount < amountMin)
2114 			{
2115 			    amountMin = absAmount;
2116 			    iMin = i;
2117 			}
2118 		    }
2119 		    adw->dodgeMaxAmount = dodgeAmount[iMin];
2120 		    adw->dodgeDirection = iMin;
2121 
2122 		    wDodgeChainLastVisited = dw;
2123 
2124 		    // Reset back to 0 for the next dodge calculation
2125 		    adw->dodgeOrder = 0;
2126 		}
2127 		if (aw->isDodgeSubject)
2128 		    aw->dodgeMaxAmount = 0;
2129 
2130 		// if subject is being lowered,
2131 		// point chain-start to the topmost dodging window
2132 		if (!raised)
2133 		{
2134 		    aw->dodgeChainStart = wDodgeChainLastVisited;
2135 		}
2136 
2137 		aw->com.animTotalTime =
2138 		    maxTransformTotalProgress * duration;
2139 	    }
2140 
2141 	    XDestroyRegion (fadeRegion);
2142 	    XDestroyRegion (thisAndSubjectIntersection);
2143 	    XDestroyRegion (thisWinRegion);
2144 	    XDestroyRegion (subjectWinRegion);
2145 	}
2146 
2147 	if (!animEnsureModel(w))
2148 	{
2149 	    postAnimationCleanup (w);
2150 	    return;
2151 	}
2152 
2153 	animActivateEvent(s, TRUE);
2154 
2155 	if (chosenEffect != AnimEffectDodge)
2156 	    aw->com.animTotalTime = duration;
2157 	aw->com.animRemainingTime = aw->com.animTotalTime;
2158 
2159 	damagePendingOnScreen (s);
2160     }
2161 }
2162 
2163 // returns whether this window is relevant for fade focus
2164 static Bool
relevantForFadeFocus(CompWindow * nw)2165 relevantForFadeFocus(CompWindow *nw)
2166 {
2167     if (!((nw->wmType &
2168 	   // these two are to be used as "host" windows
2169 	   // to host the painting of windows being focused
2170 	   // at a stacking order lower than them
2171 	   (CompWindowTypeDockMask | CompWindowTypeSplashMask)) ||
2172 	  nw->wmType == CompWindowTypeNormalMask ||
2173 	  nw->wmType == CompWindowTypeDialogMask ||
2174 	  nw->wmType == CompWindowTypeUtilMask ||
2175 	  nw->wmType == CompWindowTypeUnknownMask))
2176     {
2177 	return FALSE;
2178     }
2179     return isWinVisible(nw);
2180 }
2181 
2182 static Bool
restackInfoStillGood(CompScreen * s,RestackInfo * restackInfo)2183 restackInfoStillGood(CompScreen *s, RestackInfo *restackInfo)
2184 {
2185     Bool wStartGood = FALSE;
2186     Bool wEndGood = FALSE;
2187     Bool wOldAboveGood = FALSE;
2188     Bool wRestackedGood = FALSE;
2189 
2190     CompWindow *w;
2191     for (w = s->windows; w; w = w->next)
2192     {
2193 	if (restackInfo->wStart == w && isWinVisible(w))
2194 	    wStartGood = TRUE;
2195 	if (restackInfo->wEnd == w && isWinVisible(w))
2196 	    wEndGood = TRUE;
2197 	if (restackInfo->wRestacked == w && isWinVisible(w))
2198 	    wRestackedGood = TRUE;
2199 	if (restackInfo->wOldAbove == w && isWinVisible(w))
2200 	    wOldAboveGood = TRUE;
2201     }
2202     return (wStartGood && wEndGood && wOldAboveGood && wRestackedGood);
2203 }
2204 
2205 // Reset stacking related info
2206 static void
resetStackingInfo(CompScreen * s)2207 resetStackingInfo (CompScreen *s)
2208 {
2209     CompWindow *w;
2210     for (w = s->windows; w; w = w->next)
2211     {
2212 	ANIM_WINDOW (w);
2213 
2214 	aw->configureNotified = FALSE;
2215 	if (aw->restackInfo)
2216 	{
2217 	    free (aw->restackInfo);
2218 	    aw->restackInfo = NULL;
2219 	}
2220     }
2221 }
2222 
2223 // Returns TRUE if linking wCur to wNext would not result
2224 // in a circular chain being formed.
2225 static Bool
wontCreateCircularChain(CompWindow * wCur,CompWindow * wNext)2226 wontCreateCircularChain (CompWindow *wCur, CompWindow *wNext)
2227 {
2228     ANIM_SCREEN (wCur->screen);
2229     AnimWindow *awNext = NULL;
2230 
2231     while (wNext)
2232     {
2233 	if (wNext == wCur) // would form circular chain
2234 	    return FALSE;
2235 
2236 	awNext = GET_ANIM_WINDOW (wNext, as);
2237 	if (!awNext)
2238 	    return FALSE;
2239 
2240 	wNext = awNext->moreToBePaintedNext;
2241     }
2242     return TRUE;
2243 }
2244 
animPreparePaintScreen(CompScreen * s,int msSinceLastPaint)2245 static void animPreparePaintScreen(CompScreen * s, int msSinceLastPaint)
2246 {
2247     CompWindow *w;
2248 
2249     ANIM_SCREEN(s);
2250 
2251     // Check and update "switcher post wait" counter
2252     if (switcherPostWait > 0)
2253     {
2254 	switcherPostWait++;
2255 	if (switcherPostWait > 4) // wait over
2256 	{
2257 	    switcherPostWait = 0;
2258 
2259 	    // Reset stacking related info since it will
2260 	    // cause problems because of the restacking
2261 	    // just done by Switcher.
2262 	    resetStackingInfo (s);
2263 	}
2264     }
2265 
2266     if (as->aWinWasRestackedJustNow)
2267     {
2268 	/*
2269 	  Handle focusing windows with multiple utility/dialog windows
2270 	  (like gobby), as in this case where gobby was raised with its
2271 	  utility windows:
2272 
2273 	  was: C0001B 36000A5 1E0000C 1E0005B 1E00050 3205B63 600003
2274 	  now: C0001B 36000A5 1E0000C 1E00050 3205B63 1E0005B 600003
2275 
2276 	  was: C0001B 36000A5 1E0000C 1E00050 3205B63 1E0005B 600003
2277 	  now: C0001B 36000A5 1E0000C 3205B63 1E00050 1E0005B 600003
2278 
2279 	  was: C0001B 36000A5 1E0000C 3205B63 1E00050 1E0005B 600003
2280 	  now: C0001B 36000A5 3205B63 1E0000C 1E00050 1E0005B 600003
2281 	*/
2282 	CompWindow *wOldAbove = NULL;
2283 	for (w = s->windows; w; w = w->next)
2284 	{
2285 	    ANIM_WINDOW(w);
2286 	    if (aw->restackInfo)
2287 	    {
2288 		if (aw->com.curWindowEvent != WindowEventNone ||
2289 		    otherPluginsActive(as) ||
2290 		    // Don't animate with stale restack info
2291 		    !restackInfoStillGood(s, aw->restackInfo))
2292 		{
2293 		    continue;
2294 		}
2295 		if (!wOldAbove)
2296 		{
2297 		    // Pick the old above of the bottommost one
2298 		    wOldAbove = aw->restackInfo->wOldAbove;
2299 		}
2300 		else
2301 		{
2302 		    // Use as wOldAbove for every focus fading window
2303 		    // (i.e. the utility/dialog windows of an app.)
2304 		    if (wOldAbove != w)
2305 			aw->restackInfo->wOldAbove = wOldAbove;
2306 		}
2307 	    }
2308 	}
2309 	// do in reverse order so that focus-fading chains are handled
2310 	// properly
2311 	for (w = s->reverseWindows; w; w = w->prev)
2312 	{
2313 	    ANIM_WINDOW(w);
2314 	    if (aw->restackInfo)
2315 	    {
2316 		if (aw->com.curWindowEvent != WindowEventNone ||
2317 		    // Don't initiate focus anim for current dodgers
2318 		    aw->com.curAnimEffect != AnimEffectNone ||
2319 		    // Don't initiate focus anim for windows being passed thru
2320 		    aw->winPassingThrough ||
2321 		    otherPluginsActive(as) ||
2322 		    // Don't animate with stale restack info
2323 		    !restackInfoStillGood(s, aw->restackInfo))
2324 		{
2325 		    free(aw->restackInfo);
2326 		    aw->restackInfo = NULL;
2327 		    continue;
2328 		}
2329 
2330 		// Find the first window at a higher stacking order than w
2331 		CompWindow *nw;
2332 		for (nw = w->next; nw; nw = nw->next)
2333 		{
2334 		    if (relevantForFadeFocus(nw))
2335 			break;
2336 		}
2337 
2338 		// If w is being lowered, there has to be a window
2339 		// at a higher stacking position than w (like a panel)
2340 		// which this w's copy can be painted before.
2341 		// Otherwise the animation will only show w fading in
2342 		// rather than 2 copies of it cross-fading.
2343 		if (!aw->restackInfo->raised && !nw)
2344 		{
2345 		    // Free unnecessary restackInfo
2346 		    free(aw->restackInfo);
2347 		    aw->restackInfo = NULL;
2348 		    continue;
2349 		}
2350 
2351 		// Check if above window is focus-fading too.
2352 		// (like a dialog of an app. window)
2353 		// If so, focus-fade this together with the one above
2354 		// (link to it)
2355 		if (nw)
2356 		{
2357 		    AnimWindow *awNext = GET_ANIM_WINDOW(nw, as);
2358 		    if (awNext && awNext->winThisIsPaintedBefore &&
2359 			wontCreateCircularChain (w, nw))
2360 		    {
2361 			awNext->moreToBePaintedPrev = w;
2362 			aw->moreToBePaintedNext = nw;
2363 			aw->restackInfo->wOldAbove =
2364 			    awNext->winThisIsPaintedBefore;
2365 		    }
2366 		}
2367 		initiateFocusAnimation(w);
2368 	    }
2369 	}
2370 
2371 	for (w = s->reverseWindows; w; w = w->prev)
2372 	{
2373 	    ANIM_WINDOW(w);
2374 
2375 	    if (!aw->isDodgeSubject)
2376 		continue;
2377 	    Bool dodgersAreOnlySubjects = TRUE;
2378 	    CompWindow *dw;
2379 	    AnimWindow *adw;
2380 	    for (dw = aw->dodgeChainStart; dw; dw = adw->dodgeChainNext)
2381 	    {
2382 		adw = GET_ANIM_WINDOW(dw, as);
2383 		if (!adw)
2384 		    break;
2385 		if (!adw->isDodgeSubject)
2386 		    dodgersAreOnlySubjects = FALSE;
2387 	    }
2388 	    if (dodgersAreOnlySubjects)
2389 		aw->skipPostPrepareScreen = TRUE;
2390 	}
2391     }
2392 
2393     if (as->animInProgress)
2394     {
2395 	AnimWindow *aw;
2396 	Bool animStillInProgress = FALSE;
2397 
2398 	for (w = s->windows; w; w = w->next)
2399 	{
2400 	    aw = GET_ANIM_WINDOW(w, as);
2401 
2402 	    if (aw->com.animRemainingTime > 0 &&
2403 		(!aw->com.curAnimEffect ||
2404 		 aw->com.curAnimEffect == AnimEffectNone ||
2405 		 aw->com.curAnimEffect == AnimEffectRandom))
2406 	    {
2407 	    	postAnimationCleanup (w);
2408 	    }
2409 	    else if (aw->com.animRemainingTime > 0)
2410 	    {
2411 		if (aw->com.curAnimEffect->properties.prePrepPaintScreenFunc &&
2412 		    aw->com.curAnimEffect->properties.prePrepPaintScreenFunc
2413 			(w, msSinceLastPaint))
2414 		    animStillInProgress = TRUE;
2415 
2416 	    	// If just starting, call fx init func.
2417 		if (!aw->animInitialized &&
2418 		    aw->com.curAnimEffect->properties.initFunc)
2419 		{
2420 		    if (!aw->com.curAnimEffect->properties.initFunc (w))
2421 		    {
2422 			// Abort this window's animation
2423 			postAnimationCleanup (w);
2424 			continue;
2425 		    }
2426 		}
2427 
2428 		if (aw->com.model &&
2429 		    (aw->com.model->winWidth != WIN_W(w) ||
2430 		     aw->com.model->winHeight != WIN_H(w)))
2431 		{
2432 		    // model needs update
2433 		    // re-create model
2434 		    if (!animEnsureModel (w))
2435 		    {
2436 			// Abort this window's animation
2437 			postAnimationCleanup (w);
2438 			continue;
2439 		    }
2440 		}
2441 
2442 		if (aw->com.curAnimEffect->properties.updateBBFunc)
2443 		{
2444 		    copyResetBB (aw);
2445 
2446 		    if (!aw->animInitialized &&
2447 			(aw->com.curWindowEvent == WindowEventClose ||
2448 			 aw->com.curWindowEvent == WindowEventMinimize ||
2449 			 aw->com.curWindowEvent == WindowEventShade ||
2450 			 ((aw->com.curWindowEvent == WindowEventFocus ||
2451 			   // for dodging windows
2452 			   aw->com.curAnimEffect == AnimEffectDodge) &&
2453 			  !aw->isDodgeSubject)))
2454 			updateBBWindow (NULL, w, &aw->BB);
2455 		}
2456 		aw->animInitialized = TRUE;
2457 
2458 		if (aw->com.curAnimEffect->properties.animStepFunc)
2459 		    aw->com.curAnimEffect->properties.animStepFunc
2460 			(w, msSinceLastPaint);
2461 
2462 		if (aw->com.curAnimEffect->properties.updateBBFunc)
2463 		{
2464 		    int i;
2465 		    for (i = 0; i < s->nOutputDev; i++)
2466 			aw->com.curAnimEffect->properties.
2467 			    updateBBFunc (&s->outputDev[i], w, &aw->BB);
2468 
2469 		    if (!(s->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK))
2470 			damageBoundingBox (w);
2471 		}
2472 
2473 		if (aw->com.animRemainingTime <= 0)
2474 		{
2475 		    // Animation done
2476 		    postAnimationCleanup (w);
2477 		}
2478 		animStillInProgress |= (aw->com.animRemainingTime > 0);
2479 	    }
2480 
2481 	    if (aw->com.animRemainingTime <= 0)
2482 	    {
2483 		if (aw->com.curAnimEffect != AnimEffectNone ||
2484 		    aw->unmapCnt > 0 || aw->destroyCnt > 0)
2485 		{
2486 		    postAnimationCleanup (w);
2487 		}
2488 		aw->com.curWindowEvent = WindowEventNone;
2489 		aw->com.curAnimEffect = AnimEffectNone;
2490 	    }
2491 	}
2492 
2493 	for (w = s->windows; w; w = w->next)
2494 	{
2495 	    aw = GET_ANIM_WINDOW(w, as);
2496 	    if (aw &&
2497 		aw->com.curAnimEffect &&
2498 		aw->com.curAnimEffect != AnimEffectNone &&
2499 		aw->com.curAnimEffect != AnimEffectRandom &&
2500 		aw->com.curAnimEffect->properties.postPrepPaintScreenFunc)
2501 	    {
2502 		aw->com.curAnimEffect->properties.postPrepPaintScreenFunc (w);
2503 	    }
2504 	}
2505 
2506 	if (!animStillInProgress)
2507 	    animActivateEvent(s, FALSE);
2508     }
2509 
2510     UNWRAP(as, s, preparePaintScreen);
2511     (*s->preparePaintScreen) (s, msSinceLastPaint);
2512     WRAP(as, s, preparePaintScreen, animPreparePaintScreen);
2513 }
2514 
animDonePaintScreen(CompScreen * s)2515 static void animDonePaintScreen(CompScreen * s)
2516 {
2517     ANIM_SCREEN(s);
2518 
2519     if (as->animInProgress)
2520 	damagePendingOnScreen (s);
2521 
2522     UNWRAP(as, s, donePaintScreen);
2523     (*s->donePaintScreen) (s);
2524     WRAP(as, s, donePaintScreen, animDonePaintScreen);
2525 }
2526 
2527 // Scales z by 0 and does perspective distortion so that it
2528 // looks the same wherever on the screen
2529 void
perspectiveDistortAndResetZ(CompScreen * s,CompTransform * transform)2530 perspectiveDistortAndResetZ (CompScreen *s,
2531 			     CompTransform *transform)
2532 {
2533     float v = -1.0 / s->width;
2534     /*
2535       This does
2536       transform = M * transform, where M is
2537       1, 0, 0, 0,
2538       0, 1, 0, 0,
2539       0, 0, 0, v,
2540       0, 0, 0, 1
2541     */
2542     float *m = transform->m;
2543     m[8] = v * m[12];
2544     m[9] = v * m[13];
2545     m[10] = v * m[14];
2546     m[11] = v * m[15];
2547 }
2548 
2549 void
applyPerspectiveSkew(CompOutput * output,CompTransform * transform,Point * center)2550 applyPerspectiveSkew (CompOutput *output,
2551 		      CompTransform *transform,
2552 		      Point *center)
2553 {
2554     GLfloat skewx = -(((center->x - output->region.extents.x1) -
2555 		       output->width / 2) * 1.15);
2556     GLfloat skewy = -(((center->y - output->region.extents.y1) -
2557 		       output->height / 2) * 1.15);
2558 
2559     /* transform = M * transform, where M is the skew matrix
2560 	{1,0,0,0,
2561 	 0,1,0,0,
2562 	 skewx,skewy,1,0,
2563 	 0,0,0,1};
2564     */
2565 
2566     float *m = transform->m;
2567     m[8] = skewx * m[0] + skewy * m[4] + m[8];
2568     m[9] = skewx * m[1] + skewy * m[5] + m[9];
2569     m[10] = skewx * m[2] + skewy * m[6] + m[10];
2570     m[11] = skewx * m[3] + skewy * m[7] + m[11];
2571 }
2572 
2573 static void
animAddWindowGeometry(CompWindow * w,CompMatrix * matrix,int nMatrix,Region region,Region clip)2574 animAddWindowGeometry(CompWindow * w,
2575 		      CompMatrix * matrix,
2576 		      int nMatrix, Region region, Region clip)
2577 {
2578     ANIM_WINDOW(w);
2579     ANIM_SCREEN(w->screen);
2580 
2581     // if window is being animated
2582     if (aw->com.animRemainingTime > 0 && aw->com.model &&
2583 	!(aw->com.curAnimEffect->properties.letOthersDrawGeomsFunc &&
2584 	  aw->com.curAnimEffect->properties.letOthersDrawGeomsFunc (w)))
2585     {
2586 	BoxPtr pClip;
2587 	int nClip;
2588 	int nVertices, nIndices;
2589 	GLushort *i;
2590 	GLfloat *v;
2591 	int x1, y1, x2, y2;
2592 	float width, height;
2593 	float winContentsY, winContentsHeight;
2594 	float deformedX, deformedY;
2595 	float deformedZ = 0;
2596 	int nVertX, nVertY, wx, wy;
2597 	int vSize, it;
2598 	float gridW, gridH, x, y;
2599 	Bool rect = TRUE;
2600 	Bool useTextureQ = FALSE;
2601 	Model *model = aw->com.model;
2602 	Region awRegion = NULL;
2603 
2604 	Bool notUsing3dCoords =
2605 	    !aw->com.curAnimEffect->properties.modelAnimIs3D;
2606 
2607 	// Use Q texture coordinate to avoid jagged-looking quads
2608 	// http://www.r3.nu/~cass/qcoord/
2609 	if (aw->com.curAnimEffect->properties.useQTexCoord)
2610 	    useTextureQ = TRUE;
2611 
2612 	if (aw->com.useDrawRegion)
2613 	{
2614 	    awRegion = XCreateRegion();
2615 	    XIntersectRegion (region, aw->com.drawRegion, awRegion);
2616 	    nClip = awRegion->numRects;
2617 	    pClip = awRegion->rects;
2618 	}
2619 	else
2620 	{
2621 	    nClip = region->numRects;
2622 	    pClip = region->rects;
2623 	}
2624 
2625 	if (nClip == 0)			// nothing to do
2626 	{
2627 	    if (awRegion)
2628 		XDestroyRegion(awRegion);
2629 	    return;
2630 	}
2631 
2632 	for (it = 0; it < nMatrix; it++)
2633 	{
2634 	    if (matrix[it].xy != 0.0f || matrix[it].yx != 0.0f)
2635 	    {
2636 		rect = FALSE;
2637 		break;
2638 	    }
2639 	}
2640 
2641 	w->drawWindowGeometry = animDrawWindowGeometry;
2642 
2643 	if (aw->com.curAnimEffect->properties.addCustomGeometryFunc)
2644 	{
2645 	    if (nMatrix == 0)
2646 		return;
2647 	    aw->com.curAnimEffect->properties.
2648 		addCustomGeometryFunc (w, nClip, pClip,
2649 				       nMatrix, matrix);
2650 
2651 	    // If addGeometryFunc exists, it is expected to do everthing
2652 	    // to add geometries (instead of the rest of this function).
2653 
2654 	    if (w->vCount == 0)	// if there is no vertex
2655 	    {
2656 		// put a dummy quad in vertices and indices
2657 
2658 		w->texUnits = 1;
2659 		w->texCoordSize = 4;
2660 		vSize = 3 + w->texUnits * w->texCoordSize;
2661 
2662 		if (4 > w->indexSize)
2663 		{
2664 		    if (!moreWindowIndices(w, 4))
2665 			return;
2666 		}
2667 		if (4 * vSize > w->vertexSize)
2668 		{
2669 		    if (!moreWindowVertices(w, 4 * vSize))
2670 			return;
2671 		}
2672 		w->vCount = 4;
2673 		w->indexCount = 4;
2674 		w->vertexStride = vSize;
2675 
2676 		// Clear dummy quad coordinates/indices
2677 		memset(w->vertices, 0, sizeof(GLfloat) * 4 * vSize);
2678 		memset(w->indices, 0, sizeof(GLushort) * 4);
2679 	    }
2680 	    return;				// We're done here.
2681 	}
2682 
2683 	// window coordinates and size
2684 	wx = WIN_X(w);
2685 	wy = WIN_Y(w);
2686 	width = WIN_W(w);
2687 	height = WIN_H(w);
2688 
2689 	// to be used if event is shade/unshade
2690 	winContentsY = w->attrib.y;
2691 	winContentsHeight = w->height;
2692 
2693 	w->texUnits = nMatrix;
2694 
2695 	if (w->vCount == 0)
2696 	{
2697 	    // reset
2698 	    w->indexCount = 0;
2699 	    w->texCoordSize = 4;
2700 	}
2701 	w->vertexStride = 3 + w->texUnits * w->texCoordSize;
2702 	vSize = w->vertexStride;
2703 
2704 	nVertices = w->vCount;
2705 	nIndices = w->indexCount;
2706 
2707 	v = w->vertices + (nVertices * vSize);
2708 	i = w->indices + nIndices;
2709 
2710 	// For each clip passed to this function
2711 	for (; nClip--; pClip++)
2712 	{
2713 	    x1 = pClip->x1;
2714 	    y1 = pClip->y1;
2715 	    x2 = pClip->x2;
2716 	    y2 = pClip->y2;
2717 
2718 	    gridW = (float)width / (model->gridWidth - 1);
2719 
2720 	    if (aw->com.curWindowEvent == WindowEventShade ||
2721 		aw->com.curWindowEvent == WindowEventUnshade)
2722 	    {
2723 		if (y1 < w->attrib.y)	// if at top part
2724 		{
2725 		    gridH = model->topHeight;
2726 		}
2727 		else if (y2 > w->attrib.y + w->height)	// if at bottom
2728 		{
2729 		    gridH = model->bottomHeight;
2730 		}
2731 		else			// in window contents (only in Y coords)
2732 		{
2733 		    float winContentsHeight =
2734 			height - model->topHeight - model->bottomHeight;
2735 		    gridH = winContentsHeight / (model->gridHeight - 3);
2736 		}
2737 	    }
2738 	    else
2739 		gridH = (float)height / (model->gridHeight - 1);
2740 
2741 	    // nVertX, nVertY: number of vertices for this clip in x and y dimensions
2742 	    // + 2 to avoid running short of vertices in some cases
2743 	    nVertX = ceil((x2 - x1) / gridW) + 2;
2744 	    nVertY = (gridH ? ceil((y2 - y1) / gridH) : 0) + 2;
2745 
2746 	    // Allocate 4 indices for each quad
2747 	    int newIndexSize = nIndices + ((nVertX - 1) * (nVertY - 1) * 4);
2748 
2749 	    if (newIndexSize > w->indexSize)
2750 	    {
2751 		if (!moreWindowIndices(w, newIndexSize))
2752 		    return;
2753 
2754 		i = w->indices + nIndices;
2755 	    }
2756 	    // Assign quad vertices to indices
2757 	    int jx, jy;
2758 	    for (jy = 0; jy < nVertY - 1; jy++)
2759 	    {
2760 		for (jx = 0; jx < nVertX - 1; jx++)
2761 		{
2762 		    *i++ = nVertices + nVertX * (2 * jy + 1) + jx;
2763 		    *i++ = nVertices + nVertX * (2 * jy + 1) + jx + 1;
2764 		    *i++ = nVertices + nVertX * 2 * jy + jx + 1;
2765 		    *i++ = nVertices + nVertX * 2 * jy + jx;
2766 
2767 		    nIndices += 4;
2768 		}
2769 	    }
2770 
2771 	    // Allocate vertices
2772 	    int newVertexSize =
2773 		(nVertices + nVertX * (2 * nVertY - 2)) * vSize;
2774 	    if (newVertexSize > w->vertexSize)
2775 	    {
2776 		if (!moreWindowVertices(w, newVertexSize))
2777 		    return;
2778 
2779 		v = w->vertices + (nVertices * vSize);
2780 	    }
2781 
2782 	    float rowTexCoordQ = 1;
2783 	    float prevRowCellWidth = 0;	// this initial value won't be used
2784 	    float rowCellWidth = 0;
2785 
2786 	    // For each vertex
2787 	    for (jy = 0, y = y1; jy < nVertY; jy++)
2788 	    {
2789 		float topiyFloat;
2790 		Bool applyOffsets = TRUE;
2791 
2792 		if (y > y2)
2793 		    y = y2;
2794 
2795 		// Do calculations for y here to avoid repeating
2796 		// them unnecessarily in the x loop
2797 
2798 		if (aw->com.curWindowEvent == WindowEventShade
2799 		    || aw->com.curWindowEvent == WindowEventUnshade)
2800 		{
2801 		    if (y1 < w->attrib.y)	// if at top part
2802 		    {
2803 			topiyFloat = (y - WIN_Y(w)) / model->topHeight;
2804 			topiyFloat = MIN(topiyFloat, 0.999);	// avoid 1.0
2805 			applyOffsets = FALSE;
2806 		    }
2807 		    else if (y2 > w->attrib.y + w->height)	// if at bottom
2808 		    {
2809 			topiyFloat = (model->gridHeight - 2) +
2810 			    (model->bottomHeight ? (y - winContentsY -
2811 						    winContentsHeight) /
2812 			     model->bottomHeight : 0);
2813 			applyOffsets = FALSE;
2814 		    }
2815 		    else		// in window contents (only in Y coords)
2816 		    {
2817 			topiyFloat = (model->gridHeight - 3) *
2818 			    (y - winContentsY) / winContentsHeight + 1;
2819 		    }
2820 		}
2821 		else
2822 		{
2823 		    topiyFloat = (model->gridHeight - 1) * (y - wy) / height;
2824 		}
2825 		// topiy should be at most (model->gridHeight - 2)
2826 		int topiy = (int)(topiyFloat + 1e-4);
2827 
2828 		if (topiy == model->gridHeight - 1)
2829 		    topiy--;
2830 		int bottomiy = topiy + 1;
2831 		float iny = topiyFloat - topiy;
2832 
2833 		// End of calculations for y
2834 
2835 		for (jx = 0, x = x1; jx < nVertX; jx++)
2836 		{
2837 		    if (x > x2)
2838 			x = x2;
2839 
2840 		    // find containing grid cell (leftix rightix) x (topiy bottomiy)
2841 		    float leftixFloat =
2842 			(model->gridWidth - 1) * (x - wx) / width;
2843 		    int leftix = (int)(leftixFloat + 1e-4);
2844 
2845 		    if (leftix == model->gridWidth - 1)
2846 			leftix--;
2847 		    int rightix = leftix + 1;
2848 
2849 		    // Objects that are at top, bottom, left, right corners of quad
2850 		    Object *objToTopLeft =
2851 			&(model->objects[topiy * model->gridWidth + leftix]);
2852 		    Object *objToTopRight =
2853 			&(model->objects[topiy * model->gridWidth + rightix]);
2854 		    Object *objToBottomLeft =
2855 			&(model->objects[bottomiy * model->gridWidth + leftix]);
2856 		    Object *objToBottomRight =
2857 			&(model->objects[bottomiy * model->gridWidth + rightix]);
2858 
2859 		    // find position in cell by taking remainder of flooring
2860 		    float inx = leftixFloat - leftix;
2861 
2862 		    // Interpolate to find deformed coordinates
2863 
2864 		    float hor1x = (1 - inx) *
2865 			objToTopLeft->position.x +
2866 			inx * objToTopRight->position.x;
2867 		    float hor1y = (1 - inx) *
2868 			objToTopLeft->position.y +
2869 			inx * objToTopRight->position.y;
2870 		    float hor1z = notUsing3dCoords ? 0 :
2871 			(1 - inx) *
2872 			objToTopLeft->position.z +
2873 			inx * objToTopRight->position.z;
2874 		    float hor2x = (1 - inx) *
2875 			objToBottomLeft->position.x +
2876 			inx * objToBottomRight->position.x;
2877 		    float hor2y = (1 - inx) *
2878 			objToBottomLeft->position.y +
2879 			inx * objToBottomRight->position.y;
2880 		    float hor2z = notUsing3dCoords ? 0 :
2881 			(1 - inx) *
2882 			objToBottomLeft->position.z +
2883 			inx * objToBottomRight->position.z;
2884 
2885 		    deformedX = (1 - iny) * hor1x + iny * hor2x;
2886 		    deformedY = (1 - iny) * hor1y + iny * hor2y;
2887 		    deformedZ = (1 - iny) * hor1z + iny * hor2z;
2888 
2889 		    // Texture coordinates (s, t, r, q)
2890 
2891 		    if (useTextureQ)
2892 		    {
2893 			if (jx == 1)
2894 			    rowCellWidth = deformedX - v[-3];
2895 
2896 			// do only once per row for all rows except row 0
2897 			if (jy > 0 && jx == 1)
2898 			{
2899 			    rowTexCoordQ = (rowCellWidth / prevRowCellWidth);
2900 
2901 			    for (it = 0; it < nMatrix; it++, v += 4)
2902 			    {
2903 				// update first column
2904 				// (since we didn't know rowTexCoordQ before)
2905 				v[-vSize]     *= rowTexCoordQ; // multiply s & t by q
2906 				v[-vSize + 1] *= rowTexCoordQ;
2907 				v[-vSize + 3] = rowTexCoordQ;  // copy q
2908 			    }
2909 			    v -= nMatrix * 4;
2910 			}
2911 		    }
2912 
2913 		    // Loop for each texture element
2914 		    // (4 texture coordinates for each one)
2915 		    for (it = 0; it < nMatrix; it++, v += 4)
2916 		    {
2917 			float offsetY = 0;
2918 
2919 			if (rect)
2920 			{
2921 			    if (applyOffsets && y < y2)
2922 				offsetY = objToTopLeft->offsetTexCoordForQuadAfter.y;
2923 			    v[0] = COMP_TEX_COORD_X (&matrix[it], x); // s
2924 			    v[1] = COMP_TEX_COORD_Y (&matrix[it], y + offsetY); // t
2925 			}
2926 			else
2927 			{
2928 			    if (applyOffsets && y < y2)
2929 				// FIXME:
2930 			    	// The correct y offset below produces wrong
2931 				// texture coordinates for some reason.
2932 				offsetY = 0;
2933 				// offsetY = objToTopLeft->offsetTexCoordForQuadAfter.y;
2934 			    v[0] = COMP_TEX_COORD_XY (&matrix[it], x, y + offsetY); // s
2935 			    v[1] = COMP_TEX_COORD_YX (&matrix[it], x, y + offsetY); // t
2936 			}
2937 			v[2] = 0; // r
2938 
2939 			if (0 < jy && jy < nVertY - 1)
2940 			{
2941 			    // copy s, t, r to duplicate row
2942 			    memcpy(v + nVertX * vSize, v,
2943 				   3 * sizeof(GLfloat));
2944 			    v[3 + nVertX * vSize] = 1; // q
2945 			}
2946 
2947 			if (applyOffsets &&
2948 			    objToTopLeft->offsetTexCoordForQuadBefore.y != 0)
2949 			{
2950 			    // After copying to next row, update texture y coord
2951 			    // by following object's offset
2952 			    offsetY = objToTopLeft->offsetTexCoordForQuadBefore.y;
2953 			    if (rect)
2954 			    {
2955 				v[1] = COMP_TEX_COORD_Y (&matrix[it], y + offsetY);
2956 			    }
2957 			    else
2958 			    {
2959 				v[0] = COMP_TEX_COORD_XY (&matrix[it],
2960 							  x, y + offsetY);
2961 				v[1] = COMP_TEX_COORD_YX (&matrix[it],
2962 							  x, y + offsetY);
2963 			    }
2964 			}
2965 			if (useTextureQ)
2966 			{
2967 			    v[3] = rowTexCoordQ; // q
2968 
2969 			    if (jx > 0)	// since column 0 is updated when jx == 1
2970 			    {
2971 				// multiply s & t by q
2972 				v[0] *= rowTexCoordQ;
2973 				v[1] *= rowTexCoordQ;
2974 			    }
2975 			}
2976 			else
2977 			{
2978 			    v[3] = 1; // q
2979 			}
2980 		    }
2981 
2982 		    v[0] = deformedX;
2983 		    v[1] = deformedY;
2984 		    v[2] = deformedZ;
2985 
2986 		    // Copy vertex coordinates to duplicate row
2987 		    if (0 < jy && jy < nVertY - 1)
2988 			memcpy(v + nVertX * vSize, v, 3 * sizeof(GLfloat));
2989 
2990 		    nVertices++;
2991 
2992 		    // increment x properly (so that coordinates fall on grid intersections)
2993 		    x = rightix * gridW + wx;
2994 
2995 		    v += 3; // move on to next vertex
2996 		}
2997 		if (useTextureQ)
2998 		    prevRowCellWidth = rowCellWidth;
2999 
3000 		if (0 < jy && jy < nVertY - 1)
3001 		{
3002 		    v += nVertX * vSize;	// skip the duplicate row
3003 		    nVertices += nVertX;
3004 		}
3005 		// increment y properly (so that coordinates fall on grid intersections)
3006 		if (aw->com.curWindowEvent == WindowEventShade
3007 		    || aw->com.curWindowEvent == WindowEventUnshade)
3008 		{
3009 		    y += gridH;
3010 		}
3011 		else
3012 		{
3013 		    y = bottomiy * gridH + wy;
3014 		}
3015 	    }
3016 	}
3017 	w->vCount = nVertices;
3018 	w->indexCount = nIndices;
3019 	if (awRegion)
3020 	{
3021 	    XDestroyRegion(awRegion);
3022 	    awRegion = NULL;
3023 	}
3024     }
3025     else
3026     {
3027 	UNWRAP(as, w->screen, addWindowGeometry);
3028 	(*w->screen->addWindowGeometry) (w, matrix, nMatrix, region, clip);
3029 	WRAP(as, w->screen, addWindowGeometry, animAddWindowGeometry);
3030     }
3031 }
3032 
3033 static void
animDrawWindowTexture(CompWindow * w,CompTexture * texture,const FragmentAttrib * attrib,unsigned int mask)3034 animDrawWindowTexture(CompWindow * w, CompTexture * texture,
3035 		      const FragmentAttrib *attrib,
3036 		      unsigned int mask)
3037 {
3038     ANIM_WINDOW(w);
3039     ANIM_SCREEN(w->screen);
3040 
3041     if (aw->com.animRemainingTime > 0)	// if animation in progress, store texture
3042     {
3043 	aw->com.curPaintAttrib = *attrib;
3044     }
3045 
3046     UNWRAP(as, w->screen, drawWindowTexture);
3047     (*w->screen->drawWindowTexture) (w, texture, attrib, mask);
3048     WRAP(as, w->screen, drawWindowTexture, animDrawWindowTexture);
3049 }
3050 
3051 void
animDrawWindowGeometry(CompWindow * w)3052 animDrawWindowGeometry(CompWindow * w)
3053 {
3054     ANIM_WINDOW (w);
3055 
3056     if (aw->com.curAnimEffect->properties.drawCustomGeometryFunc)
3057     {
3058 	aw->com.curAnimEffect->properties.drawCustomGeometryFunc (w);
3059 	return;
3060     }
3061     int texUnit = w->texUnits;
3062     int currentTexUnit = 0;
3063     int stride = 3 + texUnit * w->texCoordSize;
3064     GLfloat *vertices = w->vertices + (stride - 3);
3065 
3066     stride *= sizeof(GLfloat);
3067 
3068     glVertexPointer(3, GL_FLOAT, stride, vertices);
3069 
3070     while (texUnit--)
3071     {
3072 	if (texUnit != currentTexUnit)
3073 	{
3074 	    w->screen->clientActiveTexture(GL_TEXTURE0_ARB + texUnit);
3075 	    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3076 	    currentTexUnit = texUnit;
3077 	}
3078 	vertices -= w->texCoordSize;
3079 	glTexCoordPointer(w->texCoordSize, GL_FLOAT, stride, vertices);
3080     }
3081 
3082     glDrawElements(GL_QUADS, w->indexCount, GL_UNSIGNED_SHORT,
3083 		   w->indices);
3084 
3085     // disable all texture coordinate arrays except 0
3086     texUnit = w->texUnits;
3087     if (texUnit > 1)
3088     {
3089 	while (--texUnit)
3090 	{
3091 	    (*w->screen->clientActiveTexture) (GL_TEXTURE0_ARB + texUnit);
3092 	    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3093 	}
3094 
3095 	(*w->screen->clientActiveTexture) (GL_TEXTURE0_ARB);
3096     }
3097 }
3098 
3099 static Bool
animPaintWindow(CompWindow * w,const WindowPaintAttrib * attrib,const CompTransform * transform,Region region,unsigned int mask)3100 animPaintWindow(CompWindow * w,
3101 		const WindowPaintAttrib * attrib,
3102 		const CompTransform    *transform,
3103 		Region region, unsigned int mask)
3104 {
3105     Bool status;
3106 
3107     ANIM_SCREEN(w->screen);
3108     ANIM_WINDOW(w);
3109 
3110     if (aw->com.animRemainingTime > 0)
3111     {
3112 	if (!as->animInProgress)
3113 	{
3114 	    // This window shouldn't really be undergoing animation,
3115 	    // because it won't make progress with false as->animInProgress.
3116 	    postAnimationCleanup (w);
3117 
3118 	    UNWRAP(as, w->screen, paintWindow);
3119 	    status = (*w->screen->paintWindow) (w, attrib, transform, region,
3120 						mask);
3121 	    WRAP(as, w->screen, paintWindow, animPaintWindow);
3122 
3123 	    return status;
3124 	}
3125 	if (aw->com.curAnimEffect == AnimEffectDodge &&
3126 	    aw->isDodgeSubject &&
3127 	    aw->walkerOverNewCopy)
3128 	{
3129 	    // if aw is to be painted somewhere other than in its
3130 	    // original stacking order, we don't
3131 	    // need to paint it now
3132 	    return FALSE;
3133 	}
3134 	if (aw->com.curWindowEvent == WindowEventFocus && otherPluginsActive(as))
3135 	    postAnimationCleanup (w);
3136 
3137 	WindowPaintAttrib wAttrib = *attrib;
3138 	CompTransform wTransform = *transform;
3139 
3140 	if (aw->com.curAnimEffect->properties.addCustomGeometryFunc)
3141 	{
3142 	    // Use slightly smaller brightness to force core
3143 	    // to handle <max saturation case with <max brightness.
3144 	    // Otherwise polygon effects show fully unsaturated colors
3145 	    // in that case.
3146 	    wAttrib.brightness = MAX (0, wAttrib.brightness - 1);
3147 	}
3148 	w->indexCount = 0;
3149 
3150 	// TODO: should only happen for distorting effects
3151 	mask |= PAINT_WINDOW_TRANSFORMED_MASK;
3152 
3153 	wAttrib.xScale = 1.0f;
3154 	wAttrib.yScale = 1.0f;
3155 
3156 	if (aw->com.curAnimEffect->properties.updateWindowAttribFunc)
3157 	    aw->com.curAnimEffect->properties.
3158 		updateWindowAttribFunc (w, &wAttrib);
3159 
3160 	if (aw->com.curAnimEffect->properties.updateWinTransformFunc)
3161 	    aw->com.curAnimEffect->properties.
3162 		updateWinTransformFunc (w, &wTransform);
3163 
3164 	if (aw->com.curAnimEffect->properties.prePaintWindowFunc)
3165 	    aw->com.curAnimEffect->properties.prePaintWindowFunc (w);
3166 
3167 	UNWRAP(as, w->screen, paintWindow);
3168 	status = (*w->screen->paintWindow) (w, &wAttrib, &wTransform, region, mask);
3169 	WRAP(as, w->screen, paintWindow, animPaintWindow);
3170 
3171 	if (aw->com.curAnimEffect->properties.postPaintWindowFunc)
3172 	{
3173 	    // Transform to make post-paint coincide with the window
3174 	    glPushMatrix ();
3175 	    glLoadMatrixf (wTransform.m);
3176 
3177 	    aw->com.curAnimEffect->properties.postPaintWindowFunc (w);
3178 
3179 	    glPopMatrix ();
3180 	}
3181     }
3182     else
3183     {
3184 	UNWRAP(as, w->screen, paintWindow);
3185 	status = (*w->screen->paintWindow) (w, attrib, transform, region, mask);
3186 	WRAP(as, w->screen, paintWindow, animPaintWindow);
3187     }
3188 
3189     return status;
3190 }
3191 
3192 // Go to the bottommost window in this "focus chain"
3193 // This chain is used to handle some cases: e.g when Find dialog
3194 // of an app is open, both windows should be faded when the Find
3195 // dialog is raised.
3196 static CompWindow*
getBottommostInFocusChain(CompWindow * w)3197 getBottommostInFocusChain (CompWindow *w)
3198 {
3199     if (!w)
3200 	return w;
3201 
3202     ANIM_WINDOW (w);
3203     ANIM_SCREEN (w->screen);
3204 
3205     CompWindow *bottommost = aw->winToBePaintedBeforeThis;
3206 
3207     if (!bottommost || bottommost->destroyed)
3208 	return w;
3209 
3210     AnimWindow *awBottommost = GET_ANIM_WINDOW (bottommost, as);
3211     CompWindow *wPrev = NULL;
3212 
3213     if (awBottommost)
3214 	wPrev = awBottommost->moreToBePaintedPrev;
3215     while (wPrev)
3216     {
3217 	bottommost = wPrev;
3218 	wPrev = GET_ANIM_WINDOW(wPrev, as)->moreToBePaintedPrev;
3219     }
3220     return bottommost;
3221 }
3222 
3223 static void
resetWalkerMarks(CompScreen * s)3224 resetWalkerMarks (CompScreen *s)
3225 {
3226     CompWindow *w;
3227     for (w = s->windows; w; w = w->next)
3228     {
3229 	ANIM_WINDOW(w);
3230 	aw->walkerOverNewCopy = FALSE;
3231 	aw->walkerVisitCount = 0;
3232     }
3233 }
3234 
3235 static CompWindow*
animWalkFirst(CompScreen * s)3236 animWalkFirst (CompScreen *s)
3237 {
3238     ANIM_SCREEN (s);
3239 
3240     resetWalkerMarks (s);
3241 
3242     CompWindow *w = getBottommostInFocusChain(s->windows);
3243     if (w)
3244     {
3245 	AnimWindow *aw = GET_ANIM_WINDOW (w, as);
3246 	aw->walkerVisitCount++;
3247     }
3248     return w;
3249 }
3250 
3251 static CompWindow*
animWalkLast(CompScreen * s)3252 animWalkLast (CompScreen *s)
3253 {
3254     ANIM_SCREEN (s);
3255 
3256     resetWalkerMarks (s);
3257 
3258     CompWindow *w = s->reverseWindows;
3259     if (w)
3260     {
3261 	AnimWindow *aw = GET_ANIM_WINDOW (w, as);
3262 	aw->walkerVisitCount++;
3263     }
3264     return w;
3265 }
3266 
3267 static Bool
markNewCopy(CompWindow * w)3268 markNewCopy (CompWindow *w)
3269 {
3270     ANIM_WINDOW (w);
3271 
3272     // if window is in a focus chain
3273     if (aw->winThisIsPaintedBefore ||
3274 	aw->moreToBePaintedPrev)
3275     {
3276 	aw->walkerOverNewCopy = TRUE;
3277 	return TRUE;
3278     }
3279     return FALSE;
3280 }
3281 
3282 static CompWindow*
animWalkNext(CompWindow * w)3283 animWalkNext (CompWindow *w)
3284 {
3285     ANIM_WINDOW (w);
3286     CompWindow *wRet = NULL;
3287 
3288     if (!aw->walkerOverNewCopy)
3289     {
3290 	// Within a chain? (not the 1st or 2nd window)
3291 	if (aw->moreToBePaintedNext)
3292 	    wRet = aw->moreToBePaintedNext;
3293 	else if (aw->winThisIsPaintedBefore) // 2nd one in chain?
3294 	    wRet = aw->winThisIsPaintedBefore;
3295     }
3296     else
3297 	aw->walkerOverNewCopy = FALSE;
3298 
3299     if (!wRet && w->next && markNewCopy (w->next))
3300 	wRet = w->next;
3301     else if (!wRet)
3302 	wRet = getBottommostInFocusChain(w->next);
3303 
3304     if (wRet)
3305     {
3306 	ANIM_SCREEN (w->screen);
3307 
3308 	AnimWindow *awRet = GET_ANIM_WINDOW (wRet, as);
3309 	// Prevent cycles, which cause freezes
3310 	if (awRet->walkerVisitCount > 1) // each window is visited at most twice
3311 	    return NULL;
3312 	awRet->walkerVisitCount++;
3313     }
3314     return wRet;
3315 }
3316 
3317 static CompWindow*
animWalkPrev(CompWindow * w)3318 animWalkPrev (CompWindow *w)
3319 {
3320     ANIM_WINDOW (w);
3321     CompWindow *wRet = NULL;
3322 
3323     // Focus chain start?
3324     CompWindow *w2 = aw->winToBePaintedBeforeThis;
3325     if (w2)
3326 	wRet = w2;
3327     else if (!aw->walkerOverNewCopy)
3328     {
3329 	// Within a focus chain? (not the last window)
3330 	CompWindow *wPrev = aw->moreToBePaintedPrev;
3331 	if (wPrev)
3332 	    wRet = wPrev;
3333 	else if (aw->winThisIsPaintedBefore) // Focus chain end?
3334 	    // go to the chain beginning and get the
3335 	    // prev. in X stacking order
3336 	{
3337 	    if (aw->winThisIsPaintedBefore->prev)
3338 		markNewCopy (aw->winThisIsPaintedBefore->prev);
3339 
3340 	    wRet = aw->winThisIsPaintedBefore->prev;
3341 	}
3342     }
3343     else
3344 	aw->walkerOverNewCopy = FALSE;
3345 
3346     if (!wRet && w->prev)
3347 	markNewCopy (w->prev);
3348 
3349     wRet = w->prev;
3350     if (wRet)
3351     {
3352 	ANIM_SCREEN (w->screen);
3353 
3354 	AnimWindow *awRet = GET_ANIM_WINDOW (wRet, as);
3355 	// Prevent cycles, which cause freezes
3356 	if (awRet->walkerVisitCount > 1) // each window is visited at most twice
3357 	    return NULL;
3358 	awRet->walkerVisitCount++;
3359     }
3360     return wRet;
3361 }
3362 
3363 static void
animInitWindowWalker(CompScreen * s,CompWalker * walker)3364 animInitWindowWalker (CompScreen *s,
3365 		      CompWalker *walker)
3366 {
3367     ANIM_SCREEN (s);
3368 
3369     UNWRAP (as, s, initWindowWalker);
3370     (*s->initWindowWalker) (s, walker);
3371     WRAP (as, s, initWindowWalker, animInitWindowWalker);
3372 
3373     if (as->walkerAnimCount > 0) // only walk if necessary
3374     {
3375 	if (!as->animInProgress) // just in case
3376 	{
3377 	    as->walkerAnimCount = 0;
3378 	    return;
3379 	}
3380 	walker->first = animWalkFirst;
3381 	walker->last  = animWalkLast;
3382 	walker->next  = animWalkNext;
3383 	walker->prev  = animWalkPrev;
3384     }
3385 }
3386 
animHandleCompizEvent(CompDisplay * d,const char * pluginName,const char * eventName,CompOption * option,int nOption)3387 static void animHandleCompizEvent(CompDisplay * d, const char *pluginName,
3388 				  const char *eventName, CompOption * option,
3389 				  int nOption)
3390 {
3391     ANIM_DISPLAY(d);
3392 
3393     UNWRAP (ad, d, handleCompizEvent);
3394     (*d->handleCompizEvent) (d, pluginName, eventName, option, nOption);
3395     WRAP (ad, d, handleCompizEvent, animHandleCompizEvent);
3396 
3397     int i;
3398     for (i = 0; i < NUM_WATCHED_PLUGINS; i++)
3399 	if (strcmp(pluginName, watchedPlugins[i].pluginName) == 0)
3400 	{
3401 	    if (strcmp(eventName, watchedPlugins[i].activateEventName) == 0)
3402 	    {
3403 		Window xid = getIntOptionNamed(option, nOption, "root", 0);
3404 		CompScreen *s = findScreenAtDisplay(d, xid);
3405 
3406 		if (s)
3407 		{
3408 		    ANIM_SCREEN(s);
3409 		    as->pluginActive[i] =
3410 			getBoolOptionNamed(option, nOption, "active", FALSE);
3411 		    if (i < NUM_SWITCHERS) // if it's a switcher plugin
3412 		    {
3413 			if (!as->pluginActive[i])
3414 			    switcherPostWait = 1;
3415 		    }
3416 		}
3417 	    }
3418 	    break;
3419 	}
3420 }
3421 
3422 static void
updateLastClientListStacking(CompScreen * s)3423 updateLastClientListStacking(CompScreen *s)
3424 {
3425     ANIM_SCREEN(s);
3426     int n = s->nClientList;
3427     Window *clientListStacking = (Window *) (s->clientList + n) + n;
3428 
3429     if (as->nLastClientListStacking != n) // the number of windows has changed
3430     {
3431 	Window *list;
3432 
3433 	list = realloc (as->lastClientListStacking, sizeof (Window) * n);
3434 	as->lastClientListStacking  = list;
3435 
3436 	if (!list)
3437 	{
3438 	    as->nLastClientListStacking = 0;
3439 	    return;
3440 	}
3441 
3442 	as->nLastClientListStacking = n;
3443     }
3444 
3445     // Store new client stack listing
3446     memcpy(as->lastClientListStacking, clientListStacking,
3447 	   sizeof (Window) * n);
3448 }
3449 
3450 // Returns true for windows that don't have a pixmap or certain properties,
3451 // like the dimming layer of gksudo and x-session-manager
3452 static inline Bool
shouldIgnoreForAnim(CompWindow * w,Bool checkPixmap)3453 shouldIgnoreForAnim (CompWindow *w, Bool checkPixmap)
3454 {
3455     ANIM_DISPLAY (w->screen->display);
3456 
3457     return ((checkPixmap && !w->texture->pixmap) ||
3458 	    matchEval (&ad->neverAnimateMatch, w));
3459 }
3460 
animHandleEvent(CompDisplay * d,XEvent * event)3461 static void animHandleEvent(CompDisplay * d, XEvent * event)
3462 {
3463     CompWindow *w;
3464 
3465     ANIM_DISPLAY(d);
3466 
3467     switch (event->type)
3468     {
3469     case PropertyNotify:
3470 	if (event->xproperty.atom == d->clientListStackingAtom)
3471 	{
3472 	    CompScreen *s = findScreenAtDisplay (d, event->xproperty.window);
3473 	    if (s)
3474 		updateLastClientListStacking(s);
3475 	}
3476 	break;
3477     case MapNotify:
3478 	w = findWindowAtDisplay(d, event->xmap.window);
3479 	if (w)
3480 	{
3481 	    ANIM_WINDOW(w);
3482 
3483 	    if (aw->com.animRemainingTime > 0)
3484 	    {
3485 		aw->state = aw->newState;
3486 	    }
3487 	    aw->ignoreDamage = TRUE;
3488 	    while (aw->unmapCnt)
3489 	    {
3490 		unmapWindow(w);
3491 		aw->unmapCnt--;
3492 	    }
3493 	    aw->ignoreDamage = FALSE;
3494 	}
3495 	break;
3496     case DestroyNotify:
3497 	w = findWindowAtDisplay(d, event->xdestroywindow.window);
3498 	if (w)
3499 	{
3500 	    ANIM_WINDOW(w);
3501 	    int duration;
3502 
3503 	    if (shouldIgnoreForAnim (w, TRUE))
3504 		break;
3505 
3506 	    if (AnimEffectNone ==
3507 		getMatchingAnimSelection (w, AnimEventClose, &duration))
3508 		break;
3509 
3510 	    aw->destroyCnt++;
3511 	    w->destroyRefCnt++;
3512 	    addWindowDamage(w);
3513 	}
3514 	break;
3515     case UnmapNotify:
3516 	w = findWindowAtDisplay(d, event->xunmap.window);
3517 	if (w)
3518 	{
3519 	    ANIM_SCREEN(w->screen);
3520 
3521 	    if (w->pendingUnmaps && onCurrentDesktop(w)) // Normal -> Iconic
3522 	    {
3523 		ANIM_WINDOW(w);
3524 		int duration = 200;
3525 		AnimEffect chosenEffect =
3526 		    getMatchingAnimSelection (w, AnimEventShade, &duration);
3527 
3528 		if (w->shaded)
3529 		{
3530 		    // SHADE event!
3531 
3532 		    aw->nowShaded = TRUE;
3533 
3534 		    if (chosenEffect != AnimEffectNone)
3535 		    {
3536 			Bool startingNew = TRUE;
3537 
3538 			if (aw->com.curWindowEvent != WindowEventNone)
3539 			{
3540 			    if (aw->com.curWindowEvent != WindowEventUnshade)
3541 			    {
3542 				postAnimationCleanupPrev (w, FALSE, FALSE);
3543 			    }
3544 			    else
3545 			    {
3546 				// Play the unshade effect backwards from where it left
3547 				aw->com.animRemainingTime =
3548 				    aw->com.animTotalTime -
3549 				    aw->com.animRemainingTime;
3550 
3551 				// avoid window remains
3552 				if (aw->com.animRemainingTime <= 0)
3553 				    aw->com.animRemainingTime = 1;
3554 
3555 				startingNew = FALSE;
3556 				if (aw->com.animOverrideProgressDir == 0)
3557 				    aw->com.animOverrideProgressDir = 2;
3558 				else if (aw->com.animOverrideProgressDir == 1)
3559 				    aw->com.animOverrideProgressDir = 0;
3560 			    }
3561 			}
3562 
3563 			if (startingNew)
3564 			{
3565 			    AnimEffect effectToBePlayed;
3566 			    effectToBePlayed =
3567 				animGetAnimEffect (as,
3568 						   chosenEffect,
3569 						   AnimEventShade);
3570 
3571 			    // handle empty random effect list
3572 			    if (effectToBePlayed == AnimEffectNone)
3573 				break;
3574 
3575 			    aw->com.curAnimEffect = effectToBePlayed;
3576 			    aw->com.animTotalTime = duration;
3577 			    aw->com.animRemainingTime = aw->com.animTotalTime;
3578 			}
3579 
3580 			animActivateEvent(w->screen, TRUE);
3581 			aw->com.curWindowEvent = WindowEventShade;
3582 
3583 			if (!animEnsureModel(w))
3584 			{
3585 			    postAnimationCleanup (w);
3586 			}
3587 
3588 			aw->unmapCnt++;
3589 			w->unmapRefCnt++;
3590 
3591 			damagePendingOnScreen (w->screen);
3592 		    }
3593 		}
3594 		else if (!w->invisible && !w->hidden)
3595 		{
3596 		    // MINIMIZE event!
3597 
3598 		    // Always reset stacking related info when a window is
3599 		    // minimized.
3600 		    resetStackingInfo (w->screen);
3601 
3602 		    aw->newState = IconicState;
3603 
3604 		    if (w->iconGeometrySet)
3605 		    {
3606 			aw->com.icon = w->iconGeometry;
3607 		    }
3608 		    else
3609 		    {
3610 			// Minimize to mouse pointer if there is no
3611 			// window list or if the window skips taskbar
3612 			if (!getMousePointerXY (w->screen,
3613 						&aw->com.icon.x,
3614 						&aw->com.icon.y))
3615 			{
3616 			    // Use screen center if can't get mouse coords
3617 			    aw->com.icon.x = w->screen->width / 2;
3618 			    aw->com.icon.y = w->screen->height / 2;
3619 			}
3620 			aw->com.icon.width = FAKE_ICON_SIZE;
3621 			aw->com.icon.height = FAKE_ICON_SIZE;
3622 		    }
3623 
3624 		    chosenEffect =
3625 			getMatchingAnimSelection (w, AnimEventMinimize, &duration);
3626 
3627 		    if (chosenEffect != AnimEffectNone)
3628 		    {
3629 			Bool startingNew = TRUE;
3630 
3631 			if (aw->com.curWindowEvent != WindowEventNone)
3632 			{
3633 			    if (aw->com.curWindowEvent != WindowEventUnminimize)
3634 			    {
3635 				postAnimationCleanupPrev (w, FALSE, FALSE);
3636 			    }
3637 			    else
3638 			    {
3639 				// Play the unminimize effect backwards from where it left
3640 				aw->com.animRemainingTime =
3641 				    aw->com.animTotalTime - aw->com.animRemainingTime;
3642 
3643 				// avoid window remains
3644 				if (aw->com.animRemainingTime == 0)
3645 				    aw->com.animRemainingTime = 1;
3646 
3647 				startingNew = FALSE;
3648 				if (aw->com.animOverrideProgressDir == 0)
3649 				    aw->com.animOverrideProgressDir = 2;
3650 				else if (aw->com.animOverrideProgressDir == 1)
3651 				    aw->com.animOverrideProgressDir = 0;
3652 			    }
3653 			}
3654 
3655 			if (startingNew)
3656 			{
3657 			    AnimEffect effectToBePlayed;
3658 			    effectToBePlayed =
3659 				animGetAnimEffect (as,
3660 						   chosenEffect,
3661 						   AnimEventMinimize);
3662 
3663 			    // handle empty random effect list
3664 			    if (effectToBePlayed == AnimEffectNone)
3665 			    {
3666 				aw->state = aw->newState;
3667 				break;
3668 			    }
3669 			    aw->com.curAnimEffect = effectToBePlayed;
3670 			    aw->com.animTotalTime = duration;
3671 			    aw->com.animRemainingTime = aw->com.animTotalTime;
3672 			}
3673 
3674 			animActivateEvent(w->screen, TRUE);
3675 			aw->com.curWindowEvent = WindowEventMinimize;
3676 
3677 			if (!animEnsureModel(w))
3678 			{
3679 			    postAnimationCleanup (w);
3680 			}
3681 			else
3682 			{
3683 			    aw->unmapCnt++;
3684 			    w->unmapRefCnt++;
3685 
3686 			    damagePendingOnScreen (w->screen);
3687 			}
3688 		    }
3689 		    else
3690 		        aw->state = aw->newState;
3691 		}
3692 	    }
3693 	    else				// X -> Withdrawn
3694 	    {
3695 		ANIM_WINDOW(w);
3696 		int duration = 200;
3697 
3698 		// Always reset stacking related info when a window is closed.
3699 		resetStackingInfo (w->screen);
3700 
3701 		if (shouldIgnoreForAnim (w, TRUE) ||
3702 		    otherPluginsActive (as))
3703 		    break;
3704 
3705 		AnimEffect chosenEffect =
3706 		    getMatchingAnimSelection (w, AnimEventClose, &duration);
3707 
3708 		// CLOSE event!
3709 
3710 		aw->state = NormalState;
3711 		aw->newState = WithdrawnState;
3712 
3713 		if (chosenEffect != AnimEffectNone)
3714 		{
3715 		    int tmpSteps = 0;
3716 		    Bool startingNew = TRUE;
3717 
3718 		    if (aw->com.animRemainingTime > 0 &&
3719 			aw->com.curWindowEvent != WindowEventOpen)
3720 		    {
3721 			tmpSteps = aw->com.animRemainingTime;
3722 			aw->com.animRemainingTime = 0;
3723 		    }
3724 		    if (aw->com.curWindowEvent != WindowEventNone)
3725 		    {
3726 			if (aw->com.curWindowEvent == WindowEventOpen)
3727 			{
3728 			    // Play the create effect backward from where it left
3729 			    aw->com.animRemainingTime =
3730 				aw->com.animTotalTime - aw->com.animRemainingTime;
3731 
3732 			    // avoid window remains
3733 			    if (aw->com.animRemainingTime <= 0)
3734 				aw->com.animRemainingTime = 1;
3735 
3736 			    startingNew = FALSE;
3737 			    if (aw->com.animOverrideProgressDir == 0)
3738 				aw->com.animOverrideProgressDir = 2;
3739 			    else if (aw->com.animOverrideProgressDir == 1)
3740 				aw->com.animOverrideProgressDir = 0;
3741 			}
3742 			else if (aw->com.curWindowEvent == WindowEventClose)
3743 			{
3744 			    if (aw->com.animOverrideProgressDir == 2)
3745 			    {
3746 				aw->com.animRemainingTime = tmpSteps;
3747 				startingNew = FALSE;
3748 			    }
3749 			}
3750 			else
3751 			{
3752 			    postAnimationCleanupPrev (w, TRUE, FALSE);
3753 			}
3754 		    }
3755 
3756 		    if (startingNew)
3757 		    {
3758 			AnimEffect effectToBePlayed;
3759 			effectToBePlayed = animGetAnimEffect (as,
3760 							      chosenEffect,
3761 							      AnimEventClose);
3762 
3763 			// handle empty random effect list
3764 			if (effectToBePlayed == AnimEffectNone)
3765 			{
3766 			    aw->state = aw->newState;
3767 			    break;
3768 			}
3769 			aw->com.curAnimEffect = effectToBePlayed;
3770 			aw->com.animTotalTime = duration;
3771 			aw->com.animRemainingTime = aw->com.animTotalTime;
3772 		    }
3773 		    animActivateEvent(w->screen, TRUE);
3774 		    aw->com.curWindowEvent = WindowEventClose;
3775 
3776 		    if (!animEnsureModel(w))
3777 		    {
3778 			postAnimationCleanupCustom (w, TRUE, FALSE, TRUE);
3779 		    }
3780 		    else if (getMousePointerXY
3781 			     (w->screen, &aw->com.icon.x, &aw->com.icon.y))
3782 		    {
3783 			aw->com.icon.width = FAKE_ICON_SIZE;
3784 			aw->com.icon.height = FAKE_ICON_SIZE;
3785 
3786 			if (aw->com.curAnimEffect == AnimEffectMagicLamp)
3787 			    aw->com.icon.width =
3788 				MAX(aw->com.icon.width,
3789 				    animGetI (w, ANIM_SCREEN_OPTION_MAGIC_LAMP_OPEN_START_WIDTH));
3790 			else if (aw->com.curAnimEffect == AnimEffectVacuum)
3791 			    aw->com.icon.width =
3792 				MAX(aw->com.icon.width,
3793 				    animGetI (w, ANIM_SCREEN_OPTION_VACUUM_OPEN_START_WIDTH));
3794 
3795 			aw->unmapCnt++;
3796 			w->unmapRefCnt++;
3797 
3798 			damagePendingOnScreen (w->screen);
3799 		    }
3800 		}
3801 		else if (AnimEffectNone !=
3802 			 getMatchingAnimSelection (w, AnimEventOpen, &duration))
3803 		{
3804 		    // stop the current animation and prevent it from rewinding
3805 
3806 		    if (aw->com.animRemainingTime > 0 &&
3807 			aw->com.curWindowEvent != WindowEventOpen)
3808 		    {
3809 			aw->com.animRemainingTime = 0;
3810 		    }
3811 		    if ((aw->com.curWindowEvent != WindowEventNone) &&
3812 			(aw->com.curWindowEvent != WindowEventClose))
3813 		    {
3814 			postAnimationCleanupCustom (w, TRUE, FALSE, TRUE);
3815 		    }
3816 		    // set some properties to make sure this window will use the
3817 		    // correct open effect the next time it's "opened"
3818 
3819 		    animActivateEvent(w->screen, TRUE);
3820 		    aw->com.curWindowEvent = WindowEventClose;
3821 
3822 		    aw->unmapCnt++;
3823 		    w->unmapRefCnt++;
3824 
3825 		    damagePendingOnScreen (w->screen);
3826 		}
3827 		else
3828 		    aw->state = aw->newState;
3829 	    }
3830 	}
3831 	break;
3832     case ConfigureNotify:
3833     {
3834 	XConfigureEvent *ce = &event->xconfigure;
3835 	w = findWindowAtDisplay (d, ce->window);
3836 	if (!w)
3837 	    break;
3838 	if (w->prev)
3839 	{
3840 	    if (ce->above && ce->above == w->prev->id)
3841 		break;
3842 	}
3843 	else if (ce->above == None)
3844 	    break;
3845 	CompScreen *s = findScreenAtDisplay (d, event->xproperty.window);
3846 	if (!s)
3847 	    break;
3848 
3849 	ANIM_SCREEN(s);
3850 	int n = s->nClientList;
3851 	Bool winOpenedClosed = FALSE;
3852 
3853 	Window *clientList = (Window *) (s->clientList + n);
3854 	Window *clientListStacking = clientList + n;
3855 
3856 	if (n != as->nLastClientListStacking)
3857 	    winOpenedClosed = TRUE;
3858 
3859 	// if restacking occurred and not window open/close
3860 	if (!winOpenedClosed)
3861 	{
3862 	    ANIM_WINDOW(w);
3863 	    aw->configureNotified = TRUE;
3864 
3865 	    // Find which window is restacked
3866 	    // e.g. here 8507730 was raised:
3867 	    // 54526074 8507730 48234499 14680072 6291497
3868 	    // 54526074 48234499 14680072 8507730 6291497
3869 	    // compare first changed win. of row 1 with last
3870 	    // changed win. of row 2, and vica versa
3871 	    // the matching one is the restacked one
3872 	    CompWindow *wRestacked = 0;
3873 	    CompWindow *wStart = 0;
3874 	    CompWindow *wEnd = 0;
3875 	    CompWindow *wOldAbove = 0;
3876 	    CompWindow *wChangeStart = 0;
3877 	    CompWindow *wChangeEnd = 0;
3878 
3879 	    Bool raised = FALSE;
3880 	    int changeStart = -1;
3881 	    int changeEnd = -1;
3882 
3883 	    int i;
3884 	    for (i = 0; i < n; i++)
3885 	    {
3886 		CompWindow *wi =
3887 		    findWindowAtScreen (s, clientListStacking[i]);
3888 
3889 		// skip if minimized (prevents flashing problem)
3890 		if (!wi || !isWinVisible(wi))
3891 		    continue;
3892 
3893 		// skip if (tabbed and) hidden by Group plugin
3894 		if (wi->state & (CompWindowStateSkipPagerMask |
3895 				 CompWindowStateSkipTaskbarMask))
3896 		    continue;
3897 
3898 		if (clientListStacking[i] !=
3899 		    as->lastClientListStacking[i])
3900 		{
3901 		    if (changeStart < 0)
3902 		    {
3903 			changeStart = i;
3904 			wChangeStart = wi; // make use of already found w
3905 		    }
3906 		    else
3907 		    {
3908 			changeEnd = i;
3909 			wChangeEnd = wi;
3910 		    }
3911 		}
3912 		else if (changeStart >= 0) // found some change earlier
3913 		    break;
3914 	    }
3915 
3916 	    // if restacking occurred
3917 	    if (changeStart >= 0 && changeEnd >= 0)
3918 	    {
3919 		CompWindow *w2;
3920 
3921 		// if we have only 2 windows changed,
3922 		// choose the one clicked on
3923 		Bool preferRaised = FALSE;
3924 		Bool onlyTwo = FALSE;
3925 
3926 		if (wChangeEnd &&
3927 		    clientListStacking[changeEnd] ==
3928 		    as->lastClientListStacking[changeStart] &&
3929 		    clientListStacking[changeStart] ==
3930 		    as->lastClientListStacking[changeEnd])
3931 		{
3932 		    // Check if the window coming on top was
3933 		    // configureNotified (clicked on)
3934 		    AnimWindow *aw2 = GET_ANIM_WINDOW(wChangeEnd, as);
3935 		    if (aw2->configureNotified)
3936 		    {
3937 			preferRaised = TRUE;
3938 		    }
3939 		    onlyTwo = TRUE;
3940 		}
3941 		// Clear all configureNotified's
3942 		for (w2 = s->windows; w2; w2 = w2->next)
3943 		{
3944 		    AnimWindow *aw2 = GET_ANIM_WINDOW(w2, as);
3945 		    aw2->configureNotified = FALSE;
3946 		}
3947 
3948 		if (preferRaised ||
3949 		    (!onlyTwo &&
3950 		     clientListStacking[changeEnd] ==
3951 		     as->lastClientListStacking[changeStart]))
3952 		{
3953 		    // raised
3954 		    raised = TRUE;
3955 		    wRestacked = wChangeEnd;
3956 		    wStart = wChangeStart;
3957 		    wEnd = wRestacked;
3958 		    wOldAbove = wStart;
3959 		}
3960 		else if (clientListStacking[changeStart] ==
3961 			 as->lastClientListStacking[changeEnd] && // lowered
3962 			 // We don't animate lowering if there is no
3963 			 // window above this window, since this window needs
3964 			 // to be drawn on such a "host" in animPaintWindow
3965 			 // (at least for now).
3966 			 changeEnd < n - 1)
3967 		{
3968 		    wRestacked = wChangeStart;
3969 		    wStart = wRestacked;
3970 		    wEnd = wChangeEnd;
3971 		    wOldAbove = findWindowAtScreen
3972 			(s, as->lastClientListStacking[changeEnd+1]);
3973 		}
3974 		for (; wOldAbove && !isWinVisible(wOldAbove);
3975 		     wOldAbove = wOldAbove->next)
3976 		    ;
3977 	    }
3978 	    if (wRestacked && wStart && wEnd && wOldAbove)
3979 	    {
3980 		AnimWindow *awRestacked = GET_ANIM_WINDOW(wRestacked, as);
3981 		if (awRestacked->created)
3982 		{
3983 		    RestackInfo *restackInfo = calloc(1, sizeof(RestackInfo));
3984 		    if (restackInfo)
3985 		    {
3986 			restackInfo->wRestacked = wRestacked;
3987 			restackInfo->wStart = wStart;
3988 			restackInfo->wEnd = wEnd;
3989 			restackInfo->wOldAbove = wOldAbove;
3990 			restackInfo->raised = raised;
3991 
3992 			if (awRestacked->restackInfo)
3993 			    free(awRestacked->restackInfo);
3994 
3995 			awRestacked->restackInfo = restackInfo;
3996 			as->aWinWasRestackedJustNow = TRUE;
3997 		    }
3998 		}
3999 	    }
4000 	}
4001 	updateLastClientListStacking(s);
4002     }
4003     break;
4004     default:
4005 	break;
4006     }
4007 
4008     UNWRAP(ad, d, handleEvent);
4009     (*d->handleEvent) (d, event);
4010     WRAP(ad, d, handleEvent, animHandleEvent);
4011 
4012     switch (event->type)
4013     {
4014     case PropertyNotify:
4015 	if (event->xproperty.atom == d->winActiveAtom &&
4016 	    d->activeWindow != ad->activeWindow)
4017 	{
4018 	    ad->activeWindow = d->activeWindow;
4019 	    w = findWindowAtDisplay(d, d->activeWindow);
4020 
4021 	    if (w)
4022 	    {
4023 		int duration = 200;
4024 		AnimEffect chosenEffect =
4025 		    getMatchingAnimSelection (w, AnimEventFocus, &duration);
4026 
4027 		if (!(chosenEffect == AnimEffectFocusFade ||
4028 		      chosenEffect == AnimEffectDodge))
4029 		    initiateFocusAnimation(w);
4030 	    }
4031 	}
4032 	break;
4033     case MapRequest:
4034 	w = findWindowAtDisplay (d, event->xmaprequest.window);
4035 	if (w && w->hints && w->hints->initial_state == IconicState)
4036 	{
4037 	    ANIM_WINDOW (w);
4038 	    aw->state = aw->newState = IconicState;
4039 	}
4040 	break;
4041     default:
4042 	break;
4043     }
4044 }
4045 
animDamageWindowRect(CompWindow * w,Bool initial,BoxPtr rect)4046 static Bool animDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect)
4047 {
4048     Bool status;
4049 
4050     ANIM_SCREEN(w->screen);
4051     ANIM_WINDOW(w);
4052 
4053     if (aw->ignoreDamage)
4054 	return TRUE; // if doing the unmap at animation's end, ignore the damage
4055 
4056     if (initial)				// Unminimize or Open
4057     {
4058 	int duration = 200;
4059 	AnimEffect chosenEffect;
4060 
4061 	if (aw->state == IconicState)
4062 	{
4063 	    chosenEffect =
4064 		getMatchingAnimSelection (w, AnimEventMinimize, &duration);
4065 
4066 	    // UNMINIMIZE event!
4067 
4068 	    if (!w->invisible && !w->hidden &&
4069 		chosenEffect != AnimEffectNone &&
4070 		!as->pluginActive[3]) // fadedesktop
4071 	    {
4072 		Bool startingNew = TRUE;
4073 		Bool playEffect = TRUE;
4074 
4075 		// Always reset stacking related info when a window is
4076 		// unminimized.
4077 		resetStackingInfo (w->screen);
4078 
4079 		if (aw->com.curWindowEvent != WindowEventNone)
4080 		{
4081 		    if (aw->com.curWindowEvent != WindowEventMinimize)
4082 		    {
4083 			postAnimationCleanupPrev (w, FALSE, FALSE);
4084 		    }
4085 		    else
4086 		    {
4087 			// Play the minimize effect backwards from where it left
4088 			aw->com.animRemainingTime =
4089 			    aw->com.animTotalTime - aw->com.animRemainingTime;
4090 
4091 			// avoid window remains
4092 			if (aw->com.animRemainingTime <= 0)
4093 			    aw->com.animRemainingTime = 1;
4094 
4095 			startingNew = FALSE;
4096 			if (aw->com.animOverrideProgressDir == 0)
4097 			    aw->com.animOverrideProgressDir = 1;
4098 			else if (aw->com.animOverrideProgressDir == 2)
4099 			    aw->com.animOverrideProgressDir = 0;
4100 		    }
4101 		}
4102 
4103 		if (startingNew)
4104 		{
4105 		    AnimEffect effectToBePlayed;
4106 		    effectToBePlayed = animGetAnimEffect (as,
4107 							  chosenEffect,
4108 							  AnimEventMinimize);
4109 
4110 		    // handle empty random effect list
4111 		    if (effectToBePlayed == AnimEffectNone)
4112 			playEffect = FALSE;
4113 
4114 		    if (playEffect)
4115 		    {
4116 			aw->com.curAnimEffect = effectToBePlayed;
4117 			aw->com.animTotalTime = duration;
4118 			aw->com.animRemainingTime = aw->com.animTotalTime;
4119 		    }
4120 		}
4121 
4122 		if (playEffect)
4123 		{
4124 		    animActivateEvent(w->screen, TRUE);
4125 		    aw->com.curWindowEvent = WindowEventUnminimize;
4126 
4127 		    if (animEnsureModel(w))
4128 		    {
4129 			if (w->iconGeometrySet)
4130 			{
4131 			    aw->com.icon = w->iconGeometry;
4132 			}
4133 			else
4134 			{
4135 			    // Unminimize from mouse pointer if there is no
4136 			    // window list or if the window skips taskbar
4137 			    if (!getMousePointerXY (w->screen,
4138 						    &aw->com.icon.x,
4139 						    &aw->com.icon.y))
4140 			    {
4141 				// Use screen center if can't get mouse coords
4142 				aw->com.icon.x = w->screen->width / 2;
4143 				aw->com.icon.y = w->screen->height / 2;
4144 			    }
4145 			    aw->com.icon.width = FAKE_ICON_SIZE;
4146 			    aw->com.icon.height = FAKE_ICON_SIZE;
4147 			}
4148 
4149 			damagePendingOnScreen (w->screen);
4150 		    }
4151 		    else
4152 		    {
4153 			postAnimationCleanup (w);
4154 		    }
4155 		}
4156 	    }
4157 	}
4158 	else if (aw->nowShaded)
4159 	{
4160 	    chosenEffect =
4161 		getMatchingAnimSelection (w, AnimEventShade, &duration);
4162 
4163 	    // UNSHADE event!
4164 
4165 	    aw->nowShaded = FALSE;
4166 
4167 	    if (chosenEffect != AnimEffectNone)
4168 	    {
4169 		Bool startingNew = TRUE;
4170 		Bool playEffect = TRUE;
4171 
4172 		if (aw->com.curWindowEvent != WindowEventNone)
4173 		{
4174 		    if (aw->com.curWindowEvent != WindowEventShade)
4175 		    {
4176 			postAnimationCleanupPrev (w, FALSE, FALSE);
4177 		    }
4178 		    else
4179 		    {
4180 			// Play the shade effect backwards from where it left
4181 			aw->com.animRemainingTime =
4182 			    aw->com.animTotalTime - aw->com.animRemainingTime;
4183 
4184 			// avoid window remains
4185 			if (aw->com.animRemainingTime <= 0)
4186 			    aw->com.animRemainingTime = 1;
4187 
4188 			startingNew = FALSE;
4189 			if (aw->com.animOverrideProgressDir == 0)
4190 			    aw->com.animOverrideProgressDir = 1;
4191 			else if (aw->com.animOverrideProgressDir == 2)
4192 			    aw->com.animOverrideProgressDir = 0;
4193 		    }
4194 		}
4195 
4196 		if (startingNew)
4197 		{
4198 		    AnimEffect effectToBePlayed;
4199 		    effectToBePlayed = animGetAnimEffect (as,
4200 							  chosenEffect,
4201 							  AnimEventShade);
4202 
4203 		    // handle empty random effect list
4204 		    if (effectToBePlayed == AnimEffectNone)
4205 			playEffect = FALSE;
4206 
4207 		    if (playEffect)
4208 		    {
4209 			aw->com.curAnimEffect = effectToBePlayed;
4210 			aw->com.animTotalTime = duration;
4211 			aw->com.animRemainingTime = aw->com.animTotalTime;
4212 		    }
4213 		}
4214 
4215 		if (playEffect)
4216 		{
4217 		    animActivateEvent(w->screen, TRUE);
4218 		    aw->com.curWindowEvent = WindowEventUnshade;
4219 
4220 		    if (animEnsureModel(w))
4221 			damagePendingOnScreen (w->screen);
4222 		    else
4223 			postAnimationCleanup (w);
4224 		}
4225 	    }
4226 	}
4227 	else if (!w->invisible && as->startCountdown == 0)
4228 	{
4229 	    AnimEffect chosenEffect;
4230 	    int duration = 200;
4231 
4232 	    // Always reset stacking related info when a window is opened.
4233 	    resetStackingInfo (w->screen);
4234 
4235 	    aw->created = TRUE;
4236 
4237 	    // OPEN event!
4238 
4239 	    if (!otherPluginsActive (as) &&
4240 		!shouldIgnoreForAnim (w, FALSE) &&
4241 		AnimEffectNone !=
4242 		(chosenEffect =
4243 		 getMatchingAnimSelection (w, AnimEventOpen, &duration)) &&
4244 		getMousePointerXY(w->screen, &aw->com.icon.x, &aw->com.icon.y))
4245 	    {
4246 		Bool startingNew = TRUE;
4247 		Bool playEffect = TRUE;
4248 
4249 		if (aw->com.curWindowEvent != WindowEventNone)
4250 		{
4251 		    if (aw->com.curWindowEvent != WindowEventClose)
4252 		    {
4253 			postAnimationCleanupPrev (w, FALSE, FALSE);
4254 		    }
4255 		    else
4256 		    {
4257 			// Play the close effect backwards from where it left
4258 			aw->com.animRemainingTime =
4259 			    aw->com.animTotalTime - aw->com.animRemainingTime;
4260 
4261 			// avoid window remains
4262 			if (aw->com.animRemainingTime == 0)
4263 			    aw->com.animRemainingTime = 1;
4264 
4265 			startingNew = FALSE;
4266 			if (aw->com.animOverrideProgressDir == 0)
4267 			    aw->com.animOverrideProgressDir = 1;
4268 			else if (aw->com.animOverrideProgressDir == 2)
4269 			    aw->com.animOverrideProgressDir = 0;
4270 		    }
4271 		}
4272 
4273 		if (startingNew)
4274 		{
4275 		    AnimEffect effectToBePlayed;
4276 		    effectToBePlayed = animGetAnimEffect (as,
4277 							  chosenEffect,
4278 							  AnimEventOpen);
4279 
4280 		    // handle empty random effect list
4281 		    if (effectToBePlayed == AnimEffectNone)
4282 			playEffect = FALSE;
4283 
4284 		    if (playEffect)
4285 		    {
4286 			aw->com.curAnimEffect = effectToBePlayed;
4287 			aw->com.animTotalTime = duration;
4288 			aw->com.animRemainingTime = aw->com.animTotalTime;
4289 		    }
4290 		}
4291 
4292 		if (playEffect)
4293 		{
4294 		    animActivateEvent(w->screen, TRUE);
4295 		    aw->com.curWindowEvent = WindowEventOpen;
4296 
4297 		    aw->com.icon.width = FAKE_ICON_SIZE;
4298 		    aw->com.icon.height = FAKE_ICON_SIZE;
4299 
4300 		    if (aw->com.curAnimEffect == AnimEffectMagicLamp)
4301 			aw->com.icon.width =
4302 			    MAX(aw->com.icon.width,
4303 				animGetI (w, ANIM_SCREEN_OPTION_MAGIC_LAMP_OPEN_START_WIDTH));
4304 		    else if (aw->com.curAnimEffect == AnimEffectVacuum)
4305 			aw->com.icon.width =
4306 			    MAX(aw->com.icon.width,
4307 				animGetI (w, ANIM_SCREEN_OPTION_VACUUM_OPEN_START_WIDTH));
4308 
4309 		    aw->com.icon.x -= aw->com.icon.width / 2;
4310 		    aw->com.icon.y -= aw->com.icon.height / 2;
4311 
4312 		    if (animEnsureModel(w))
4313 			damagePendingOnScreen (w->screen);
4314 		    else
4315 			postAnimationCleanup (w);
4316 		}
4317 	    }
4318 	}
4319 
4320 	aw->newState = NormalState;
4321     }
4322 
4323     UNWRAP(as, w->screen, damageWindowRect);
4324     status = (*w->screen->damageWindowRect) (w, initial, rect);
4325     WRAP(as, w->screen, damageWindowRect, animDamageWindowRect);
4326 
4327     return status;
4328 }
4329 
animWindowResizeNotify(CompWindow * w,int dx,int dy,int dwidth,int dheight)4330 static void animWindowResizeNotify(CompWindow * w, int dx, int dy, int dwidth, int dheight)
4331 {
4332     ANIM_SCREEN(w->screen);
4333     ANIM_WINDOW(w);
4334 
4335     // Don't let transient window open anim be interrupted with a resize notify
4336     if (!(aw->com.curWindowEvent == WindowEventOpen &&
4337 	  (w->wmType &
4338 	   (CompWindowTypeDropdownMenuMask |
4339 	    CompWindowTypePopupMenuMask |
4340        	    CompWindowTypeMenuMask |
4341 	    CompWindowTypeTooltipMask |
4342 	    CompWindowTypeNotificationMask |
4343 	    CompWindowTypeComboMask |
4344 	    CompWindowTypeDndMask))))
4345     {
4346 	if (aw->com.curAnimEffect->properties.refreshFunc)
4347 	    aw->com.curAnimEffect->properties.refreshFunc (w, aw->animInitialized);
4348 
4349 	if (aw->com.animRemainingTime > 0)
4350 	{
4351 	    aw->com.animRemainingTime = 0;
4352 	    postAnimationCleanup (w);
4353 	}
4354     }
4355 
4356     if (aw->com.model)
4357     {
4358 	modelInitObjects(aw->com.model,
4359 			 WIN_X(w), WIN_Y(w),
4360 			 WIN_W(w), WIN_H(w));
4361     }
4362 
4363     UNWRAP(as, w->screen, windowResizeNotify);
4364     (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
4365     WRAP(as, w->screen, windowResizeNotify, animWindowResizeNotify);
4366 }
4367 
4368 static void
animWindowMoveNotify(CompWindow * w,int dx,int dy,Bool immediate)4369 animWindowMoveNotify(CompWindow * w, int dx, int dy, Bool immediate)
4370 {
4371     ANIM_SCREEN(w->screen);
4372     ANIM_WINDOW(w);
4373 
4374     if (!immediate)
4375     {
4376 	if (!(aw->com.animRemainingTime > 0 &&
4377 	      (aw->com.curAnimEffect == AnimEffectFocusFade ||
4378 	       aw->com.curAnimEffect == AnimEffectDodge)))
4379 	{
4380 	    CompWindow *w2;
4381 
4382 	    if (aw->com.curAnimEffect->properties.refreshFunc)
4383 		aw->com.curAnimEffect->properties.refreshFunc
4384 		    (w, aw->animInitialized);
4385 
4386 	    if (aw->com.animRemainingTime > 0 && aw->grabbed)
4387 	    {
4388 		aw->com.animRemainingTime = 0;
4389 		if (as->animInProgress)
4390 		{
4391 		    Bool animStillInProgress = FALSE;
4392 		    for (w2 = w->screen->windows; w2; w2 = w2->next)
4393 		    {
4394 			AnimWindow *aw2;
4395 
4396 			aw2 = GET_ANIM_WINDOW(w2, as);
4397 			if (aw2->com.animRemainingTime > 0)
4398 			{
4399 			    animStillInProgress = TRUE;
4400 			    break;
4401 			}
4402 		    }
4403 
4404 		    if (!animStillInProgress)
4405 			animActivateEvent(w->screen, FALSE);
4406 		}
4407 		postAnimationCleanup (w);
4408 	    }
4409 
4410 	    if (aw->com.model)
4411 	    {
4412 		modelInitObjects(aw->com.model, WIN_X(w), WIN_Y(w), WIN_W(w),
4413 				 WIN_H(w));
4414 	    }
4415 	}
4416     }
4417     else if (aw->com.model)
4418 	modelMove (aw->com.model, dx, dy);
4419 
4420     UNWRAP(as, w->screen, windowMoveNotify);
4421     (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
4422     WRAP(as, w->screen, windowMoveNotify, animWindowMoveNotify);
4423 }
4424 
4425 static void
animWindowGrabNotify(CompWindow * w,int x,int y,unsigned int state,unsigned int mask)4426 animWindowGrabNotify(CompWindow * w,
4427 		     int x, int y, unsigned int state, unsigned int mask)
4428 {
4429     ANIM_SCREEN(w->screen);
4430     ANIM_WINDOW(w);
4431 
4432     aw->grabbed = TRUE;
4433 
4434     UNWRAP(as, w->screen, windowGrabNotify);
4435     (*w->screen->windowGrabNotify) (w, x, y, state, mask);
4436     WRAP(as, w->screen, windowGrabNotify, animWindowGrabNotify);
4437 }
4438 
animWindowUngrabNotify(CompWindow * w)4439 static void animWindowUngrabNotify(CompWindow * w)
4440 {
4441     ANIM_SCREEN(w->screen);
4442     ANIM_WINDOW(w);
4443 
4444     aw->grabbed = FALSE;
4445 
4446     UNWRAP(as, w->screen, windowUngrabNotify);
4447     (*w->screen->windowUngrabNotify) (w);
4448     WRAP(as, w->screen, windowUngrabNotify, animWindowUngrabNotify);
4449 }
4450 
4451 static Bool
animPaintOutput(CompScreen * s,const ScreenPaintAttrib * sAttrib,const CompTransform * transform,Region region,CompOutput * output,unsigned int mask)4452 animPaintOutput(CompScreen * s,
4453 		const ScreenPaintAttrib * sAttrib,
4454 		const CompTransform    *transform,
4455 		Region region, CompOutput *output,
4456 		unsigned int mask)
4457 {
4458     Bool status;
4459 
4460     ANIM_SCREEN(s);
4461 
4462     if (as->animInProgress)
4463     {
4464 	int p;
4465 	for (p = 0; p < as->nExtensionPlugins; p++)
4466 	{
4467 	    const ExtensionPluginInfo *extPlugin = as->extensionPlugins[p];
4468 	    if (extPlugin->prePaintOutputFunc)
4469 		extPlugin->prePaintOutputFunc (s, output);
4470 	}
4471 
4472 	mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
4473     }
4474 
4475     as->output = output;
4476 
4477     UNWRAP(as, s, paintOutput);
4478     status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
4479     WRAP(as, s, paintOutput, animPaintOutput);
4480 
4481     CompWindow *w;
4482     if (as->aWinWasRestackedJustNow)
4483     {
4484 	as->aWinWasRestackedJustNow = FALSE;
4485     }
4486     if (as->startCountdown > 0)
4487     {
4488 	as->startCountdown--;
4489 	if (as->startCountdown == 0)
4490 	{
4491 	    // Mark all windows as "created"
4492 	    for (w = s->windows; w; w = w->next)
4493 	    {
4494 		ANIM_WINDOW(w);
4495 		aw->created = TRUE;
4496 	    }
4497 	}
4498     }
4499 
4500     return status;
4501 }
4502 
4503 static const CompMetadataOptionInfo animDisplayOptionInfo[] = {
4504     { "abi", "int", 0, 0, 0 },
4505     { "index", "int", 0, 0, 0 }
4506 };
4507 
4508 static CompOption *
animGetDisplayOptions(CompPlugin * plugin,CompDisplay * display,int * count)4509 animGetDisplayOptions (CompPlugin  *plugin,
4510 		       CompDisplay *display,
4511 		       int         *count)
4512 {
4513     ANIM_DISPLAY (display);
4514     *count = NUM_OPTIONS (ad);
4515     return ad->opt;
4516 }
4517 
4518 static Bool
animSetDisplayOption(CompPlugin * plugin,CompDisplay * display,const char * name,CompOptionValue * value)4519 animSetDisplayOption (CompPlugin      *plugin,
4520 		      CompDisplay     *display,
4521 		      const char      *name,
4522 		      CompOptionValue *value)
4523 {
4524     CompOption      *o;
4525     int	            index;
4526     ANIM_DISPLAY (display);
4527     o = compFindOption (ad->opt, NUM_OPTIONS (ad), name, &index);
4528     if (!o)
4529 	return FALSE;
4530 
4531     switch (index) {
4532     case ANIM_DISPLAY_OPTION_ABI:
4533     case ANIM_DISPLAY_OPTION_INDEX:
4534         break;
4535     default:
4536         return compSetDisplayOption (display, o, value);
4537     }
4538 
4539     return FALSE;
4540 }
4541 
4542 static AnimWindowCommon *
getAnimWindowCommon(CompWindow * w)4543 getAnimWindowCommon (CompWindow *w)
4544 {
4545     ANIM_WINDOW (w);
4546 
4547     return &aw->com;
4548 }
4549 
4550 AnimBaseFunctions animBaseFunctions =
4551 {
4552     .addExtension		= animAddExtension,
4553     .removeExtension		= animRemoveExtension,
4554     .getPluginOptVal		= animGetPluginOptVal,
4555     .getMousePointerXY		= getMousePointerXY,
4556     .defaultAnimInit		= defaultAnimInit,
4557     .defaultAnimStep		= defaultAnimStep,
4558     .defaultUpdateWindowTransform = defaultUpdateWindowTransform,
4559     .getProgressAndCenter	= getProgressAndCenter,
4560     .defaultAnimProgress	= defaultAnimProgress,
4561     .sigmoidAnimProgress	= sigmoidAnimProgress,
4562     .decelerateProgressCustom	= decelerateProgressCustom,
4563     .decelerateProgress		= decelerateProgress,
4564     .updateBBScreen		= updateBBScreen,
4565     .updateBBWindow		= updateBBWindow,
4566     .modelUpdateBB		= modelUpdateBB,
4567     .compTransformUpdateBB	= compTransformUpdateBB,
4568     .getActualAnimDirection	= getActualAnimDirection,
4569     .expandBoxWithBox		= expandBoxWithBox,
4570     .expandBoxWithPoint		= expandBoxWithPoint,
4571     .prepareTransform		= prepareTransform,
4572     .getAnimWindowCommon	= getAnimWindowCommon,
4573     .returnTrue			= returnTrue,
4574     .postAnimationCleanup	= postAnimationCleanup,
4575     .fxZoomUpdateWindowAttrib	= fxZoomUpdateWindowAttrib
4576 };
4577 
animInitDisplay(CompPlugin * p,CompDisplay * d)4578 static Bool animInitDisplay(CompPlugin * p, CompDisplay * d)
4579 {
4580     AnimDisplay *ad;
4581 
4582     if (!checkPluginABI ("core", CORE_ABIVERSION))
4583 	return FALSE;
4584 
4585     ad = calloc(1, sizeof(AnimDisplay));
4586     if (!ad)
4587 	return FALSE;
4588 
4589     if (!compInitDisplayOptionsFromMetadata (d,
4590 					     &animMetadata,
4591 					     animDisplayOptionInfo,
4592 					     ad->opt,
4593 					     ANIM_DISPLAY_OPTION_NUM))
4594     {
4595 	free (ad);
4596 	return FALSE;
4597     }
4598 
4599     ad->screenPrivateIndex = allocateScreenPrivateIndex(d);
4600     if (ad->screenPrivateIndex < 0)
4601     {
4602 	free(ad);
4603 	return FALSE;
4604     }
4605 
4606     // Never animate screen-dimming layer of logout window and gksu.
4607     matchInit (&ad->neverAnimateMatch);
4608     matchAddExp (&ad->neverAnimateMatch, 0, "title=gksu");
4609     matchAddExp (&ad->neverAnimateMatch, 0, "title=x-session-manager");
4610     matchAddExp (&ad->neverAnimateMatch, 0, "title=gnome-session");
4611     matchUpdate (d, &ad->neverAnimateMatch);
4612 
4613     WRAP(ad, d, handleEvent, animHandleEvent);
4614     WRAP(ad, d, handleCompizEvent, animHandleCompizEvent);
4615 
4616     ad->opt[ANIM_DISPLAY_OPTION_ABI].value.i   = ANIMATION_ABIVERSION;
4617     ad->opt[ANIM_DISPLAY_OPTION_INDEX].value.i = animFunctionsPrivateIndex;
4618 
4619     d->base.privates[animDisplayPrivateIndex].ptr = ad;
4620     d->base.privates[animFunctionsPrivateIndex].ptr = &animBaseFunctions;
4621 
4622     return TRUE;
4623 }
4624 
animFiniDisplay(CompPlugin * p,CompDisplay * d)4625 static void animFiniDisplay(CompPlugin * p, CompDisplay * d)
4626 {
4627     ANIM_DISPLAY(d);
4628 
4629     freeScreenPrivateIndex(d, ad->screenPrivateIndex);
4630 
4631     matchFini (&ad->neverAnimateMatch);
4632 
4633     compFiniDisplayOptions (d, ad->opt, ANIM_DISPLAY_OPTION_NUM);
4634 
4635     UNWRAP(ad, d, handleCompizEvent);
4636     UNWRAP(ad, d, handleEvent);
4637 
4638     free(ad);
4639 }
4640 
4641 AnimEffect AnimEffectNone = &(AnimEffectInfo)
4642     {"animation:None",
4643      {TRUE, TRUE, TRUE, TRUE, TRUE}};
4644 
4645 AnimEffect AnimEffectRandom = &(AnimEffectInfo)
4646     {"animation:Random",
4647      {TRUE, TRUE, TRUE, TRUE, FALSE}};
4648 
4649 AnimEffect AnimEffectCurvedFold = &(AnimEffectInfo)
4650     {"animation:Curved Fold",
4651      {TRUE, TRUE, TRUE, TRUE, FALSE},
4652      {.updateWindowAttribFunc	= fxFoldUpdateWindowAttrib,
4653       .animStepFunc		= fxCurvedFoldModelStep,
4654       .initFunc			= animWithTransformInit,
4655       .initGridFunc		= fxMagicLampInitGrid,
4656       .updateWinTransformFunc	= defaultUpdateWindowTransform,
4657       .updateBBFunc		= modelUpdateBB,
4658       .zoomToIconFunc		= fxCurvedFoldZoomToIcon,
4659       .modelAnimIs3D		= TRUE}};
4660 
4661 AnimEffect AnimEffectDodge = &(AnimEffectInfo)
4662     {"animation:Dodge",
4663      {FALSE, FALSE, FALSE, FALSE, TRUE},
4664      {.animStepFunc		= fxDodgeAnimStep,
4665       .initFunc			= defaultAnimInit,
4666       .letOthersDrawGeomsFunc	= returnTrue,
4667       .updateWinTransformFunc	= fxDodgeUpdateWindowTransform,
4668       .postPrepPaintScreenFunc	= fxDodgePostPreparePaintScreen,
4669       .updateBBFunc 		= fxDodgeUpdateBB}};
4670 
4671 AnimEffect AnimEffectDream = &(AnimEffectInfo)
4672     {"animation:Dream",
4673      {TRUE, TRUE, TRUE, FALSE, FALSE},
4674      {.updateWindowAttribFunc 	= fxDreamUpdateWindowAttrib,
4675       .animStepFunc		= fxDreamModelStep,
4676       .initFunc			= fxDreamAnimInit,
4677       .initGridFunc		= fxMagicLampInitGrid,
4678       .updateWinTransformFunc	= defaultUpdateWindowTransform,
4679       .updateBBFunc		= modelUpdateBB,
4680       .zoomToIconFunc		= fxDreamZoomToIcon}};
4681 
4682 AnimEffect AnimEffectFade = &(AnimEffectInfo)
4683     {"animation:Fade",
4684      {TRUE, TRUE, TRUE, FALSE, FALSE},
4685      {.updateWindowAttribFunc	= fxFadeUpdateWindowAttrib,
4686       .animStepFunc 		= defaultAnimStep,
4687       .initFunc			= defaultAnimInit,
4688       .letOthersDrawGeomsFunc	= returnTrue,
4689       .updateBBFunc		= updateBBWindow}};
4690 
4691 AnimEffect AnimEffectFocusFade = &(AnimEffectInfo)
4692     {"animation:Focus Fade",
4693      {FALSE, FALSE, FALSE, FALSE, TRUE},
4694      {.updateWindowAttribFunc	= fxFocusFadeUpdateWindowAttrib,
4695       .animStepFunc		= defaultAnimStep,
4696       .initFunc			= defaultAnimInit,
4697       .letOthersDrawGeomsFunc	= returnTrue,
4698       .updateBBFunc		= updateBBWindow}};
4699 
4700 AnimEffect AnimEffectGlide1 = &(AnimEffectInfo)
4701     {"animation:Glide 1",
4702      {TRUE, TRUE, TRUE, FALSE, FALSE},
4703      {.updateWindowAttribFunc	= fxGlideUpdateWindowAttrib,
4704       .prePaintWindowFunc	= fxGlidePrePaintWindow,
4705       .postPaintWindowFunc	= fxGlidePostPaintWindow,
4706       .animStepFunc		= fxGlideAnimStep,
4707       .initFunc			= fxGlideInit,
4708       .letOthersDrawGeomsFunc	= returnTrue,
4709       .updateWinTransformFunc	= fxGlideUpdateWindowTransform,
4710       .updateBBFunc		= compTransformUpdateBB,
4711       .zoomToIconFunc		= fxGlideZoomToIcon}};
4712 
4713 AnimEffect AnimEffectGlide2 = &(AnimEffectInfo)
4714     {"animation:Glide 2",
4715      {TRUE, TRUE, TRUE, FALSE, FALSE},
4716      {.updateWindowAttribFunc	= fxGlideUpdateWindowAttrib,
4717       .prePaintWindowFunc	= fxGlidePrePaintWindow,
4718       .postPaintWindowFunc	= fxGlidePostPaintWindow,
4719       .animStepFunc		= fxGlideAnimStep,
4720       .initFunc			= fxGlideInit,
4721       .letOthersDrawGeomsFunc	= returnTrue,
4722       .updateWinTransformFunc	= fxGlideUpdateWindowTransform,
4723       .updateBBFunc		= compTransformUpdateBB,
4724       .zoomToIconFunc		= fxGlideZoomToIcon}};
4725 
4726 AnimEffect AnimEffectHorizontalFolds = &(AnimEffectInfo)
4727     {"animation:Horizontal Folds",
4728      {TRUE, TRUE, TRUE, TRUE, FALSE},
4729      {.updateWindowAttribFunc	= fxFoldUpdateWindowAttrib,
4730       .animStepFunc		= fxHorizontalFoldsModelStep,
4731       .initFunc			= animWithTransformInit,
4732       .initGridFunc		= fxHorizontalFoldsInitGrid,
4733       .updateWinTransformFunc	= defaultUpdateWindowTransform,
4734       .updateBBFunc		= modelUpdateBB,
4735       .zoomToIconFunc		= fxHorizontalFoldsZoomToIcon,
4736       .modelAnimIs3D		= TRUE}};
4737 
4738 AnimEffect AnimEffectMagicLamp = &(AnimEffectInfo)
4739     {"animation:Magic Lamp",
4740      {TRUE, TRUE, TRUE, FALSE, FALSE},
4741      {.animStepFunc		= fxMagicLampModelStep,
4742       .initFunc			= fxMagicLampInit,
4743       .initGridFunc		= fxMagicLampInitGrid,
4744       .updateBBFunc		= modelUpdateBB,
4745       .useQTexCoord		= TRUE}};
4746 
4747 AnimEffect AnimEffectRollUp = &(AnimEffectInfo)
4748     {"animation:Roll Up",
4749      {TRUE, TRUE, TRUE, TRUE, FALSE},
4750      {.animStepFunc		= fxRollUpModelStep,
4751       .initFunc			= fxRollUpAnimInit,
4752       .initGridFunc		= fxRollUpInitGrid,
4753       .updateBBFunc		= modelUpdateBB}};
4754 
4755 AnimEffect AnimEffectSidekick = &(AnimEffectInfo)
4756     {"animation:Sidekick",
4757      {TRUE, TRUE, TRUE, FALSE, FALSE},
4758      {.updateWindowAttribFunc	= fxZoomUpdateWindowAttrib,
4759       .animStepFunc		= defaultAnimStep,
4760       .initFunc			= fxSidekickInit,
4761       .letOthersDrawGeomsFunc	= returnTrue,
4762       .updateWinTransformFunc	= defaultUpdateWindowTransform,
4763       .updateBBFunc		= compTransformUpdateBB,
4764       .zoomToIconFunc		= returnTrue}};
4765 
4766 AnimEffect AnimEffectVacuum = &(AnimEffectInfo)
4767     {"animation:Vacuum",
4768      {TRUE, TRUE, FALSE, FALSE, FALSE},
4769      {.animStepFunc		= fxMagicLampModelStep,
4770       .initFunc			= fxMagicLampInit,
4771       .initGridFunc		= fxVacuumInitGrid,
4772       .updateBBFunc		= modelUpdateBB,
4773       .useQTexCoord		= TRUE}};
4774 
4775 AnimEffect AnimEffectWave = &(AnimEffectInfo)
4776     {"animation:Wave",
4777      {TRUE, TRUE, TRUE, FALSE, TRUE},
4778      {.animStepFunc		= fxWaveModelStep,
4779       .initFunc			= animWithTransformInit,
4780       .initGridFunc		= fxMagicLampInitGrid,
4781       .updateWinTransformFunc	= defaultUpdateWindowTransform,
4782       .updateBBFunc		= modelUpdateBB,
4783       .modelAnimIs3D		= TRUE}};
4784 
4785 AnimEffect AnimEffectZoom = &(AnimEffectInfo)
4786     {"animation:Zoom",
4787      {TRUE, TRUE, TRUE, FALSE, FALSE},
4788      {.updateWindowAttribFunc	= fxZoomUpdateWindowAttrib,
4789       .animStepFunc		= defaultAnimStep,
4790       .initFunc			= fxZoomInit,
4791       .letOthersDrawGeomsFunc	= returnTrue,
4792       .updateWinTransformFunc	= defaultUpdateWindowTransform,
4793       .updateBBFunc		= compTransformUpdateBB,
4794       .zoomToIconFunc		= returnTrue}};
4795 
4796 AnimEffect animEffects[NUM_EFFECTS];
4797 
4798 ExtensionPluginInfo animExtensionPluginInfo = {
4799     .nEffects		= NUM_EFFECTS,
4800     .effects		= animEffects,
4801 
4802     .nEffectOptions	= ANIM_SCREEN_OPTION_NUM - NUM_NONEFFECT_OPTIONS,
4803 };
4804 
animInitScreen(CompPlugin * p,CompScreen * s)4805 static Bool animInitScreen(CompPlugin * p, CompScreen * s)
4806 {
4807     AnimScreen *as;
4808     CompDisplay *d = s->display;
4809 
4810     ANIM_DISPLAY(d);
4811 
4812     as = calloc(1, sizeof(AnimScreen));
4813     if (!as)
4814 	return FALSE;
4815 
4816     if (!compInitScreenOptionsFromMetadata (s,
4817 					    &animMetadata,
4818 					    animScreenOptionInfo,
4819 					    as->opt,
4820 					    ANIM_SCREEN_OPTION_NUM))
4821     {
4822 	free (as);
4823 	return FALSE;
4824     }
4825 
4826     as->windowPrivateIndex = allocateWindowPrivateIndex(s);
4827     if (as->windowPrivateIndex < 0)
4828     {
4829 	compFiniScreenOptions (s, as->opt, ANIM_SCREEN_OPTION_NUM);
4830 	free(as);
4831 	return FALSE;
4832     }
4833 
4834     s->base.privates[ad->screenPrivateIndex].ptr = as;
4835 
4836     as->animInProgress = FALSE;
4837 
4838     AnimEffect animEffectsTmp[NUM_EFFECTS] =
4839     {
4840 	AnimEffectNone,
4841 	AnimEffectRandom,
4842 	AnimEffectCurvedFold,
4843 	AnimEffectDodge,
4844 	AnimEffectDream,
4845 	AnimEffectFade,
4846 	AnimEffectFocusFade,
4847 	AnimEffectGlide1,
4848 	AnimEffectGlide2,
4849 	AnimEffectHorizontalFolds,
4850 	AnimEffectMagicLamp,
4851 	AnimEffectRollUp,
4852 	AnimEffectSidekick,
4853 	AnimEffectVacuum,
4854 	AnimEffectWave,
4855 	AnimEffectZoom
4856     };
4857     memcpy (animEffects,
4858 	    animEffectsTmp,
4859 	    NUM_EFFECTS * sizeof (AnimEffect));
4860 
4861     animExtensionPluginInfo.effectOptions = &as->opt[NUM_NONEFFECT_OPTIONS];
4862 
4863     // Extends itself with the basic set of animation effects.
4864     animAddExtension (s, &animExtensionPluginInfo);
4865 
4866     AnimEvent e;
4867     for (e = 0; e < AnimEventNum; e++) // for each anim event
4868 	updateOptionSets (s, e);
4869 
4870     updateAllEventEffects (s);
4871 
4872     as->lastClientListStacking = NULL;
4873     as->nLastClientListStacking = 0;
4874 
4875     WRAP(as, s, preparePaintScreen, animPreparePaintScreen);
4876     WRAP(as, s, donePaintScreen, animDonePaintScreen);
4877     WRAP(as, s, paintOutput, animPaintOutput);
4878     WRAP(as, s, paintWindow, animPaintWindow);
4879     WRAP(as, s, damageWindowRect, animDamageWindowRect);
4880     WRAP(as, s, addWindowGeometry, animAddWindowGeometry);
4881     WRAP(as, s, drawWindowTexture, animDrawWindowTexture);
4882     WRAP(as, s, windowResizeNotify, animWindowResizeNotify);
4883     WRAP(as, s, windowMoveNotify, animWindowMoveNotify);
4884     WRAP(as, s, windowGrabNotify, animWindowGrabNotify);
4885     WRAP(as, s, windowUngrabNotify, animWindowUngrabNotify);
4886     WRAP(as, s, initWindowWalker, animInitWindowWalker);
4887 
4888     as->startCountdown = 20; // start the countdown
4889 
4890     return TRUE;
4891 }
4892 
animFiniScreen(CompPlugin * p,CompScreen * s)4893 static void animFiniScreen(CompPlugin * p, CompScreen * s)
4894 {
4895     ANIM_SCREEN(s);
4896 
4897     if (as->animInProgress)
4898 	animActivateEvent(s, FALSE);
4899 
4900     freeWindowPrivateIndex(s, as->windowPrivateIndex);
4901 
4902     if (as->lastClientListStacking)
4903 	free(as->lastClientListStacking);
4904 
4905     free (as->extensionPlugins);
4906     freeAllEffects (as);
4907     freeAllOptionSets (as);
4908 
4909     UNWRAP(as, s, preparePaintScreen);
4910     UNWRAP(as, s, donePaintScreen);
4911     UNWRAP(as, s, paintOutput);
4912     UNWRAP(as, s, paintWindow);
4913     UNWRAP(as, s, damageWindowRect);
4914     UNWRAP(as, s, addWindowGeometry);
4915     UNWRAP(as, s, drawWindowTexture);
4916     UNWRAP(as, s, windowResizeNotify);
4917     UNWRAP(as, s, windowMoveNotify);
4918     UNWRAP(as, s, windowGrabNotify);
4919     UNWRAP(as, s, windowUngrabNotify);
4920     UNWRAP(as, s, initWindowWalker);
4921 
4922     compFiniScreenOptions (s, as->opt, ANIM_SCREEN_OPTION_NUM);
4923 
4924     free(as);
4925 }
4926 
animInitWindow(CompPlugin * p,CompWindow * w)4927 static Bool animInitWindow(CompPlugin * p, CompWindow * w)
4928 {
4929     AnimWindow *aw;
4930 
4931     ANIM_SCREEN(w->screen);
4932 
4933     aw = calloc(1, sizeof(AnimWindow));
4934     if (!aw)
4935 	return FALSE;
4936 
4937     aw->com.model = 0;
4938     aw->com.animRemainingTime = 0;
4939     aw->animInitialized = FALSE;
4940     aw->com.curAnimEffect = AnimEffectNone;
4941     aw->com.curWindowEvent = WindowEventNone;
4942     aw->com.animOverrideProgressDir = 0;
4943     aw->curAnimSelectionRow = -1;
4944 
4945     w->indexCount = 0;
4946 
4947     aw->unmapCnt = 0;
4948     aw->destroyCnt = 0;
4949 
4950     aw->grabbed = FALSE;
4951 
4952     aw->com.useDrawRegion = FALSE;
4953     aw->com.drawRegion = NULL;
4954 
4955     aw->BB.x1 = aw->BB.y1 = MAXSHORT;
4956     aw->BB.x2 = aw->BB.y2 = MINSHORT;
4957 
4958     aw->nowShaded = FALSE;
4959 
4960     if (w->minimized)
4961     {
4962 	aw->state = aw->newState = IconicState;
4963     }
4964     else if (w->shaded)
4965     {
4966 	aw->state = aw->newState = NormalState;
4967 	aw->nowShaded = TRUE;
4968     }
4969     else
4970     {
4971 	aw->state = aw->newState = animGetWindowState(w);
4972     }
4973 
4974     w->base.privates[as->windowPrivateIndex].ptr = aw;
4975 
4976     return TRUE;
4977 }
4978 
animFiniWindow(CompPlugin * p,CompWindow * w)4979 static void animFiniWindow(CompPlugin * p, CompWindow * w)
4980 {
4981     ANIM_SCREEN(w->screen);
4982     ANIM_WINDOW(w);
4983 
4984     postAnimationCleanupCustom (w, FALSE, TRUE, TRUE);
4985 
4986     animFreeModel(aw);
4987 
4988     free(aw);
4989     w->base.privates[as->windowPrivateIndex].ptr = NULL;
4990 }
4991 
4992 static CompBool
animInitObject(CompPlugin * p,CompObject * o)4993 animInitObject (CompPlugin *p,
4994 		CompObject *o)
4995 {
4996     static InitPluginObjectProc dispTab[] = {
4997 	(InitPluginObjectProc) 0, /* InitCore */
4998 	(InitPluginObjectProc) animInitDisplay,
4999 	(InitPluginObjectProc) animInitScreen,
5000 	(InitPluginObjectProc) animInitWindow
5001     };
5002 
5003     RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
5004 }
5005 
5006 static void
animFiniObject(CompPlugin * p,CompObject * o)5007 animFiniObject (CompPlugin *p,
5008 		CompObject *o)
5009 {
5010     static FiniPluginObjectProc dispTab[] = {
5011 	(FiniPluginObjectProc) 0, /* FiniCore */
5012 	(FiniPluginObjectProc) animFiniDisplay,
5013 	(FiniPluginObjectProc) animFiniScreen,
5014 	(FiniPluginObjectProc) animFiniWindow
5015     };
5016 
5017     DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
5018 }
5019 
5020 static CompOption *
animGetObjectOptions(CompPlugin * plugin,CompObject * object,int * count)5021 animGetObjectOptions (CompPlugin *plugin,
5022 		      CompObject *object,
5023 		      int	   *count)
5024 {
5025     static GetPluginObjectOptionsProc dispTab[] = {
5026 	(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
5027 	(GetPluginObjectOptionsProc) animGetDisplayOptions,
5028 	(GetPluginObjectOptionsProc) animGetScreenOptions
5029     };
5030 
5031     *count = 0;
5032     RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
5033 		     NULL, (plugin, object, count));
5034 }
5035 
5036 static CompBool
animSetObjectOption(CompPlugin * plugin,CompObject * object,const char * name,CompOptionValue * value)5037 animSetObjectOption (CompPlugin      *plugin,
5038 		     CompObject      *object,
5039 		     const char      *name,
5040 		     CompOptionValue *value)
5041 {
5042     static SetPluginObjectOptionProc dispTab[] = {
5043 	(SetPluginObjectOptionProc) 0, /* SetCoreOption */
5044 	(SetPluginObjectOptionProc) animSetDisplayOption,
5045 	(SetPluginObjectOptionProc) animSetScreenOptions
5046     };
5047 
5048     RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
5049 		     (plugin, object, name, value));
5050 }
5051 
animInit(CompPlugin * p)5052 static Bool animInit(CompPlugin * p)
5053 {
5054     if (!compInitPluginMetadataFromInfo (&animMetadata,
5055 					 p->vTable->name,
5056 					 0, 0,
5057 					 animScreenOptionInfo,
5058 					 ANIM_SCREEN_OPTION_NUM))
5059 	return FALSE;
5060 
5061     animDisplayPrivateIndex = allocateDisplayPrivateIndex();
5062     if (animDisplayPrivateIndex < 0)
5063     {
5064 	compFiniMetadata (&animMetadata);
5065 	return FALSE;
5066     }
5067 
5068     animFunctionsPrivateIndex = allocateDisplayPrivateIndex ();
5069     if (animFunctionsPrivateIndex < 0)
5070     {
5071 	freeDisplayPrivateIndex (animDisplayPrivateIndex);
5072 	compFiniMetadata (&animMetadata);
5073 	return FALSE;
5074     }
5075 
5076     compAddMetadataFromFile (&animMetadata, p->vTable->name);
5077 
5078     return TRUE;
5079 }
5080 
animFini(CompPlugin * p)5081 static void animFini(CompPlugin * p)
5082 {
5083     freeDisplayPrivateIndex(animDisplayPrivateIndex);
5084     freeDisplayPrivateIndex (animFunctionsPrivateIndex);
5085     compFiniMetadata (&animMetadata);
5086 }
5087 
5088 static CompMetadata *
animGetMetadata(CompPlugin * plugin)5089 animGetMetadata (CompPlugin *plugin)
5090 {
5091     return &animMetadata;
5092 }
5093 
5094 CompPluginVTable animVTable = {
5095     "animation",
5096     animGetMetadata,
5097     animInit,
5098     animFini,
5099     animInitObject,
5100     animFiniObject,
5101     animGetObjectOptions,
5102     animSetObjectOption,
5103 };
5104 
5105 CompPluginVTable*
getCompPluginInfo20070830(void)5106 getCompPluginInfo20070830 (void)
5107 {
5108     return &animVTable;
5109 }
5110 
5111