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