1 /*
2  *  Window Maker window manager
3  *
4  *  Copyright (c) 1997-2003 Alfredo K. Kojima
5  *  Copyright (c) 1998-2003 Dan Pascu
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, write to the Free Software Foundation, Inc.,
19  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include "wconfig.h"
23 
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <math.h>
30 #include <time.h>
31 
32 #include <wraster.h>
33 
34 #include "WindowMaker.h"
35 #include "screen.h"
36 #include "superfluous.h"
37 #include "framewin.h"
38 #include "window.h"
39 #include "actions.h"
40 #include "xinerama.h"
41 #include "stacking.h"
42 
43 #define PIECES ((64/ICON_KABOOM_PIECE_SIZE)*(64/ICON_KABOOM_PIECE_SIZE))
44 #define KAB_PRECISION		4
45 #define BOUNCE_HZ		25
46 #define BOUNCE_DELAY		(1000/BOUNCE_HZ)
47 #define BOUNCE_HEIGHT		24
48 #define BOUNCE_LENGTH		0.3
49 #define BOUNCE_DAMP		0.6
50 #define URGENT_BOUNCE_DELAY	3000
51 
52 
DoKaboom(WScreen * scr,Window win,int x,int y)53 void DoKaboom(WScreen * scr, Window win, int x, int y)
54 {
55 #ifdef NORMAL_ICON_KABOOM
56 	int i, j, k;
57 	int sw = scr->scr_width, sh = scr->scr_height;
58 	int px[PIECES];
59 	short py[PIECES];
60 	char pvx[PIECES], pvy[PIECES];
61 	/* in MkLinux/PPC gcc seems to think that char is unsigned? */
62 	signed char ax[PIECES], ay[PIECES];
63 	Pixmap tmp;
64 
65 	XSetClipMask(dpy, scr->copy_gc, None);
66 	tmp = XCreatePixmap(dpy, scr->root_win, wPreferences.icon_size, wPreferences.icon_size, scr->depth);
67 	if (scr->w_visual == DefaultVisual(dpy, scr->screen))
68 		XCopyArea(dpy, win, tmp, scr->copy_gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size, 0, 0);
69 	else {
70 		XImage *image;
71 
72 		image = XGetImage(dpy, win, 0, 0, wPreferences.icon_size,
73 				  wPreferences.icon_size, AllPlanes, ZPixmap);
74 		if (!image) {
75 			XUnmapWindow(dpy, win);
76 			return;
77 		}
78 		XPutImage(dpy, tmp, scr->copy_gc, image, 0, 0, 0, 0,
79 			  wPreferences.icon_size, wPreferences.icon_size);
80 		XDestroyImage(image);
81 	}
82 
83 	for (i = 0, k = 0; i < wPreferences.icon_size / ICON_KABOOM_PIECE_SIZE && k < PIECES; i++) {
84 		for (j = 0; j < wPreferences.icon_size / ICON_KABOOM_PIECE_SIZE && k < PIECES; j++) {
85 			if (rand() % 2) {
86 				ax[k] = i;
87 				ay[k] = j;
88 				px[k] = (x + i * ICON_KABOOM_PIECE_SIZE) << KAB_PRECISION;
89 				py[k] = y + j * ICON_KABOOM_PIECE_SIZE;
90 				pvx[k] = rand() % (1 << (KAB_PRECISION + 3)) - (1 << (KAB_PRECISION + 3)) / 2;
91 				pvy[k] = -15 - rand() % 7;
92 				k++;
93 			} else {
94 				ax[k] = -1;
95 			}
96 		}
97 	}
98 
99 	XUnmapWindow(dpy, win);
100 
101 	j = k;
102 	while (k > 0) {
103 		XEvent foo;
104 
105 		if (XCheckTypedEvent(dpy, ButtonPress, &foo)) {
106 			XPutBackEvent(dpy, &foo);
107 			XClearWindow(dpy, scr->root_win);
108 			break;
109 		}
110 
111 		for (i = 0; i < j; i++) {
112 			if (ax[i] >= 0) {
113 				int _px = px[i] >> KAB_PRECISION;
114 				XClearArea(dpy, scr->root_win, _px, py[i],
115 					   ICON_KABOOM_PIECE_SIZE, ICON_KABOOM_PIECE_SIZE, False);
116 				px[i] += pvx[i];
117 				py[i] += pvy[i];
118 				_px = px[i] >> KAB_PRECISION;
119 				pvy[i]++;
120 				if (_px < -wPreferences.icon_size || _px > sw || py[i] >= sh) {
121 					ax[i] = -1;
122 					k--;
123 				} else {
124 					XCopyArea(dpy, tmp, scr->root_win, scr->copy_gc,
125 						  ax[i] * ICON_KABOOM_PIECE_SIZE, ay[i] * ICON_KABOOM_PIECE_SIZE,
126 						  ICON_KABOOM_PIECE_SIZE, ICON_KABOOM_PIECE_SIZE, _px, py[i]);
127 				}
128 			}
129 		}
130 
131 		XFlush(dpy);
132 		wusleep(MINIATURIZE_ANIMATION_DELAY_Z * 2);
133 	}
134 
135 	XFreePixmap(dpy, tmp);
136 #endif	/* NORMAL_ICON_KABOOM */
137 }
138 
MakeGhostIcon(WScreen * scr,Drawable drawable)139 Pixmap MakeGhostIcon(WScreen * scr, Drawable drawable)
140 {
141 	RImage *back;
142 	RColor color;
143 	Pixmap pixmap;
144 
145 	if (!drawable)
146 		return None;
147 
148 	back = RCreateImageFromDrawable(scr->rcontext, drawable, None);
149 	if (!back)
150 		return None;
151 
152 	color.red = 0xff;
153 	color.green = 0xff;
154 	color.blue = 0xff;
155 	color.alpha = 200;
156 
157 	RClearImage(back, &color);
158 	RConvertImage(scr->rcontext, back, &pixmap);
159 
160 	RReleaseImage(back);
161 
162 	return pixmap;
163 }
164 
DoWindowBirth(WWindow * wwin)165 void DoWindowBirth(WWindow *wwin)
166 {
167 #ifdef WINDOW_BIRTH_ZOOM
168 	int center_x, center_y;
169 	int width = wwin->frame->core->width;
170 	int height = wwin->frame->core->height;
171 	int w = WMIN(width, 20);
172 	int h = WMIN(height, 20);
173 	WScreen *scr = wwin->screen_ptr;
174 
175 	center_x = wwin->frame_x + (width - w) / 2;
176 	center_y = wwin->frame_y + (height - h) / 2;
177 
178 	animateResize(scr, center_x, center_y, 1, 1, wwin->frame_x, wwin->frame_y, width, height);
179 #else
180 	/* Parameter not used, but tell the compiler that it is ok */
181 	(void) wwin;
182 #endif
183 }
184 
185 typedef struct AppBouncerData {
186 	WApplication *wapp;
187 	int count;
188 	int pow;
189 	int dir;
190 	WMHandlerID *timer;
191 } AppBouncerData;
192 
doAppBounce(void * arg)193 static void doAppBounce(void *arg)
194 {
195 	AppBouncerData *data = (AppBouncerData*)arg;
196 	WAppIcon *aicon = data->wapp->app_icon;
197 
198 	if (!aicon)
199 		return;
200 
201 reinit:
202 	if (data->wapp->refcount > 1) {
203 		if (wPreferences.raise_appicons_when_bouncing)
204 			XRaiseWindow(dpy, aicon->icon->core->window);
205 
206 		const double ticks = BOUNCE_HZ * BOUNCE_LENGTH;
207 		const double s = sqrt(BOUNCE_HEIGHT)/(ticks/2);
208 		double h = BOUNCE_HEIGHT*pow(BOUNCE_DAMP, data->pow);
209 		double sqrt_h = sqrt(h);
210 		if (h > 3) {
211 			double offset, x = s * data->count - sqrt_h;
212 			if (x > sqrt_h) {
213 				++data->pow;
214 				data->count = 0;
215 				goto reinit;
216 			} else ++data->count;
217 			offset = h - x*x;
218 
219 			switch (data->dir) {
220 			case 0: /* left, bounce to right */
221 				XMoveWindow(dpy, aicon->icon->core->window,
222 					    aicon->x_pos + (int)offset, aicon->y_pos);
223 				break;
224 			case 1: /* right, bounce to left */
225 				XMoveWindow(dpy, aicon->icon->core->window,
226 					    aicon->x_pos - (int)offset, aicon->y_pos);
227 				break;
228 			case 2: /* top, bounce down */
229 				XMoveWindow(dpy, aicon->icon->core->window,
230 					    aicon->x_pos, aicon->y_pos + (int)offset);
231 				break;
232 			case 3: /* bottom, bounce up */
233 				XMoveWindow(dpy, aicon->icon->core->window,
234 					    aicon->x_pos, aicon->y_pos - (int)offset);
235 				break;
236 			}
237 			return;
238 		}
239 	}
240 
241 	XMoveWindow(dpy, aicon->icon->core->window,
242 			aicon->x_pos, aicon->y_pos);
243 	CommitStackingForWindow(aicon->icon->core);
244 	data->wapp->flags.bouncing = 0;
245 	WMDeleteTimerHandler(data->timer);
246 	wApplicationDestroy(data->wapp);
247 	free(data);
248 }
249 
bounceDirection(WAppIcon * aicon)250 static int bounceDirection(WAppIcon *aicon)
251 {
252 	enum { left_e = 1, right_e = 2, top_e = 4, bottom_e = 8 };
253 
254 	WScreen *scr = aicon->icon->core->screen_ptr;
255 	WMRect rr, sr;
256 	int l, r, t, b, h, v;
257 	int dir = 0;
258 
259 	rr.pos.x = aicon->x_pos;
260 	rr.pos.y = aicon->y_pos;
261 	rr.size.width = rr.size.height = 64;
262 
263 	sr = wGetRectForHead(scr, wGetHeadForRect(scr, rr));
264 
265 	l = rr.pos.x - sr.pos.x;
266 	r = sr.pos.x + sr.size.width - rr.pos.x - rr.size.width;
267 	t = rr.pos.y - sr.pos.y;
268 	b = sr.pos.y + sr.size.height - rr.pos.y - rr.size.height;
269 
270 	if (l < r) {
271 		dir |= left_e;
272 		h = l;
273 	} else {
274 		dir |= right_e;
275 		h = r;
276 	}
277 
278 	if (t < b) {
279 		dir |= top_e;
280 		v = t;
281 	} else {
282 		dir |= bottom_e;
283 		v = b;
284 	}
285 
286 	if (aicon->dock && abs(aicon->xindex) != abs(aicon->yindex)) {
287 		if (abs(aicon->xindex) < abs(aicon->yindex)) dir &= ~(top_e | bottom_e);
288 		else dir &= ~(left_e | right_e);
289 	} else {
290 		if (h < v) dir &= ~(top_e | bottom_e);
291 		else dir &= ~(left_e | right_e);
292 	}
293 
294 	switch (dir) {
295 	case left_e:
296 		dir = 0;
297 		break;
298 
299 	case right_e:
300 		dir = 1;
301 		break;
302 
303 	case top_e:
304 		dir = 2;
305 		break;
306 
307 	case bottom_e:
308 		dir = 3;
309 		break;
310 
311 	default:
312 		wwarning(_("Impossible direction: %d"), dir);
313 		dir = 3;
314 		break;
315 	}
316 
317 	return dir;
318 }
319 
wAppBounce(WApplication * wapp)320 void wAppBounce(WApplication *wapp)
321 {
322 	if (!wPreferences.no_animations && wapp->app_icon && !wapp->flags.bouncing
323 		&& !wPreferences.do_not_make_appicons_bounce) {
324 		++wapp->refcount;
325 		wapp->flags.bouncing = 1;
326 
327 		AppBouncerData *data = (AppBouncerData *)malloc(sizeof(AppBouncerData));
328 		data->wapp = wapp;
329 		data->count = data->pow = 0;
330 		data->dir = bounceDirection(wapp->app_icon);
331 		data->timer = WMAddPersistentTimerHandler(BOUNCE_DELAY, doAppBounce, data);
332 	}
333 }
334 
appIsUrgent(WApplication * wapp)335 static int appIsUrgent(WApplication *wapp)
336 {
337 	WScreen *scr;
338 	WWindow *wlist;
339 
340 	if (!wapp->main_window_desc) {
341 		wwarning("group leader not found for window group");
342 		return 0;
343 	}
344 	scr = wapp->main_window_desc->screen_ptr;
345 	wlist = scr->focused_window;
346 	if (!wlist)
347 		return 0;
348 
349 	while (wlist) {
350 		if (wlist->main_window == wapp->main_window) {
351 			if (wlist->flags.urgent)
352 				return 1;
353 		}
354 		wlist = wlist->prev;
355 	}
356 
357 	return 0;
358 }
359 
doAppUrgentBounce(void * arg)360 static void doAppUrgentBounce(void *arg)
361 {
362 	WApplication *wapp = (WApplication *)arg;
363 
364 	if (appIsUrgent(wapp)) {
365 		if(wPreferences.bounce_appicons_when_urgent) wAppBounce(wapp);
366 	} else {
367 		WMDeleteTimerHandler(wapp->urgent_bounce_timer);
368 		wapp->urgent_bounce_timer = NULL;
369 	}
370 }
371 
wAppBounceWhileUrgent(WApplication * wapp)372 void wAppBounceWhileUrgent(WApplication *wapp)
373 {
374 	if (!wapp) return;
375 	if (appIsUrgent(wapp)) {
376 		if (!wapp->urgent_bounce_timer) {
377 			wapp->urgent_bounce_timer = WMAddPersistentTimerHandler(URGENT_BOUNCE_DELAY, doAppUrgentBounce, wapp);
378 			doAppUrgentBounce(wapp);
379 		}
380 	} else {
381 		if (wapp->urgent_bounce_timer) {
382 			WMDeleteTimerHandler(wapp->urgent_bounce_timer);
383 			wapp->urgent_bounce_timer = NULL;
384 		}
385 	}
386 }
387