Bouncing appicon effect
[wmaker-crm.git] / src / superfluous.c
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
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20  *  USA.
21  */
22
23 #include "wconfig.h"
24
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <math.h>
31 #include <time.h>
32
33 #include <wraster.h>
34
35 #include "WindowMaker.h"
36 #include "screen.h"
37 #include "dock.h"
38 #include "superfluous.h"
39 #include "framewin.h"
40 #include "window.h"
41 #include "actions.h"
42 #include "xinerama.h"
43
44 extern WPreferences wPreferences;
45
46 #ifdef NORMAL_ICON_KABOOM
47 void DoKaboom(WScreen * scr, Window win, int x, int y)
48 {
49         int i, j, k;
50         int sw = scr->scr_width, sh = scr->scr_height;
51 #define KAB_PRECISION   4
52         int px[PIECES];
53         short py[PIECES];
54         char pvx[PIECES], pvy[PIECES];
55         /* in MkLinux/PPC gcc seems to think that char is unsigned? */
56         signed char ax[PIECES], ay[PIECES];
57         Pixmap tmp;
58
59         XSetClipMask(dpy, scr->copy_gc, None);
60         tmp = XCreatePixmap(dpy, scr->root_win, wPreferences.icon_size, wPreferences.icon_size, scr->depth);
61         if (scr->w_visual == DefaultVisual(dpy, scr->screen))
62                 XCopyArea(dpy, win, tmp, scr->copy_gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size, 0, 0);
63         else {
64                 XImage *image;
65
66                 image = XGetImage(dpy, win, 0, 0, wPreferences.icon_size,
67                                   wPreferences.icon_size, AllPlanes, ZPixmap);
68                 if (!image) {
69                         XUnmapWindow(dpy, win);
70                         return;
71                 }
72                 XPutImage(dpy, tmp, scr->copy_gc, image, 0, 0, 0, 0,
73                           wPreferences.icon_size, wPreferences.icon_size);
74                 XDestroyImage(image);
75         }
76
77         for (i = 0, k = 0; i < wPreferences.icon_size / ICON_KABOOM_PIECE_SIZE && k < PIECES; i++) {
78                 for (j = 0; j < wPreferences.icon_size / ICON_KABOOM_PIECE_SIZE && k < PIECES; j++) {
79                         if (rand() % 2) {
80                                 ax[k] = i;
81                                 ay[k] = j;
82                                 px[k] = (x + i * ICON_KABOOM_PIECE_SIZE) << KAB_PRECISION;
83                                 py[k] = y + j * ICON_KABOOM_PIECE_SIZE;
84                                 pvx[k] = rand() % (1 << (KAB_PRECISION + 3)) - (1 << (KAB_PRECISION + 3)) / 2;
85                                 pvy[k] = -15 - rand() % 7;
86                                 k++;
87                         } else {
88                                 ax[k] = -1;
89                         }
90                 }
91         }
92
93         XUnmapWindow(dpy, win);
94
95         j = k;
96         while (k > 0) {
97                 XEvent foo;
98
99                 if (XCheckTypedEvent(dpy, ButtonPress, &foo)) {
100                         XPutBackEvent(dpy, &foo);
101                         XClearWindow(dpy, scr->root_win);
102                         break;
103                 }
104
105                 for (i = 0; i < j; i++) {
106                         if (ax[i] >= 0) {
107                                 int _px = px[i] >> KAB_PRECISION;
108                                 XClearArea(dpy, scr->root_win, _px, py[i],
109                                            ICON_KABOOM_PIECE_SIZE, ICON_KABOOM_PIECE_SIZE, False);
110                                 px[i] += pvx[i];
111                                 py[i] += pvy[i];
112                                 _px = px[i] >> KAB_PRECISION;
113                                 pvy[i]++;
114                                 if (_px < -wPreferences.icon_size || _px > sw || py[i] >= sh) {
115                                         ax[i] = -1;
116                                         k--;
117                                 } else {
118                                         XCopyArea(dpy, tmp, scr->root_win, scr->copy_gc,
119                                                   ax[i] * ICON_KABOOM_PIECE_SIZE, ay[i] * ICON_KABOOM_PIECE_SIZE,
120                                                   ICON_KABOOM_PIECE_SIZE, ICON_KABOOM_PIECE_SIZE, _px, py[i]);
121                                 }
122                         }
123                 }
124
125                 XFlush(dpy);
126                 wusleep(MINIATURIZE_ANIMATION_DELAY_Z * 2);
127         }
128
129         XFreePixmap(dpy, tmp);
130 }
131 #endif  /* NORMAL_ICON_KABOOM */
132
133 Pixmap MakeGhostDock(WDock * dock, int sx, int dx, int y)
134 {
135         WScreen *scr = dock->screen_ptr;
136         XImage *img;
137         RImage *back, *dock_image;
138         Pixmap pixmap;
139         int i, virtual_tiles, h, j, n;
140         unsigned long red_mask, green_mask, blue_mask;
141
142         virtual_tiles = 0;
143         for (i = 0; i < dock->max_icons; i++) {
144                 if (dock->icon_array[i] != NULL && dock->icon_array[i]->yindex > virtual_tiles)
145                         virtual_tiles = dock->icon_array[i]->yindex;
146         }
147         virtual_tiles++;
148         h = virtual_tiles * wPreferences.icon_size;
149         h = (y + h > scr->scr_height) ? scr->scr_height - y : h;
150         virtual_tiles = h / wPreferences.icon_size;     /* The visible ones */
151         if (h % wPreferences.icon_size)
152                 virtual_tiles++;        /* There is one partially visible tile at end */
153
154         img = XGetImage(dpy, scr->root_win, dx, y, wPreferences.icon_size, h, AllPlanes, ZPixmap);
155         if (!img)
156                 return None;
157
158         red_mask = img->red_mask;
159         green_mask = img->green_mask;
160         blue_mask = img->blue_mask;
161
162         back = RCreateImageFromXImage(scr->rcontext, img, NULL);
163         XDestroyImage(img);
164         if (!back) {
165                 return None;
166         }
167
168         for (i = 0; i < dock->max_icons; i++) {
169                 if (dock->icon_array[i] != NULL && dock->icon_array[i]->yindex < virtual_tiles) {
170                         Pixmap which;
171                         j = dock->icon_array[i]->yindex * wPreferences.icon_size;
172                         n = (h - j < wPreferences.icon_size) ? h - j : wPreferences.icon_size;
173                         if (dock->icon_array[i]->icon->pixmap)
174                                 which = dock->icon_array[i]->icon->pixmap;
175                         else
176                                 which = dock->icon_array[i]->icon->core->window;
177
178                         img = XGetImage(dpy, which, 0, 0, wPreferences.icon_size, n, AllPlanes, ZPixmap);
179
180                         if (!img) {
181                                 RReleaseImage(back);
182                                 return None;
183                         }
184                         img->red_mask = red_mask;
185                         img->green_mask = green_mask;
186                         img->blue_mask = blue_mask;
187
188                         dock_image = RCreateImageFromXImage(scr->rcontext, img, NULL);
189                         XDestroyImage(img);
190                         if (!dock_image) {
191                                 RReleaseImage(back);
192                                 return None;
193                         }
194                         RCombineAreaWithOpaqueness(back, dock_image, 0, 0,
195                                                    wPreferences.icon_size, n, 0, j, 30 * 256 / 100);
196                         RReleaseImage(dock_image);
197                 }
198         }
199
200         RConvertImage(scr->rcontext, back, &pixmap);
201
202         RReleaseImage(back);
203
204         return pixmap;
205 }
206
207 Pixmap MakeGhostIcon(WScreen * scr, Drawable drawable)
208 {
209         RImage *back;
210         RColor color;
211         Pixmap pixmap;
212
213         if (!drawable)
214                 return None;
215
216         back = RCreateImageFromDrawable(scr->rcontext, drawable, None);
217         if (!back)
218                 return None;
219
220         color.red = 0xff;
221         color.green = 0xff;
222         color.blue = 0xff;
223         color.alpha = 200;
224
225         RClearImage(back, &color);
226         RConvertImage(scr->rcontext, back, &pixmap);
227
228         RReleaseImage(back);
229
230         return pixmap;
231 }
232
233 #ifdef WINDOW_BIRTH_ZOOM
234
235 void DoWindowBirth(WWindow *wwin)
236 {
237         int center_x, center_y;
238         int width = wwin->frame->core->width;
239         int height = wwin->frame->core->height;
240         int w = WMIN(width, 20);
241         int h = WMIN(height, 20);
242         WScreen *scr = wwin->screen_ptr;
243
244         center_x = wwin->frame_x + (width - w) / 2;
245         center_y = wwin->frame_y + (height - h) / 2;
246
247         animateResize(scr, center_x, center_y, 1, 1, wwin->frame_x, wwin->frame_y, width, height);
248 }
249 #else
250 void DoWindowBirth(WWindow *wwin)
251 {
252         /* dummy stub */
253 }
254 #endif
255
256 #ifdef BOUNCE_APP
257
258 #define BOUNCE_HZ       25
259 #define BOUNCE_DELAY    (1000/BOUNCE_HZ)
260 #define BOUNCE_HEIGHT   24
261 #define BOUNCE_LENGTH   0.3
262 #define BOUNCE_DAMP     0.6
263
264 typedef struct AppBouncerData {
265         WApplication *wapp;
266         int count;
267         int pow;
268         int dir;
269         WMHandlerID *timer;
270 } AppBouncerData;
271
272 static void doAppBounce(void *arg)
273 {
274         AppBouncerData *data = (AppBouncerData*)arg;
275         WAppIcon *aicon = data->wapp->app_icon;
276
277 reinit:
278         if (aicon && data->wapp->refcount > 1) {
279                 const double ticks = BOUNCE_HZ * BOUNCE_LENGTH;
280                 const double s = sqrt(BOUNCE_HEIGHT)/(ticks/2);
281                 double h = BOUNCE_HEIGHT*pow(BOUNCE_DAMP, data->pow);
282                 double sqrt_h = sqrt(h);
283                 if (h > 3) {
284                         double offset, x = s * data->count - sqrt_h;
285                         if (x > sqrt_h) {
286                                 ++data->pow;
287                                 data->count = 0;
288                                 goto reinit;
289                         } else ++data->count;
290                         offset = h - x*x;
291
292                         switch (data->dir) {
293                         case 0: /* left, bounce to right */
294                                 XMoveWindow(dpy, aicon->icon->core->window,
295                                             aicon->x_pos + (int)offset, aicon->y_pos);
296                                 break;
297                         case 1: /* right, bounce to left */
298                                 XMoveWindow(dpy, aicon->icon->core->window,
299                                             aicon->x_pos - (int)offset, aicon->y_pos);
300                                 break;
301                         case 2: /* top, bounce down */
302                                 XMoveWindow(dpy, aicon->icon->core->window,
303                                             aicon->x_pos, aicon->y_pos + (int)offset);
304                                 break;
305                         case 3: /* bottom, bounce up */
306                                 XMoveWindow(dpy, aicon->icon->core->window,
307                                             aicon->x_pos, aicon->y_pos - (int)offset);
308                                 break;
309                         }
310                         return;
311                 }
312                 XMoveWindow(dpy, aicon->icon->core->window,
313                             aicon->x_pos, aicon->y_pos);
314         }
315
316         data->wapp->flags.bouncing = 0;
317         WMDeleteTimerHandler(data->timer);
318         wApplicationDestroy(data->wapp);
319         free(data);
320 }
321
322 static int bounceDirection(WAppIcon *aicon)
323 {
324         enum { left_e = 1, right_e = 2, top_e = 4, bottom_e = 8 };
325
326         WScreen *scr = aicon->icon->core->screen_ptr;
327         WMRect rr, sr;
328         int l, r, t, b, h, v;
329         int dir = 0;
330
331         rr.pos.x = aicon->x_pos;
332         rr.pos.y = aicon->y_pos;
333         rr.size.width = rr.size.height = 64;
334
335         sr = wGetRectForHead(scr, wGetHeadForRect(scr, rr));
336
337         l = rr.pos.x - sr.pos.x;
338         r = sr.pos.x + sr.size.width - rr.pos.x - rr.size.width;
339         t = rr.pos.y - sr.pos.y;
340         b = sr.pos.y + sr.size.height - rr.pos.y - rr.size.height;
341
342         if (l < r) {
343                 dir |= left_e;
344                 h = l;
345         } else {
346                 dir |= right_e;
347                 h = r;
348         }
349
350         if (t < b) {
351                 dir |= top_e;
352                 v = t;
353         } else {
354                 dir |= bottom_e;
355                 v = b;
356         }
357
358         if (h < v) dir &= ~(top_e | bottom_e);
359         else dir &= ~(left_e | right_e);
360
361         switch (dir) {
362         case left_e:
363                 dir = 0;
364                 break;
365
366         case right_e:
367                 dir = 1;
368                 break;
369
370         case top_e:
371                 dir = 2;
372                 break;
373
374         case bottom_e:
375                 dir = 3;
376                 break;
377
378         default:
379                 wwarning(_("Impossible direction: %d\n"), dir);
380                 dir = 3;
381                 break;
382         }
383
384         return dir;
385 }
386
387 void wAppBounce(WApplication *wapp)
388 {
389         if (wapp->app_icon && !wapp->flags.bouncing) {
390                 ++wapp->refcount;
391                 wapp->flags.bouncing = 1;
392
393                 AppBouncerData *data = (AppBouncerData *)malloc(sizeof(AppBouncerData));
394                 data->wapp = wapp;
395                 data->count = data->pow = 0;
396                 data->dir = bounceDirection(wapp->app_icon);
397                 data->timer = WMAddPersistentTimerHandler(BOUNCE_DELAY, doAppBounce, data);
398         }
399 }
400
401 #else
402 void wAppBounce(WApplication *wapp)
403 {
404 }
405 #endif