1/*
2 * Animation routines
3 */
4
5
6#include "ctwm.h"
7
8#include <sys/time.h>
9#include <assert.h>
10#include <stdio.h>
11#include <string.h>
12
13#include <X11/extensions/shape.h>
14
15#include "ctwm_atoms.h"
16#include "events.h"
17#include "icons.h"
18#include "image.h"
19#include "screen.h"
20#include "util.h"
21#include "vscreen.h"
22#include "win_utils.h"
23
24#include "animate.h"
25
26
27#define MAXANIMATIONSPEED 20
28
29
30int  Animating        = 0;
31int  AnimationSpeed   = 0;
32bool AnimationActive  = false;
33bool MaybeAnimate     = true;
34struct timeval AnimateTimeout;
35
36
37static void Animate(void);
38static void AnimateButton(TBWindow *tbw);
39static void AnimateHighlight(TwmWindow *t);
40static void AnimateIcons(ScreenInfo *scr, Icon *icon);
41static bool AnimateRoot(void);
42
43
44/*
45 * XXX We're directly looking at this for hopefully hysterical raisins.
46 * Rexamine the whole tracefile subsystem at some point when we look at
47 * debugging.
48 *
49 * Currently get it via pollution from events.h anyway.
50 *
51 * extern FILE *tracefile;
52 */
53
54void
55TryToAnimate(void)
56{
57	struct timeval  tp;
58	static unsigned long lastsec;
59	static long lastusec;
60	unsigned long gap;
61
62	if(Animating > 1) {
63		return;        /* rate limiting */
64	}
65
66	gettimeofday(&tp, NULL);
67	gap = ((tp.tv_sec - lastsec) * 1000000) + (tp.tv_usec - lastusec);
68	if(tracefile) {
69		fprintf(tracefile, "Time = %lu, %ld, %ld, %ld, %lu\n", lastsec,
70		        lastusec, (long)tp.tv_sec, (long)tp.tv_usec, gap);
71		fflush(tracefile);
72	}
73	gap *= AnimationSpeed;
74	if(gap < 1000000) {
75		return;
76	}
77	if(tracefile) {
78		fprintf(tracefile, "Animate\n");
79		fflush(tracefile);
80	}
81	Animate();
82	lastsec  = tp.tv_sec;
83	lastusec = tp.tv_usec;
84}
85
86
87
88void
89StartAnimation(void)
90{
91
92	if(AnimationSpeed > MAXANIMATIONSPEED) {
93		AnimationSpeed = MAXANIMATIONSPEED;
94	}
95	if(AnimationSpeed <= 0) {
96		AnimationSpeed = 0;
97	}
98	if(AnimationActive) {
99		return;
100	}
101	switch(AnimationSpeed) {
102		case 0 :
103			return;
104		case 1 :
105			AnimateTimeout.tv_sec  = 1;
106			AnimateTimeout.tv_usec = 0;
107			break;
108		default :
109			AnimateTimeout.tv_sec  = 0;
110			AnimateTimeout.tv_usec = 1000000 / AnimationSpeed;
111	}
112	AnimationActive = true;
113}
114
115
116void
117StopAnimation(void)
118{
119	AnimationActive = false;
120}
121
122
123void
124SetAnimationSpeed(int speed)
125{
126	AnimationSpeed = speed;
127	if(AnimationSpeed > MAXANIMATIONSPEED) {
128		AnimationSpeed = MAXANIMATIONSPEED;
129	}
130}
131
132
133void
134ModifyAnimationSpeed(int incr)
135{
136	if((AnimationSpeed + incr) < 0) {
137		return;
138	}
139	if((AnimationSpeed + incr) == 0) {
140		if(AnimationActive) {
141			StopAnimation();
142		}
143		AnimationSpeed = 0;
144		return;
145	}
146	AnimationSpeed += incr;
147	if(AnimationSpeed > MAXANIMATIONSPEED) {
148		AnimationSpeed = MAXANIMATIONSPEED;
149	}
150
151	if(AnimationSpeed == 1) {
152		AnimateTimeout.tv_sec  = 1;
153		AnimateTimeout.tv_usec = 0;
154	}
155	else {
156		AnimateTimeout.tv_sec  = 0;
157		AnimateTimeout.tv_usec = 1000000 / AnimationSpeed;
158	}
159	AnimationActive = true;
160}
161
162
163
164/*
165 * Only called from TryToAnimate
166 */
167static void
168Animate(void)
169{
170	TwmWindow   *t;
171	int         scrnum;
172	ScreenInfo  *scr;
173	int         i;
174	TBWindow    *tbw;
175	int         nb;
176
177	if(AnimationSpeed == 0) {
178		return;
179	}
180	if(Animating > 1) {
181		return;        /* rate limiting */
182	}
183
184	/* Impossible? */
185	if(NumScreens < 1) {
186		return;
187	}
188
189	MaybeAnimate = false;
190	scr = NULL;
191	for(scrnum = 0; scrnum < NumScreens; scrnum++) {
192		if((scr = ScreenList [scrnum]) == NULL) {
193			continue;
194		}
195
196		for(t = scr->FirstWindow; t != NULL; t = t->next) {
197			if(! visible(t)) {
198				continue;
199			}
200			if(t->icon_on && t->icon && t->icon->bm_w && t->icon->image &&
201			                t->icon->image->next) {
202				AnimateIcons(scr, t->icon);
203				MaybeAnimate = true;
204			}
205			else if(t->mapped && t->titlebuttons) {
206				nb = scr->TBInfo.nleft + scr->TBInfo.nright;
207				for(i = 0, tbw = t->titlebuttons; i < nb; i++, tbw++) {
208					if(tbw->image && tbw->image->next) {
209						AnimateButton(tbw);
210						MaybeAnimate = true;
211					}
212				}
213			}
214		}
215		if(scr->Focus) {
216			t = scr->Focus;
217			if(t->mapped && t->titlehighlight && t->title_height &&
218			                t->HiliteImage && t->HiliteImage->next) {
219				AnimateHighlight(t);
220				MaybeAnimate = true;
221			}
222		}
223	}
224	MaybeAnimate |= AnimateRoot();
225	if(MaybeAnimate) {
226		// Impossible: scr==NULL means we had no valid screens, which
227		// means we'd'a bomed out WAY earlier than trying to animate
228		// something...
229		assert(scr != NULL);
230		Animating++;
231		send_clientmessage(scr->currentvs->wsw->w, XA_WM_END_OF_ANIMATION,
232		                   EventTime);
233	}
234	XFlush(dpy);
235	return;
236}
237
238
239/* Originally in add_window.c */
240static void
241AnimateButton(TBWindow *tbw)
242{
243	Image       *image;
244	XSetWindowAttributes attr;
245
246	image = tbw->image;
247	attr.background_pixmap = image->pixmap;
248	XChangeWindowAttributes(dpy, tbw->window, CWBackPixmap, &attr);
249	XClearWindow(dpy, tbw->window);
250	tbw->image = image->next;
251}
252
253/* Originally in add_window.c */
254static void
255AnimateHighlight(TwmWindow *t)
256{
257	Image       *image;
258	XSetWindowAttributes attr;
259
260	image = t->HiliteImage;
261	attr.background_pixmap = image->pixmap;
262	if(t->hilite_wl) {
263		XChangeWindowAttributes(dpy, t->hilite_wl, CWBackPixmap, &attr);
264		XClearWindow(dpy, t->hilite_wl);
265	}
266	if(t->hilite_wr) {
267		XChangeWindowAttributes(dpy, t->hilite_wr, CWBackPixmap, &attr);
268		XClearWindow(dpy, t->hilite_wr);
269	}
270	t->HiliteImage = image->next;
271}
272
273
274/* Originally in icons.c */
275static void
276AnimateIcons(ScreenInfo *scr, Icon *icon)
277{
278	Image       *image;
279	XRectangle  rect;
280	XSetWindowAttributes attr;
281	int         x;
282
283	image = icon->image;
284	attr.background_pixmap = image->pixmap;
285	XChangeWindowAttributes(dpy, icon->bm_w, CWBackPixmap, &attr);
286
287	if(image->mask != None) {
288		x = GetIconOffset(icon);
289		XShapeCombineMask(dpy, icon->bm_w, ShapeBounding, 0, 0, image->mask, ShapeSet);
290		if(icon->has_title) {
291			rect.x      = 0;
292			rect.y      = icon->height;
293			rect.width  = icon->w_width;
294			rect.height = scr->IconFont.height + 6;
295
296			XShapeCombineShape(dpy, scr->ShapeWindow, ShapeBounding, x, 0, icon->bm_w,
297			                   ShapeBounding, ShapeSet);
298			XShapeCombineRectangles(dpy, scr->ShapeWindow, ShapeBounding, 0, 0, &rect, 1,
299			                        ShapeUnion, 0);
300			XShapeCombineShape(dpy, icon->w, ShapeBounding, 0, 0, scr->ShapeWindow,
301			                   ShapeBounding, ShapeSet);
302		}
303		else
304			XShapeCombineShape(dpy, icon->w, ShapeBounding, x, 0, icon->bm_w,
305			                   ShapeBounding, ShapeSet);
306	}
307	XClearWindow(dpy, icon->bm_w);
308	icon->image  = image->next;
309	return;
310}
311
312
313/* Original in workmgr.c */
314static bool
315AnimateRoot(void)
316{
317	VirtualScreen *vs;
318	ScreenInfo *scr;
319	int        scrnum;
320	Image      *image;
321	WorkSpace  *ws;
322	bool       maybeanimate;
323
324	maybeanimate = false;
325	for(scrnum = 0; scrnum < NumScreens; scrnum++) {
326		if((scr = ScreenList [scrnum]) == NULL) {
327			continue;
328		}
329		if(! scr->workSpaceManagerActive) {
330			continue;
331		}
332
333		for(vs = scr->vScreenList; vs != NULL; vs = vs->next) {
334			if(! vs->wsw->currentwspc) {
335				continue;
336			}
337			image = vs->wsw->currentwspc->image;
338			if((image == NULL) || (image->next == NULL)) {
339				continue;
340			}
341			if(scr->DontPaintRootWindow) {
342				continue;
343			}
344
345			XSetWindowBackgroundPixmap(dpy, vs->window, image->pixmap);
346			XClearWindow(dpy, scr->Root);
347			vs->wsw->currentwspc->image = image->next;
348			maybeanimate = true;
349		}
350	}
351	for(scrnum = 0; scrnum < NumScreens; scrnum++) {
352		if((scr = ScreenList [scrnum]) == NULL) {
353			continue;
354		}
355
356		for(vs = scr->vScreenList; vs != NULL; vs = vs->next) {
357			if(vs->wsw->state == WMS_buttons) {
358				continue;
359			}
360			for(ws = scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
361				image = ws->image;
362
363				if((image == NULL) || (image->next == NULL)) {
364					continue;
365				}
366				if(ws == vs->wsw->currentwspc) {
367					continue;
368				}
369				XSetWindowBackgroundPixmap(dpy, vs->wsw->mswl [ws->number]->w, image->pixmap);
370				XClearWindow(dpy, vs->wsw->mswl [ws->number]->w);
371				ws->image = image->next;
372				maybeanimate = true;
373			}
374		}
375	}
376	return maybeanimate;
377}
378