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