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