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