Merge branch 'backport'
[dana/openbox.git] / openbox / frame.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    frame.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "frame.h"
21 #include "client.h"
22 #include "openbox.h"
23 #include "extensions.h"
24 #include "prop.h"
25 #include "grab.h"
26 #include "config.h"
27 #include "framerender.h"
28 #include "mainloop.h"
29 #include "focus_cycle.h"
30 #include "focus_cycle_indicator.h"
31 #include "moveresize.h"
32 #include "screen.h"
33 #include "render/theme.h"
34
35 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
36                          ButtonPressMask | ButtonReleaseMask | \
37                          SubstructureRedirectMask | FocusChangeMask)
38 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
39                            ButtonMotionMask | PointerMotionMask | \
40                            EnterWindowMask | LeaveWindowMask)
41
42 #define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
43 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (G_USEC_PER_SEC / 60) /* 60 Hz */
44
45 #define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b)
46
47 static void flash_done(gpointer data);
48 static gboolean flash_timeout(gpointer data);
49
50 static void layout_title(ObFrame *self);
51 static void set_theme_statics(ObFrame *self);
52 static void free_theme_statics(ObFrame *self);
53 static gboolean frame_animate_iconify(gpointer self);
54 static void frame_adjust_cursors(ObFrame *self);
55
56 static Window createWindow(Window parent, Visual *visual,
57                            gulong mask, XSetWindowAttributes *attrib)
58 {
59     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
60                          (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
61                          (visual ? visual : RrVisual(ob_rr_inst)),
62                          mask, attrib);
63
64 }
65
66 static Visual *check_32bit_client(ObClient *c)
67 {
68     XWindowAttributes wattrib;
69     Status ret;
70
71     /* we're already running at 32 bit depth, yay. we don't need to use their
72        visual */
73     if (RrDepth(ob_rr_inst) == 32)
74         return NULL;
75
76     ret = XGetWindowAttributes(ob_display, c->window, &wattrib);
77     g_assert(ret != BadDrawable);
78     g_assert(ret != BadWindow);
79
80     if (wattrib.depth == 32)
81         return wattrib.visual;
82     return NULL;
83 }
84
85 ObFrame *frame_new(ObClient *client)
86 {
87     XSetWindowAttributes attrib;
88     gulong mask;
89     ObFrame *self;
90     Visual *visual;
91
92     self = g_new0(ObFrame, 1);
93     self->client = client;
94
95     visual = check_32bit_client(client);
96
97     /* create the non-visible decor windows */
98
99     mask = 0;
100     if (visual) {
101         /* client has a 32-bit visual */
102         mask |= CWColormap | CWBackPixel | CWBorderPixel;
103         /* create a colormap with the visual */
104         self->colormap = attrib.colormap =
105             XCreateColormap(ob_display,
106                             RootWindow(ob_display, ob_screen),
107                             visual, AllocNone);
108         attrib.background_pixel = BlackPixel(ob_display, ob_screen);
109         attrib.border_pixel = BlackPixel(ob_display, ob_screen);
110     }
111     self->window = createWindow(RootWindow(ob_display, ob_screen), visual,
112                                 mask, &attrib);
113
114     /* create the visible decor windows */
115
116     mask = 0;
117     if (visual) {
118         /* client has a 32-bit visual */
119         mask |= CWColormap | CWBackPixel | CWBorderPixel;
120         attrib.colormap = RrColormap(ob_rr_inst);
121     }
122
123     self->backback = createWindow(self->window, NULL, mask, &attrib);
124     self->backfront = createWindow(self->backback, NULL, mask, &attrib);
125
126     mask |= CWEventMask;
127     attrib.event_mask = ELEMENT_EVENTMASK;
128     self->innerleft = createWindow(self->window, NULL, mask, &attrib);
129     self->innertop = createWindow(self->window, NULL, mask, &attrib);
130     self->innerright = createWindow(self->window, NULL, mask, &attrib);
131     self->innerbottom = createWindow(self->window, NULL, mask, &attrib);
132
133     self->innerblb = createWindow(self->innerbottom, NULL, mask, &attrib);
134     self->innerbrb = createWindow(self->innerbottom, NULL, mask, &attrib);
135     self->innerbll = createWindow(self->innerleft, NULL, mask, &attrib);
136     self->innerbrr = createWindow(self->innerright, NULL, mask, &attrib);
137
138     self->title = createWindow(self->window, NULL, mask, &attrib);
139     self->titleleft = createWindow(self->window, NULL, mask, &attrib);
140     self->titletop = createWindow(self->window, NULL, mask, &attrib);
141     self->titletopleft = createWindow(self->window, NULL, mask, &attrib);
142     self->titletopright = createWindow(self->window, NULL, mask, &attrib);
143     self->titleright = createWindow(self->window, NULL, mask, &attrib);
144     self->titlebottom = createWindow(self->window, NULL, mask, &attrib);
145
146     self->topresize = createWindow(self->title, NULL, mask, &attrib);
147     self->tltresize = createWindow(self->title, NULL, mask, &attrib);
148     self->tllresize = createWindow(self->title, NULL, mask, &attrib);
149     self->trtresize = createWindow(self->title, NULL, mask, &attrib);
150     self->trrresize = createWindow(self->title, NULL, mask, &attrib);
151
152     self->left = createWindow(self->window, NULL, mask, &attrib);
153     self->right = createWindow(self->window, NULL, mask, &attrib);
154
155     self->label = createWindow(self->title, NULL, mask, &attrib);
156     self->max = createWindow(self->title, NULL, mask, &attrib);
157     self->close = createWindow(self->title, NULL, mask, &attrib);
158     self->desk = createWindow(self->title, NULL, mask, &attrib);
159     self->shade = createWindow(self->title, NULL, mask, &attrib);
160     self->icon = createWindow(self->title, NULL, mask, &attrib);
161     self->iconify = createWindow(self->title, NULL, mask, &attrib);
162
163     self->handle = createWindow(self->window, NULL, mask, &attrib);
164     self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
165     self->rgrip = createWindow(self->handle, NULL, mask, &attrib);
166
167     self->handleleft = createWindow(self->handle, NULL, mask, &attrib);
168     self->handleright = createWindow(self->handle, NULL, mask, &attrib);
169
170     self->handletop = createWindow(self->window, NULL, mask, &attrib);
171     self->handlebottom = createWindow(self->window, NULL, mask, &attrib);
172     self->lgripleft = createWindow(self->window, NULL, mask, &attrib);
173     self->lgriptop = createWindow(self->window, NULL, mask, &attrib);
174     self->lgripbottom = createWindow(self->window, NULL, mask, &attrib);
175     self->rgripright = createWindow(self->window, NULL, mask, &attrib);
176     self->rgriptop = createWindow(self->window, NULL, mask, &attrib);
177     self->rgripbottom = createWindow(self->window, NULL, mask, &attrib);
178
179     self->focused = FALSE;
180
181     /* the other stuff is shown based on decor settings */
182     XMapWindow(ob_display, self->label);
183     XMapWindow(ob_display, self->backback);
184     XMapWindow(ob_display, self->backfront);
185
186     self->max_press = self->close_press = self->desk_press =
187         self->iconify_press = self->shade_press = FALSE;
188     self->max_hover = self->close_hover = self->desk_hover =
189         self->iconify_hover = self->shade_hover = FALSE;
190
191     set_theme_statics(self);
192
193     return (ObFrame*)self;
194 }
195
196 static void set_theme_statics(ObFrame *self)
197 {
198     /* set colors/appearance/sizes for stuff that doesn't change */
199     XResizeWindow(ob_display, self->max,
200                   ob_rr_theme->button_size, ob_rr_theme->button_size);
201     XResizeWindow(ob_display, self->iconify,
202                   ob_rr_theme->button_size, ob_rr_theme->button_size);
203     XResizeWindow(ob_display, self->icon,
204                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
205     XResizeWindow(ob_display, self->close,
206                   ob_rr_theme->button_size, ob_rr_theme->button_size);
207     XResizeWindow(ob_display, self->desk,
208                   ob_rr_theme->button_size, ob_rr_theme->button_size);
209     XResizeWindow(ob_display, self->shade,
210                   ob_rr_theme->button_size, ob_rr_theme->button_size);
211     XResizeWindow(ob_display, self->tltresize,
212                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
213     XResizeWindow(ob_display, self->trtresize,
214                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
215     XResizeWindow(ob_display, self->tllresize,
216                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
217     XResizeWindow(ob_display, self->trrresize,
218                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
219
220     /* set up the dynamic appearances */
221     self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
222     self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
223     self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
224     self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
225     self->a_unfocused_handle =
226         RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
227     self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
228     self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
229 }
230
231 static void free_theme_statics(ObFrame *self)
232 {
233     RrAppearanceFree(self->a_unfocused_title);
234     RrAppearanceFree(self->a_focused_title);
235     RrAppearanceFree(self->a_unfocused_label);
236     RrAppearanceFree(self->a_focused_label);
237     RrAppearanceFree(self->a_unfocused_handle);
238     RrAppearanceFree(self->a_focused_handle);
239     RrAppearanceFree(self->a_icon);
240 }
241
242 void frame_free(ObFrame *self)
243 {
244     free_theme_statics(self);
245
246     XDestroyWindow(ob_display, self->window);
247     if (self->colormap)
248         XFreeColormap(ob_display, self->colormap);
249
250     g_free(self);
251 }
252
253 void frame_show(ObFrame *self)
254 {
255     if (!self->visible) {
256         self->visible = TRUE;
257         framerender_frame(self);
258         /* Grab the server to make sure that the frame window is mapped before
259            the client gets its MapNotify, i.e. to make sure the client is
260            _visible_ when it gets MapNotify. */
261         grab_server(TRUE);
262         XMapWindow(ob_display, self->client->window);
263         XMapWindow(ob_display, self->window);
264         grab_server(FALSE);
265     }
266 }
267
268 void frame_hide(ObFrame *self)
269 {
270     if (self->visible) {
271         self->visible = FALSE;
272         if (!frame_iconify_animating(self))
273             XUnmapWindow(ob_display, self->window);
274         /* we unmap the client itself so that we can get MapRequest
275            events, and because the ICCCM tells us to! */
276         XUnmapWindow(ob_display, self->client->window);
277         self->client->ignore_unmaps += 1;
278     }
279 }
280
281 void frame_adjust_theme(ObFrame *self)
282 {
283     free_theme_statics(self);
284     set_theme_statics(self);
285 }
286
287 void frame_adjust_shape(ObFrame *self)
288 {
289 #ifdef SHAPE
290     gint num;
291     XRectangle xrect[2];
292
293     if (!self->client->shaped) {
294         /* clear the shape on the frame window */
295         XShapeCombineMask(ob_display, self->window, ShapeBounding,
296                           self->size.left,
297                           self->size.top,
298                           None, ShapeSet);
299     } else {
300         /* make the frame's shape match the clients */
301         XShapeCombineShape(ob_display, self->window, ShapeBounding,
302                            self->size.left,
303                            self->size.top,
304                            self->client->window,
305                            ShapeBounding, ShapeSet);
306
307         num = 0;
308         if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
309             xrect[0].x = 0;
310             xrect[0].y = 0;
311             xrect[0].width = self->area.width;
312             xrect[0].height = self->size.top;
313             ++num;
314         }
315
316         if (self->decorations & OB_FRAME_DECOR_HANDLE &&
317             ob_rr_theme->handle_height > 0)
318         {
319             xrect[1].x = 0;
320             xrect[1].y = FRAME_HANDLE_Y(self);
321             xrect[1].width = self->area.width;
322             xrect[1].height = ob_rr_theme->handle_height +
323                 self->bwidth * 2;
324             ++num;
325         }
326
327         XShapeCombineRectangles(ob_display, self->window,
328                                 ShapeBounding, 0, 0, xrect, num,
329                                 ShapeUnion, Unsorted);
330     }
331 #endif
332 }
333
334 void frame_adjust_area(ObFrame *self, gboolean moved,
335                        gboolean resized, gboolean fake)
336 {
337     Strut oldsize;
338
339     oldsize = self->size;
340
341     if (resized) {
342         /* do this before changing the frame's status like max_horz max_vert */
343         frame_adjust_cursors(self);
344
345         self->functions = self->client->functions;
346         self->decorations = self->client->decorations;
347         self->max_horz = self->client->max_horz;
348         self->max_vert = self->client->max_vert;
349         self->shaded = self->client->shaded;
350
351         if (self->decorations & OB_FRAME_DECOR_BORDER ||
352             (self->client->undecorated && config_theme_keepborder))
353             self->bwidth = ob_rr_theme->fbwidth;
354         else
355             self->bwidth = 0;
356
357         if (self->decorations & OB_FRAME_DECOR_BORDER) {
358             self->cbwidth_l = self->cbwidth_r = ob_rr_theme->cbwidthx;
359             self->cbwidth_t = self->cbwidth_b = ob_rr_theme->cbwidthy;
360         } else
361             self->cbwidth_l = self->cbwidth_t =
362                 self->cbwidth_r = self->cbwidth_b = 0;
363
364         if (self->max_horz) {
365             self->cbwidth_l = self->cbwidth_r = 0;
366             self->width = self->client->area.width;
367             if (self->max_vert)
368                 self->cbwidth_b = 0;
369         } else
370             self->width = self->client->area.width +
371                 self->cbwidth_l + self->cbwidth_r;
372
373         /* some elements are sized based of the width, so don't let them have
374            negative values */
375         self->width = MAX(self->width,
376                           (ob_rr_theme->grip_width + self->bwidth) * 2 + 1);
377
378         STRUT_SET(self->size,
379                   self->cbwidth_l + (!self->max_horz ? self->bwidth : 0),
380                   self->cbwidth_t +
381                   (!self->max_horz || !self->max_vert ||
382                    !self->client->undecorated ? self->bwidth : 0),
383                   self->cbwidth_r + (!self->max_horz ? self->bwidth : 0),
384                   self->cbwidth_b +
385                   (!self->max_horz || !self->max_vert ? self->bwidth : 0));
386
387         if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
388             self->size.top += ob_rr_theme->title_height + self->bwidth;
389         if (self->decorations & OB_FRAME_DECOR_HANDLE &&
390             ob_rr_theme->handle_height > 0)
391         {
392             self->size.bottom += ob_rr_theme->handle_height + self->bwidth;
393         }
394
395         /* position/size and map/unmap all the windows */
396
397         if (!fake) {
398             gint innercornerheight =
399                 ob_rr_theme->grip_width - self->size.bottom;
400
401             if (self->cbwidth_l) {
402                 XMoveResizeWindow(ob_display, self->innerleft,
403                                   self->size.left - self->cbwidth_l,
404                                   self->size.top,
405                                   self->cbwidth_l, self->client->area.height);
406
407                 XMapWindow(ob_display, self->innerleft);
408             } else
409                 XUnmapWindow(ob_display, self->innerleft);
410
411             if (self->cbwidth_l && innercornerheight > 0) {
412                 XMoveResizeWindow(ob_display, self->innerbll,
413                                   0,
414                                   self->client->area.height - 
415                                   (ob_rr_theme->grip_width -
416                                    self->size.bottom),
417                                   self->cbwidth_l,
418                                   ob_rr_theme->grip_width - self->size.bottom);
419
420                 XMapWindow(ob_display, self->innerbll);
421             } else
422                 XUnmapWindow(ob_display, self->innerbll);
423
424             if (self->cbwidth_r) {
425                 XMoveResizeWindow(ob_display, self->innerright,
426                                   self->size.left + self->client->area.width,
427                                   self->size.top,
428                                   self->cbwidth_r, self->client->area.height);
429
430                 XMapWindow(ob_display, self->innerright);
431             } else
432                 XUnmapWindow(ob_display, self->innerright);
433
434             if (self->cbwidth_r && innercornerheight > 0) {
435                 XMoveResizeWindow(ob_display, self->innerbrr,
436                                   0,
437                                   self->client->area.height - 
438                                   (ob_rr_theme->grip_width -
439                                    self->size.bottom),
440                                   self->cbwidth_r,
441                                   ob_rr_theme->grip_width - self->size.bottom);
442
443                 XMapWindow(ob_display, self->innerbrr);
444             } else
445                 XUnmapWindow(ob_display, self->innerbrr);
446
447             if (self->cbwidth_t) {
448                 XMoveResizeWindow(ob_display, self->innertop,
449                                   self->size.left - self->cbwidth_l,
450                                   self->size.top - self->cbwidth_t,
451                                   self->client->area.width +
452                                   self->cbwidth_l + self->cbwidth_r,
453                                   self->cbwidth_t);
454
455                 XMapWindow(ob_display, self->innertop);
456             } else
457                 XUnmapWindow(ob_display, self->innertop);
458
459             if (self->cbwidth_b) {
460                 XMoveResizeWindow(ob_display, self->innerbottom,
461                                   self->size.left - self->cbwidth_l,
462                                   self->size.top + self->client->area.height,
463                                   self->client->area.width +
464                                   self->cbwidth_l + self->cbwidth_r,
465                                   self->cbwidth_b);
466
467                 XMoveResizeWindow(ob_display, self->innerblb,
468                                   0, 0,
469                                   ob_rr_theme->grip_width + self->bwidth,
470                                   self->cbwidth_b);
471                 XMoveResizeWindow(ob_display, self->innerbrb,
472                                   self->client->area.width +
473                                   self->cbwidth_l + self->cbwidth_r -
474                                   (ob_rr_theme->grip_width + self->bwidth),
475                                   0,
476                                   ob_rr_theme->grip_width + self->bwidth,
477                                   self->cbwidth_b);
478
479                 XMapWindow(ob_display, self->innerbottom);
480                 XMapWindow(ob_display, self->innerblb);
481                 XMapWindow(ob_display, self->innerbrb);
482             } else {
483                 XUnmapWindow(ob_display, self->innerbottom);
484                 XUnmapWindow(ob_display, self->innerblb);
485                 XUnmapWindow(ob_display, self->innerbrb);
486             }
487
488             if (self->bwidth) {
489                 gint titlesides;
490
491                 /* height of titleleft and titleright */
492                 titlesides = (!self->max_horz ? ob_rr_theme->grip_width : 0);
493
494                 XMoveResizeWindow(ob_display, self->titletop,
495                                   ob_rr_theme->grip_width + self->bwidth, 0,
496                                   /* width + bwidth*2 - bwidth*2 - grips*2 */
497                                   self->width - ob_rr_theme->grip_width * 2,
498                                   self->bwidth);
499                 XMoveResizeWindow(ob_display, self->titletopleft,
500                                   0, 0,
501                                   ob_rr_theme->grip_width + self->bwidth,
502                                   self->bwidth);
503                 XMoveResizeWindow(ob_display, self->titletopright,
504                                   self->client->area.width +
505                                   self->size.left + self->size.right -
506                                   ob_rr_theme->grip_width - self->bwidth,
507                                   0,
508                                   ob_rr_theme->grip_width + self->bwidth,
509                                   self->bwidth);
510
511                 if (titlesides > 0) {
512                     XMoveResizeWindow(ob_display, self->titleleft,
513                                       0, self->bwidth,
514                                       self->bwidth,
515                                       titlesides);
516                     XMoveResizeWindow(ob_display, self->titleright,
517                                       self->client->area.width +
518                                       self->size.left + self->size.right -
519                                       self->bwidth,
520                                       self->bwidth,
521                                       self->bwidth,
522                                       titlesides);
523
524                     XMapWindow(ob_display, self->titleleft);
525                     XMapWindow(ob_display, self->titleright);
526                 } else {
527                     XUnmapWindow(ob_display, self->titleleft);
528                     XUnmapWindow(ob_display, self->titleright);
529                 }
530
531                 XMapWindow(ob_display, self->titletop);
532                 XMapWindow(ob_display, self->titletopleft);
533                 XMapWindow(ob_display, self->titletopright);
534
535                 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
536                     XMoveResizeWindow(ob_display, self->titlebottom,
537                                       (self->max_horz ? 0 : self->bwidth),
538                                       ob_rr_theme->title_height + self->bwidth,
539                                       self->width,
540                                       self->bwidth);
541
542                     XMapWindow(ob_display, self->titlebottom);
543                 } else
544                     XUnmapWindow(ob_display, self->titlebottom);
545             } else {
546                 XUnmapWindow(ob_display, self->titlebottom);
547
548                 XUnmapWindow(ob_display, self->titletop);
549                 XUnmapWindow(ob_display, self->titletopleft);
550                 XUnmapWindow(ob_display, self->titletopright);
551                 XUnmapWindow(ob_display, self->titleleft);
552                 XUnmapWindow(ob_display, self->titleright);
553             }
554
555             if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
556                 XMoveResizeWindow(ob_display, self->title,
557                                   (self->max_horz ? 0 : self->bwidth),
558                                   self->bwidth,
559                                   self->width, ob_rr_theme->title_height);
560
561                 XMapWindow(ob_display, self->title);
562
563                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
564                     XMoveResizeWindow(ob_display, self->topresize,
565                                       ob_rr_theme->grip_width,
566                                       0,
567                                       self->width - ob_rr_theme->grip_width *2,
568                                       ob_rr_theme->paddingy + 1);
569
570                     XMoveWindow(ob_display, self->tltresize, 0, 0);
571                     XMoveWindow(ob_display, self->tllresize, 0, 0);
572                     XMoveWindow(ob_display, self->trtresize,
573                                 self->width - ob_rr_theme->grip_width, 0);
574                     XMoveWindow(ob_display, self->trrresize,
575                                 self->width - ob_rr_theme->paddingx - 1, 0);
576
577                     XMapWindow(ob_display, self->topresize);
578                     XMapWindow(ob_display, self->tltresize);
579                     XMapWindow(ob_display, self->tllresize);
580                     XMapWindow(ob_display, self->trtresize);
581                     XMapWindow(ob_display, self->trrresize);
582                 } else {
583                     XUnmapWindow(ob_display, self->topresize);
584                     XUnmapWindow(ob_display, self->tltresize);
585                     XUnmapWindow(ob_display, self->tllresize);
586                     XUnmapWindow(ob_display, self->trtresize);
587                     XUnmapWindow(ob_display, self->trrresize);
588                 }
589             } else
590                 XUnmapWindow(ob_display, self->title);
591         }
592
593         if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
594             /* layout the title bar elements */
595             layout_title(self);
596
597         if (!fake) {
598             gint sidebwidth = self->max_horz ? 0 : self->bwidth;
599
600             if (self->bwidth && self->size.bottom) {
601                 XMoveResizeWindow(ob_display, self->handlebottom,
602                                   ob_rr_theme->grip_width +
603                                   self->bwidth + sidebwidth,
604                                   self->size.top + self->client->area.height +
605                                   self->size.bottom - self->bwidth,
606                                   self->width - (ob_rr_theme->grip_width +
607                                                  sidebwidth) * 2,
608                                   self->bwidth);
609
610
611                 if (sidebwidth) {
612                     XMoveResizeWindow(ob_display, self->lgripleft,
613                                       0,
614                                       self->size.top +
615                                       self->client->area.height +
616                                       self->size.bottom -
617                                       (!self->max_horz ?
618                                        ob_rr_theme->grip_width :
619                                        self->size.bottom - self->cbwidth_b),
620                                       self->bwidth,
621                                       (!self->max_horz ?
622                                        ob_rr_theme->grip_width :
623                                        self->size.bottom - self->cbwidth_b));
624                     XMoveResizeWindow(ob_display, self->rgripright,
625                                   self->size.left +
626                                       self->client->area.width +
627                                       self->size.right - self->bwidth,
628                                       self->size.top +
629                                       self->client->area.height +
630                                       self->size.bottom -
631                                       (!self->max_horz ?
632                                        ob_rr_theme->grip_width :
633                                        self->size.bottom - self->cbwidth_b),
634                                       self->bwidth,
635                                       (!self->max_horz ?
636                                        ob_rr_theme->grip_width :
637                                        self->size.bottom - self->cbwidth_b));
638
639                     XMapWindow(ob_display, self->lgripleft);
640                     XMapWindow(ob_display, self->rgripright);
641                 } else {
642                     XUnmapWindow(ob_display, self->lgripleft);
643                     XUnmapWindow(ob_display, self->rgripright);
644                 }
645
646                 XMoveResizeWindow(ob_display, self->lgripbottom,
647                                   sidebwidth,
648                                   self->size.top + self->client->area.height +
649                                   self->size.bottom - self->bwidth,
650                                   ob_rr_theme->grip_width + self->bwidth,
651                                   self->bwidth);
652                 XMoveResizeWindow(ob_display, self->rgripbottom,
653                                   self->size.left + self->client->area.width +
654                                   self->size.right - self->bwidth - sidebwidth-
655                                   ob_rr_theme->grip_width,
656                                   self->size.top + self->client->area.height +
657                                   self->size.bottom - self->bwidth,
658                                   ob_rr_theme->grip_width + self->bwidth,
659                                   self->bwidth);
660
661                 XMapWindow(ob_display, self->handlebottom);
662                 XMapWindow(ob_display, self->lgripbottom);
663                 XMapWindow(ob_display, self->rgripbottom);
664
665                 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
666                     ob_rr_theme->handle_height > 0)
667                 {
668                     XMoveResizeWindow(ob_display, self->handletop,
669                                       ob_rr_theme->grip_width +
670                                       self->bwidth + sidebwidth,
671                                       FRAME_HANDLE_Y(self),
672                                       self->width - (ob_rr_theme->grip_width +
673                                                      sidebwidth) * 2,
674                                       self->bwidth);
675                     XMapWindow(ob_display, self->handletop);
676
677                     if (self->decorations & OB_FRAME_DECOR_GRIPS) {
678                         XMoveResizeWindow(ob_display, self->handleleft,
679                                           ob_rr_theme->grip_width,
680                                           0,
681                                           self->bwidth,
682                                           ob_rr_theme->handle_height);
683                         XMoveResizeWindow(ob_display, self->handleright,
684                                           self->width -
685                                           ob_rr_theme->grip_width -
686                                           self->bwidth,
687                                           0,
688                                           self->bwidth,
689                                           ob_rr_theme->handle_height);
690
691                         XMoveResizeWindow(ob_display, self->lgriptop,
692                                           sidebwidth,
693                                           FRAME_HANDLE_Y(self),
694                                           ob_rr_theme->grip_width +
695                                           self->bwidth,
696                                           self->bwidth);
697                         XMoveResizeWindow(ob_display, self->rgriptop,
698                                           self->size.left +
699                                           self->client->area.width +
700                                           self->size.right - self->bwidth -
701                                           sidebwidth - ob_rr_theme->grip_width,
702                                           FRAME_HANDLE_Y(self),
703                                           ob_rr_theme->grip_width +
704                                           self->bwidth,
705                                           self->bwidth);
706
707                         XMapWindow(ob_display, self->handleleft);
708                         XMapWindow(ob_display, self->handleright);
709                         XMapWindow(ob_display, self->lgriptop);
710                         XMapWindow(ob_display, self->rgriptop);
711                     } else {
712                         XUnmapWindow(ob_display, self->handleleft);
713                         XUnmapWindow(ob_display, self->handleright);
714                         XUnmapWindow(ob_display, self->lgriptop);
715                         XUnmapWindow(ob_display, self->rgriptop);
716                     }
717                 } else {
718                     XUnmapWindow(ob_display, self->handleleft);
719                     XUnmapWindow(ob_display, self->handleright);
720                     XUnmapWindow(ob_display, self->lgriptop);
721                     XUnmapWindow(ob_display, self->rgriptop);
722
723                     XUnmapWindow(ob_display, self->handletop);
724                 }
725             } else {
726                 XUnmapWindow(ob_display, self->handleleft);
727                 XUnmapWindow(ob_display, self->handleright);
728                 XUnmapWindow(ob_display, self->lgriptop);
729                 XUnmapWindow(ob_display, self->rgriptop);
730
731                 XUnmapWindow(ob_display, self->handletop);
732
733                 XUnmapWindow(ob_display, self->handlebottom);
734                 XUnmapWindow(ob_display, self->lgripleft);
735                 XUnmapWindow(ob_display, self->rgripright);
736                 XUnmapWindow(ob_display, self->lgripbottom);
737                 XUnmapWindow(ob_display, self->rgripbottom);
738             }
739
740             if (self->decorations & OB_FRAME_DECOR_HANDLE &&
741                 ob_rr_theme->handle_height > 0)
742             {
743                 XMoveResizeWindow(ob_display, self->handle,
744                                   sidebwidth,
745                                   FRAME_HANDLE_Y(self) + self->bwidth,
746                                   self->width, ob_rr_theme->handle_height);
747                 XMapWindow(ob_display, self->handle);
748
749                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
750                     XMoveResizeWindow(ob_display, self->lgrip,
751                                       0, 0,
752                                       ob_rr_theme->grip_width,
753                                       ob_rr_theme->handle_height);
754                     XMoveResizeWindow(ob_display, self->rgrip,
755                                       self->width - ob_rr_theme->grip_width,
756                                       0,
757                                       ob_rr_theme->grip_width,
758                                       ob_rr_theme->handle_height);
759
760                     XMapWindow(ob_display, self->lgrip);
761                     XMapWindow(ob_display, self->rgrip);
762                 } else {
763                     XUnmapWindow(ob_display, self->lgrip);
764                     XUnmapWindow(ob_display, self->rgrip);
765                 }
766             } else {
767                 XUnmapWindow(ob_display, self->lgrip);
768                 XUnmapWindow(ob_display, self->rgrip);
769
770                 XUnmapWindow(ob_display, self->handle);
771             }
772
773             if (self->bwidth && !self->max_horz &&
774                 (self->client->area.height + self->size.top +
775                  self->size.bottom) > ob_rr_theme->grip_width * 2)
776             {
777                 XMoveResizeWindow(ob_display, self->left,
778                                   0,
779                                   self->bwidth + ob_rr_theme->grip_width,
780                                   self->bwidth,
781                                   self->client->area.height +
782                                   self->size.top + self->size.bottom -
783                                   ob_rr_theme->grip_width * 2);
784
785                 XMapWindow(ob_display, self->left);
786             } else
787                 XUnmapWindow(ob_display, self->left);
788
789             if (self->bwidth && !self->max_horz &&
790                 (self->client->area.height + self->size.top +
791                  self->size.bottom) > ob_rr_theme->grip_width * 2)
792             {
793                 XMoveResizeWindow(ob_display, self->right,
794                                   self->client->area.width + self->cbwidth_l +
795                                   self->cbwidth_r + self->bwidth,
796                                   self->bwidth + ob_rr_theme->grip_width,
797                                   self->bwidth,
798                                   self->client->area.height +
799                                   self->size.top + self->size.bottom -
800                                   ob_rr_theme->grip_width * 2);
801
802                 XMapWindow(ob_display, self->right);
803             } else
804                 XUnmapWindow(ob_display, self->right);
805
806             XMoveResizeWindow(ob_display, self->backback,
807                               self->size.left, self->size.top,
808                               self->client->area.width,
809                               self->client->area.height);
810         }
811     }
812
813     /* shading can change without being moved or resized */
814     RECT_SET_SIZE(self->area,
815                   self->client->area.width +
816                   self->size.left + self->size.right,
817                   (self->client->shaded ?
818                    ob_rr_theme->title_height + self->bwidth * 2:
819                    self->client->area.height +
820                    self->size.top + self->size.bottom));
821
822     if ((moved || resized) && !fake) {
823         /* find the new coordinates, done after setting the frame.size, for
824            frame_client_gravity. */
825         self->area.x = self->client->area.x;
826         self->area.y = self->client->area.y;
827         frame_client_gravity(self, &self->area.x, &self->area.y);
828     }
829
830     if (!fake) {
831         if (!frame_iconify_animating(self))
832             /* move and resize the top level frame.
833                shading can change without being moved or resized.
834
835                but don't do this during an iconify animation. it will be
836                reflected afterwards.
837             */
838             XMoveResizeWindow(ob_display, self->window,
839                               self->area.x,
840                               self->area.y,
841                               self->area.width,
842                               self->area.height);
843
844         /* when the client has StaticGravity, it likes to move around.
845            also this correctly positions the client when it maps.
846            this also needs to be run when the frame's decorations sizes change!
847         */
848         XMoveWindow(ob_display, self->client->window,
849                     self->size.left, self->size.top);
850
851         if (resized) {
852             self->need_render = TRUE;
853             framerender_frame(self);
854             frame_adjust_shape(self);
855         }
856
857         if (!STRUT_EQUAL(self->size, oldsize)) {
858             gulong vals[4];
859             vals[0] = self->size.left;
860             vals[1] = self->size.right;
861             vals[2] = self->size.top;
862             vals[3] = self->size.bottom;
863             PROP_SETA32(self->client->window, net_frame_extents,
864                         cardinal, vals, 4);
865             PROP_SETA32(self->client->window, kde_net_wm_frame_strut,
866                         cardinal, vals, 4);
867         }
868
869         /* if this occurs while we are focus cycling, the indicator needs to
870            match the changes */
871         if (focus_cycle_target == self->client)
872             focus_cycle_draw_indicator(self->client);
873     }
874     if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR))
875         XResizeWindow(ob_display, self->label, self->label_width,
876                       ob_rr_theme->label_height);
877
878 }
879
880 static void frame_adjust_cursors(ObFrame *self)
881 {
882     if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
883         (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
884         self->max_horz != self->client->max_horz ||
885         self->max_vert != self->client->max_vert ||
886         self->shaded != self->client->shaded)
887     {
888         gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
889             !(self->client->max_horz && self->client->max_vert);
890         gboolean topbot = !self->client->max_vert;
891         gboolean sh = self->client->shaded;
892         XSetWindowAttributes a;
893
894         /* these ones turn off when max vert, and some when shaded */
895         a.cursor = ob_cursor(r && topbot && !sh ?
896                              OB_CURSOR_NORTH : OB_CURSOR_NONE);
897         XChangeWindowAttributes(ob_display, self->topresize, CWCursor, &a);
898         XChangeWindowAttributes(ob_display, self->titletop, CWCursor, &a);
899         a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
900         XChangeWindowAttributes(ob_display, self->handle, CWCursor, &a);
901         XChangeWindowAttributes(ob_display, self->handletop, CWCursor, &a);
902         XChangeWindowAttributes(ob_display, self->handlebottom, CWCursor, &a);
903         XChangeWindowAttributes(ob_display, self->innerbottom, CWCursor, &a);
904
905         /* these ones change when shaded */
906         a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
907                              OB_CURSOR_NONE);
908         XChangeWindowAttributes(ob_display, self->titleleft, CWCursor, &a);
909         XChangeWindowAttributes(ob_display, self->tltresize, CWCursor, &a);
910         XChangeWindowAttributes(ob_display, self->tllresize, CWCursor, &a);
911         XChangeWindowAttributes(ob_display, self->titletopleft, CWCursor, &a);
912         a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
913                              OB_CURSOR_NONE);
914         XChangeWindowAttributes(ob_display, self->titleright, CWCursor, &a);
915         XChangeWindowAttributes(ob_display, self->trtresize, CWCursor, &a);
916         XChangeWindowAttributes(ob_display, self->trrresize, CWCursor, &a);
917         XChangeWindowAttributes(ob_display, self->titletopright, CWCursor, &a);
918
919         /* these ones are pretty static */
920         a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
921         XChangeWindowAttributes(ob_display, self->left, CWCursor, &a);
922         XChangeWindowAttributes(ob_display, self->innerleft, CWCursor, &a);
923         a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
924         XChangeWindowAttributes(ob_display, self->right, CWCursor, &a);
925         XChangeWindowAttributes(ob_display, self->innerright, CWCursor, &a);
926         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
927         XChangeWindowAttributes(ob_display, self->lgrip, CWCursor, &a);
928         XChangeWindowAttributes(ob_display, self->handleleft, CWCursor, &a);
929         XChangeWindowAttributes(ob_display, self->lgripleft, CWCursor, &a);
930         XChangeWindowAttributes(ob_display, self->lgriptop, CWCursor, &a);
931         XChangeWindowAttributes(ob_display, self->lgripbottom, CWCursor, &a);
932         XChangeWindowAttributes(ob_display, self->innerbll, CWCursor, &a);
933         XChangeWindowAttributes(ob_display, self->innerblb, CWCursor, &a);
934         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
935         XChangeWindowAttributes(ob_display, self->rgrip, CWCursor, &a);
936         XChangeWindowAttributes(ob_display, self->handleright, CWCursor, &a);
937         XChangeWindowAttributes(ob_display, self->rgripright, CWCursor, &a);
938         XChangeWindowAttributes(ob_display, self->rgriptop, CWCursor, &a);
939         XChangeWindowAttributes(ob_display, self->rgripbottom, CWCursor, &a);
940         XChangeWindowAttributes(ob_display, self->innerbrr, CWCursor, &a);
941         XChangeWindowAttributes(ob_display, self->innerbrb, CWCursor, &a);
942     }
943 }
944
945 void frame_adjust_client_area(ObFrame *self)
946 {
947     /* adjust the window which is there to prevent flashing on unmap */
948     XMoveResizeWindow(ob_display, self->backfront, 0, 0,
949                       self->client->area.width,
950                       self->client->area.height);
951 }
952
953 void frame_adjust_state(ObFrame *self)
954 {
955     self->need_render = TRUE;
956     framerender_frame(self);
957 }
958
959 void frame_adjust_focus(ObFrame *self, gboolean hilite)
960 {
961     self->focused = hilite;
962     self->need_render = TRUE;
963     framerender_frame(self);
964     XFlush(ob_display);
965 }
966
967 void frame_adjust_title(ObFrame *self)
968 {
969     self->need_render = TRUE;
970     framerender_frame(self);
971 }
972
973 void frame_adjust_icon(ObFrame *self)
974 {
975     self->need_render = TRUE;
976     framerender_frame(self);
977 }
978
979 void frame_grab_client(ObFrame *self)
980 {
981     /* DO NOT map the client window here. we used to do that, but it is bogus.
982        we need to set up the client's dimensions and everything before we
983        send a mapnotify or we create race conditions.
984     */
985
986     /* reparent the client to the frame */
987     XReparentWindow(ob_display, self->client->window, self->window, 0, 0);
988
989     /*
990       When reparenting the client window, it is usually not mapped yet, since
991       this occurs from a MapRequest. However, in the case where Openbox is
992       starting up, the window is already mapped, so we'll see an unmap event
993       for it.
994     */
995     if (ob_state() == OB_STATE_STARTING)
996         ++self->client->ignore_unmaps;
997
998     /* select the event mask on the client's parent (to receive config/map
999        req's) the ButtonPress is to catch clicks on the client border */
1000     XSelectInput(ob_display, self->window, FRAME_EVENTMASK);
1001
1002     /* set all the windows for the frame in the window_map */
1003     g_hash_table_insert(window_map, &self->window, self->client);
1004     g_hash_table_insert(window_map, &self->backback, self->client);
1005     g_hash_table_insert(window_map, &self->backfront, self->client);
1006     g_hash_table_insert(window_map, &self->innerleft, self->client);
1007     g_hash_table_insert(window_map, &self->innertop, self->client);
1008     g_hash_table_insert(window_map, &self->innerright, self->client);
1009     g_hash_table_insert(window_map, &self->innerbottom, self->client);
1010     g_hash_table_insert(window_map, &self->innerblb, self->client);
1011     g_hash_table_insert(window_map, &self->innerbll, self->client);
1012     g_hash_table_insert(window_map, &self->innerbrb, self->client);
1013     g_hash_table_insert(window_map, &self->innerbrr, self->client);
1014     g_hash_table_insert(window_map, &self->title, self->client);
1015     g_hash_table_insert(window_map, &self->label, self->client);
1016     g_hash_table_insert(window_map, &self->max, self->client);
1017     g_hash_table_insert(window_map, &self->close, self->client);
1018     g_hash_table_insert(window_map, &self->desk, self->client);
1019     g_hash_table_insert(window_map, &self->shade, self->client);
1020     g_hash_table_insert(window_map, &self->icon, self->client);
1021     g_hash_table_insert(window_map, &self->iconify, self->client);
1022     g_hash_table_insert(window_map, &self->handle, self->client);
1023     g_hash_table_insert(window_map, &self->lgrip, self->client);
1024     g_hash_table_insert(window_map, &self->rgrip, self->client);
1025     g_hash_table_insert(window_map, &self->topresize, self->client);
1026     g_hash_table_insert(window_map, &self->tltresize, self->client);
1027     g_hash_table_insert(window_map, &self->tllresize, self->client);
1028     g_hash_table_insert(window_map, &self->trtresize, self->client);
1029     g_hash_table_insert(window_map, &self->trrresize, self->client);
1030     g_hash_table_insert(window_map, &self->left, self->client);
1031     g_hash_table_insert(window_map, &self->right, self->client);
1032     g_hash_table_insert(window_map, &self->titleleft, self->client);
1033     g_hash_table_insert(window_map, &self->titletop, self->client);
1034     g_hash_table_insert(window_map, &self->titletopleft, self->client);
1035     g_hash_table_insert(window_map, &self->titletopright, self->client);
1036     g_hash_table_insert(window_map, &self->titleright, self->client);
1037     g_hash_table_insert(window_map, &self->titlebottom, self->client);
1038     g_hash_table_insert(window_map, &self->handleleft, self->client);
1039     g_hash_table_insert(window_map, &self->handletop, self->client);
1040     g_hash_table_insert(window_map, &self->handleright, self->client);
1041     g_hash_table_insert(window_map, &self->handlebottom, self->client);
1042     g_hash_table_insert(window_map, &self->lgripleft, self->client);
1043     g_hash_table_insert(window_map, &self->lgriptop, self->client);
1044     g_hash_table_insert(window_map, &self->lgripbottom, self->client);
1045     g_hash_table_insert(window_map, &self->rgripright, self->client);
1046     g_hash_table_insert(window_map, &self->rgriptop, self->client);
1047     g_hash_table_insert(window_map, &self->rgripbottom, self->client);
1048 }
1049
1050 void frame_release_client(ObFrame *self)
1051 {
1052     XEvent ev;
1053     gboolean reparent = TRUE;
1054
1055     /* if there was any animation going on, kill it */
1056     ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1057                                      self, FALSE);
1058
1059     /* check if the app has already reparented its window away */
1060     while (XCheckTypedWindowEvent(ob_display, self->client->window,
1061                                   ReparentNotify, &ev))
1062     {
1063         /* This check makes sure we don't catch our own reparent action to
1064            our frame window. This doesn't count as the app reparenting itself
1065            away of course.
1066
1067            Reparent events that are generated by us are just discarded here.
1068            They are of no consequence to us anyhow.
1069         */
1070         if (ev.xreparent.parent != self->window) {
1071             reparent = FALSE;
1072             XPutBackEvent(ob_display, &ev);
1073             break;
1074         }
1075     }
1076
1077     if (reparent) {
1078         /* according to the ICCCM - if the client doesn't reparent itself,
1079            then we will reparent the window to root for them */
1080         XReparentWindow(ob_display, self->client->window,
1081                         RootWindow(ob_display, ob_screen),
1082                         self->client->area.x,
1083                         self->client->area.y);
1084     }
1085
1086     /* remove all the windows for the frame from the window_map */
1087     g_hash_table_remove(window_map, &self->window);
1088     g_hash_table_remove(window_map, &self->backback);
1089     g_hash_table_remove(window_map, &self->backfront);
1090     g_hash_table_remove(window_map, &self->innerleft);
1091     g_hash_table_remove(window_map, &self->innertop);
1092     g_hash_table_remove(window_map, &self->innerright);
1093     g_hash_table_remove(window_map, &self->innerbottom);
1094     g_hash_table_remove(window_map, &self->innerblb);
1095     g_hash_table_remove(window_map, &self->innerbll);
1096     g_hash_table_remove(window_map, &self->innerbrb);
1097     g_hash_table_remove(window_map, &self->innerbrr);
1098     g_hash_table_remove(window_map, &self->title);
1099     g_hash_table_remove(window_map, &self->label);
1100     g_hash_table_remove(window_map, &self->max);
1101     g_hash_table_remove(window_map, &self->close);
1102     g_hash_table_remove(window_map, &self->desk);
1103     g_hash_table_remove(window_map, &self->shade);
1104     g_hash_table_remove(window_map, &self->icon);
1105     g_hash_table_remove(window_map, &self->iconify);
1106     g_hash_table_remove(window_map, &self->handle);
1107     g_hash_table_remove(window_map, &self->lgrip);
1108     g_hash_table_remove(window_map, &self->rgrip);
1109     g_hash_table_remove(window_map, &self->topresize);
1110     g_hash_table_remove(window_map, &self->tltresize);
1111     g_hash_table_remove(window_map, &self->tllresize);
1112     g_hash_table_remove(window_map, &self->trtresize);
1113     g_hash_table_remove(window_map, &self->trrresize);
1114     g_hash_table_remove(window_map, &self->left);
1115     g_hash_table_remove(window_map, &self->right);
1116     g_hash_table_remove(window_map, &self->titleleft);
1117     g_hash_table_remove(window_map, &self->titletop);
1118     g_hash_table_remove(window_map, &self->titletopleft);
1119     g_hash_table_remove(window_map, &self->titletopright);
1120     g_hash_table_remove(window_map, &self->titleright);
1121     g_hash_table_remove(window_map, &self->titlebottom);
1122     g_hash_table_remove(window_map, &self->handleleft);
1123     g_hash_table_remove(window_map, &self->handletop);
1124     g_hash_table_remove(window_map, &self->handleright);
1125     g_hash_table_remove(window_map, &self->handlebottom);
1126     g_hash_table_remove(window_map, &self->lgripleft);
1127     g_hash_table_remove(window_map, &self->lgriptop);
1128     g_hash_table_remove(window_map, &self->lgripbottom);
1129     g_hash_table_remove(window_map, &self->rgripright);
1130     g_hash_table_remove(window_map, &self->rgriptop);
1131     g_hash_table_remove(window_map, &self->rgripbottom);
1132
1133     ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
1134 }
1135
1136 /* is there anything present between us and the label? */
1137 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
1138     for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
1139         if (*lc == ' ') continue; /* it was invalid */
1140         if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
1141             return TRUE;
1142         if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
1143             return TRUE;
1144         if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
1145             return TRUE;
1146         if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
1147             return TRUE;
1148         if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
1149             return TRUE;
1150         if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
1151             return TRUE;
1152         if (*lc == 'L') return FALSE;
1153     }
1154     return FALSE;
1155 }
1156
1157 static void layout_title(ObFrame *self)
1158 {
1159     gchar *lc;
1160     gint i;
1161
1162     const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
1163     /* position of the left most button */
1164     const gint left = ob_rr_theme->paddingx + 1;
1165     /* position of the right most button */
1166     const gint right = self->width;
1167
1168     /* turn them all off */
1169     self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
1170         self->max_on = self->close_on = self->label_on = FALSE;
1171     self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
1172     self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
1173
1174     /* figure out what's being show, find each element's position, and the
1175        width of the label
1176
1177        do the ones before the label, then after the label,
1178        i will be +1 the first time through when working to the left,
1179        and -1 the second time through when working to the right */
1180     for (i = 1; i >= -1; i-=2) {
1181         gint x;
1182         ObFrameContext *firstcon;
1183
1184         if (i > 0) {
1185             x = left;
1186             lc = config_title_layout;
1187             firstcon = &self->leftmost;
1188         } else {
1189             x = right;
1190             lc = config_title_layout + strlen(config_title_layout)-1;
1191             firstcon = &self->rightmost;
1192         }
1193
1194         /* stop at the end of the string (or the label, which calls break) */
1195         for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1196             if (*lc == 'L') {
1197                 if (i > 0) {
1198                     self->label_on = TRUE;
1199                     self->label_x = x;
1200                 }
1201                 break; /* break the for loop, do other side of label */
1202             } else if (*lc == 'N') {
1203                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1204                 if ((self->icon_on = is_button_present(self, lc, i))) {
1205                     /* icon is bigger than buttons */
1206                     self->label_width -= bwidth + 2;
1207                     if (i > 0) self->icon_x = x;
1208                     x += i * (bwidth + 2);
1209                     if (i < 0) self->icon_x = x;
1210                 }
1211             } else if (*lc == 'D') {
1212                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1213                 if ((self->desk_on = is_button_present(self, lc, i))) {
1214                     self->label_width -= bwidth;
1215                     if (i > 0) self->desk_x = x;
1216                     x += i * bwidth;
1217                     if (i < 0) self->desk_x = x;
1218                 }
1219             } else if (*lc == 'S') {
1220                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1221                 if ((self->shade_on = is_button_present(self, lc, i))) {
1222                     self->label_width -= bwidth;
1223                     if (i > 0) self->shade_x = x;
1224                     x += i * bwidth;
1225                     if (i < 0) self->shade_x = x;
1226                 }
1227             } else if (*lc == 'I') {
1228                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1229                 if ((self->iconify_on = is_button_present(self, lc, i))) {
1230                     self->label_width -= bwidth;
1231                     if (i > 0) self->iconify_x = x;
1232                     x += i * bwidth;
1233                     if (i < 0) self->iconify_x = x;
1234                 }
1235             } else if (*lc == 'M') {
1236                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1237                 if ((self->max_on = is_button_present(self, lc, i))) {
1238                     self->label_width -= bwidth;
1239                     if (i > 0) self->max_x = x;
1240                     x += i * bwidth;
1241                     if (i < 0) self->max_x = x;
1242                 }
1243             } else if (*lc == 'C') {
1244                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1245                 if ((self->close_on = is_button_present(self, lc, i))) {
1246                     self->label_width -= bwidth;
1247                     if (i > 0) self->close_x = x;
1248                     x += i * bwidth;
1249                     if (i < 0) self->close_x = x;
1250                 }
1251             } else
1252                 continue; /* don't set firstcon */
1253             firstcon = NULL;
1254         }
1255     }
1256
1257     /* position and map the elements */
1258     if (self->icon_on) {
1259         XMapWindow(ob_display, self->icon);
1260         XMoveWindow(ob_display, self->icon, self->icon_x,
1261                     ob_rr_theme->paddingy);
1262     } else
1263         XUnmapWindow(ob_display, self->icon);
1264
1265     if (self->desk_on) {
1266         XMapWindow(ob_display, self->desk);
1267         XMoveWindow(ob_display, self->desk, self->desk_x,
1268                     ob_rr_theme->paddingy + 1);
1269     } else
1270         XUnmapWindow(ob_display, self->desk);
1271
1272     if (self->shade_on) {
1273         XMapWindow(ob_display, self->shade);
1274         XMoveWindow(ob_display, self->shade, self->shade_x,
1275                     ob_rr_theme->paddingy + 1);
1276     } else
1277         XUnmapWindow(ob_display, self->shade);
1278
1279     if (self->iconify_on) {
1280         XMapWindow(ob_display, self->iconify);
1281         XMoveWindow(ob_display, self->iconify, self->iconify_x,
1282                     ob_rr_theme->paddingy + 1);
1283     } else
1284         XUnmapWindow(ob_display, self->iconify);
1285
1286     if (self->max_on) {
1287         XMapWindow(ob_display, self->max);
1288         XMoveWindow(ob_display, self->max, self->max_x,
1289                     ob_rr_theme->paddingy + 1);
1290     } else
1291         XUnmapWindow(ob_display, self->max);
1292
1293     if (self->close_on) {
1294         XMapWindow(ob_display, self->close);
1295         XMoveWindow(ob_display, self->close, self->close_x,
1296                     ob_rr_theme->paddingy + 1);
1297     } else
1298         XUnmapWindow(ob_display, self->close);
1299
1300     if (self->label_on) {
1301         self->label_width = MAX(1, self->label_width); /* no lower than 1 */
1302         XMapWindow(ob_display, self->label);
1303         XMoveWindow(ob_display, self->label, self->label_x,
1304                     ob_rr_theme->paddingy);
1305     } else
1306         XUnmapWindow(ob_display, self->label);
1307 }
1308
1309 ObFrameContext frame_context_from_string(const gchar *name)
1310 {
1311     if (!g_ascii_strcasecmp("Desktop", name))
1312         return OB_FRAME_CONTEXT_DESKTOP;
1313     else if (!g_ascii_strcasecmp("Root", name))
1314         return OB_FRAME_CONTEXT_ROOT;
1315     else if (!g_ascii_strcasecmp("Client", name))
1316         return OB_FRAME_CONTEXT_CLIENT;
1317     else if (!g_ascii_strcasecmp("Titlebar", name))
1318         return OB_FRAME_CONTEXT_TITLEBAR;
1319     else if (!g_ascii_strcasecmp("Frame", name))
1320         return OB_FRAME_CONTEXT_FRAME;
1321     else if (!g_ascii_strcasecmp("TLCorner", name))
1322         return OB_FRAME_CONTEXT_TLCORNER;
1323     else if (!g_ascii_strcasecmp("TRCorner", name))
1324         return OB_FRAME_CONTEXT_TRCORNER;
1325     else if (!g_ascii_strcasecmp("BLCorner", name))
1326         return OB_FRAME_CONTEXT_BLCORNER;
1327     else if (!g_ascii_strcasecmp("BRCorner", name))
1328         return OB_FRAME_CONTEXT_BRCORNER;
1329     else if (!g_ascii_strcasecmp("Top", name))
1330         return OB_FRAME_CONTEXT_TOP;
1331     else if (!g_ascii_strcasecmp("Bottom", name))
1332         return OB_FRAME_CONTEXT_BOTTOM;
1333     else if (!g_ascii_strcasecmp("Left", name))
1334         return OB_FRAME_CONTEXT_LEFT;
1335     else if (!g_ascii_strcasecmp("Right", name))
1336         return OB_FRAME_CONTEXT_RIGHT;
1337     else if (!g_ascii_strcasecmp("Maximize", name))
1338         return OB_FRAME_CONTEXT_MAXIMIZE;
1339     else if (!g_ascii_strcasecmp("AllDesktops", name))
1340         return OB_FRAME_CONTEXT_ALLDESKTOPS;
1341     else if (!g_ascii_strcasecmp("Shade", name))
1342         return OB_FRAME_CONTEXT_SHADE;
1343     else if (!g_ascii_strcasecmp("Iconify", name))
1344         return OB_FRAME_CONTEXT_ICONIFY;
1345     else if (!g_ascii_strcasecmp("Icon", name))
1346         return OB_FRAME_CONTEXT_ICON;
1347     else if (!g_ascii_strcasecmp("Close", name))
1348         return OB_FRAME_CONTEXT_CLOSE;
1349     else if (!g_ascii_strcasecmp("MoveResize", name))
1350         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1351     return OB_FRAME_CONTEXT_NONE;
1352 }
1353
1354 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1355 {
1356     ObFrame *self;
1357
1358     if (moveresize_in_progress)
1359         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1360
1361     if (win == RootWindow(ob_display, ob_screen))
1362         return OB_FRAME_CONTEXT_ROOT ;
1363     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1364     if (win == client->window) {
1365         /* conceptually, this is the desktop, as far as users are
1366            concerned */
1367         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1368             return OB_FRAME_CONTEXT_DESKTOP;
1369         return OB_FRAME_CONTEXT_CLIENT;
1370     }
1371
1372     self = client->frame;
1373
1374     /* when the user clicks in the corners of the titlebar and the client
1375        is fully maximized, then treat it like they clicked in the
1376        button that is there */
1377     if (self->max_horz && self->max_vert &&
1378         (win == self->title || win == self->titletop ||
1379          win == self->titleleft || win == self->titletopleft ||
1380          win == self->titleright || win == self->titletopright))
1381     {
1382         /* get the mouse coords in reference to the whole frame */
1383         gint fx = x;
1384         gint fy = y;
1385
1386         /* these windows are down a border width from the top of the frame */
1387         if (win == self->title ||
1388             win == self->titleleft || win == self->titleright)
1389             fy += self->bwidth;
1390
1391         /* title is a border width in from the edge */
1392         if (win == self->title)
1393             fx += self->bwidth;
1394         /* titletop is a bit to the right */
1395         else if (win == self->titletop)
1396             fx += ob_rr_theme->grip_width + self->bwidth;
1397         /* titletopright is way to the right edge */
1398         else if (win == self->titletopright)
1399             fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1400         /* titleright is even more way to the right edge */
1401         else if (win == self->titleright)
1402             fx += self->area.width - self->bwidth;
1403
1404         /* figure out if we're over the area that should be considered a
1405            button */
1406         if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1407             ob_rr_theme->button_size)
1408         {
1409             if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1410                       ob_rr_theme->button_size))
1411             {
1412                 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1413                     return self->leftmost;
1414             }
1415             else if (fx >= (self->area.width -
1416                             (self->bwidth + ob_rr_theme->paddingx + 1 +
1417                              ob_rr_theme->button_size)))
1418             {
1419                 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1420                     return self->rightmost;
1421             }
1422         }
1423
1424         /* there is no resizing maximized windows so make them the titlebar
1425            context */
1426         return OB_FRAME_CONTEXT_TITLEBAR;
1427     }
1428     else if (self->max_vert &&
1429              (win == self->titletop || win == self->topresize))
1430         /* can't resize vertically when max vert */
1431         return OB_FRAME_CONTEXT_TITLEBAR;
1432     else if (self->shaded &&
1433              (win == self->titletop || win == self->topresize))
1434         /* can't resize vertically when shaded */
1435         return OB_FRAME_CONTEXT_TITLEBAR;
1436
1437     if (win == self->window)            return OB_FRAME_CONTEXT_FRAME;
1438     if (win == self->label)             return OB_FRAME_CONTEXT_TITLEBAR;
1439     if (win == self->handle)            return OB_FRAME_CONTEXT_BOTTOM;
1440     if (win == self->handletop)         return OB_FRAME_CONTEXT_BOTTOM;
1441     if (win == self->handlebottom)      return OB_FRAME_CONTEXT_BOTTOM;
1442     if (win == self->handleleft)        return OB_FRAME_CONTEXT_BLCORNER;
1443     if (win == self->lgrip)             return OB_FRAME_CONTEXT_BLCORNER;
1444     if (win == self->lgripleft)         return OB_FRAME_CONTEXT_BLCORNER;
1445     if (win == self->lgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1446     if (win == self->lgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1447     if (win == self->handleright)       return OB_FRAME_CONTEXT_BRCORNER;
1448     if (win == self->rgrip)             return OB_FRAME_CONTEXT_BRCORNER;
1449     if (win == self->rgripright)        return OB_FRAME_CONTEXT_BRCORNER;
1450     if (win == self->rgriptop)          return OB_FRAME_CONTEXT_BRCORNER;
1451     if (win == self->rgripbottom)       return OB_FRAME_CONTEXT_BRCORNER;
1452     if (win == self->title)             return OB_FRAME_CONTEXT_TITLEBAR;
1453     if (win == self->titlebottom)       return OB_FRAME_CONTEXT_TITLEBAR;
1454     if (win == self->titleleft)         return OB_FRAME_CONTEXT_TLCORNER;
1455     if (win == self->titletopleft)      return OB_FRAME_CONTEXT_TLCORNER;
1456     if (win == self->titleright)        return OB_FRAME_CONTEXT_TRCORNER;
1457     if (win == self->titletopright)     return OB_FRAME_CONTEXT_TRCORNER;
1458     if (win == self->titletop)          return OB_FRAME_CONTEXT_TOP;
1459     if (win == self->topresize)         return OB_FRAME_CONTEXT_TOP;
1460     if (win == self->tltresize)         return OB_FRAME_CONTEXT_TLCORNER;
1461     if (win == self->tllresize)         return OB_FRAME_CONTEXT_TLCORNER;
1462     if (win == self->trtresize)         return OB_FRAME_CONTEXT_TRCORNER;
1463     if (win == self->trrresize)         return OB_FRAME_CONTEXT_TRCORNER;
1464     if (win == self->left)              return OB_FRAME_CONTEXT_LEFT;
1465     if (win == self->right)             return OB_FRAME_CONTEXT_RIGHT;
1466     if (win == self->innertop)          return OB_FRAME_CONTEXT_TITLEBAR;
1467     if (win == self->innerleft)         return OB_FRAME_CONTEXT_LEFT;
1468     if (win == self->innerbottom)       return OB_FRAME_CONTEXT_BOTTOM;
1469     if (win == self->innerright)        return OB_FRAME_CONTEXT_RIGHT;
1470     if (win == self->innerbll)          return OB_FRAME_CONTEXT_BLCORNER;
1471     if (win == self->innerblb)          return OB_FRAME_CONTEXT_BLCORNER;
1472     if (win == self->innerbrr)          return OB_FRAME_CONTEXT_BRCORNER;
1473     if (win == self->innerbrb)          return OB_FRAME_CONTEXT_BRCORNER;
1474     if (win == self->max)               return OB_FRAME_CONTEXT_MAXIMIZE;
1475     if (win == self->iconify)           return OB_FRAME_CONTEXT_ICONIFY;
1476     if (win == self->close)             return OB_FRAME_CONTEXT_CLOSE;
1477     if (win == self->icon)              return OB_FRAME_CONTEXT_ICON;
1478     if (win == self->desk)              return OB_FRAME_CONTEXT_ALLDESKTOPS;
1479     if (win == self->shade)             return OB_FRAME_CONTEXT_SHADE;
1480
1481     return OB_FRAME_CONTEXT_NONE;
1482 }
1483
1484 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
1485 {
1486     /* horizontal */
1487     switch (self->client->gravity) {
1488     default:
1489     case NorthWestGravity:
1490     case SouthWestGravity:
1491     case WestGravity:
1492         break;
1493
1494     case NorthGravity:
1495     case SouthGravity:
1496     case CenterGravity:
1497         /* the middle of the client will be the middle of the frame */
1498         *x -= (self->size.right - self->size.left) / 2;
1499         break;
1500
1501     case NorthEastGravity:
1502     case SouthEastGravity:
1503     case EastGravity:
1504         /* the right side of the client will be the right side of the frame */
1505         *x -= self->size.right + self->size.left -
1506             self->client->border_width * 2;
1507         break;
1508
1509     case ForgetGravity:
1510     case StaticGravity:
1511         /* the client's position won't move */
1512         *x -= self->size.left - self->client->border_width;
1513         break;
1514     }
1515
1516     /* vertical */
1517     switch (self->client->gravity) {
1518     default:
1519     case NorthWestGravity:
1520     case NorthEastGravity:
1521     case NorthGravity:
1522         break;
1523
1524     case CenterGravity:
1525     case EastGravity:
1526     case WestGravity:
1527         /* the middle of the client will be the middle of the frame */
1528         *y -= (self->size.bottom - self->size.top) / 2;
1529         break;
1530
1531     case SouthWestGravity:
1532     case SouthEastGravity:
1533     case SouthGravity:
1534         /* the bottom of the client will be the bottom of the frame */
1535         *y -= self->size.bottom + self->size.top -
1536             self->client->border_width * 2;
1537         break;
1538
1539     case ForgetGravity:
1540     case StaticGravity:
1541         /* the client's position won't move */
1542         *y -= self->size.top - self->client->border_width;
1543         break;
1544     }
1545 }
1546
1547 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
1548 {
1549     /* horizontal */
1550     switch (self->client->gravity) {
1551     default:
1552     case NorthWestGravity:
1553     case WestGravity:
1554     case SouthWestGravity:
1555         break;
1556     case NorthGravity:
1557     case CenterGravity:
1558     case SouthGravity:
1559         /* the middle of the client will be the middle of the frame */
1560         *x += (self->size.right - self->size.left) / 2;
1561         break;
1562     case NorthEastGravity:
1563     case EastGravity:
1564     case SouthEastGravity:
1565         /* the right side of the client will be the right side of the frame */
1566         *x += self->size.right + self->size.left -
1567             self->client->border_width * 2;
1568         break;
1569     case StaticGravity:
1570     case ForgetGravity:
1571         /* the client's position won't move */
1572         *x += self->size.left - self->client->border_width;
1573         break;
1574     }
1575
1576     /* vertical */
1577     switch (self->client->gravity) {
1578     default:
1579     case NorthWestGravity:
1580     case NorthGravity:
1581     case NorthEastGravity:
1582         break;
1583     case WestGravity:
1584     case CenterGravity:
1585     case EastGravity:
1586         /* the middle of the client will be the middle of the frame */
1587         *y += (self->size.bottom - self->size.top) / 2;
1588         break;
1589     case SouthWestGravity:
1590     case SouthGravity:
1591     case SouthEastGravity:
1592         /* the bottom of the client will be the bottom of the frame */
1593         *y += self->size.bottom + self->size.top -
1594             self->client->border_width * 2;
1595         break;
1596     case StaticGravity:
1597     case ForgetGravity:
1598         /* the client's position won't move */
1599         *y += self->size.top - self->client->border_width;
1600         break;
1601     }
1602 }
1603
1604 void frame_rect_to_frame(ObFrame *self, Rect *r)
1605 {
1606     r->width += self->size.left + self->size.right;
1607     r->height += self->size.top + self->size.bottom;
1608     frame_client_gravity(self, &r->x, &r->y);
1609 }
1610
1611 void frame_rect_to_client(ObFrame *self, Rect *r)
1612 {
1613     r->width -= self->size.left + self->size.right;
1614     r->height -= self->size.top + self->size.bottom;
1615     frame_frame_gravity(self, &r->x, &r->y);
1616 }
1617
1618 static void flash_done(gpointer data)
1619 {
1620     ObFrame *self = data;
1621
1622     if (self->focused != self->flash_on)
1623         frame_adjust_focus(self, self->focused);
1624 }
1625
1626 static gboolean flash_timeout(gpointer data)
1627 {
1628     ObFrame *self = data;
1629     GTimeVal now;
1630
1631     g_get_current_time(&now);
1632     if (now.tv_sec > self->flash_end.tv_sec ||
1633         (now.tv_sec == self->flash_end.tv_sec &&
1634          now.tv_usec >= self->flash_end.tv_usec))
1635         self->flashing = FALSE;
1636
1637     if (!self->flashing)
1638         return FALSE; /* we are done */
1639
1640     self->flash_on = !self->flash_on;
1641     if (!self->focused) {
1642         frame_adjust_focus(self, self->flash_on);
1643         self->focused = FALSE;
1644     }
1645
1646     return TRUE; /* go again */
1647 }
1648
1649 void frame_flash_start(ObFrame *self)
1650 {
1651     self->flash_on = self->focused;
1652
1653     if (!self->flashing)
1654         ob_main_loop_timeout_add(ob_main_loop,
1655                                  G_USEC_PER_SEC * 0.6,
1656                                  flash_timeout,
1657                                  self,
1658                                  g_direct_equal,
1659                                  flash_done);
1660     g_get_current_time(&self->flash_end);
1661     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1662
1663     self->flashing = TRUE;
1664 }
1665
1666 void frame_flash_stop(ObFrame *self)
1667 {
1668     self->flashing = FALSE;
1669 }
1670
1671 static gulong frame_animate_iconify_time_left(ObFrame *self,
1672                                               const GTimeVal *now)
1673 {
1674     glong sec, usec;
1675     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1676     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1677     if (usec < 0) {
1678         usec += G_USEC_PER_SEC;
1679         sec--;
1680     }
1681     /* no negative values */
1682     return MAX(sec * G_USEC_PER_SEC + usec, 0);
1683 }
1684
1685 static gboolean frame_animate_iconify(gpointer p)
1686 {
1687     ObFrame *self = p;
1688     gint x, y, w, h;
1689     gint iconx, icony, iconw;
1690     GTimeVal now;
1691     gulong time;
1692     gboolean iconifying;
1693
1694     if (self->client->icon_geometry.width == 0) {
1695         /* there is no icon geometry set so just go straight down */
1696         Rect *a = screen_physical_area_monitor
1697             (screen_find_monitor(&self->area));
1698         iconx = self->area.x + self->area.width / 2 + 32;
1699         icony = a->y + a->width;
1700         iconw = 64;
1701         g_free(a);
1702     } else {
1703         iconx = self->client->icon_geometry.x;
1704         icony = self->client->icon_geometry.y;
1705         iconw = self->client->icon_geometry.width;
1706     }
1707
1708     iconifying = self->iconify_animation_going > 0;
1709
1710     /* how far do we have left to go ? */
1711     g_get_current_time(&now);
1712     time = frame_animate_iconify_time_left(self, &now);
1713
1714     if (time == 0 || iconifying) {
1715         /* start where the frame is supposed to be */
1716         x = self->area.x;
1717         y = self->area.y;
1718         w = self->area.width;
1719         h = self->area.height;
1720     } else {
1721         /* start at the icon */
1722         x = iconx;
1723         y = icony;
1724         w = iconw;
1725         h = self->size.top; /* just the titlebar */
1726     }
1727
1728     if (time > 0) {
1729         glong dx, dy, dw;
1730         glong elapsed;
1731
1732         dx = self->area.x - iconx;
1733         dy = self->area.y - icony;
1734         dw = self->area.width - self->bwidth * 2 - iconw;
1735          /* if restoring, we move in the opposite direction */
1736         if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1737
1738         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1739         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1740         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1741         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1742         h = self->size.top; /* just the titlebar */
1743     }
1744
1745     if (time == 0)
1746         frame_end_iconify_animation(self);
1747     else {
1748         XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1749         XFlush(ob_display);
1750     }
1751
1752     return time > 0; /* repeat until we're out of time */
1753 }
1754
1755 void frame_end_iconify_animation(ObFrame *self)
1756 {
1757     /* see if there is an animation going */
1758     if (self->iconify_animation_going == 0) return;
1759
1760     if (!self->visible)
1761         XUnmapWindow(ob_display, self->window);
1762     else {
1763         /* Send a ConfigureNotify when the animation is done, this fixes
1764            KDE's pager showing the window in the wrong place.  since the
1765            window is mapped at a different location and is then moved, we
1766            need to send the synthetic configurenotify, since apps may have
1767            read the position when the client mapped, apparently. */
1768         client_reconfigure(self->client, TRUE);
1769     }
1770
1771     /* we're not animating any more ! */
1772     self->iconify_animation_going = 0;
1773
1774     XMoveResizeWindow(ob_display, self->window,
1775                       self->area.x, self->area.y,
1776                       self->area.width, self->area.height);
1777     /* we delay re-rendering until after we're done animating */
1778     framerender_frame(self);
1779     XFlush(ob_display);
1780 }
1781
1782 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1783 {
1784     gulong time;
1785     gboolean new_anim = FALSE;
1786     gboolean set_end = TRUE;
1787     GTimeVal now;
1788
1789     /* if there is no titlebar, just don't animate for now
1790        XXX it would be nice tho.. */
1791     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1792         return;
1793
1794     /* get the current time */
1795     g_get_current_time(&now);
1796
1797     /* get how long until the end */
1798     time = FRAME_ANIMATE_ICONIFY_TIME;
1799     if (self->iconify_animation_going) {
1800         if (!!iconifying != (self->iconify_animation_going > 0)) {
1801             /* animation was already going on in the opposite direction */
1802             time = time - frame_animate_iconify_time_left(self, &now);
1803         } else
1804             /* animation was already going in the same direction */
1805             set_end = FALSE;
1806     } else
1807         new_anim = TRUE;
1808     self->iconify_animation_going = iconifying ? 1 : -1;
1809
1810     /* set the ending time */
1811     if (set_end) {
1812         self->iconify_animation_end.tv_sec = now.tv_sec;
1813         self->iconify_animation_end.tv_usec = now.tv_usec;
1814         g_time_val_add(&self->iconify_animation_end, time);
1815     }
1816
1817     if (new_anim) {
1818         ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1819                                          self, FALSE);
1820         ob_main_loop_timeout_add(ob_main_loop,
1821                                  FRAME_ANIMATE_ICONIFY_STEP_TIME,
1822                                  frame_animate_iconify, self,
1823                                  g_direct_equal, NULL);
1824
1825         /* do the first step */
1826         frame_animate_iconify(self);
1827
1828         /* show it during the animation even if it is not "visible" */
1829         if (!self->visible)
1830             XMapWindow(ob_display, self->window);
1831     }
1832 }