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