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  * Particle system added by : (C) 2006 Dennis Kasprzyk
14  * E-mail                   : onestone@beryl-project.org
15  *
16  * Beam-Up added by : Florencio Guimaraes
17  * E-mail           : florencio@nexcorp.com.br
18  *
19  * Hexagon tessellator added by : Mike Slegeir
20  * E-mail                       : mikeslegeir@mail.utexas.edu>
21  *
22  * This program is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU General Public License
24  * as published by the Free Software Foundation; either version 2
25  * of the License, or (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
35  */
36 
37 #include "animation-internal.h"
38 
39 // =====================  Effect: Dodge  =========================
40 
41 static void
fxDodgeProcessSubject(CompWindow * wCur,Region wRegion,Region dodgeRegion,Bool alwaysInclude)42 fxDodgeProcessSubject (CompWindow *wCur,
43 		       Region wRegion,
44 		       Region dodgeRegion,
45 		       Bool alwaysInclude)
46 {
47     XRectangle rect;
48     rect.x = WIN_X(wCur);
49     rect.y = WIN_Y(wCur);
50     rect.width = WIN_W(wCur);
51     rect.height = WIN_H(wCur);
52     Region wCurRegion = XCreateRegion();
53     if (!wCurRegion)
54 	return;
55 
56     XUnionRectWithRegion(&rect, &emptyRegion, wCurRegion);
57     if (!alwaysInclude)
58     {
59 	Region intersectionRegion = XCreateRegion();
60 	if (intersectionRegion)
61 	{
62 	    XIntersectRegion(wRegion, wCurRegion,
63 			     intersectionRegion);
64 	    if (!XEmptyRegion(intersectionRegion))
65 		XUnionRegion(dodgeRegion, wCurRegion, dodgeRegion);
66 	    XDestroyRegion (intersectionRegion);
67 	}
68     }
69     else
70 	XUnionRegion(dodgeRegion, wCurRegion, dodgeRegion);
71 
72     XDestroyRegion (wCurRegion);
73 }
74 
75 // Returns FALSE if the subject is destroyed or if there was an error when
76 // calculating the dodge box
77 static Bool
fxDodgeFindDodgeBox(CompWindow * w,XRectangle * dodgeBox)78 fxDodgeFindDodgeBox (CompWindow *w, XRectangle *dodgeBox)
79 {
80     ANIM_SCREEN(w->screen);
81     ANIM_WINDOW(w);
82 
83     if (!aw->dodgeSubjectWin)  // if the subject is destroyed
84 	return FALSE;
85 
86     // Find the box to be dodged, it can contain multiple windows
87     // when there are dialog/utility windows of subject windows
88     // (stacked in the moreToBePaintedNext chain)
89     // Then this would be a bounding box of the subject windows
90     // intersecting with dodger.
91     Region wRegion = XCreateRegion();
92     if (!wRegion)
93 	return FALSE;
94 
95     Region dodgeRegion = XCreateRegion();
96     if (!dodgeRegion)
97     {
98 	XDestroyRegion (wRegion);
99 	return FALSE;
100     }
101 
102     XRectangle rect;
103     rect.x = WIN_X(w);
104     rect.y = WIN_Y(w);
105     rect.width = WIN_W(w);
106     rect.height = WIN_H(w);
107 
108     int dodgeMaxAmount = (int)aw->dodgeMaxAmount;
109 
110     // to compute if subject(s) intersect with dodger w,
111     // enlarge dodger window's box so that it encloses all of the covered
112     // region during dodge movement. This corrects the animation when
113     // there are >1 subjects (a window with its dialog/utility windows).
114     switch (aw->dodgeDirection)
115     {
116     case 0:
117 	rect.y += dodgeMaxAmount;
118 	rect.height -= dodgeMaxAmount;
119 	break;
120     case 1:
121 	rect.height += dodgeMaxAmount;
122 	break;
123     case 2:
124 	rect.x += dodgeMaxAmount;
125 	rect.width -= dodgeMaxAmount;
126 	break;
127     case 3:
128 	rect.width += dodgeMaxAmount;
129 	break;
130     }
131     XUnionRectWithRegion(&rect, &emptyRegion, wRegion);
132 
133     AnimWindow *awCur;
134     CompWindow *wCur = aw->dodgeSubjectWin;
135     for (; wCur; wCur = awCur->moreToBePaintedNext)
136     {
137 	fxDodgeProcessSubject(wCur, wRegion, dodgeRegion,
138 			      wCur == aw->dodgeSubjectWin);
139 	awCur = GET_ANIM_WINDOW(wCur, as);
140 	if (!awCur)
141 	    break;
142     }
143 
144     AnimWindow *awSubj = GET_ANIM_WINDOW(aw->dodgeSubjectWin, as);
145     wCur = awSubj->moreToBePaintedPrev;
146     for (; wCur; wCur = awCur->moreToBePaintedPrev)
147     {
148 	fxDodgeProcessSubject(wCur, wRegion, dodgeRegion, FALSE);
149 	awCur = GET_ANIM_WINDOW(wCur, as);
150 	if (!awCur)
151 	    break;
152     }
153 
154     XClipBox(dodgeRegion, dodgeBox);
155 
156     XDestroyRegion (wRegion);
157     XDestroyRegion (dodgeRegion);
158 
159     return TRUE;
160 }
161 
162 static void
applyDodgeTransform(CompWindow * w,CompTransform * transform)163 applyDodgeTransform (CompWindow * w, CompTransform *transform)
164 {
165     ANIM_WINDOW(w);
166 
167     if (aw->isDodgeSubject)
168 	return;
169 
170     float amount = sin(M_PI * aw->com.transformProgress) * aw->dodgeMaxAmount;
171 
172     if (aw->dodgeDirection > 1) // if x axis
173 	matrixTranslate (transform, amount, 0.0f, 0.0f);
174     else
175 	matrixTranslate (transform, 0.0f, amount, 0.0f);
176 }
177 
178 void
fxDodgeAnimStep(CompWindow * w,float time)179 fxDodgeAnimStep (CompWindow *w, float time)
180 {
181     XRectangle dodgeBox;
182 
183     defaultAnimStep (w, time);
184 
185     ANIM_WINDOW(w);
186 
187     aw->com.transformProgress = 0;
188 
189     float forwardProgress = defaultAnimProgress (w);
190     if (forwardProgress > aw->com.transformStartProgress)
191     {
192 	aw->com.transformProgress =
193 	    (forwardProgress - aw->com.transformStartProgress) /
194 	    (1 - aw->com.transformStartProgress);
195     }
196 
197     if (!aw->isDodgeSubject &&
198 	aw->com.transformProgress <= 0.5f &&
199 	fxDodgeFindDodgeBox (w, &dodgeBox))
200     {
201 	// Update dodge amount if subject window has moved during dodge
202 	float newDodgeAmount =
203 	    DODGE_AMOUNT_BOX(dodgeBox, w, aw->dodgeDirection);
204 
205 	// Only update if amount got larger
206 	if (((newDodgeAmount > 0 && aw->dodgeMaxAmount > 0) ||
207 	     (newDodgeAmount < 0 && aw->dodgeMaxAmount < 0)) &&
208 	    abs(newDodgeAmount) > abs(aw->dodgeMaxAmount))
209 	{
210 	    aw->dodgeMaxAmount = newDodgeAmount;
211 	}
212     }
213 
214     matrixGetIdentity (&aw->com.transform);
215     applyDodgeTransform (w, &aw->com.transform);
216 }
217 
218 void
fxDodgeUpdateWindowTransform(CompWindow * w,CompTransform * wTransform)219 fxDodgeUpdateWindowTransform (CompWindow *w,
220 			      CompTransform *wTransform)
221 {
222     ANIM_WINDOW(w);
223 
224     if (aw->isDodgeSubject)
225 	return;
226 
227     applyTransform (wTransform, &aw->com.transform);
228 }
229 
230 void
fxDodgePostPreparePaintScreen(CompWindow * w)231 fxDodgePostPreparePaintScreen (CompWindow *w)
232 {
233     ANIM_SCREEN(w->screen);
234     ANIM_WINDOW(w);
235 
236     // Only dodge subjects should be processed here
237     if (!aw->isDodgeSubject)
238 	return;
239 
240     if (!aw->restackInfo)
241 	return;
242 
243     if (aw->skipPostPrepareScreen)
244 	return;
245 
246     // Dodgy window
247     CompWindow *dw;
248     AnimWindow *adw = NULL;
249     for (dw = aw->dodgeChainStart; dw; dw = adw->dodgeChainNext)
250     {
251 	adw = GET_ANIM_WINDOW(dw, as);
252 	if (!adw)
253 	    break;
254 	// find the first dodging window that hasn't yet
255 	// reached 50% progress yet. The subject window should be
256 	// painted right behind that one (or right in front of it if
257 	// the subject window is being lowered).
258 	if (!(adw->com.transformProgress > 0.5f))
259 	    break;
260     }
261     AnimWindow *awOldHost = NULL;
262 
263     if (aw->restackInfo->raised &&
264 	dw != aw->winThisIsPaintedBefore) // w's host is changing
265     {
266 	if (aw->winThisIsPaintedBefore)
267 	{
268 	    // Clear old host
269 	    awOldHost = GET_ANIM_WINDOW(aw->winThisIsPaintedBefore, as);
270 	    awOldHost->winToBePaintedBeforeThis = NULL;
271 	}
272 	if (dw && adw) // if a dodgy win. is still at <0.5 progress
273 	{
274 	    // Put subject right behind adw (new host)
275 	    adw->winToBePaintedBeforeThis = w;
276 	}
277 	// otherwise all dodgy win.s have passed 0.5 progress
278 
279 	CompWindow *wCur = w;
280 	while (wCur)
281 	{
282 	    AnimWindow *awCur = GET_ANIM_WINDOW(wCur, as);
283 	    awCur->winThisIsPaintedBefore = dw; // dw can be null, which is ok
284 	    wCur = awCur->moreToBePaintedNext;
285 	}
286     }
287     else if (!aw->restackInfo->raised)
288     {
289 	// Put subject right in front of dw
290 	// But we need to find the dodgy window above dw
291 	// (since we need to put subject *behind* another one)
292 
293 	CompWindow *wDodgeChainAbove = NULL;
294 
295 	if (dw && adw) // if a dodgy win. is still at <0.5 progress
296 	{
297 	    if (adw->dodgeChainPrev)
298 		wDodgeChainAbove = adw->dodgeChainPrev;
299 	    else
300 		wDodgeChainAbove = aw->restackInfo->wOldAbove;
301 
302 	    if (!wDodgeChainAbove)
303 		compLogMessage ("animation", CompLogLevelError,
304 				"%s: error at line %d", __FILE__, __LINE__);
305 	    else if (aw->winThisIsPaintedBefore !=
306 		     wDodgeChainAbove) // w's host is changing
307 	    {
308 		AnimWindow *adw2 = GET_ANIM_WINDOW(wDodgeChainAbove, as);
309 
310 		// Put subject right behind adw2 (new host)
311 		adw2->winToBePaintedBeforeThis = w;
312 	    }
313 	}
314 	if (aw->winThisIsPaintedBefore &&
315 	    aw->winThisIsPaintedBefore != wDodgeChainAbove)
316 	{
317 	    awOldHost = GET_ANIM_WINDOW(aw->winThisIsPaintedBefore, as);
318 
319 	    // Clear old host
320 	    awOldHost->winToBePaintedBeforeThis = NULL;
321 	}
322 	// otherwise all dodgy win.s have passed 0.5 progress
323 
324 	// wDodgeChainAbove can be null, which is ok
325 	aw->winThisIsPaintedBefore = wDodgeChainAbove;
326     }
327 }
328 
329 void
fxDodgeUpdateBB(CompOutput * output,CompWindow * w,Box * BB)330 fxDodgeUpdateBB (CompOutput *output,
331 		 CompWindow * w,
332 		 Box *BB)
333 {
334     ANIM_WINDOW(w);
335 
336     if (!aw->isDodgeSubject)
337 	compTransformUpdateBB (output, w, BB);
338 }
339