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