Left Half / Right Half Maximize
[wmaker-crm.git] / src / actions.c
1 /* action.c- misc. window commands (miniaturize, hide etc.)
2  *
3  *  Window Maker window manager
4  *
5  *  Copyright (c) 1997-2003 Alfredo K. Kojima
6  *  Copyright (c) 1998-2003 Dan Pascu
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21  *  USA.
22  */
23
24 #include "wconfig.h"
25
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <math.h>
32 #include <time.h>
33
34 #include "WindowMaker.h"
35 #include "wcore.h"
36 #include "framewin.h"
37 #include "window.h"
38 #include "client.h"
39 #include "icon.h"
40 #include "funcs.h"
41 #include "application.h"
42 #include "actions.h"
43 #include "stacking.h"
44 #include "appicon.h"
45 #include "dock.h"
46 #include "appmenu.h"
47 #include "winspector.h"
48 #include "workspace.h"
49 #include "wsound.h"
50 #include "xinerama.h"
51
52 /****** Global Variables ******/
53 extern Time LastTimestamp;
54 extern Time LastFocusChange;
55
56 extern Cursor wCursor[WCUR_LAST];
57
58 extern WPreferences wPreferences;
59
60 extern Atom _XA_WM_TAKE_FOCUS;
61
62 extern void ProcessPendingEvents();
63
64 /******* Local Variables *******/
65 static struct {
66         int steps;
67         int delay;
68 } shadePars[5] = {
69         {
70         SHADE_STEPS_UF, SHADE_DELAY_UF}, {
71         SHADE_STEPS_F, SHADE_DELAY_F}, {
72         SHADE_STEPS_M, SHADE_DELAY_M}, {
73         SHADE_STEPS_S, SHADE_DELAY_S}, {
74 SHADE_STEPS_US, SHADE_DELAY_US}};
75
76 #define SHADE_STEPS     shadePars[(int)wPreferences.shade_speed].steps
77 #define SHADE_DELAY     shadePars[(int)wPreferences.shade_speed].delay
78
79 static int compareTimes(Time t1, Time t2)
80 {
81         Time diff;
82         if (t1 == t2)
83                 return 0;
84         diff = t1 - t2;
85         return (diff < 60000) ? 1 : -1;
86 }
87
88 /*
89  *----------------------------------------------------------------------
90  * wSetFocusTo--
91  *      Changes the window focus to the one passed as argument.
92  * If the window to focus is not already focused, it will be brought
93  * to the head of the list of windows. Previously focused window is
94  * unfocused.
95  *
96  * Side effects:
97  *      Window list may be reordered and the window focus is changed.
98  *
99  *----------------------------------------------------------------------
100  */
101 void wSetFocusTo(WScreen * scr, WWindow * wwin)
102 {
103         static WScreen *old_scr = NULL;
104
105         WWindow *old_focused;
106         WWindow *focused = scr->focused_window;
107         Time timestamp = LastTimestamp;
108         WApplication *oapp = NULL, *napp = NULL;
109         int wasfocused;
110
111         if (scr->flags.ignore_focus_events || compareTimes(LastFocusChange, timestamp) > 0)
112                 return;
113
114         if (!old_scr)
115                 old_scr = scr;
116         old_focused = old_scr->focused_window;
117
118         LastFocusChange = timestamp;
119
120         if (old_focused)
121                 oapp = wApplicationOf(old_focused->main_window);
122
123         if (wwin == NULL) {
124                 XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
125                 if (old_focused) {
126                         wWindowUnfocus(old_focused);
127                 }
128                 if (oapp) {
129                         wAppMenuUnmap(oapp->menu);
130 #ifdef NEWAPPICON
131                         wApplicationDeactivate(oapp);
132 #endif
133                 }
134
135                 WMPostNotificationName(WMNChangedFocus, NULL, (void *)True);
136                 return;
137         } else if (old_scr != scr && old_focused) {
138                 wWindowUnfocus(old_focused);
139         }
140
141         wasfocused = wwin->flags.focused;
142         napp = wApplicationOf(wwin->main_window);
143
144         /* remember last workspace where the app has been */
145         if (napp) {
146                 /*napp->last_workspace = wwin->screen_ptr->current_workspace; */
147                 napp->last_workspace = wwin->frame->workspace;
148         }
149
150         if (wwin->flags.mapped && !WFLAGP(wwin, no_focusable)) {
151                 /* install colormap if colormap mode is lock mode */
152                 if (wPreferences.colormap_mode == WCM_CLICK)
153                         wColormapInstallForWindow(scr, wwin);
154
155                 /* set input focus */
156                 switch (wwin->focus_mode) {
157                 case WFM_NO_INPUT:
158                         XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
159                         break;
160
161                 case WFM_PASSIVE:
162                 case WFM_LOCALLY_ACTIVE:
163                         XSetInputFocus(dpy, wwin->client_win, RevertToParent, CurrentTime);
164                         break;
165
166                 case WFM_GLOBALLY_ACTIVE:
167                         break;
168                 }
169                 XFlush(dpy);
170                 if (wwin->protocols.TAKE_FOCUS) {
171                         wClientSendProtocol(wwin, _XA_WM_TAKE_FOCUS, timestamp);
172                 }
173                 XSync(dpy, False);
174         } else {
175                 XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
176         }
177         if (WFLAGP(wwin, no_focusable))
178                 return;
179
180         /* if this is not the focused window focus it */
181         if (focused != wwin) {
182                 /* change the focus window list order */
183                 if (wwin->prev)
184                         wwin->prev->next = wwin->next;
185
186                 if (wwin->next)
187                         wwin->next->prev = wwin->prev;
188
189                 wwin->prev = focused;
190                 focused->next = wwin;
191                 wwin->next = NULL;
192                 scr->focused_window = wwin;
193
194                 if (oapp && oapp != napp) {
195                         wAppMenuUnmap(oapp->menu);
196 #ifdef NEWAPPICON
197                         wApplicationDeactivate(oapp);
198 #endif
199                 }
200         }
201
202         wWindowFocus(wwin, focused);
203
204         if (napp && !wasfocused) {
205 #ifdef USER_MENU
206                 wUserMenuRefreshInstances(napp->menu, wwin);
207 #endif                          /* USER_MENU */
208
209                 if (wwin->flags.mapped)
210                         wAppMenuMap(napp->menu, wwin);
211 #ifdef NEWAPPICON
212                 wApplicationActivate(napp);
213 #endif
214         }
215
216         XFlush(dpy);
217         old_scr = scr;
218 }
219
220 void wShadeWindow(WWindow * wwin)
221 {
222         time_t time0;
223 #ifdef ANIMATIONS
224         int y, s, w, h;
225 #endif
226
227         if (wwin->flags.shaded)
228                 return;
229
230         time0 = time(NULL);
231
232         XLowerWindow(dpy, wwin->client_win);
233
234         wSoundPlay(WSOUND_SHADE);
235
236 #ifdef ANIMATIONS
237         if (!wwin->screen_ptr->flags.startup && !wwin->flags.skip_next_animation && !wPreferences.no_animations) {
238                 /* do the shading animation */
239                 h = wwin->frame->core->height;
240                 s = h / SHADE_STEPS;
241                 if (s < 1)
242                         s = 1;
243                 w = wwin->frame->core->width;
244                 y = wwin->frame->top_width;
245                 while (h > wwin->frame->top_width + 1) {
246                         XMoveWindow(dpy, wwin->client_win, 0, y);
247                         XResizeWindow(dpy, wwin->frame->core->window, w, h);
248                         XFlush(dpy);
249
250                         if (time(NULL) - time0 > MAX_ANIMATION_TIME)
251                                 break;
252
253                         if (SHADE_DELAY > 0) {
254                                 wusleep(SHADE_DELAY * 1000L);
255                         } else {
256                                 wusleep(10);
257                         }
258                         h -= s;
259                         y -= s;
260                 }
261                 XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width);
262         }
263 #endif                          /* ANIMATIONS */
264
265         wwin->flags.skip_next_animation = 0;
266         wwin->flags.shaded = 1;
267         wwin->flags.mapped = 0;
268         /* prevent window withdrawal when getting UnmapNotify */
269         XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask);
270         XUnmapWindow(dpy, wwin->client_win);
271         XSelectInput(dpy, wwin->client_win, wwin->event_mask);
272
273         /* for the client it's just like iconification */
274         wFrameWindowResize(wwin->frame, wwin->frame->core->width, wwin->frame->top_width - 1);
275
276         wwin->client.y = wwin->frame_y - wwin->client.height + wwin->frame->top_width;
277         wWindowSynthConfigureNotify(wwin);
278
279         /*
280            wClientSetState(wwin, IconicState, None);
281          */
282
283         WMPostNotificationName(WMNChangedState, wwin, "shade");
284
285 #ifdef ANIMATIONS
286         if (!wwin->screen_ptr->flags.startup) {
287                 /* Catch up with events not processed while animation was running */
288                 ProcessPendingEvents();
289         }
290 #endif
291 }
292
293 void wUnshadeWindow(WWindow * wwin)
294 {
295         time_t time0;
296 #ifdef ANIMATIONS
297         int y, s, w, h;
298 #endif                          /* ANIMATIONS */
299
300         if (!wwin->flags.shaded)
301                 return;
302
303         time0 = time(NULL);
304
305         wwin->flags.shaded = 0;
306         wwin->flags.mapped = 1;
307         XMapWindow(dpy, wwin->client_win);
308
309         wSoundPlay(WSOUND_UNSHADE);
310
311 #ifdef ANIMATIONS
312         if (!wPreferences.no_animations && !wwin->flags.skip_next_animation) {
313                 /* do the shading animation */
314                 h = wwin->frame->top_width + wwin->frame->bottom_width;
315                 y = wwin->frame->top_width - wwin->client.height;
316                 s = abs(y) / SHADE_STEPS;
317                 if (s < 1)
318                         s = 1;
319                 w = wwin->frame->core->width;
320                 XMoveWindow(dpy, wwin->client_win, 0, y);
321                 if (s > 0) {
322                         while (h < wwin->client.height + wwin->frame->top_width + wwin->frame->bottom_width) {
323                                 XResizeWindow(dpy, wwin->frame->core->window, w, h);
324                                 XMoveWindow(dpy, wwin->client_win, 0, y);
325                                 XFlush(dpy);
326                                 if (SHADE_DELAY > 0) {
327                                         wusleep(SHADE_DELAY * 2000L / 3);
328                                 } else {
329                                         wusleep(10);
330                                 }
331                                 h += s;
332                                 y += s;
333
334                                 if (time(NULL) - time0 > MAX_ANIMATION_TIME)
335                                         break;
336                         }
337                 }
338                 XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width);
339         }
340 #endif                          /* ANIMATIONS */
341
342         wwin->flags.skip_next_animation = 0;
343         wFrameWindowResize(wwin->frame, wwin->frame->core->width,
344                            wwin->frame->top_width + wwin->client.height + wwin->frame->bottom_width);
345
346         wwin->client.y = wwin->frame_y + wwin->frame->top_width;
347         wWindowSynthConfigureNotify(wwin);
348
349         /*
350            wClientSetState(wwin, NormalState, None);
351          */
352         /* if the window is focused, set the focus again as it was disabled during
353          * shading */
354         if (wwin->flags.focused)
355                 wSetFocusTo(wwin->screen_ptr, wwin);
356
357         WMPostNotificationName(WMNChangedState, wwin, "shade");
358 }
359
360 void wMaximizeWindow(WWindow * wwin, int directions)
361 {
362         int new_x, new_y;
363         unsigned int new_width, new_height;
364         int changed_h, changed_v, shrink_h, shrink_v;
365         WArea usableArea, totalArea;
366
367         if (!IS_RESIZABLE(wwin))
368                 return;
369
370         totalArea.x1 = 0;
371         totalArea.y1 = 0;
372         totalArea.x2 = wwin->screen_ptr->scr_width;
373         totalArea.y2 = wwin->screen_ptr->scr_height;
374         usableArea = totalArea;
375
376         if (!(directions & MAX_IGNORE_XINERAMA)) {
377                 WScreen *scr = wwin->screen_ptr;
378                 int head;
379
380                 if (directions & MAX_KEYBOARD)
381                         head = wGetHeadForWindow(wwin);
382                 else
383                         head = wGetHeadForPointerLocation(scr);
384
385                 usableArea = wGetUsableAreaForHead(scr, head, &totalArea, True);
386         }
387
388         if (WFLAGP(wwin, full_maximize)) {
389                 usableArea = totalArea;
390         }
391
392         if (wwin->flags.shaded) {
393                 wwin->flags.skip_next_animation = 1;
394                 wUnshadeWindow(wwin);
395         }
396         /* Only save directions, not kbd or xinerama hints */
397         directions &= (MAX_HORIZONTAL|MAX_VERTICAL|MAX_LEFTHALF|MAX_RIGHTHALF);
398
399         changed_h = ((wwin->flags.maximized ^ directions) & MAX_HORIZONTAL);
400         changed_v = ((wwin->flags.maximized ^ directions) & MAX_VERTICAL);
401         shrink_h = (changed_h && (directions & MAX_HORIZONTAL) == 0);
402         shrink_v = (changed_v && (directions & MAX_VERTICAL) == 0);
403
404         if (wwin->flags.maximized) {
405                 /* if already maximized in some direction, we only update the
406                  * appropriate old x, old y coordinates. This is necessary to
407                  * allow succesive maximizations in different directions without
408                  * the need to first do an un-maximize (to avoid flicker).
409                  */
410                 if (!(wwin->flags.maximized & (MAX_HORIZONTAL|MAX_LEFTHALF|MAX_RIGHTHALF))) {
411                         wwin->old_geometry.x = wwin->frame_x;
412                 }
413                 if (!(wwin->flags.maximized & MAX_VERTICAL)) {
414                         wwin->old_geometry.y = wwin->frame_y;
415                 }
416         } else {
417                 wwin->old_geometry.width = wwin->client.width;
418                 wwin->old_geometry.height = wwin->client.height;
419                 wwin->old_geometry.x = wwin->frame_x;
420                 wwin->old_geometry.y = wwin->frame_y;
421         }
422         wwin->flags.maximized = directions;
423
424         if (directions & MAX_HORIZONTAL) {
425                 new_width = usableArea.x2 - usableArea.x1;
426                 if (HAS_BORDER(wwin))
427                         new_width -= FRAME_BORDER_WIDTH * 2;
428                 new_x = usableArea.x1;
429         } else if (directions & MAX_LEFTHALF) {
430                 new_width = (usableArea.x2 - usableArea.x1)/2;
431                 if (HAS_BORDER(wwin))
432                         new_width -= FRAME_BORDER_WIDTH * 2;
433                 new_x = usableArea.x1;
434         } else if (directions & MAX_RIGHTHALF) {
435                 new_width = (usableArea.x2 - usableArea.x1)/2;
436                 if (HAS_BORDER(wwin))
437                         new_width -= FRAME_BORDER_WIDTH * 2;
438                 new_x = usableArea.x1+((usableArea.x2 - usableArea.x1)/2);
439         } else if (shrink_h) {
440                 new_x = wwin->old_geometry.x;
441                 new_width = wwin->old_geometry.width;
442         } else {
443                 new_x = wwin->frame_x;
444                 new_width = wwin->frame->core->width;
445         }
446
447         if (directions & MAX_VERTICAL) {
448                 new_height = usableArea.y2 - usableArea.y1;
449                 if (HAS_BORDER(wwin))
450                         new_height -= FRAME_BORDER_WIDTH * 2;
451                 new_y = usableArea.y1;
452                 if (WFLAGP(wwin, full_maximize)) {
453                         new_y -= wwin->frame->top_width;
454                         new_height += wwin->frame->bottom_width - 1;
455                 }
456         } else if (shrink_v) {
457                 new_y = wwin->old_geometry.y;
458                 new_height = wwin->old_geometry.height;
459         } else {
460                 new_y = wwin->frame_y;
461                 new_height = wwin->frame->core->height;
462         }
463
464         if (!WFLAGP(wwin, full_maximize)) {
465                 new_height -= wwin->frame->top_width + wwin->frame->bottom_width;
466         }
467
468         wWindowConstrainSize(wwin, &new_width, &new_height);
469
470         wWindowCropSize(wwin, usableArea.x2 - usableArea.x1,
471                         usableArea.y2 - usableArea.y1, &new_width, &new_height);
472
473         wWindowConfigure(wwin, new_x, new_y, new_width, new_height);
474
475         WMPostNotificationName(WMNChangedState, wwin, "maximize");
476
477         wSoundPlay(WSOUND_MAXIMIZE);
478 }
479
480 void wUnmaximizeWindow(WWindow * wwin)
481 {
482         int x, y, w, h;
483
484         if (!wwin->flags.maximized)
485                 return;
486
487         if (wwin->flags.shaded) {
488                 wwin->flags.skip_next_animation = 1;
489                 wUnshadeWindow(wwin);
490         }
491         x = ((wwin->flags.maximized & (MAX_HORIZONTAL|MAX_LEFTHALF|MAX_RIGHTHALF)) && wwin->old_geometry.x) ?
492             wwin->old_geometry.x : wwin->frame_x;
493         y = ((wwin->flags.maximized & MAX_VERTICAL) && wwin->old_geometry.y) ?
494             wwin->old_geometry.y : wwin->frame_y;
495         w = wwin->old_geometry.width ? wwin->old_geometry.width : wwin->client.width;
496         h = wwin->old_geometry.height ? wwin->old_geometry.height : wwin->client.height;
497
498         wwin->flags.maximized = 0;
499         wWindowConfigure(wwin, x, y, w, h);
500
501         WMPostNotificationName(WMNChangedState, wwin, "maximize");
502
503         wSoundPlay(WSOUND_UNMAXIMIZE);
504 }
505
506 void wFullscreenWindow(WWindow * wwin)
507 {
508         int head;
509         WMRect rect;
510
511         if (wwin->flags.fullscreen)
512                 return;
513
514         wwin->flags.fullscreen = True;
515
516         wWindowConfigureBorders(wwin);
517
518         ChangeStackingLevel(wwin->frame->core, WMFullscreenLevel);
519
520         wwin->bfs_geometry.x = wwin->frame_x;
521         wwin->bfs_geometry.y = wwin->frame_y;
522         wwin->bfs_geometry.width = wwin->frame->core->width;
523         wwin->bfs_geometry.height = wwin->frame->core->height;
524
525         head = wGetHeadForWindow(wwin);
526         rect = wGetRectForHead(wwin->screen_ptr, head);
527         wWindowConfigure(wwin, rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
528
529         WMPostNotificationName(WMNChangedState, wwin, "fullscreen");
530 }
531
532 void wUnfullscreenWindow(WWindow * wwin)
533 {
534         if (!wwin->flags.fullscreen)
535                 return;
536
537         wwin->flags.fullscreen = False;
538
539         if (wwin->frame->core->stacking->window_level == WMFullscreenLevel) {
540                 if (WFLAGP(wwin, sunken)) {
541                         ChangeStackingLevel(wwin->frame->core, WMSunkenLevel);
542                 } else if (WFLAGP(wwin, floating)) {
543                         ChangeStackingLevel(wwin->frame->core, WMFloatingLevel);
544                 } else {
545                         ChangeStackingLevel(wwin->frame->core, WMNormalLevel);
546                 }
547         }
548
549         wWindowConfigure(wwin, wwin->bfs_geometry.x, wwin->bfs_geometry.y,
550                          wwin->bfs_geometry.width, wwin->bfs_geometry.height);
551
552         wWindowConfigureBorders(wwin);
553         /*
554            // seems unnecessary, but also harmless (doesn't generate flicker) -Dan
555            wFrameWindowPaint(wwin->frame);
556          */
557
558         WMPostNotificationName(WMNChangedState, wwin, "fullscreen");
559 }
560
561 #ifdef ANIMATIONS
562 static void animateResizeFlip(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
563 {
564 #define FRAMES (MINIATURIZE_ANIMATION_FRAMES_F)
565         float cx, cy, cw, ch;
566         float xstep, ystep, wstep, hstep;
567         XPoint points[5];
568         float dx, dch, midy;
569         float angle, final_angle, delta;
570
571         xstep = (float)(fx - x) / steps;
572         ystep = (float)(fy - y) / steps;
573         wstep = (float)(fw - w) / steps;
574         hstep = (float)(fh - h) / steps;
575
576         cx = (float)x;
577         cy = (float)y;
578         cw = (float)w;
579         ch = (float)h;
580
581         final_angle = 2 * WM_PI * MINIATURIZE_ANIMATION_TWIST_F;
582         delta = (float)(final_angle / FRAMES);
583         for (angle = 0;; angle += delta) {
584                 if (angle > final_angle)
585                         angle = final_angle;
586
587                 dx = (cw / 10) - ((cw / 5) * sin(angle));
588                 dch = (ch / 2) * cos(angle);
589                 midy = cy + (ch / 2);
590
591                 points[0].x = cx + dx;
592                 points[0].y = midy - dch;
593                 points[1].x = cx + cw - dx;
594                 points[1].y = points[0].y;
595                 points[2].x = cx + cw + dx;
596                 points[2].y = midy + dch;
597                 points[3].x = cx - dx;
598                 points[3].y = points[2].y;
599                 points[4].x = points[0].x;
600                 points[4].y = points[0].y;
601
602                 XGrabServer(dpy);
603                 XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
604                 XFlush(dpy);
605 #if (MINIATURIZE_ANIMATION_DELAY_F > 0)
606                 wusleep(MINIATURIZE_ANIMATION_DELAY_F);
607 #else
608                 wusleep(10);
609 #endif
610
611                 XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
612                 XUngrabServer(dpy);
613                 cx += xstep;
614                 cy += ystep;
615                 cw += wstep;
616                 ch += hstep;
617                 if (angle >= final_angle)
618                         break;
619
620         }
621         XFlush(dpy);
622 }
623
624 #undef FRAMES
625
626 static void
627 animateResizeTwist(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
628 {
629 #define FRAMES (MINIATURIZE_ANIMATION_FRAMES_T)
630         float cx, cy, cw, ch;
631         float xstep, ystep, wstep, hstep;
632         XPoint points[5];
633         float angle, final_angle, a, d, delta;
634
635         x += w / 2;
636         y += h / 2;
637         fx += fw / 2;
638         fy += fh / 2;
639
640         xstep = (float)(fx - x) / steps;
641         ystep = (float)(fy - y) / steps;
642         wstep = (float)(fw - w) / steps;
643         hstep = (float)(fh - h) / steps;
644
645         cx = (float)x;
646         cy = (float)y;
647         cw = (float)w;
648         ch = (float)h;
649
650         final_angle = 2 * WM_PI * MINIATURIZE_ANIMATION_TWIST_T;
651         delta = (float)(final_angle / FRAMES);
652         for (angle = 0;; angle += delta) {
653                 if (angle > final_angle)
654                         angle = final_angle;
655
656                 a = atan(ch / cw);
657                 d = sqrt((cw / 2) * (cw / 2) + (ch / 2) * (ch / 2));
658
659                 points[0].x = cx + cos(angle - a) * d;
660                 points[0].y = cy + sin(angle - a) * d;
661                 points[1].x = cx + cos(angle + a) * d;
662                 points[1].y = cy + sin(angle + a) * d;
663                 points[2].x = cx + cos(angle - a + WM_PI) * d;
664                 points[2].y = cy + sin(angle - a + WM_PI) * d;
665                 points[3].x = cx + cos(angle + a + WM_PI) * d;
666                 points[3].y = cy + sin(angle + a + WM_PI) * d;
667                 points[4].x = cx + cos(angle - a) * d;
668                 points[4].y = cy + sin(angle - a) * d;
669                 XGrabServer(dpy);
670                 XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
671                 XFlush(dpy);
672 #if (MINIATURIZE_ANIMATION_DELAY_T > 0)
673                 wusleep(MINIATURIZE_ANIMATION_DELAY_T);
674 #else
675                 wusleep(10);
676 #endif
677
678                 XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
679                 XUngrabServer(dpy);
680                 cx += xstep;
681                 cy += ystep;
682                 cw += wstep;
683                 ch += hstep;
684                 if (angle >= final_angle)
685                         break;
686
687         }
688         XFlush(dpy);
689 }
690
691 #undef FRAMES
692
693 static void animateResizeZoom(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
694 {
695 #define FRAMES (MINIATURIZE_ANIMATION_FRAMES_Z)
696         float cx[FRAMES], cy[FRAMES], cw[FRAMES], ch[FRAMES];
697         float xstep, ystep, wstep, hstep;
698         int i, j;
699
700         xstep = (float)(fx - x) / steps;
701         ystep = (float)(fy - y) / steps;
702         wstep = (float)(fw - w) / steps;
703         hstep = (float)(fh - h) / steps;
704
705         for (j = 0; j < FRAMES; j++) {
706                 cx[j] = (float)x;
707                 cy[j] = (float)y;
708                 cw[j] = (float)w;
709                 ch[j] = (float)h;
710         }
711         XGrabServer(dpy);
712         for (i = 0; i < steps; i++) {
713                 for (j = 0; j < FRAMES; j++) {
714                         XDrawRectangle(dpy, scr->root_win, scr->frame_gc,
715                                        (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
716                 }
717                 XFlush(dpy);
718 #if (MINIATURIZE_ANIMATION_DELAY_Z > 0)
719                 wusleep(MINIATURIZE_ANIMATION_DELAY_Z);
720 #else
721                 wusleep(10);
722 #endif
723                 for (j = 0; j < FRAMES; j++) {
724                         XDrawRectangle(dpy, scr->root_win, scr->frame_gc,
725                                        (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
726                         if (j < FRAMES - 1) {
727                                 cx[j] = cx[j + 1];
728                                 cy[j] = cy[j + 1];
729                                 cw[j] = cw[j + 1];
730                                 ch[j] = ch[j + 1];
731                         } else {
732                                 cx[j] += xstep;
733                                 cy[j] += ystep;
734                                 cw[j] += wstep;
735                                 ch[j] += hstep;
736                         }
737                 }
738         }
739
740         for (j = 0; j < FRAMES; j++) {
741                 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
742         }
743         XFlush(dpy);
744 #if (MINIATURIZE_ANIMATION_DELAY_Z > 0)
745         wusleep(MINIATURIZE_ANIMATION_DELAY_Z);
746 #else
747         wusleep(10);
748 #endif
749         for (j = 0; j < FRAMES; j++) {
750                 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
751         }
752
753         XUngrabServer(dpy);
754 }
755
756 #undef FRAMES
757
758 void animateResize(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int hiding)
759 {
760         int style = wPreferences.iconification_style;   /* Catch the value */
761         int steps, k;
762
763         if (style == WIS_NONE)
764                 return;
765
766         if (style == WIS_RANDOM) {
767                 style = rand() % 3;
768         }
769
770         k = (hiding ? 2 : 3);
771
772         switch (style) {
773         case WIS_TWIST:
774                 steps = (MINIATURIZE_ANIMATION_STEPS_T * k) / 3;
775                 if (steps > 0)
776                         animateResizeTwist(scr, x, y, w, h, fx, fy, fw, fh, steps);
777                 break;
778         case WIS_FLIP:
779                 steps = (MINIATURIZE_ANIMATION_STEPS_F * k) / 3;
780                 if (steps > 0)
781                         animateResizeFlip(scr, x, y, w, h, fx, fy, fw, fh, steps);
782                 break;
783         case WIS_ZOOM:
784         default:
785                 steps = (MINIATURIZE_ANIMATION_STEPS_Z * k) / 3;
786                 if (steps > 0)
787                         animateResizeZoom(scr, x, y, w, h, fx, fy, fw, fh, steps);
788                 break;
789         }
790 }
791 #endif                          /* ANIMATIONS */
792
793 static void flushExpose()
794 {
795         XEvent tmpev;
796
797         while (XCheckTypedEvent(dpy, Expose, &tmpev))
798                 WMHandleEvent(&tmpev);
799         XSync(dpy, 0);
800 }
801
802 static void unmapTransientsFor(WWindow * wwin)
803 {
804         WWindow *tmp;
805
806         tmp = wwin->screen_ptr->focused_window;
807         while (tmp) {
808                 /* unmap the transients for this transient */
809                 if (tmp != wwin && tmp->transient_for == wwin->client_win
810                     && (tmp->flags.mapped || wwin->screen_ptr->flags.startup || tmp->flags.shaded)) {
811                         unmapTransientsFor(tmp);
812                         tmp->flags.miniaturized = 1;
813                         if (!tmp->flags.shaded) {
814                                 wWindowUnmap(tmp);
815                         } else {
816                                 XUnmapWindow(dpy, tmp->frame->core->window);
817                         }
818                         /*
819                            if (!tmp->flags.shaded)
820                          */
821                         wClientSetState(tmp, IconicState, None);
822
823                         WMPostNotificationName(WMNChangedState, tmp, "iconify-transient");
824                 }
825                 tmp = tmp->prev;
826         }
827 }
828
829 static void mapTransientsFor(WWindow * wwin)
830 {
831         WWindow *tmp;
832
833         tmp = wwin->screen_ptr->focused_window;
834         while (tmp) {
835                 /* recursively map the transients for this transient */
836                 if (tmp != wwin && tmp->transient_for == wwin->client_win && /*!tmp->flags.mapped */ tmp->flags.miniaturized
837                     && tmp->icon == NULL) {
838                         mapTransientsFor(tmp);
839                         tmp->flags.miniaturized = 0;
840                         if (!tmp->flags.shaded) {
841                                 wWindowMap(tmp);
842                         } else {
843                                 XMapWindow(dpy, tmp->frame->core->window);
844                         }
845                         tmp->flags.semi_focused = 0;
846                         /*
847                            if (!tmp->flags.shaded)
848                          */
849                         wClientSetState(tmp, NormalState, None);
850
851                         WMPostNotificationName(WMNChangedState, tmp, "iconify-transient");
852                 }
853                 tmp = tmp->prev;
854         }
855 }
856
857 #if 0
858 static void setupIconGrabs(WIcon * icon)
859 {
860         /* setup passive grabs on the icon */
861         XGrabButton(dpy, Button1, AnyModifier, icon->core->window, True,
862                     ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
863         XGrabButton(dpy, Button2, AnyModifier, icon->core->window, True,
864                     ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
865         XGrabButton(dpy, Button3, AnyModifier, icon->core->window, True,
866                     ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
867         XSync(dpy, 0);
868 }
869 #endif
870
871 static WWindow *recursiveTransientFor(WWindow * wwin)
872 {
873         int i;
874
875         if (!wwin)
876                 return None;
877
878         /* hackish way to detect transient_for cycle */
879         i = wwin->screen_ptr->window_count + 1;
880
881         while (wwin && wwin->transient_for != None && i > 0) {
882                 wwin = wWindowFor(wwin->transient_for);
883                 i--;
884         }
885         if (i == 0 && wwin) {
886                 wwarning("%s has a severely broken WM_TRANSIENT_FOR hint.", wwin->frame->title);
887                 return NULL;
888         }
889
890         return wwin;
891 }
892
893 #if 0
894 static void removeIconGrabs(WIcon * icon)
895 {
896         /* remove passive grabs on the icon */
897         XUngrabButton(dpy, Button1, AnyModifier, icon->core->window);
898         XUngrabButton(dpy, Button2, AnyModifier, icon->core->window);
899         XUngrabButton(dpy, Button3, AnyModifier, icon->core->window);
900         XSync(dpy, 0);
901 }
902 #endif
903
904 void wIconifyWindow(WWindow * wwin)
905 {
906         XWindowAttributes attribs;
907         int present;
908
909         if (!XGetWindowAttributes(dpy, wwin->client_win, &attribs)) {
910                 /* the window doesn't exist anymore */
911                 return;
912         }
913
914         if (wwin->flags.miniaturized) {
915                 return;
916         }
917
918         if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
919                 WWindow *owner = wWindowFor(wwin->transient_for);
920
921                 if (owner && owner->flags.miniaturized)
922                         return;
923         }
924
925         present = wwin->frame->workspace == wwin->screen_ptr->current_workspace;
926
927         /* if the window is in another workspace, simplify process */
928         if (present) {
929                 /* icon creation may take a while */
930                 XGrabPointer(dpy, wwin->screen_ptr->root_win, False,
931                              ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
932                              GrabModeAsync, None, None, CurrentTime);
933         }
934
935         if (!wPreferences.disable_miniwindows
936 #ifdef NETWM_HINTS
937             && !wwin->flags.net_handle_icon
938 #endif
939             ) {
940                 if (!wwin->flags.icon_moved) {
941                         PlaceIcon(wwin->screen_ptr, &wwin->icon_x, &wwin->icon_y, wGetHeadForWindow(wwin));
942                 }
943                 wwin->icon = wIconCreate(wwin);
944
945                 wwin->icon->mapped = 1;
946         }
947
948         wwin->flags.miniaturized = 1;
949         wwin->flags.mapped = 0;
950
951         /* unmap transients */
952
953         unmapTransientsFor(wwin);
954
955         if (present) {
956                 wSoundPlay(WSOUND_ICONIFY);
957
958                 XUngrabPointer(dpy, CurrentTime);
959                 wWindowUnmap(wwin);
960                 /* let all Expose events arrive so that we can repaint
961                  * something before the animation starts (and the server is grabbed) */
962                 XSync(dpy, 0);
963
964                 if (wPreferences.disable_miniwindows
965 #ifdef NETWM_HINTS
966                     || wwin->flags.net_handle_icon
967 #endif
968                     )
969                         wClientSetState(wwin, IconicState, None);
970                 else
971                         wClientSetState(wwin, IconicState, wwin->icon->icon_win);
972
973                 flushExpose();
974 #ifdef ANIMATIONS
975                 if (!wwin->screen_ptr->flags.startup && !wwin->flags.skip_next_animation
976                     && !wPreferences.no_animations) {
977                         int ix, iy, iw, ih;
978
979                         if (!wPreferences.disable_miniwindows
980 #ifdef NETWM_HINTS
981                             && !wwin->flags.net_handle_icon
982 #endif
983                             ) {
984                                 ix = wwin->icon_x;
985                                 iy = wwin->icon_y;
986                                 iw = wwin->icon->core->width;
987                                 ih = wwin->icon->core->height;
988                         } else {
989 #ifdef NETWM_HINTS
990                                 if (wwin->flags.net_handle_icon) {
991                                         ix = wwin->icon_x;
992                                         iy = wwin->icon_y;
993                                         iw = wwin->icon_w;
994                                         ih = wwin->icon_h;
995                                 } else
996 #endif
997                                 {
998                                         ix = 0;
999                                         iy = 0;
1000                                         iw = wwin->screen_ptr->scr_width;
1001                                         ih = wwin->screen_ptr->scr_height;
1002                                 }
1003                         }
1004                         animateResize(wwin->screen_ptr, wwin->frame_x, wwin->frame_y,
1005                                       wwin->frame->core->width, wwin->frame->core->height, ix, iy, iw, ih, False);
1006                 }
1007 #endif
1008         }
1009
1010         wwin->flags.skip_next_animation = 0;
1011
1012         if (!wPreferences.disable_miniwindows
1013 #ifdef NETWM_HINTS
1014             && !wwin->flags.net_handle_icon
1015 #endif
1016             ) {
1017
1018                 if (wwin->screen_ptr->current_workspace == wwin->frame->workspace ||
1019                     IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)
1020
1021                         XMapWindow(dpy, wwin->icon->core->window);
1022
1023                 AddToStackList(wwin->icon->core);
1024
1025                 wLowerFrame(wwin->icon->core);
1026         }
1027
1028         if (present) {
1029                 WWindow *owner = recursiveTransientFor(wwin->screen_ptr->focused_window);
1030
1031                 /*
1032                  * It doesn't seem to be working and causes button event hangup
1033                  * when deiconifying a transient window.
1034                  setupIconGrabs(wwin->icon);
1035                  */
1036                 if ((wwin->flags.focused || (owner && wwin->client_win == owner->client_win))
1037                     && wPreferences.focus_mode == WKF_CLICK) {
1038                         WWindow *tmp;
1039
1040                         tmp = wwin->prev;
1041                         while (tmp) {
1042                                 if (!WFLAGP(tmp, no_focusable)
1043                                     && !(tmp->flags.hidden || tmp->flags.miniaturized)
1044                                     && (wwin->frame->workspace == tmp->frame->workspace))
1045                                         break;
1046                                 tmp = tmp->prev;
1047                         }
1048                         wSetFocusTo(wwin->screen_ptr, tmp);
1049                 } else if (wPreferences.focus_mode != WKF_CLICK) {
1050                         wSetFocusTo(wwin->screen_ptr, NULL);
1051                 }
1052 #ifdef ANIMATIONS
1053                 if (!wwin->screen_ptr->flags.startup) {
1054                         /* Catch up with events not processed while animation was running */
1055                         Window clientwin = wwin->client_win;
1056
1057                         ProcessPendingEvents();
1058
1059                         /* the window can disappear while ProcessPendingEvents() runs */
1060                         if (!wWindowFor(clientwin)) {
1061                                 return;
1062                         }
1063                 }
1064 #endif
1065         }
1066
1067         /* maybe we want to do this regardless of net_handle_icon
1068          * it seems to me we might break behaviour this way.
1069          */
1070         if (wwin->flags.selected && !wPreferences.disable_miniwindows
1071 #ifdef NETWM_HINTS
1072             && !wwin->flags.net_handle_icon
1073 #endif
1074             )
1075                 wIconSelect(wwin->icon);
1076
1077         WMPostNotificationName(WMNChangedState, wwin, "iconify");
1078 }
1079
1080 void wDeiconifyWindow(WWindow * wwin)
1081 {
1082 #ifdef NETWM_HINTS
1083         /* we're hiding for show_desktop */
1084         int netwm_hidden = wwin->flags.net_show_desktop &&
1085             wwin->frame->workspace != wwin->screen_ptr->current_workspace;
1086 #else
1087         int netwm_hidden = False;
1088 #endif
1089
1090         if (!netwm_hidden)
1091                 wWindowChangeWorkspace(wwin, wwin->screen_ptr->current_workspace);
1092
1093         if (!wwin->flags.miniaturized)
1094                 return;
1095
1096         if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
1097                 WWindow *owner = recursiveTransientFor(wwin);
1098
1099                 if (owner && owner->flags.miniaturized) {
1100                         wDeiconifyWindow(owner);
1101                         wSetFocusTo(wwin->screen_ptr, wwin);
1102                         wRaiseFrame(wwin->frame->core);
1103                         return;
1104                 }
1105         }
1106
1107         wwin->flags.miniaturized = 0;
1108
1109         if (!netwm_hidden && !wwin->flags.shaded) {
1110                 wwin->flags.mapped = 1;
1111         }
1112
1113         if (!netwm_hidden || wPreferences.sticky_icons) {
1114                 /* maybe we want to do this regardless of net_handle_icon
1115                  * it seems to me we might break behaviour this way.
1116                  */
1117                 if (!wPreferences.disable_miniwindows
1118 #ifdef NETWM_HINTS
1119                     && !wwin->flags.net_handle_icon
1120 #endif
1121                     && wwin->icon != NULL) {
1122                         if (wwin->icon->selected)
1123                                 wIconSelect(wwin->icon);
1124
1125                         XUnmapWindow(dpy, wwin->icon->core->window);
1126                 }
1127         }
1128
1129         if (!netwm_hidden)
1130                 wSoundPlay(WSOUND_DEICONIFY);
1131
1132         /* if the window is in another workspace, do it silently */
1133         if (!netwm_hidden) {
1134 #ifdef ANIMATIONS
1135                 if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations
1136                     && !wwin->flags.skip_next_animation && wwin->icon != NULL) {
1137                         int ix, iy, iw, ih;
1138
1139                         if (!wPreferences.disable_miniwindows
1140 #ifdef NETWM_HINTS
1141                             && !wwin->flags.net_handle_icon
1142 #endif
1143                             ) {
1144                                 ix = wwin->icon_x;
1145                                 iy = wwin->icon_y;
1146                                 iw = wwin->icon->core->width;
1147                                 ih = wwin->icon->core->height;
1148                         } else {
1149 #ifdef NETWM_HINTS
1150                                 if (wwin->flags.net_handle_icon) {
1151                                         ix = wwin->icon_x;
1152                                         iy = wwin->icon_y;
1153                                         iw = wwin->icon_w;
1154                                         ih = wwin->icon_h;
1155                                 } else
1156 #endif
1157                                 {
1158                                         ix = 0;
1159                                         iy = 0;
1160                                         iw = wwin->screen_ptr->scr_width;
1161                                         ih = wwin->screen_ptr->scr_height;
1162                                 }
1163                         }
1164                         animateResize(wwin->screen_ptr, ix, iy, iw, ih,
1165                                       wwin->frame_x, wwin->frame_y,
1166                                       wwin->frame->core->width, wwin->frame->core->height, False);
1167                 }
1168 #endif                          /* ANIMATIONS */
1169                 wwin->flags.skip_next_animation = 0;
1170                 XGrabServer(dpy);
1171                 if (!wwin->flags.shaded) {
1172                         XMapWindow(dpy, wwin->client_win);
1173                 }
1174                 XMapWindow(dpy, wwin->frame->core->window);
1175                 wRaiseFrame(wwin->frame->core);
1176                 if (!wwin->flags.shaded) {
1177                         wClientSetState(wwin, NormalState, None);
1178                 }
1179                 mapTransientsFor(wwin);
1180         }
1181
1182         if (!wPreferences.disable_miniwindows && wwin->icon != NULL
1183 #ifdef NETWM_HINTS
1184             && !wwin->flags.net_handle_icon
1185 #endif
1186             ) {
1187                 RemoveFromStackList(wwin->icon->core);
1188                 /*    removeIconGrabs(wwin->icon); */
1189                 wIconDestroy(wwin->icon);
1190                 wwin->icon = NULL;
1191         }
1192
1193         if (!netwm_hidden) {
1194                 XUngrabServer(dpy);
1195
1196                 wSetFocusTo(wwin->screen_ptr, wwin);
1197
1198 #ifdef ANIMATIONS
1199                 if (!wwin->screen_ptr->flags.startup) {
1200                         /* Catch up with events not processed while animation was running */
1201                         Window clientwin = wwin->client_win;
1202
1203                         ProcessPendingEvents();
1204
1205                         /* the window can disappear while ProcessPendingEvents() runs */
1206                         if (!wWindowFor(clientwin)) {
1207                                 return;
1208                         }
1209                 }
1210 #endif
1211         }
1212
1213         if (wPreferences.auto_arrange_icons) {
1214                 wArrangeIcons(wwin->screen_ptr, True);
1215         }
1216
1217         WMPostNotificationName(WMNChangedState, wwin, "iconify");
1218
1219         /* In case we were shaded and iconified, also unshade */
1220         if (!netwm_hidden)
1221                 wUnshadeWindow(wwin);
1222 }
1223
1224 static void hideWindow(WIcon * icon, int icon_x, int icon_y, WWindow * wwin, int animate)
1225 {
1226         if (wwin->flags.miniaturized) {
1227                 if (wwin->icon) {
1228                         XUnmapWindow(dpy, wwin->icon->core->window);
1229                         wwin->icon->mapped = 0;
1230                 }
1231                 wwin->flags.hidden = 1;
1232
1233                 WMPostNotificationName(WMNChangedState, wwin, "hide");
1234                 return;
1235         }
1236
1237         if (wwin->flags.inspector_open) {
1238                 wHideInspectorForWindow(wwin);
1239         }
1240
1241         wwin->flags.hidden = 1;
1242         wWindowUnmap(wwin);
1243
1244         wClientSetState(wwin, IconicState, icon->icon_win);
1245         flushExpose();
1246         wSoundPlay(WSOUND_HIDE);
1247 #ifdef ANIMATIONS
1248         if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations &&
1249             !wwin->flags.skip_next_animation && animate) {
1250                 animateResize(wwin->screen_ptr, wwin->frame_x, wwin->frame_y,
1251                               wwin->frame->core->width, wwin->frame->core->height,
1252                               icon_x, icon_y, icon->core->width, icon->core->height, True);
1253         }
1254 #endif
1255         wwin->flags.skip_next_animation = 0;
1256
1257         WMPostNotificationName(WMNChangedState, wwin, "hide");
1258 }
1259
1260 void wHideOtherApplications(WWindow * awin)
1261 {
1262         WWindow *wwin;
1263         WApplication *tapp;
1264
1265         if (!awin)
1266                 return;
1267         wwin = awin->screen_ptr->focused_window;
1268
1269         while (wwin) {
1270                 if (wwin != awin
1271                     && wwin->frame->workspace == awin->screen_ptr->current_workspace
1272                     && !(wwin->flags.miniaturized || wwin->flags.hidden)
1273                     && !wwin->flags.internal_window
1274                     && wGetWindowOfInspectorForWindow(wwin) != awin && !WFLAGP(wwin, no_hide_others)) {
1275
1276                         if (wwin->main_window == None || WFLAGP(wwin, no_appicon)) {
1277                                 if (!WFLAGP(wwin, no_miniaturizable)) {
1278                                         wwin->flags.skip_next_animation = 1;
1279                                         wIconifyWindow(wwin);
1280                                 }
1281                         } else if (wwin->main_window != None && awin->main_window != wwin->main_window) {
1282                                 tapp = wApplicationOf(wwin->main_window);
1283                                 if (tapp) {
1284                                         tapp->flags.skip_next_animation = 1;
1285                                         wHideApplication(tapp);
1286                                 } else {
1287                                         if (!WFLAGP(wwin, no_miniaturizable)) {
1288                                                 wwin->flags.skip_next_animation = 1;
1289                                                 wIconifyWindow(wwin);
1290                                         }
1291                                 }
1292                         }
1293                 }
1294                 wwin = wwin->prev;
1295         }
1296         /*
1297            wSetFocusTo(awin->screen_ptr, awin);
1298          */
1299 }
1300
1301 void wHideApplication(WApplication * wapp)
1302 {
1303         WScreen *scr;
1304         WWindow *wlist;
1305         int hadfocus;
1306         int animate;
1307
1308         if (!wapp) {
1309                 wwarning("trying to hide a non grouped window");
1310                 return;
1311         }
1312         if (!wapp->main_window_desc) {
1313                 wwarning("group leader not found for window group");
1314                 return;
1315         }
1316         scr = wapp->main_window_desc->screen_ptr;
1317         hadfocus = 0;
1318         wlist = scr->focused_window;
1319         if (!wlist)
1320                 return;
1321
1322         if (wlist->main_window == wapp->main_window)
1323                 wapp->last_focused = wlist;
1324         else
1325                 wapp->last_focused = NULL;
1326
1327         animate = !wapp->flags.skip_next_animation;
1328
1329         while (wlist) {
1330                 if (wlist->main_window == wapp->main_window) {
1331                         if (wlist->flags.focused) {
1332                                 hadfocus = 1;
1333                         }
1334                         if (wapp->app_icon) {
1335                                 hideWindow(wapp->app_icon->icon, wapp->app_icon->x_pos,
1336                                            wapp->app_icon->y_pos, wlist, animate);
1337                                 animate = False;
1338                         }
1339                 }
1340                 wlist = wlist->prev;
1341         }
1342
1343         wapp->flags.skip_next_animation = 0;
1344
1345         if (hadfocus) {
1346                 if (wPreferences.focus_mode == WKF_CLICK) {
1347                         wlist = scr->focused_window;
1348                         while (wlist) {
1349                                 if (!WFLAGP(wlist, no_focusable) && !wlist->flags.hidden
1350                                     && (wlist->flags.mapped || wlist->flags.shaded))
1351                                         break;
1352                                 wlist = wlist->prev;
1353                         }
1354                         wSetFocusTo(scr, wlist);
1355                 } else {
1356                         wSetFocusTo(scr, NULL);
1357                 }
1358         }
1359
1360         wapp->flags.hidden = 1;
1361
1362         if (wPreferences.auto_arrange_icons) {
1363                 wArrangeIcons(scr, True);
1364         }
1365 #ifdef HIDDENDOT
1366         if (wapp->app_icon)
1367                 wAppIconPaint(wapp->app_icon);
1368 #endif
1369 }
1370
1371 static void unhideWindow(WIcon * icon, int icon_x, int icon_y, WWindow * wwin, int animate, int bringToCurrentWS)
1372 {
1373         if (bringToCurrentWS)
1374                 wWindowChangeWorkspace(wwin, wwin->screen_ptr->current_workspace);
1375
1376         wwin->flags.hidden = 0;
1377
1378         wSoundPlay(WSOUND_UNHIDE);
1379 #ifdef ANIMATIONS
1380         if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations && animate) {
1381                 animateResize(wwin->screen_ptr, icon_x, icon_y,
1382                               icon->core->width, icon->core->height,
1383                               wwin->frame_x, wwin->frame_y,
1384                               wwin->frame->core->width, wwin->frame->core->height, True);
1385         }
1386 #endif
1387         wwin->flags.skip_next_animation = 0;
1388         if (wwin->screen_ptr->current_workspace == wwin->frame->workspace) {
1389                 XMapWindow(dpy, wwin->client_win);
1390                 XMapWindow(dpy, wwin->frame->core->window);
1391                 wClientSetState(wwin, NormalState, None);
1392                 wwin->flags.mapped = 1;
1393                 wRaiseFrame(wwin->frame->core);
1394         }
1395         if (wwin->flags.inspector_open) {
1396                 wUnhideInspectorForWindow(wwin);
1397         }
1398
1399         WMPostNotificationName(WMNChangedState, wwin, "hide");
1400 }
1401
1402 void wUnhideApplication(WApplication * wapp, Bool miniwindows, Bool bringToCurrentWS)
1403 {
1404         WScreen *scr;
1405         WWindow *wlist, *next;
1406         WWindow *focused = NULL;
1407         int animate;
1408
1409         if (!wapp)
1410                 return;
1411
1412         scr = wapp->main_window_desc->screen_ptr;
1413         wlist = scr->focused_window;
1414         if (!wlist)
1415                 return;
1416
1417         /* goto beginning of list */
1418         while (wlist->prev)
1419                 wlist = wlist->prev;
1420
1421         animate = !wapp->flags.skip_next_animation;
1422
1423         while (wlist) {
1424                 next = wlist->next;
1425
1426                 if (wlist->main_window == wapp->main_window) {
1427                         if (wlist->flags.focused)
1428                                 focused = wlist;
1429                         else if (!focused || !focused->flags.focused)
1430                                 focused = wlist;
1431
1432                         if (wlist->flags.miniaturized) {
1433                                 if ((bringToCurrentWS || wPreferences.sticky_icons ||
1434                                      wlist->frame->workspace == scr->current_workspace) && wlist->icon) {
1435                                         if (!wlist->icon->mapped) {
1436                                                 int x, y;
1437
1438                                                 PlaceIcon(scr, &x, &y, wGetHeadForWindow(wlist));
1439                                                 if (wlist->icon_x != x || wlist->icon_y != y) {
1440                                                         XMoveWindow(dpy, wlist->icon->core->window, x, y);
1441                                                 }
1442                                                 wlist->icon_x = x;
1443                                                 wlist->icon_y = y;
1444                                                 XMapWindow(dpy, wlist->icon->core->window);
1445                                                 wlist->icon->mapped = 1;
1446                                         }
1447                                         wRaiseFrame(wlist->icon->core);
1448                                 }
1449                                 if (bringToCurrentWS)
1450                                         wWindowChangeWorkspace(wlist, scr->current_workspace);
1451                                 wlist->flags.hidden = 0;
1452                                 if (miniwindows && wlist->frame->workspace == scr->current_workspace) {
1453                                         wDeiconifyWindow(wlist);
1454                                 }
1455                                 WMPostNotificationName(WMNChangedState, wlist, "hide");
1456                         } else if (wlist->flags.shaded) {
1457                                 if (bringToCurrentWS)
1458                                         wWindowChangeWorkspace(wlist, scr->current_workspace);
1459                                 wlist->flags.hidden = 0;
1460                                 if (wlist->frame->workspace == scr->current_workspace) {
1461                                         XMapWindow(dpy, wlist->frame->core->window);
1462                                         if (miniwindows) {
1463                                                 wUnshadeWindow(wlist);
1464                                                 wRaiseFrame(wlist->frame->core);
1465                                         }
1466                                 }
1467                                 WMPostNotificationName(WMNChangedState, wlist, "hide");
1468                         } else if (wlist->flags.hidden) {
1469                                 unhideWindow(wapp->app_icon->icon, wapp->app_icon->x_pos,
1470                                              wapp->app_icon->y_pos, wlist, animate, bringToCurrentWS);
1471                                 animate = False;
1472                         } else {
1473                                 if (bringToCurrentWS && wlist->frame->workspace != scr->current_workspace) {
1474                                         wWindowChangeWorkspace(wlist, scr->current_workspace);
1475                                 }
1476                                 wRaiseFrame(wlist->frame->core);
1477                         }
1478                 }
1479                 wlist = next;
1480         }
1481
1482         wapp->flags.skip_next_animation = 0;
1483         wapp->flags.hidden = 0;
1484
1485         if (wapp->last_focused && wapp->last_focused->flags.mapped) {
1486                 wRaiseFrame(wapp->last_focused->frame->core);
1487                 wSetFocusTo(scr, wapp->last_focused);
1488         } else if (focused) {
1489                 wSetFocusTo(scr, focused);
1490         }
1491         wapp->last_focused = NULL;
1492         if (wPreferences.auto_arrange_icons) {
1493                 wArrangeIcons(scr, True);
1494         }
1495 #ifdef HIDDENDOT
1496         wAppIconPaint(wapp->app_icon);
1497 #endif
1498 }
1499
1500 void wShowAllWindows(WScreen * scr)
1501 {
1502         WWindow *wwin, *old_foc;
1503         WApplication *wapp;
1504
1505         old_foc = wwin = scr->focused_window;
1506         while (wwin) {
1507                 if (!wwin->flags.internal_window &&
1508                     (scr->current_workspace == wwin->frame->workspace || IS_OMNIPRESENT(wwin))) {
1509                         if (wwin->flags.miniaturized) {
1510                                 wwin->flags.skip_next_animation = 1;
1511                                 wDeiconifyWindow(wwin);
1512                         } else if (wwin->flags.hidden) {
1513                                 wapp = wApplicationOf(wwin->main_window);
1514                                 if (wapp) {
1515                                         wUnhideApplication(wapp, False, False);
1516                                 } else {
1517                                         wwin->flags.skip_next_animation = 1;
1518                                         wDeiconifyWindow(wwin);
1519                                 }
1520                         }
1521                 }
1522                 wwin = wwin->prev;
1523         }
1524         wSetFocusTo(scr, old_foc);
1525         /*wRaiseFrame(old_foc->frame->core); */
1526 }
1527
1528 void wRefreshDesktop(WScreen * scr)
1529 {
1530         Window win;
1531         XSetWindowAttributes attr;
1532
1533         attr.backing_store = NotUseful;
1534         attr.save_under = False;
1535         win = XCreateWindow(dpy, scr->root_win, 0, 0, scr->scr_width,
1536                             scr->scr_height, 0, CopyFromParent, CopyFromParent,
1537                             (Visual *) CopyFromParent, CWBackingStore | CWSaveUnder, &attr);
1538         XMapRaised(dpy, win);
1539         XDestroyWindow(dpy, win);
1540         XFlush(dpy);
1541 }
1542
1543 void wArrangeIcons(WScreen * scr, Bool arrangeAll)
1544 {
1545         WWindow *wwin;
1546         WAppIcon *aicon;
1547
1548         int head;
1549         const int heads = wXineramaHeads(scr);
1550
1551         struct HeadVars {
1552                 int pf;         /* primary axis */
1553                 int sf;         /* secondary axis */
1554                 int fullW;
1555                 int fullH;
1556                 int pi, si;
1557                 int sx1, sx2, sy1, sy2; /* screen boundary */
1558                 int sw, sh;
1559                 int xo, yo;
1560                 int xs, ys;
1561         } *vars;
1562
1563         int isize = wPreferences.icon_size;
1564
1565         vars = (struct HeadVars *)wmalloc(sizeof(struct HeadVars) * heads);
1566
1567         for (head = 0; head < heads; ++head) {
1568 #if 0
1569                 WMRect rect = wGetRectForHead(scr, head);
1570 #else
1571                 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
1572                 WMRect rect = wmkrect(area.x1, area.y1, area.x2 - area.x1, area.y2 - area.y1);
1573 #endif
1574
1575                 vars[head].pi = vars[head].si = 0;
1576                 vars[head].sx1 = rect.pos.x;
1577                 vars[head].sy1 = rect.pos.y;
1578                 vars[head].sw = rect.size.width;
1579                 vars[head].sh = rect.size.height;
1580                 vars[head].sx2 = vars[head].sx1 + vars[head].sw;
1581                 vars[head].sy2 = vars[head].sy1 + vars[head].sh;
1582
1583 #if 0
1584                 if (scr->dock) {
1585                         if (scr->dock->on_right_side)
1586                                 vars[head].sx2 -= isize + DOCK_EXTRA_SPACE;
1587                         else
1588                                 vars[head].sx1 += isize + DOCK_EXTRA_SPACE;
1589                 }
1590 #endif
1591
1592                 vars[head].sw = isize * (vars[head].sw / isize);
1593                 vars[head].sh = isize * (vars[head].sh / isize);
1594                 vars[head].fullW = (vars[head].sx2 - vars[head].sx1) / isize;
1595                 vars[head].fullH = (vars[head].sy2 - vars[head].sy1) / isize;
1596
1597                 /* icon yard boundaries */
1598                 if (wPreferences.icon_yard & IY_VERT) {
1599                         vars[head].pf = vars[head].fullH;
1600                         vars[head].sf = vars[head].fullW;
1601                 } else {
1602                         vars[head].pf = vars[head].fullW;
1603                         vars[head].sf = vars[head].fullH;
1604                 }
1605                 if (wPreferences.icon_yard & IY_RIGHT) {
1606                         vars[head].xo = vars[head].sx2 - isize;
1607                         vars[head].xs = -1;
1608                 } else {
1609                         vars[head].xo = vars[head].sx1;
1610                         vars[head].xs = 1;
1611                 }
1612                 if (wPreferences.icon_yard & IY_TOP) {
1613                         vars[head].yo = vars[head].sy1;
1614                         vars[head].ys = 1;
1615                 } else {
1616                         vars[head].yo = vars[head].sy2 - isize;
1617                         vars[head].ys = -1;
1618                 }
1619         }
1620
1621 #define X ((wPreferences.icon_yard & IY_VERT) \
1622     ? vars[head].xo + vars[head].xs*(vars[head].si*isize) \
1623     : vars[head].xo + vars[head].xs*(vars[head].pi*isize))
1624
1625 #define Y ((wPreferences.icon_yard & IY_VERT) \
1626     ? vars[head].yo + vars[head].ys*(vars[head].pi*isize) \
1627     : vars[head].yo + vars[head].ys*(vars[head].si*isize))
1628
1629         /* arrange application icons */
1630         aicon = scr->app_icon_list;
1631         /* reverse them to avoid unnecessarily sliding of icons */
1632         while (aicon && aicon->next)
1633                 aicon = aicon->next;
1634
1635         while (aicon) {
1636                 if (!aicon->docked) {
1637                         /* CHECK: can icon be NULL here ? */
1638                         /* The intention here is to place the AppIcon on the head that
1639                          * contains most of the applications _main_ window. */
1640                         head = wGetHeadForWindow(aicon->icon->owner);
1641
1642                         if (aicon->x_pos != X || aicon->y_pos != Y) {
1643 #ifdef ANIMATIONS
1644                                 if (!wPreferences.no_animations) {
1645                                         SlideWindow(aicon->icon->core->window, aicon->x_pos, aicon->y_pos, X, Y);
1646                                 }
1647 #endif                          /* ANIMATIONS */
1648                         }
1649                         wAppIconMove(aicon, X, Y);
1650                         vars[head].pi++;
1651                         if (vars[head].pi >= vars[head].pf) {
1652                                 vars[head].pi = 0;
1653                                 vars[head].si++;
1654                         }
1655                 }
1656                 aicon = aicon->prev;
1657         }
1658
1659         /* arrange miniwindows */
1660         wwin = scr->focused_window;
1661         /* reverse them to avoid unnecessarily shuffling */
1662         while (wwin && wwin->prev)
1663                 wwin = wwin->prev;
1664
1665         while (wwin) {
1666                 if (wwin->icon && wwin->flags.miniaturized && !wwin->flags.hidden &&
1667                     (wwin->frame->workspace == scr->current_workspace ||
1668                      IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)) {
1669
1670                         head = wGetHeadForWindow(wwin);
1671
1672                         if (arrangeAll || !wwin->flags.icon_moved) {
1673                                 if (wwin->icon_x != X || wwin->icon_y != Y) {
1674 #ifdef ANIMATIONS
1675                                         if (wPreferences.no_animations) {
1676                                                 XMoveWindow(dpy, wwin->icon->core->window, X, Y);
1677                                         } else {
1678                                                 SlideWindow(wwin->icon->core->window, wwin->icon_x,
1679                                                             wwin->icon_y, X, Y);
1680                                         }
1681 #else
1682                                         XMoveWindow(dpy, wwin->icon->core->window, X, Y);
1683 #endif                          /* ANIMATIONS */
1684                                 }
1685                                 wwin->icon_x = X;
1686                                 wwin->icon_y = Y;
1687
1688                                 vars[head].pi++;
1689                                 if (vars[head].pi >= vars[head].pf) {
1690                                         vars[head].pi = 0;
1691                                         vars[head].si++;
1692                                 }
1693                         }
1694                 }
1695                 if (arrangeAll) {
1696                         wwin->flags.icon_moved = 0;
1697                 }
1698                 /* we reversed the order, so we use next */
1699                 wwin = wwin->next;
1700         }
1701
1702         wfree(vars);
1703 }
1704
1705 #if 0
1706 void wArrangeIcons(WScreen * scr, Bool arrangeAll)
1707 {
1708         WWindow *wwin;
1709         WAppIcon *aicon;
1710         int pf;                 /* primary axis */
1711         int sf;                 /* secondary axis */
1712         int fullW;
1713         int fullH;
1714         int pi, si;
1715         int sx1, sx2, sy1, sy2; /* screen boundary */
1716         int sw, sh;
1717         int xo, yo;
1718         int xs, ys;
1719         int isize = wPreferences.icon_size;
1720
1721         /*
1722          * Find out screen boundaries.
1723          */
1724
1725         /*
1726          * Allows each head to have miniwindows
1727          */
1728         WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1729
1730         sx1 = rect.pos.x;
1731         sy1 = rect.pos.y;
1732         sw = rect.size.width;
1733         sh = rect.size.height;
1734         sx2 = sx1 + sw;
1735         sy2 = sy1 + sh;
1736         if (scr->dock) {
1737                 if (scr->dock->on_right_side)
1738                         sx2 -= isize + DOCK_EXTRA_SPACE;
1739                 else
1740                         sx1 += isize + DOCK_EXTRA_SPACE;
1741         }
1742 #if 0
1743         sw = isize * (scr->scr_width / isize);
1744         sh = isize * (scr->scr_height / isize);
1745 #else
1746         sw = isize * (sw / isize);
1747         sh = isize * (sh / isize);
1748 #endif
1749         fullW = (sx2 - sx1) / isize;
1750         fullH = (sy2 - sy1) / isize;
1751
1752         /* icon yard boundaries */
1753         if (wPreferences.icon_yard & IY_VERT) {
1754                 pf = fullH;
1755                 sf = fullW;
1756         } else {
1757                 pf = fullW;
1758                 sf = fullH;
1759         }
1760         if (wPreferences.icon_yard & IY_RIGHT) {
1761                 xo = sx2 - isize;
1762                 xs = -1;
1763         } else {
1764                 xo = sx1;
1765                 xs = 1;
1766         }
1767         if (wPreferences.icon_yard & IY_TOP) {
1768                 yo = sy1;
1769                 ys = 1;
1770         } else {
1771                 yo = sy2 - isize;
1772                 ys = -1;
1773         }
1774
1775         /* arrange icons putting the most recently focused window
1776          * as the last icon */
1777 #define X ((wPreferences.icon_yard & IY_VERT) ? xo + xs*(si*isize)\
1778     : xo + xs*(pi*isize))
1779 #define Y ((wPreferences.icon_yard & IY_VERT) ? yo + ys*(pi*isize)\
1780     : yo + ys*(si*isize))
1781
1782         /* arrange application icons */
1783         aicon = scr->app_icon_list;
1784         /* reverse them to avoid unnecessarily sliding of icons */
1785         while (aicon && aicon->next)
1786                 aicon = aicon->next;
1787
1788         pi = 0;
1789         si = 0;
1790         while (aicon) {
1791                 if (!aicon->docked) {
1792                         if (aicon->x_pos != X || aicon->y_pos != Y) {
1793 #ifdef ANIMATIONS
1794                                 if (!wPreferences.no_animations) {
1795                                         SlideWindow(aicon->icon->core->window, aicon->x_pos, aicon->y_pos, X, Y);
1796                                 }
1797 #endif                          /* ANIMATIONS */
1798                         }
1799                         wAppIconMove(aicon, X, Y);
1800                         pi++;
1801                 }
1802                 /* we reversed the order so we use prev */
1803                 aicon = aicon->prev;
1804                 if (pi >= pf) {
1805                         pi = 0;
1806                         si++;
1807                 }
1808         }
1809
1810         /* arrange miniwindows */
1811
1812         wwin = scr->focused_window;
1813         /* reverse them to avoid unnecessarily shuffling */
1814         while (wwin && wwin->prev)
1815                 wwin = wwin->prev;
1816
1817         while (wwin) {
1818                 if (wwin->icon && wwin->flags.miniaturized && !wwin->flags.hidden &&
1819                     (wwin->frame->workspace == scr->current_workspace ||
1820                      IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)) {
1821
1822                         if (arrangeAll || !wwin->flags.icon_moved) {
1823                                 if (wwin->icon_x != X || wwin->icon_y != Y) {
1824 #ifdef ANIMATIONS
1825                                         if (wPreferences.no_animations) {
1826                                                 XMoveWindow(dpy, wwin->icon->core->window, X, Y);
1827                                         } else {
1828                                                 SlideWindow(wwin->icon->core->window, wwin->icon_x,
1829                                                             wwin->icon_y, X, Y);
1830                                         }
1831 #else
1832                                         XMoveWindow(dpy, wwin->icon->core->window, X, Y);
1833 #endif                          /* ANIMATIONS */
1834                                 }
1835                                 wwin->icon_x = X;
1836                                 wwin->icon_y = Y;
1837                                 pi++;
1838                         }
1839                 }
1840                 if (arrangeAll) {
1841                         wwin->flags.icon_moved = 0;
1842                 }
1843                 /* we reversed the order, so we use next */
1844                 wwin = wwin->next;
1845                 if (pi >= pf) {
1846                         pi = 0;
1847                         si++;
1848                 }
1849         }
1850 }
1851 #endif
1852
1853 void wSelectWindow(WWindow * wwin, Bool flag)
1854 {
1855         WScreen *scr = wwin->screen_ptr;
1856
1857         if (flag) {
1858                 wwin->flags.selected = 1;
1859                 XSetWindowBorder(dpy, wwin->frame->core->window, scr->white_pixel);
1860
1861                 if (!HAS_BORDER(wwin)) {
1862                         XSetWindowBorderWidth(dpy, wwin->frame->core->window, FRAME_BORDER_WIDTH);
1863                 }
1864
1865                 if (!scr->selected_windows)
1866                         scr->selected_windows = WMCreateArray(4);
1867                 WMAddToArray(scr->selected_windows, wwin);
1868         } else {
1869                 wwin->flags.selected = 0;
1870                 XSetWindowBorder(dpy, wwin->frame->core->window, scr->frame_border_pixel);
1871
1872                 if (!HAS_BORDER(wwin)) {
1873                         XSetWindowBorderWidth(dpy, wwin->frame->core->window, 0);
1874                 }
1875
1876                 if (scr->selected_windows) {
1877                         WMRemoveFromArray(scr->selected_windows, wwin);
1878                 }
1879         }
1880 }
1881
1882 void wMakeWindowVisible(WWindow * wwin)
1883 {
1884         if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
1885                 wWorkspaceChange(wwin->screen_ptr, wwin->frame->workspace);
1886
1887         if (wwin->flags.shaded) {
1888                 wUnshadeWindow(wwin);
1889         }
1890         if (wwin->flags.hidden) {
1891                 WApplication *app;
1892
1893                 app = wApplicationOf(wwin->main_window);
1894                 if (app) {
1895                         /* trick to get focus to this window */
1896                         app->last_focused = wwin;
1897                         wUnhideApplication(app, False, False);
1898                 }
1899         }
1900         if (wwin->flags.miniaturized) {
1901                 wDeiconifyWindow(wwin);
1902         } else {
1903                 if (!WFLAGP(wwin, no_focusable))
1904                         wSetFocusTo(wwin->screen_ptr, wwin);
1905                 wRaiseFrame(wwin->frame->core);
1906         }
1907 }