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