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