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