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