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