Change to the linux kernel coding style
[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);
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)) {
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 (shrink_h) {
430                 new_x = wwin->old_geometry.x;
431                 new_width = wwin->old_geometry.width;
432         } else {
433                 new_x = wwin->frame_x;
434                 new_width = wwin->frame->core->width;
435         }
436
437         if (directions & MAX_VERTICAL) {
438                 new_height = usableArea.y2 - usableArea.y1;
439                 if (HAS_BORDER(wwin))
440                         new_height -= FRAME_BORDER_WIDTH * 2;
441                 new_y = usableArea.y1;
442                 if (WFLAGP(wwin, full_maximize)) {
443                         new_y -= wwin->frame->top_width;
444                         new_height += wwin->frame->bottom_width - 1;
445                 }
446         } else if (shrink_v) {
447                 new_y = wwin->old_geometry.y;
448                 new_height = wwin->old_geometry.height;
449         } else {
450                 new_y = wwin->frame_y;
451                 new_height = wwin->frame->core->height;
452         }
453
454         if (!WFLAGP(wwin, full_maximize)) {
455                 new_height -= wwin->frame->top_width + wwin->frame->bottom_width;
456         }
457
458         wWindowConstrainSize(wwin, &new_width, &new_height);
459
460         wWindowCropSize(wwin, usableArea.x2 - usableArea.x1,
461                         usableArea.y2 - usableArea.y1, &new_width, &new_height);
462
463         wWindowConfigure(wwin, new_x, new_y, new_width, new_height);
464
465         WMPostNotificationName(WMNChangedState, wwin, "maximize");
466
467         wSoundPlay(WSOUND_MAXIMIZE);
468 }
469
470 void wUnmaximizeWindow(WWindow * wwin)
471 {
472         int x, y, w, h;
473
474         if (!wwin->flags.maximized)
475                 return;
476
477         if (wwin->flags.shaded) {
478                 wwin->flags.skip_next_animation = 1;
479                 wUnshadeWindow(wwin);
480         }
481         x = ((wwin->flags.maximized & MAX_HORIZONTAL) && wwin->old_geometry.x) ?
482             wwin->old_geometry.x : wwin->frame_x;
483         y = ((wwin->flags.maximized & MAX_VERTICAL) && wwin->old_geometry.y) ?
484             wwin->old_geometry.y : wwin->frame_y;
485         w = wwin->old_geometry.width ? wwin->old_geometry.width : wwin->client.width;
486         h = wwin->old_geometry.height ? wwin->old_geometry.height : wwin->client.height;
487
488         wwin->flags.maximized = 0;
489         wWindowConfigure(wwin, x, y, w, h);
490
491         WMPostNotificationName(WMNChangedState, wwin, "maximize");
492
493         wSoundPlay(WSOUND_UNMAXIMIZE);
494 }
495
496 void wFullscreenWindow(WWindow * wwin)
497 {
498         int head;
499         WMRect rect;
500
501         if (wwin->flags.fullscreen)
502                 return;
503
504         wwin->flags.fullscreen = True;
505
506         wWindowConfigureBorders(wwin);
507
508         ChangeStackingLevel(wwin->frame->core, WMFullscreenLevel);
509
510         wwin->bfs_geometry.x = wwin->frame_x;
511         wwin->bfs_geometry.y = wwin->frame_y;
512         wwin->bfs_geometry.width = wwin->frame->core->width;
513         wwin->bfs_geometry.height = wwin->frame->core->height;
514
515         head = wGetHeadForWindow(wwin);
516         rect = wGetRectForHead(wwin->screen_ptr, head);
517         wWindowConfigure(wwin, rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
518
519         WMPostNotificationName(WMNChangedState, wwin, "fullscreen");
520 }
521
522 void wUnfullscreenWindow(WWindow * wwin)
523 {
524         if (!wwin->flags.fullscreen)
525                 return;
526
527         wwin->flags.fullscreen = False;
528
529         if (wwin->frame->core->stacking->window_level == WMFullscreenLevel) {
530                 if (WFLAGP(wwin, sunken)) {
531                         ChangeStackingLevel(wwin->frame->core, WMSunkenLevel);
532                 } else if (WFLAGP(wwin, floating)) {
533                         ChangeStackingLevel(wwin->frame->core, WMFloatingLevel);
534                 } else {
535                         ChangeStackingLevel(wwin->frame->core, WMNormalLevel);
536                 }
537         }
538
539         wWindowConfigure(wwin, wwin->bfs_geometry.x, wwin->bfs_geometry.y,
540                          wwin->bfs_geometry.width, wwin->bfs_geometry.height);
541
542         wWindowConfigureBorders(wwin);
543         /*
544            // seems unnecessary, but also harmless (doesn't generate flicker) -Dan
545            wFrameWindowPaint(wwin->frame);
546          */
547
548         WMPostNotificationName(WMNChangedState, wwin, "fullscreen");
549 }
550
551 #ifdef ANIMATIONS
552 static void animateResizeFlip(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
553 {
554 #define FRAMES (MINIATURIZE_ANIMATION_FRAMES_F)
555         float cx, cy, cw, ch;
556         float xstep, ystep, wstep, hstep;
557         XPoint points[5];
558         float dx, dch, midy;
559         float angle, final_angle, delta;
560
561         xstep = (float)(fx - x) / steps;
562         ystep = (float)(fy - y) / steps;
563         wstep = (float)(fw - w) / steps;
564         hstep = (float)(fh - h) / steps;
565
566         cx = (float)x;
567         cy = (float)y;
568         cw = (float)w;
569         ch = (float)h;
570
571         final_angle = 2 * WM_PI * MINIATURIZE_ANIMATION_TWIST_F;
572         delta = (float)(final_angle / FRAMES);
573         for (angle = 0;; angle += delta) {
574                 if (angle > final_angle)
575                         angle = final_angle;
576
577                 dx = (cw / 10) - ((cw / 5) * sin(angle));
578                 dch = (ch / 2) * cos(angle);
579                 midy = cy + (ch / 2);
580
581                 points[0].x = cx + dx;
582                 points[0].y = midy - dch;
583                 points[1].x = cx + cw - dx;
584                 points[1].y = points[0].y;
585                 points[2].x = cx + cw + dx;
586                 points[2].y = midy + dch;
587                 points[3].x = cx - dx;
588                 points[3].y = points[2].y;
589                 points[4].x = points[0].x;
590                 points[4].y = points[0].y;
591
592                 XGrabServer(dpy);
593                 XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
594                 XFlush(dpy);
595 #if (MINIATURIZE_ANIMATION_DELAY_F > 0)
596                 wusleep(MINIATURIZE_ANIMATION_DELAY_F);
597 #else
598                 wusleep(10);
599 #endif
600
601                 XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
602                 XUngrabServer(dpy);
603                 cx += xstep;
604                 cy += ystep;
605                 cw += wstep;
606                 ch += hstep;
607                 if (angle >= final_angle)
608                         break;
609
610         }
611         XFlush(dpy);
612 }
613
614 #undef FRAMES
615
616 static void
617 animateResizeTwist(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
618 {
619 #define FRAMES (MINIATURIZE_ANIMATION_FRAMES_T)
620         float cx, cy, cw, ch;
621         float xstep, ystep, wstep, hstep;
622         XPoint points[5];
623         float angle, final_angle, a, d, delta;
624
625         x += w / 2;
626         y += h / 2;
627         fx += fw / 2;
628         fy += fh / 2;
629
630         xstep = (float)(fx - x) / steps;
631         ystep = (float)(fy - y) / steps;
632         wstep = (float)(fw - w) / steps;
633         hstep = (float)(fh - h) / steps;
634
635         cx = (float)x;
636         cy = (float)y;
637         cw = (float)w;
638         ch = (float)h;
639
640         final_angle = 2 * WM_PI * MINIATURIZE_ANIMATION_TWIST_T;
641         delta = (float)(final_angle / FRAMES);
642         for (angle = 0;; angle += delta) {
643                 if (angle > final_angle)
644                         angle = final_angle;
645
646                 a = atan(ch / cw);
647                 d = sqrt((cw / 2) * (cw / 2) + (ch / 2) * (ch / 2));
648
649                 points[0].x = cx + cos(angle - a) * d;
650                 points[0].y = cy + sin(angle - a) * d;
651                 points[1].x = cx + cos(angle + a) * d;
652                 points[1].y = cy + sin(angle + a) * d;
653                 points[2].x = cx + cos(angle - a + WM_PI) * d;
654                 points[2].y = cy + sin(angle - a + WM_PI) * d;
655                 points[3].x = cx + cos(angle + a + WM_PI) * d;
656                 points[3].y = cy + sin(angle + a + WM_PI) * d;
657                 points[4].x = cx + cos(angle - a) * d;
658                 points[4].y = cy + sin(angle - a) * d;
659                 XGrabServer(dpy);
660                 XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
661                 XFlush(dpy);
662 #if (MINIATURIZE_ANIMATION_DELAY_T > 0)
663                 wusleep(MINIATURIZE_ANIMATION_DELAY_T);
664 #else
665                 wusleep(10);
666 #endif
667
668                 XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
669                 XUngrabServer(dpy);
670                 cx += xstep;
671                 cy += ystep;
672                 cw += wstep;
673                 ch += hstep;
674                 if (angle >= final_angle)
675                         break;
676
677         }
678         XFlush(dpy);
679 }
680
681 #undef FRAMES
682
683 static void animateResizeZoom(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
684 {
685 #define FRAMES (MINIATURIZE_ANIMATION_FRAMES_Z)
686         float cx[FRAMES], cy[FRAMES], cw[FRAMES], ch[FRAMES];
687         float xstep, ystep, wstep, hstep;
688         int i, j;
689
690         xstep = (float)(fx - x) / steps;
691         ystep = (float)(fy - y) / steps;
692         wstep = (float)(fw - w) / steps;
693         hstep = (float)(fh - h) / steps;
694
695         for (j = 0; j < FRAMES; j++) {
696                 cx[j] = (float)x;
697                 cy[j] = (float)y;
698                 cw[j] = (float)w;
699                 ch[j] = (float)h;
700         }
701         XGrabServer(dpy);
702         for (i = 0; i < steps; i++) {
703                 for (j = 0; j < FRAMES; j++) {
704                         XDrawRectangle(dpy, scr->root_win, scr->frame_gc,
705                                        (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
706                 }
707                 XFlush(dpy);
708 #if (MINIATURIZE_ANIMATION_DELAY_Z > 0)
709                 wusleep(MINIATURIZE_ANIMATION_DELAY_Z);
710 #else
711                 wusleep(10);
712 #endif
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                         if (j < FRAMES - 1) {
717                                 cx[j] = cx[j + 1];
718                                 cy[j] = cy[j + 1];
719                                 cw[j] = cw[j + 1];
720                                 ch[j] = ch[j + 1];
721                         } else {
722                                 cx[j] += xstep;
723                                 cy[j] += ystep;
724                                 cw[j] += wstep;
725                                 ch[j] += hstep;
726                         }
727                 }
728         }
729
730         for (j = 0; j < FRAMES; j++) {
731                 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
732         }
733         XFlush(dpy);
734 #if (MINIATURIZE_ANIMATION_DELAY_Z > 0)
735         wusleep(MINIATURIZE_ANIMATION_DELAY_Z);
736 #else
737         wusleep(10);
738 #endif
739         for (j = 0; j < FRAMES; j++) {
740                 XDrawRectangle(dpy, scr->root_win, scr->frame_gc, (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
741         }
742
743         XUngrabServer(dpy);
744 }
745
746 #undef FRAMES
747
748 void animateResize(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int hiding)
749 {
750         int style = wPreferences.iconification_style;   /* Catch the value */
751         int steps, k;
752
753         if (style == WIS_NONE)
754                 return;
755
756         if (style == WIS_RANDOM) {
757                 style = rand() % 3;
758         }
759
760         k = (hiding ? 2 : 3);
761
762         switch (style) {
763         case WIS_TWIST:
764                 steps = (MINIATURIZE_ANIMATION_STEPS_T * k) / 3;
765                 if (steps > 0)
766                         animateResizeTwist(scr, x, y, w, h, fx, fy, fw, fh, steps);
767                 break;
768         case WIS_FLIP:
769                 steps = (MINIATURIZE_ANIMATION_STEPS_F * k) / 3;
770                 if (steps > 0)
771                         animateResizeFlip(scr, x, y, w, h, fx, fy, fw, fh, steps);
772                 break;
773         case WIS_ZOOM:
774         default:
775                 steps = (MINIATURIZE_ANIMATION_STEPS_Z * k) / 3;
776                 if (steps > 0)
777                         animateResizeZoom(scr, x, y, w, h, fx, fy, fw, fh, steps);
778                 break;
779         }
780 }
781 #endif                          /* ANIMATIONS */
782
783 static void flushExpose()
784 {
785         XEvent tmpev;
786
787         while (XCheckTypedEvent(dpy, Expose, &tmpev))
788                 WMHandleEvent(&tmpev);
789         XSync(dpy, 0);
790 }
791
792 static void unmapTransientsFor(WWindow * wwin)
793 {
794         WWindow *tmp;
795
796         tmp = wwin->screen_ptr->focused_window;
797         while (tmp) {
798                 /* unmap the transients for this transient */
799                 if (tmp != wwin && tmp->transient_for == wwin->client_win
800                     && (tmp->flags.mapped || wwin->screen_ptr->flags.startup || tmp->flags.shaded)) {
801                         unmapTransientsFor(tmp);
802                         tmp->flags.miniaturized = 1;
803                         if (!tmp->flags.shaded) {
804                                 wWindowUnmap(tmp);
805                         } else {
806                                 XUnmapWindow(dpy, tmp->frame->core->window);
807                         }
808                         /*
809                            if (!tmp->flags.shaded)
810                          */
811                         wClientSetState(tmp, IconicState, None);
812
813                         WMPostNotificationName(WMNChangedState, tmp, "iconify-transient");
814                 }
815                 tmp = tmp->prev;
816         }
817 }
818
819 static void mapTransientsFor(WWindow * wwin)
820 {
821         WWindow *tmp;
822
823         tmp = wwin->screen_ptr->focused_window;
824         while (tmp) {
825                 /* recursively map the transients for this transient */
826                 if (tmp != wwin && tmp->transient_for == wwin->client_win && /*!tmp->flags.mapped */ tmp->flags.miniaturized
827                     && tmp->icon == NULL) {
828                         mapTransientsFor(tmp);
829                         tmp->flags.miniaturized = 0;
830                         if (!tmp->flags.shaded) {
831                                 wWindowMap(tmp);
832                         } else {
833                                 XMapWindow(dpy, tmp->frame->core->window);
834                         }
835                         tmp->flags.semi_focused = 0;
836                         /*
837                            if (!tmp->flags.shaded)
838                          */
839                         wClientSetState(tmp, NormalState, None);
840
841                         WMPostNotificationName(WMNChangedState, tmp, "iconify-transient");
842                 }
843                 tmp = tmp->prev;
844         }
845 }
846
847 #if 0
848 static void setupIconGrabs(WIcon * icon)
849 {
850         /* setup passive grabs on the icon */
851         XGrabButton(dpy, Button1, AnyModifier, icon->core->window, True,
852                     ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
853         XGrabButton(dpy, Button2, AnyModifier, icon->core->window, True,
854                     ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
855         XGrabButton(dpy, Button3, AnyModifier, icon->core->window, True,
856                     ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
857         XSync(dpy, 0);
858 }
859 #endif
860
861 static WWindow *recursiveTransientFor(WWindow * wwin)
862 {
863         int i;
864
865         if (!wwin)
866                 return None;
867
868         /* hackish way to detect transient_for cycle */
869         i = wwin->screen_ptr->window_count + 1;
870
871         while (wwin && wwin->transient_for != None && i > 0) {
872                 wwin = wWindowFor(wwin->transient_for);
873                 i--;
874         }
875         if (i == 0 && wwin) {
876                 wwarning("%s has a severely broken WM_TRANSIENT_FOR hint.", wwin->frame->title);
877                 return NULL;
878         }
879
880         return wwin;
881 }
882
883 #if 0
884 static void removeIconGrabs(WIcon * icon)
885 {
886         /* remove passive grabs on the icon */
887         XUngrabButton(dpy, Button1, AnyModifier, icon->core->window);
888         XUngrabButton(dpy, Button2, AnyModifier, icon->core->window);
889         XUngrabButton(dpy, Button3, AnyModifier, icon->core->window);
890         XSync(dpy, 0);
891 }
892 #endif
893
894 void wIconifyWindow(WWindow * wwin)
895 {
896         XWindowAttributes attribs;
897         int present;
898
899         if (!XGetWindowAttributes(dpy, wwin->client_win, &attribs)) {
900                 /* the window doesn't exist anymore */
901                 return;
902         }
903
904         if (wwin->flags.miniaturized) {
905                 return;
906         }
907
908         if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
909                 WWindow *owner = wWindowFor(wwin->transient_for);
910
911                 if (owner && owner->flags.miniaturized)
912                         return;
913         }
914
915         present = wwin->frame->workspace == wwin->screen_ptr->current_workspace;
916
917         /* if the window is in another workspace, simplify process */
918         if (present) {
919                 /* icon creation may take a while */
920                 XGrabPointer(dpy, wwin->screen_ptr->root_win, False,
921                              ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
922                              GrabModeAsync, None, None, CurrentTime);
923         }
924
925         if (!wPreferences.disable_miniwindows
926 #ifdef NETWM_HINTS
927             && !wwin->flags.net_handle_icon
928 #endif
929             ) {
930                 if (!wwin->flags.icon_moved) {
931                         PlaceIcon(wwin->screen_ptr, &wwin->icon_x, &wwin->icon_y, wGetHeadForWindow(wwin));
932                 }
933                 wwin->icon = wIconCreate(wwin);
934
935                 wwin->icon->mapped = 1;
936         }
937
938         wwin->flags.miniaturized = 1;
939         wwin->flags.mapped = 0;
940
941         /* unmap transients */
942
943         unmapTransientsFor(wwin);
944
945         if (present) {
946                 wSoundPlay(WSOUND_ICONIFY);
947
948                 XUngrabPointer(dpy, CurrentTime);
949                 wWindowUnmap(wwin);
950                 /* let all Expose events arrive so that we can repaint
951                  * something before the animation starts (and the server is grabbed) */
952                 XSync(dpy, 0);
953
954                 if (wPreferences.disable_miniwindows
955 #ifdef NETWM_HINTS
956                     || wwin->flags.net_handle_icon
957 #endif
958                     )
959                         wClientSetState(wwin, IconicState, None);
960                 else
961                         wClientSetState(wwin, IconicState, wwin->icon->icon_win);
962
963                 flushExpose();
964 #ifdef ANIMATIONS
965                 if (!wwin->screen_ptr->flags.startup && !wwin->flags.skip_next_animation
966                     && !wPreferences.no_animations) {
967                         int ix, iy, iw, ih;
968
969                         if (!wPreferences.disable_miniwindows
970 #ifdef NETWM_HINTS
971                             && !wwin->flags.net_handle_icon
972 #endif
973                             ) {
974                                 ix = wwin->icon_x;
975                                 iy = wwin->icon_y;
976                                 iw = wwin->icon->core->width;
977                                 ih = wwin->icon->core->height;
978                         } else {
979 #ifdef NETWM_HINTS
980                                 if (wwin->flags.net_handle_icon) {
981                                         ix = wwin->icon_x;
982                                         iy = wwin->icon_y;
983                                         iw = wwin->icon_w;
984                                         ih = wwin->icon_h;
985                                 } else
986 #endif
987                                 {
988                                         ix = 0;
989                                         iy = 0;
990                                         iw = wwin->screen_ptr->scr_width;
991                                         ih = wwin->screen_ptr->scr_height;
992                                 }
993                         }
994                         animateResize(wwin->screen_ptr, wwin->frame_x, wwin->frame_y,
995                                       wwin->frame->core->width, wwin->frame->core->height, ix, iy, iw, ih, False);
996                 }
997 #endif
998         }
999
1000         wwin->flags.skip_next_animation = 0;
1001
1002         if (!wPreferences.disable_miniwindows
1003 #ifdef NETWM_HINTS
1004             && !wwin->flags.net_handle_icon
1005 #endif
1006             ) {
1007
1008                 if (wwin->screen_ptr->current_workspace == wwin->frame->workspace ||
1009                     IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)
1010
1011                         XMapWindow(dpy, wwin->icon->core->window);
1012
1013                 AddToStackList(wwin->icon->core);
1014
1015                 wLowerFrame(wwin->icon->core);
1016         }
1017
1018         if (present) {
1019                 WWindow *owner = recursiveTransientFor(wwin->screen_ptr->focused_window);
1020
1021                 /*
1022                  * It doesn't seem to be working and causes button event hangup
1023                  * when deiconifying a transient window.
1024                  setupIconGrabs(wwin->icon);
1025                  */
1026                 if ((wwin->flags.focused || (owner && wwin->client_win == owner->client_win))
1027                     && wPreferences.focus_mode == WKF_CLICK) {
1028                         WWindow *tmp;
1029
1030                         tmp = wwin->prev;
1031                         while (tmp) {
1032                                 if (!WFLAGP(tmp, no_focusable)
1033                                     && !(tmp->flags.hidden || tmp->flags.miniaturized)
1034                                     && (wwin->frame->workspace == tmp->frame->workspace))
1035                                         break;
1036                                 tmp = tmp->prev;
1037                         }
1038                         wSetFocusTo(wwin->screen_ptr, tmp);
1039                 } else if (wPreferences.focus_mode != WKF_CLICK) {
1040                         wSetFocusTo(wwin->screen_ptr, NULL);
1041                 }
1042 #ifdef ANIMATIONS
1043                 if (!wwin->screen_ptr->flags.startup) {
1044                         /* Catch up with events not processed while animation was running */
1045                         Window clientwin = wwin->client_win;
1046
1047                         ProcessPendingEvents();
1048
1049                         /* the window can disappear while ProcessPendingEvents() runs */
1050                         if (!wWindowFor(clientwin)) {
1051                                 return;
1052                         }
1053                 }
1054 #endif
1055         }
1056
1057         /* maybe we want to do this regardless of net_handle_icon
1058          * it seems to me we might break behaviour this way.
1059          */
1060         if (wwin->flags.selected && !wPreferences.disable_miniwindows
1061 #ifdef NETWM_HINTS
1062             && !wwin->flags.net_handle_icon
1063 #endif
1064             )
1065                 wIconSelect(wwin->icon);
1066
1067         WMPostNotificationName(WMNChangedState, wwin, "iconify");
1068 }
1069
1070 void wDeiconifyWindow(WWindow * wwin)
1071 {
1072 #ifdef NETWM_HINTS
1073         /* we're hiding for show_desktop */
1074         int netwm_hidden = wwin->flags.net_show_desktop &&
1075             wwin->frame->workspace != wwin->screen_ptr->current_workspace;
1076 #else
1077         int netwm_hidden = False;
1078 #endif
1079
1080         if (!netwm_hidden)
1081                 wWindowChangeWorkspace(wwin, wwin->screen_ptr->current_workspace);
1082
1083         if (!wwin->flags.miniaturized)
1084                 return;
1085
1086         if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
1087                 WWindow *owner = recursiveTransientFor(wwin);
1088
1089                 if (owner && owner->flags.miniaturized) {
1090                         wDeiconifyWindow(owner);
1091                         wSetFocusTo(wwin->screen_ptr, wwin);
1092                         wRaiseFrame(wwin->frame->core);
1093                         return;
1094                 }
1095         }
1096
1097         wwin->flags.miniaturized = 0;
1098
1099         if (!netwm_hidden && !wwin->flags.shaded) {
1100                 wwin->flags.mapped = 1;
1101         }
1102
1103         if (!netwm_hidden || wPreferences.sticky_icons) {
1104                 /* maybe we want to do this regardless of net_handle_icon
1105                  * it seems to me we might break behaviour this way.
1106                  */
1107                 if (!wPreferences.disable_miniwindows
1108 #ifdef NETWM_HINTS
1109                     && !wwin->flags.net_handle_icon
1110 #endif
1111                     && wwin->icon != NULL) {
1112                         if (wwin->icon->selected)
1113                                 wIconSelect(wwin->icon);
1114
1115                         XUnmapWindow(dpy, wwin->icon->core->window);
1116                 }
1117         }
1118
1119         if (!netwm_hidden)
1120                 wSoundPlay(WSOUND_DEICONIFY);
1121
1122         /* if the window is in another workspace, do it silently */
1123         if (!netwm_hidden) {
1124 #ifdef ANIMATIONS
1125                 if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations
1126                     && !wwin->flags.skip_next_animation && wwin->icon != NULL) {
1127                         int ix, iy, iw, ih;
1128
1129                         if (!wPreferences.disable_miniwindows
1130 #ifdef NETWM_HINTS
1131                             && !wwin->flags.net_handle_icon
1132 #endif
1133                             ) {
1134                                 ix = wwin->icon_x;
1135                                 iy = wwin->icon_y;
1136                                 iw = wwin->icon->core->width;
1137                                 ih = wwin->icon->core->height;
1138                         } else {
1139 #ifdef NETWM_HINTS
1140                                 if (wwin->flags.net_handle_icon) {
1141                                         ix = wwin->icon_x;
1142                                         iy = wwin->icon_y;
1143                                         iw = wwin->icon_w;
1144                                         ih = wwin->icon_h;
1145                                 } else
1146 #endif
1147                                 {
1148                                         ix = 0;
1149                                         iy = 0;
1150                                         iw = wwin->screen_ptr->scr_width;
1151                                         ih = wwin->screen_ptr->scr_height;
1152                                 }
1153                         }
1154                         animateResize(wwin->screen_ptr, ix, iy, iw, ih,
1155                                       wwin->frame_x, wwin->frame_y,
1156                                       wwin->frame->core->width, wwin->frame->core->height, False);
1157                 }
1158 #endif                          /* ANIMATIONS */
1159                 wwin->flags.skip_next_animation = 0;
1160                 XGrabServer(dpy);
1161                 if (!wwin->flags.shaded) {
1162                         XMapWindow(dpy, wwin->client_win);
1163                 }
1164                 XMapWindow(dpy, wwin->frame->core->window);
1165                 wRaiseFrame(wwin->frame->core);
1166                 if (!wwin->flags.shaded) {
1167                         wClientSetState(wwin, NormalState, None);
1168                 }
1169                 mapTransientsFor(wwin);
1170         }
1171
1172         if (!wPreferences.disable_miniwindows && wwin->icon != NULL
1173 #ifdef NETWM_HINTS
1174             && !wwin->flags.net_handle_icon
1175 #endif
1176             ) {
1177                 RemoveFromStackList(wwin->icon->core);
1178                 /*    removeIconGrabs(wwin->icon); */
1179                 wIconDestroy(wwin->icon);
1180                 wwin->icon = NULL;
1181         }
1182
1183         if (!netwm_hidden) {
1184                 XUngrabServer(dpy);
1185
1186                 wSetFocusTo(wwin->screen_ptr, wwin);
1187
1188 #ifdef ANIMATIONS
1189                 if (!wwin->screen_ptr->flags.startup) {
1190                         /* Catch up with events not processed while animation was running */
1191                         Window clientwin = wwin->client_win;
1192
1193                         ProcessPendingEvents();
1194
1195                         /* the window can disappear while ProcessPendingEvents() runs */
1196                         if (!wWindowFor(clientwin)) {
1197                                 return;
1198                         }
1199                 }
1200 #endif
1201         }
1202
1203         if (wPreferences.auto_arrange_icons) {
1204                 wArrangeIcons(wwin->screen_ptr, True);
1205         }
1206
1207         WMPostNotificationName(WMNChangedState, wwin, "iconify");
1208
1209         /* In case we were shaded and iconified, also unshade */
1210         if (!netwm_hidden)
1211                 wUnshadeWindow(wwin);
1212 }
1213
1214 static void hideWindow(WIcon * icon, int icon_x, int icon_y, WWindow * wwin, int animate)
1215 {
1216         if (wwin->flags.miniaturized) {
1217                 if (wwin->icon) {
1218                         XUnmapWindow(dpy, wwin->icon->core->window);
1219                         wwin->icon->mapped = 0;
1220                 }
1221                 wwin->flags.hidden = 1;
1222
1223                 WMPostNotificationName(WMNChangedState, wwin, "hide");
1224                 return;
1225         }
1226
1227         if (wwin->flags.inspector_open) {
1228                 wHideInspectorForWindow(wwin);
1229         }
1230
1231         wwin->flags.hidden = 1;
1232         wWindowUnmap(wwin);
1233
1234         wClientSetState(wwin, IconicState, icon->icon_win);
1235         flushExpose();
1236         wSoundPlay(WSOUND_HIDE);
1237 #ifdef ANIMATIONS
1238         if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations &&
1239             !wwin->flags.skip_next_animation && animate) {
1240                 animateResize(wwin->screen_ptr, wwin->frame_x, wwin->frame_y,
1241                               wwin->frame->core->width, wwin->frame->core->height,
1242                               icon_x, icon_y, icon->core->width, icon->core->height, True);
1243         }
1244 #endif
1245         wwin->flags.skip_next_animation = 0;
1246
1247         WMPostNotificationName(WMNChangedState, wwin, "hide");
1248 }
1249
1250 void wHideOtherApplications(WWindow * awin)
1251 {
1252         WWindow *wwin;
1253         WApplication *tapp;
1254
1255         if (!awin)
1256                 return;
1257         wwin = awin->screen_ptr->focused_window;
1258
1259         while (wwin) {
1260                 if (wwin != awin
1261                     && wwin->frame->workspace == awin->screen_ptr->current_workspace
1262                     && !(wwin->flags.miniaturized || wwin->flags.hidden)
1263                     && !wwin->flags.internal_window
1264                     && wGetWindowOfInspectorForWindow(wwin) != awin && !WFLAGP(wwin, no_hide_others)) {
1265
1266                         if (wwin->main_window == None || WFLAGP(wwin, no_appicon)) {
1267                                 if (!WFLAGP(wwin, no_miniaturizable)) {
1268                                         wwin->flags.skip_next_animation = 1;
1269                                         wIconifyWindow(wwin);
1270                                 }
1271                         } else if (wwin->main_window != None && awin->main_window != wwin->main_window) {
1272                                 tapp = wApplicationOf(wwin->main_window);
1273                                 if (tapp) {
1274                                         tapp->flags.skip_next_animation = 1;
1275                                         wHideApplication(tapp);
1276                                 } else {
1277                                         if (!WFLAGP(wwin, no_miniaturizable)) {
1278                                                 wwin->flags.skip_next_animation = 1;
1279                                                 wIconifyWindow(wwin);
1280                                         }
1281                                 }
1282                         }
1283                 }
1284                 wwin = wwin->prev;
1285         }
1286         /*
1287            wSetFocusTo(awin->screen_ptr, awin);
1288          */
1289 }
1290
1291 void wHideApplication(WApplication * wapp)
1292 {
1293         WScreen *scr;
1294         WWindow *wlist;
1295         int hadfocus;
1296         int animate;
1297
1298         if (!wapp) {
1299                 wwarning("trying to hide a non grouped window");
1300                 return;
1301         }
1302         if (!wapp->main_window_desc) {
1303                 wwarning("group leader not found for window group");
1304                 return;
1305         }
1306         scr = wapp->main_window_desc->screen_ptr;
1307         hadfocus = 0;
1308         wlist = scr->focused_window;
1309         if (!wlist)
1310                 return;
1311
1312         if (wlist->main_window == wapp->main_window)
1313                 wapp->last_focused = wlist;
1314         else
1315                 wapp->last_focused = NULL;
1316
1317         animate = !wapp->flags.skip_next_animation;
1318
1319         while (wlist) {
1320                 if (wlist->main_window == wapp->main_window) {
1321                         if (wlist->flags.focused) {
1322                                 hadfocus = 1;
1323                         }
1324                         if (wapp->app_icon) {
1325                                 hideWindow(wapp->app_icon->icon, wapp->app_icon->x_pos,
1326                                            wapp->app_icon->y_pos, wlist, animate);
1327                                 animate = False;
1328                         }
1329                 }
1330                 wlist = wlist->prev;
1331         }
1332
1333         wapp->flags.skip_next_animation = 0;
1334
1335         if (hadfocus) {
1336                 if (wPreferences.focus_mode == WKF_CLICK) {
1337                         wlist = scr->focused_window;
1338                         while (wlist) {
1339                                 if (!WFLAGP(wlist, no_focusable) && !wlist->flags.hidden
1340                                     && (wlist->flags.mapped || wlist->flags.shaded))
1341                                         break;
1342                                 wlist = wlist->prev;
1343                         }
1344                         wSetFocusTo(scr, wlist);
1345                 } else {
1346                         wSetFocusTo(scr, NULL);
1347                 }
1348         }
1349
1350         wapp->flags.hidden = 1;
1351
1352         if (wPreferences.auto_arrange_icons) {
1353                 wArrangeIcons(scr, True);
1354         }
1355 #ifdef HIDDENDOT
1356         if (wapp->app_icon)
1357                 wAppIconPaint(wapp->app_icon);
1358 #endif
1359 }
1360
1361 static void unhideWindow(WIcon * icon, int icon_x, int icon_y, WWindow * wwin, int animate, int bringToCurrentWS)
1362 {
1363         if (bringToCurrentWS)
1364                 wWindowChangeWorkspace(wwin, wwin->screen_ptr->current_workspace);
1365
1366         wwin->flags.hidden = 0;
1367
1368         wSoundPlay(WSOUND_UNHIDE);
1369 #ifdef ANIMATIONS
1370         if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations && animate) {
1371                 animateResize(wwin->screen_ptr, icon_x, icon_y,
1372                               icon->core->width, icon->core->height,
1373                               wwin->frame_x, wwin->frame_y,
1374                               wwin->frame->core->width, wwin->frame->core->height, True);
1375         }
1376 #endif
1377         wwin->flags.skip_next_animation = 0;
1378         if (wwin->screen_ptr->current_workspace == wwin->frame->workspace) {
1379                 XMapWindow(dpy, wwin->client_win);
1380                 XMapWindow(dpy, wwin->frame->core->window);
1381                 wClientSetState(wwin, NormalState, None);
1382                 wwin->flags.mapped = 1;
1383                 wRaiseFrame(wwin->frame->core);
1384         }
1385         if (wwin->flags.inspector_open) {
1386                 wUnhideInspectorForWindow(wwin);
1387         }
1388
1389         WMPostNotificationName(WMNChangedState, wwin, "hide");
1390 }
1391
1392 void wUnhideApplication(WApplication * wapp, Bool miniwindows, Bool bringToCurrentWS)
1393 {
1394         WScreen *scr;
1395         WWindow *wlist, *next;
1396         WWindow *focused = NULL;
1397         int animate;
1398
1399         if (!wapp)
1400                 return;
1401
1402         scr = wapp->main_window_desc->screen_ptr;
1403         wlist = scr->focused_window;
1404         if (!wlist)
1405                 return;
1406
1407         /* goto beginning of list */
1408         while (wlist->prev)
1409                 wlist = wlist->prev;
1410
1411         animate = !wapp->flags.skip_next_animation;
1412
1413         while (wlist) {
1414                 next = wlist->next;
1415
1416                 if (wlist->main_window == wapp->main_window) {
1417                         if (wlist->flags.focused)
1418                                 focused = wlist;
1419                         else if (!focused || !focused->flags.focused)
1420                                 focused = wlist;
1421
1422                         if (wlist->flags.miniaturized) {
1423                                 if ((bringToCurrentWS || wPreferences.sticky_icons ||
1424                                      wlist->frame->workspace == scr->current_workspace) && wlist->icon) {
1425                                         if (!wlist->icon->mapped) {
1426                                                 int x, y;
1427
1428                                                 PlaceIcon(scr, &x, &y, wGetHeadForWindow(wlist));
1429                                                 if (wlist->icon_x != x || wlist->icon_y != y) {
1430                                                         XMoveWindow(dpy, wlist->icon->core->window, x, y);
1431                                                 }
1432                                                 wlist->icon_x = x;
1433                                                 wlist->icon_y = y;
1434                                                 XMapWindow(dpy, wlist->icon->core->window);
1435                                                 wlist->icon->mapped = 1;
1436                                         }
1437                                         wRaiseFrame(wlist->icon->core);
1438                                 }
1439                                 if (bringToCurrentWS)
1440                                         wWindowChangeWorkspace(wlist, scr->current_workspace);
1441                                 wlist->flags.hidden = 0;
1442                                 if (miniwindows && wlist->frame->workspace == scr->current_workspace) {
1443                                         wDeiconifyWindow(wlist);
1444                                 }
1445                                 WMPostNotificationName(WMNChangedState, wlist, "hide");
1446                         } else if (wlist->flags.shaded) {
1447                                 if (bringToCurrentWS)
1448                                         wWindowChangeWorkspace(wlist, scr->current_workspace);
1449                                 wlist->flags.hidden = 0;
1450                                 if (wlist->frame->workspace == scr->current_workspace) {
1451                                         XMapWindow(dpy, wlist->frame->core->window);
1452                                         if (miniwindows) {
1453                                                 wUnshadeWindow(wlist);
1454                                                 wRaiseFrame(wlist->frame->core);
1455                                         }
1456                                 }
1457                                 WMPostNotificationName(WMNChangedState, wlist, "hide");
1458                         } else if (wlist->flags.hidden) {
1459                                 unhideWindow(wapp->app_icon->icon, wapp->app_icon->x_pos,
1460                                              wapp->app_icon->y_pos, wlist, animate, bringToCurrentWS);
1461                                 animate = False;
1462                         } else {
1463                                 if (bringToCurrentWS && wlist->frame->workspace != scr->current_workspace) {
1464                                         wWindowChangeWorkspace(wlist, scr->current_workspace);
1465                                 }
1466                                 wRaiseFrame(wlist->frame->core);
1467                         }
1468                 }
1469                 wlist = next;
1470         }
1471
1472         wapp->flags.skip_next_animation = 0;
1473         wapp->flags.hidden = 0;
1474
1475         if (wapp->last_focused && wapp->last_focused->flags.mapped) {
1476                 wRaiseFrame(wapp->last_focused->frame->core);
1477                 wSetFocusTo(scr, wapp->last_focused);
1478         } else if (focused) {
1479                 wSetFocusTo(scr, focused);
1480         }
1481         wapp->last_focused = NULL;
1482         if (wPreferences.auto_arrange_icons) {
1483                 wArrangeIcons(scr, True);
1484         }
1485 #ifdef HIDDENDOT
1486         wAppIconPaint(wapp->app_icon);
1487 #endif
1488 }
1489
1490 void wShowAllWindows(WScreen * scr)
1491 {
1492         WWindow *wwin, *old_foc;
1493         WApplication *wapp;
1494
1495         old_foc = wwin = scr->focused_window;
1496         while (wwin) {
1497                 if (!wwin->flags.internal_window &&
1498                     (scr->current_workspace == wwin->frame->workspace || IS_OMNIPRESENT(wwin))) {
1499                         if (wwin->flags.miniaturized) {
1500                                 wwin->flags.skip_next_animation = 1;
1501                                 wDeiconifyWindow(wwin);
1502                         } else if (wwin->flags.hidden) {
1503                                 wapp = wApplicationOf(wwin->main_window);
1504                                 if (wapp) {
1505                                         wUnhideApplication(wapp, False, False);
1506                                 } else {
1507                                         wwin->flags.skip_next_animation = 1;
1508                                         wDeiconifyWindow(wwin);
1509                                 }
1510                         }
1511                 }
1512                 wwin = wwin->prev;
1513         }
1514         wSetFocusTo(scr, old_foc);
1515         /*wRaiseFrame(old_foc->frame->core); */
1516 }
1517
1518 void wRefreshDesktop(WScreen * scr)
1519 {
1520         Window win;
1521         XSetWindowAttributes attr;
1522
1523         attr.backing_store = NotUseful;
1524         attr.save_under = False;
1525         win = XCreateWindow(dpy, scr->root_win, 0, 0, scr->scr_width,
1526                             scr->scr_height, 0, CopyFromParent, CopyFromParent,
1527                             (Visual *) CopyFromParent, CWBackingStore | CWSaveUnder, &attr);
1528         XMapRaised(dpy, win);
1529         XDestroyWindow(dpy, win);
1530         XFlush(dpy);
1531 }
1532
1533 void wArrangeIcons(WScreen * scr, Bool arrangeAll)
1534 {
1535         WWindow *wwin;
1536         WAppIcon *aicon;
1537
1538         int head;
1539         const int heads = wXineramaHeads(scr);
1540
1541         struct HeadVars {
1542                 int pf;         /* primary axis */
1543                 int sf;         /* secondary axis */
1544                 int fullW;
1545                 int fullH;
1546                 int pi, si;
1547                 int sx1, sx2, sy1, sy2; /* screen boundary */
1548                 int sw, sh;
1549                 int xo, yo;
1550                 int xs, ys;
1551         } *vars;
1552
1553         int isize = wPreferences.icon_size;
1554
1555         vars = (struct HeadVars *)wmalloc(sizeof(struct HeadVars) * heads);
1556
1557         for (head = 0; head < heads; ++head) {
1558 #if 0
1559                 WMRect rect = wGetRectForHead(scr, head);
1560 #else
1561                 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
1562                 WMRect rect = wmkrect(area.x1, area.y1, area.x2 - area.x1, area.y2 - area.y1);
1563 #endif
1564
1565                 vars[head].pi = vars[head].si = 0;
1566                 vars[head].sx1 = rect.pos.x;
1567                 vars[head].sy1 = rect.pos.y;
1568                 vars[head].sw = rect.size.width;
1569                 vars[head].sh = rect.size.height;
1570                 vars[head].sx2 = vars[head].sx1 + vars[head].sw;
1571                 vars[head].sy2 = vars[head].sy1 + vars[head].sh;
1572
1573 #if 0
1574                 if (scr->dock) {
1575                         if (scr->dock->on_right_side)
1576                                 vars[head].sx2 -= isize + DOCK_EXTRA_SPACE;
1577                         else
1578                                 vars[head].sx1 += isize + DOCK_EXTRA_SPACE;
1579                 }
1580 #endif
1581
1582                 vars[head].sw = isize * (vars[head].sw / isize);
1583                 vars[head].sh = isize * (vars[head].sh / isize);
1584                 vars[head].fullW = (vars[head].sx2 - vars[head].sx1) / isize;
1585                 vars[head].fullH = (vars[head].sy2 - vars[head].sy1) / isize;
1586
1587                 /* icon yard boundaries */
1588                 if (wPreferences.icon_yard & IY_VERT) {
1589                         vars[head].pf = vars[head].fullH;
1590                         vars[head].sf = vars[head].fullW;
1591                 } else {
1592                         vars[head].pf = vars[head].fullW;
1593                         vars[head].sf = vars[head].fullH;
1594                 }
1595                 if (wPreferences.icon_yard & IY_RIGHT) {
1596                         vars[head].xo = vars[head].sx2 - isize;
1597                         vars[head].xs = -1;
1598                 } else {
1599                         vars[head].xo = vars[head].sx1;
1600                         vars[head].xs = 1;
1601                 }
1602                 if (wPreferences.icon_yard & IY_TOP) {
1603                         vars[head].yo = vars[head].sy1;
1604                         vars[head].ys = 1;
1605                 } else {
1606                         vars[head].yo = vars[head].sy2 - isize;
1607                         vars[head].ys = -1;
1608                 }
1609         }
1610
1611 #define X ((wPreferences.icon_yard & IY_VERT) \
1612     ? vars[head].xo + vars[head].xs*(vars[head].si*isize) \
1613     : vars[head].xo + vars[head].xs*(vars[head].pi*isize))
1614
1615 #define Y ((wPreferences.icon_yard & IY_VERT) \
1616     ? vars[head].yo + vars[head].ys*(vars[head].pi*isize) \
1617     : vars[head].yo + vars[head].ys*(vars[head].si*isize))
1618
1619         /* arrange application icons */
1620         aicon = scr->app_icon_list;
1621         /* reverse them to avoid unnecessarily sliding of icons */
1622         while (aicon && aicon->next)
1623                 aicon = aicon->next;
1624
1625         while (aicon) {
1626                 if (!aicon->docked) {
1627                         /* CHECK: can icon be NULL here ? */
1628                         /* The intention here is to place the AppIcon on the head that
1629                          * contains most of the applications _main_ window. */
1630                         head = wGetHeadForWindow(aicon->icon->owner);
1631
1632                         if (aicon->x_pos != X || aicon->y_pos != Y) {
1633 #ifdef ANIMATIONS
1634                                 if (!wPreferences.no_animations) {
1635                                         SlideWindow(aicon->icon->core->window, aicon->x_pos, aicon->y_pos, X, Y);
1636                                 }
1637 #endif                          /* ANIMATIONS */
1638                         }
1639                         wAppIconMove(aicon, X, Y);
1640                         vars[head].pi++;
1641                         if (vars[head].pi >= vars[head].pf) {
1642                                 vars[head].pi = 0;
1643                                 vars[head].si++;
1644                         }
1645                 }
1646                 aicon = aicon->prev;
1647         }
1648
1649         /* arrange miniwindows */
1650         wwin = scr->focused_window;
1651         /* reverse them to avoid unnecessarily shuffling */
1652         while (wwin && wwin->prev)
1653                 wwin = wwin->prev;
1654
1655         while (wwin) {
1656                 if (wwin->icon && wwin->flags.miniaturized && !wwin->flags.hidden &&
1657                     (wwin->frame->workspace == scr->current_workspace ||
1658                      IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)) {
1659
1660                         head = wGetHeadForWindow(wwin);
1661
1662                         if (arrangeAll || !wwin->flags.icon_moved) {
1663                                 if (wwin->icon_x != X || wwin->icon_y != Y) {
1664 #ifdef ANIMATIONS
1665                                         if (wPreferences.no_animations) {
1666                                                 XMoveWindow(dpy, wwin->icon->core->window, X, Y);
1667                                         } else {
1668                                                 SlideWindow(wwin->icon->core->window, wwin->icon_x,
1669                                                             wwin->icon_y, X, Y);
1670                                         }
1671 #else
1672                                         XMoveWindow(dpy, wwin->icon->core->window, X, Y);
1673 #endif                          /* ANIMATIONS */
1674                                 }
1675                                 wwin->icon_x = X;
1676                                 wwin->icon_y = Y;
1677
1678                                 vars[head].pi++;
1679                                 if (vars[head].pi >= vars[head].pf) {
1680                                         vars[head].pi = 0;
1681                                         vars[head].si++;
1682                                 }
1683                         }
1684                 }
1685                 if (arrangeAll) {
1686                         wwin->flags.icon_moved = 0;
1687                 }
1688                 /* we reversed the order, so we use next */
1689                 wwin = wwin->next;
1690         }
1691
1692         wfree(vars);
1693 }
1694
1695 #if 0
1696 void wArrangeIcons(WScreen * scr, Bool arrangeAll)
1697 {
1698         WWindow *wwin;
1699         WAppIcon *aicon;
1700         int pf;                 /* primary axis */
1701         int sf;                 /* secondary axis */
1702         int fullW;
1703         int fullH;
1704         int pi, si;
1705         int sx1, sx2, sy1, sy2; /* screen boundary */
1706         int sw, sh;
1707         int xo, yo;
1708         int xs, ys;
1709         int isize = wPreferences.icon_size;
1710
1711         /*
1712          * Find out screen boundaries.
1713          */
1714
1715         /*
1716          * Allows each head to have miniwindows
1717          */
1718         WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1719
1720         sx1 = rect.pos.x;
1721         sy1 = rect.pos.y;
1722         sw = rect.size.width;
1723         sh = rect.size.height;
1724         sx2 = sx1 + sw;
1725         sy2 = sy1 + sh;
1726         if (scr->dock) {
1727                 if (scr->dock->on_right_side)
1728                         sx2 -= isize + DOCK_EXTRA_SPACE;
1729                 else
1730                         sx1 += isize + DOCK_EXTRA_SPACE;
1731         }
1732 #if 0
1733         sw = isize * (scr->scr_width / isize);
1734         sh = isize * (scr->scr_height / isize);
1735 #else
1736         sw = isize * (sw / isize);
1737         sh = isize * (sh / isize);
1738 #endif
1739         fullW = (sx2 - sx1) / isize;
1740         fullH = (sy2 - sy1) / isize;
1741
1742         /* icon yard boundaries */
1743         if (wPreferences.icon_yard & IY_VERT) {
1744                 pf = fullH;
1745                 sf = fullW;
1746         } else {
1747                 pf = fullW;
1748                 sf = fullH;
1749         }
1750         if (wPreferences.icon_yard & IY_RIGHT) {
1751                 xo = sx2 - isize;
1752                 xs = -1;
1753         } else {
1754                 xo = sx1;
1755                 xs = 1;
1756         }
1757         if (wPreferences.icon_yard & IY_TOP) {
1758                 yo = sy1;
1759                 ys = 1;
1760         } else {
1761                 yo = sy2 - isize;
1762                 ys = -1;
1763         }
1764
1765         /* arrange icons putting the most recently focused window
1766          * as the last icon */
1767 #define X ((wPreferences.icon_yard & IY_VERT) ? xo + xs*(si*isize)\
1768     : xo + xs*(pi*isize))
1769 #define Y ((wPreferences.icon_yard & IY_VERT) ? yo + ys*(pi*isize)\
1770     : yo + ys*(si*isize))
1771
1772         /* arrange application icons */
1773         aicon = scr->app_icon_list;
1774         /* reverse them to avoid unnecessarily sliding of icons */
1775         while (aicon && aicon->next)
1776                 aicon = aicon->next;
1777
1778         pi = 0;
1779         si = 0;
1780         while (aicon) {
1781                 if (!aicon->docked) {
1782                         if (aicon->x_pos != X || aicon->y_pos != Y) {
1783 #ifdef ANIMATIONS
1784                                 if (!wPreferences.no_animations) {
1785                                         SlideWindow(aicon->icon->core->window, aicon->x_pos, aicon->y_pos, X, Y);
1786                                 }
1787 #endif                          /* ANIMATIONS */
1788                         }
1789                         wAppIconMove(aicon, X, Y);
1790                         pi++;
1791                 }
1792                 /* we reversed the order so we use prev */
1793                 aicon = aicon->prev;
1794                 if (pi >= pf) {
1795                         pi = 0;
1796                         si++;
1797                 }
1798         }
1799
1800         /* arrange miniwindows */
1801
1802         wwin = scr->focused_window;
1803         /* reverse them to avoid unnecessarily shuffling */
1804         while (wwin && wwin->prev)
1805                 wwin = wwin->prev;
1806
1807         while (wwin) {
1808                 if (wwin->icon && wwin->flags.miniaturized && !wwin->flags.hidden &&
1809                     (wwin->frame->workspace == scr->current_workspace ||
1810                      IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)) {
1811
1812                         if (arrangeAll || !wwin->flags.icon_moved) {
1813                                 if (wwin->icon_x != X || wwin->icon_y != Y) {
1814 #ifdef ANIMATIONS
1815                                         if (wPreferences.no_animations) {
1816                                                 XMoveWindow(dpy, wwin->icon->core->window, X, Y);
1817                                         } else {
1818                                                 SlideWindow(wwin->icon->core->window, wwin->icon_x,
1819                                                             wwin->icon_y, X, Y);
1820                                         }
1821 #else
1822                                         XMoveWindow(dpy, wwin->icon->core->window, X, Y);
1823 #endif                          /* ANIMATIONS */
1824                                 }
1825                                 wwin->icon_x = X;
1826                                 wwin->icon_y = Y;
1827                                 pi++;
1828                         }
1829                 }
1830                 if (arrangeAll) {
1831                         wwin->flags.icon_moved = 0;
1832                 }
1833                 /* we reversed the order, so we use next */
1834                 wwin = wwin->next;
1835                 if (pi >= pf) {
1836                         pi = 0;
1837                         si++;
1838                 }
1839         }
1840 }
1841 #endif
1842
1843 void wSelectWindow(WWindow * wwin, Bool flag)
1844 {
1845         WScreen *scr = wwin->screen_ptr;
1846
1847         if (flag) {
1848                 wwin->flags.selected = 1;
1849                 XSetWindowBorder(dpy, wwin->frame->core->window, scr->white_pixel);
1850
1851                 if (!HAS_BORDER(wwin)) {
1852                         XSetWindowBorderWidth(dpy, wwin->frame->core->window, FRAME_BORDER_WIDTH);
1853                 }
1854
1855                 if (!scr->selected_windows)
1856                         scr->selected_windows = WMCreateArray(4);
1857                 WMAddToArray(scr->selected_windows, wwin);
1858         } else {
1859                 wwin->flags.selected = 0;
1860                 XSetWindowBorder(dpy, wwin->frame->core->window, scr->frame_border_pixel);
1861
1862                 if (!HAS_BORDER(wwin)) {
1863                         XSetWindowBorderWidth(dpy, wwin->frame->core->window, 0);
1864                 }
1865
1866                 if (scr->selected_windows) {
1867                         WMRemoveFromArray(scr->selected_windows, wwin);
1868                 }
1869         }
1870 }
1871
1872 void wMakeWindowVisible(WWindow * wwin)
1873 {
1874         if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
1875                 wWorkspaceChange(wwin->screen_ptr, wwin->frame->workspace);
1876
1877         if (wwin->flags.shaded) {
1878                 wUnshadeWindow(wwin);
1879         }
1880         if (wwin->flags.hidden) {
1881                 WApplication *app;
1882
1883                 app = wApplicationOf(wwin->main_window);
1884                 if (app) {
1885                         /* trick to get focus to this window */
1886                         app->last_focused = wwin;
1887                         wUnhideApplication(app, False, False);
1888                 }
1889         }
1890         if (wwin->flags.miniaturized) {
1891                 wDeiconifyWindow(wwin);
1892         } else {
1893                 if (!WFLAGP(wwin, no_focusable))
1894                         wSetFocusTo(wwin->screen_ptr, wwin);
1895                 wRaiseFrame(wwin->frame->core);
1896         }
1897 }