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