CommitStackingForWindow prototype
[wmaker-crm.git] / src / superfluous.c
blob18fe27ee02884bd8ecce0d51ca483cdbef44305f
1 /*
2 * Window Maker window manager
4 * Copyright (c) 1997-2003 Alfredo K. Kojima
5 * Copyright (c) 1998-2003 Dan Pascu
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.
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.
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.
23 #include "wconfig.h"
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <math.h>
31 #include <time.h>
33 #include <wraster.h>
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 #include "stacking.h"
45 extern WPreferences wPreferences;
47 #ifdef NORMAL_ICON_KABOOM
48 void DoKaboom(WScreen * scr, Window win, int x, int y)
50 int i, j, k;
51 int sw = scr->scr_width, sh = scr->scr_height;
52 #define KAB_PRECISION 4
53 int px[PIECES];
54 short py[PIECES];
55 char pvx[PIECES], pvy[PIECES];
56 /* in MkLinux/PPC gcc seems to think that char is unsigned? */
57 signed char ax[PIECES], ay[PIECES];
58 Pixmap tmp;
60 XSetClipMask(dpy, scr->copy_gc, None);
61 tmp = XCreatePixmap(dpy, scr->root_win, wPreferences.icon_size, wPreferences.icon_size, scr->depth);
62 if (scr->w_visual == DefaultVisual(dpy, scr->screen))
63 XCopyArea(dpy, win, tmp, scr->copy_gc, 0, 0, wPreferences.icon_size, wPreferences.icon_size, 0, 0);
64 else {
65 XImage *image;
67 image = XGetImage(dpy, win, 0, 0, wPreferences.icon_size,
68 wPreferences.icon_size, AllPlanes, ZPixmap);
69 if (!image) {
70 XUnmapWindow(dpy, win);
71 return;
73 XPutImage(dpy, tmp, scr->copy_gc, image, 0, 0, 0, 0,
74 wPreferences.icon_size, wPreferences.icon_size);
75 XDestroyImage(image);
78 for (i = 0, k = 0; i < wPreferences.icon_size / ICON_KABOOM_PIECE_SIZE && k < PIECES; i++) {
79 for (j = 0; j < wPreferences.icon_size / ICON_KABOOM_PIECE_SIZE && k < PIECES; j++) {
80 if (rand() % 2) {
81 ax[k] = i;
82 ay[k] = j;
83 px[k] = (x + i * ICON_KABOOM_PIECE_SIZE) << KAB_PRECISION;
84 py[k] = y + j * ICON_KABOOM_PIECE_SIZE;
85 pvx[k] = rand() % (1 << (KAB_PRECISION + 3)) - (1 << (KAB_PRECISION + 3)) / 2;
86 pvy[k] = -15 - rand() % 7;
87 k++;
88 } else {
89 ax[k] = -1;
94 XUnmapWindow(dpy, win);
96 j = k;
97 while (k > 0) {
98 XEvent foo;
100 if (XCheckTypedEvent(dpy, ButtonPress, &foo)) {
101 XPutBackEvent(dpy, &foo);
102 XClearWindow(dpy, scr->root_win);
103 break;
106 for (i = 0; i < j; i++) {
107 if (ax[i] >= 0) {
108 int _px = px[i] >> KAB_PRECISION;
109 XClearArea(dpy, scr->root_win, _px, py[i],
110 ICON_KABOOM_PIECE_SIZE, ICON_KABOOM_PIECE_SIZE, False);
111 px[i] += pvx[i];
112 py[i] += pvy[i];
113 _px = px[i] >> KAB_PRECISION;
114 pvy[i]++;
115 if (_px < -wPreferences.icon_size || _px > sw || py[i] >= sh) {
116 ax[i] = -1;
117 k--;
118 } else {
119 XCopyArea(dpy, tmp, scr->root_win, scr->copy_gc,
120 ax[i] * ICON_KABOOM_PIECE_SIZE, ay[i] * ICON_KABOOM_PIECE_SIZE,
121 ICON_KABOOM_PIECE_SIZE, ICON_KABOOM_PIECE_SIZE, _px, py[i]);
126 XFlush(dpy);
127 wusleep(MINIATURIZE_ANIMATION_DELAY_Z * 2);
130 XFreePixmap(dpy, tmp);
132 #endif /* NORMAL_ICON_KABOOM */
134 Pixmap MakeGhostDock(WDock * dock, int sx, int dx, int y)
136 WScreen *scr = dock->screen_ptr;
137 XImage *img;
138 RImage *back, *dock_image;
139 Pixmap pixmap;
140 int i, virtual_tiles, h, j, n;
141 unsigned long red_mask, green_mask, blue_mask;
143 virtual_tiles = 0;
144 for (i = 0; i < dock->max_icons; i++) {
145 if (dock->icon_array[i] != NULL && dock->icon_array[i]->yindex > virtual_tiles)
146 virtual_tiles = dock->icon_array[i]->yindex;
148 virtual_tiles++;
149 h = virtual_tiles * wPreferences.icon_size;
150 h = (y + h > scr->scr_height) ? scr->scr_height - y : h;
151 virtual_tiles = h / wPreferences.icon_size; /* The visible ones */
152 if (h % wPreferences.icon_size)
153 virtual_tiles++; /* There is one partially visible tile at end */
155 img = XGetImage(dpy, scr->root_win, dx, y, wPreferences.icon_size, h, AllPlanes, ZPixmap);
156 if (!img)
157 return None;
159 red_mask = img->red_mask;
160 green_mask = img->green_mask;
161 blue_mask = img->blue_mask;
163 back = RCreateImageFromXImage(scr->rcontext, img, NULL);
164 XDestroyImage(img);
165 if (!back) {
166 return None;
169 for (i = 0; i < dock->max_icons; i++) {
170 if (dock->icon_array[i] != NULL && dock->icon_array[i]->yindex < virtual_tiles) {
171 Pixmap which;
172 j = dock->icon_array[i]->yindex * wPreferences.icon_size;
173 n = (h - j < wPreferences.icon_size) ? h - j : wPreferences.icon_size;
174 if (dock->icon_array[i]->icon->pixmap)
175 which = dock->icon_array[i]->icon->pixmap;
176 else
177 which = dock->icon_array[i]->icon->core->window;
179 img = XGetImage(dpy, which, 0, 0, wPreferences.icon_size, n, AllPlanes, ZPixmap);
181 if (!img) {
182 RReleaseImage(back);
183 return None;
185 img->red_mask = red_mask;
186 img->green_mask = green_mask;
187 img->blue_mask = blue_mask;
189 dock_image = RCreateImageFromXImage(scr->rcontext, img, NULL);
190 XDestroyImage(img);
191 if (!dock_image) {
192 RReleaseImage(back);
193 return None;
195 RCombineAreaWithOpaqueness(back, dock_image, 0, 0,
196 wPreferences.icon_size, n, 0, j, 30 * 256 / 100);
197 RReleaseImage(dock_image);
201 RConvertImage(scr->rcontext, back, &pixmap);
203 RReleaseImage(back);
205 return pixmap;
208 Pixmap MakeGhostIcon(WScreen * scr, Drawable drawable)
210 RImage *back;
211 RColor color;
212 Pixmap pixmap;
214 if (!drawable)
215 return None;
217 back = RCreateImageFromDrawable(scr->rcontext, drawable, None);
218 if (!back)
219 return None;
221 color.red = 0xff;
222 color.green = 0xff;
223 color.blue = 0xff;
224 color.alpha = 200;
226 RClearImage(back, &color);
227 RConvertImage(scr->rcontext, back, &pixmap);
229 RReleaseImage(back);
231 return pixmap;
234 #ifdef WINDOW_BIRTH_ZOOM
236 void DoWindowBirth(WWindow *wwin)
238 int center_x, center_y;
239 int width = wwin->frame->core->width;
240 int height = wwin->frame->core->height;
241 int w = WMIN(width, 20);
242 int h = WMIN(height, 20);
243 WScreen *scr = wwin->screen_ptr;
245 center_x = wwin->frame_x + (width - w) / 2;
246 center_y = wwin->frame_y + (height - h) / 2;
248 animateResize(scr, center_x, center_y, 1, 1, wwin->frame_x, wwin->frame_y, width, height);
250 #else
251 void DoWindowBirth(WWindow *wwin)
253 /* dummy stub */
255 #endif
257 #define BOUNCE_HZ 25
258 #define BOUNCE_DELAY (1000/BOUNCE_HZ)
259 #define BOUNCE_HEIGHT 24
260 #define BOUNCE_LENGTH 0.3
261 #define BOUNCE_DAMP 0.6
262 #define URGENT_BOUNCE_DELAY 3000
264 typedef struct AppBouncerData {
265 WApplication *wapp;
266 int count;
267 int pow;
268 int dir;
269 WMHandlerID *timer;
270 } AppBouncerData;
272 static void doAppBounce(void *arg)
274 AppBouncerData *data = (AppBouncerData*)arg;
275 WAppIcon *aicon = data->wapp->app_icon;
277 reinit:
278 if (aicon && data->wapp->refcount > 1) {
279 if (wPreferences.raise_appicons_when_bouncing)
280 XRaiseWindow(dpy, aicon->icon->core->window);
282 const double ticks = BOUNCE_HZ * BOUNCE_LENGTH;
283 const double s = sqrt(BOUNCE_HEIGHT)/(ticks/2);
284 double h = BOUNCE_HEIGHT*pow(BOUNCE_DAMP, data->pow);
285 double sqrt_h = sqrt(h);
286 if (h > 3) {
287 double offset, x = s * data->count - sqrt_h;
288 if (x > sqrt_h) {
289 ++data->pow;
290 data->count = 0;
291 goto reinit;
292 } else ++data->count;
293 offset = h - x*x;
295 switch (data->dir) {
296 case 0: /* left, bounce to right */
297 XMoveWindow(dpy, aicon->icon->core->window,
298 aicon->x_pos + (int)offset, aicon->y_pos);
299 break;
300 case 1: /* right, bounce to left */
301 XMoveWindow(dpy, aicon->icon->core->window,
302 aicon->x_pos - (int)offset, aicon->y_pos);
303 break;
304 case 2: /* top, bounce down */
305 XMoveWindow(dpy, aicon->icon->core->window,
306 aicon->x_pos, aicon->y_pos + (int)offset);
307 break;
308 case 3: /* bottom, bounce up */
309 XMoveWindow(dpy, aicon->icon->core->window,
310 aicon->x_pos, aicon->y_pos - (int)offset);
311 break;
313 return;
315 XMoveWindow(dpy, aicon->icon->core->window,
316 aicon->x_pos, aicon->y_pos);
319 CommitStackingForWindow(aicon->icon->core);
320 data->wapp->flags.bouncing = 0;
321 WMDeleteTimerHandler(data->timer);
322 wApplicationDestroy(data->wapp);
323 free(data);
326 static int bounceDirection(WAppIcon *aicon)
328 enum { left_e = 1, right_e = 2, top_e = 4, bottom_e = 8 };
330 WScreen *scr = aicon->icon->core->screen_ptr;
331 WMRect rr, sr;
332 int l, r, t, b, h, v;
333 int dir = 0;
335 rr.pos.x = aicon->x_pos;
336 rr.pos.y = aicon->y_pos;
337 rr.size.width = rr.size.height = 64;
339 sr = wGetRectForHead(scr, wGetHeadForRect(scr, rr));
341 l = rr.pos.x - sr.pos.x;
342 r = sr.pos.x + sr.size.width - rr.pos.x - rr.size.width;
343 t = rr.pos.y - sr.pos.y;
344 b = sr.pos.y + sr.size.height - rr.pos.y - rr.size.height;
346 if (l < r) {
347 dir |= left_e;
348 h = l;
349 } else {
350 dir |= right_e;
351 h = r;
354 if (t < b) {
355 dir |= top_e;
356 v = t;
357 } else {
358 dir |= bottom_e;
359 v = b;
362 if (aicon->dock && abs(aicon->xindex) != abs(aicon->yindex)) {
363 if (abs(aicon->xindex) < abs(aicon->yindex)) dir &= ~(top_e | bottom_e);
364 else dir &= ~(left_e | right_e);
365 } else {
366 if (h < v) dir &= ~(top_e | bottom_e);
367 else dir &= ~(left_e | right_e);
370 switch (dir) {
371 case left_e:
372 dir = 0;
373 break;
375 case right_e:
376 dir = 1;
377 break;
379 case top_e:
380 dir = 2;
381 break;
383 case bottom_e:
384 dir = 3;
385 break;
387 default:
388 wwarning(_("Impossible direction: %d\n"), dir);
389 dir = 3;
390 break;
393 return dir;
396 void wAppBounce(WApplication *wapp)
398 if (!wPreferences.no_animations && wapp->app_icon && !wapp->flags.bouncing) {
399 ++wapp->refcount;
400 wapp->flags.bouncing = 1;
402 AppBouncerData *data = (AppBouncerData *)malloc(sizeof(AppBouncerData));
403 data->wapp = wapp;
404 data->count = data->pow = 0;
405 data->dir = bounceDirection(wapp->app_icon);
406 data->timer = WMAddPersistentTimerHandler(BOUNCE_DELAY, doAppBounce, data);
410 static int appIsUrgent(WApplication *wapp)
412 WScreen *scr;
413 WWindow *wlist;
415 if (!wapp->main_window_desc) {
416 wwarning("group leader not found for window group");
417 return 0;
419 scr = wapp->main_window_desc->screen_ptr;
420 wlist = scr->focused_window;
421 if (!wlist)
422 return 0;
424 while (wlist) {
425 if (wlist->main_window == wapp->main_window) {
426 if (wlist->flags.urgent)
427 return 1;
429 wlist = wlist->prev;
432 return 0;
435 static void doAppUrgentBounce(void *arg)
437 WApplication *wapp = (WApplication *)arg;
439 if (appIsUrgent(wapp)) {
440 if(wPreferences.bounce_appicons_when_urgent) wAppBounce(wapp);
441 } else {
442 WMDeleteTimerHandler(wapp->urgent_bounce_timer);
443 wapp->urgent_bounce_timer = NULL;
447 void wAppBounceWhileUrgent(WApplication *wapp)
449 if (!wapp) return;
450 if (appIsUrgent(wapp)) {
451 if (!wapp->urgent_bounce_timer) {
452 wapp->urgent_bounce_timer = WMAddPersistentTimerHandler(URGENT_BOUNCE_DELAY, doAppUrgentBounce, wapp);
453 doAppUrgentBounce(wapp);
455 } else {
456 if (wapp->urgent_bounce_timer) {
457 WMDeleteTimerHandler(wapp->urgent_bounce_timer);
458 wapp->urgent_bounce_timer = NULL;