add restart action
[dana/openbox-history.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                              self->client->area.width,
760                              self->client->area.height);
761     }
762
763     if (!fake) {
764         if (!frame_iconify_animating(self))
765             /* move and resize the top level frame.
766                shading can change without being moved or resized.
767                
768                but don't do this during an iconify animation. it will be
769                reflected afterwards.
770             */
771             XMoveResizeWindow(ob_display, self->window,
772                               self->area.x,
773                               self->area.y,
774                               self->area.width,
775                               self->area.height);
776
777         /* when the client has StaticGravity, it likes to move around.
778            also this correctly positions the client when it maps.
779            this also needs to be run when the frame's decorations sizes change!
780         */
781         XMoveWindow(ob_display, self->client->window,
782                     self->size.left, self->size.top);
783
784         if (resized) {
785             self->need_render = TRUE;
786             framerender_frame(self);
787             frame_adjust_shape(self);
788
789             /* the offscreen buffer's shape needs to match */
790             frame_get_offscreen_buffer(self);
791         }
792
793         if (!STRUT_EQUAL(self->size, oldsize)) {
794             gulong vals[4];
795             vals[0] = self->size.left;
796             vals[1] = self->size.right;
797             vals[2] = self->size.top;
798             vals[3] = self->size.bottom;
799             PROP_SETA32(self->client->window, net_frame_extents,
800                         cardinal, vals, 4);
801             PROP_SETA32(self->client->window, kde_net_wm_frame_strut,
802                         cardinal, vals, 4);
803         }
804
805         /* if this occurs while we are focus cycling, the indicator needs to
806            match the changes */
807         if (focus_cycle_target == self->client)
808             focus_cycle_draw_indicator(self->client);
809     }
810     if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR))
811         XResizeWindow(ob_display, self->label, self->label_width,
812                       ob_rr_theme->label_height);
813
814 }
815
816 static void frame_adjust_cursors(ObFrame *self)
817 {
818     if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
819         (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
820         self->max_horz != self->client->max_horz ||
821         self->max_vert != self->client->max_vert ||
822         self->shaded != self->client->shaded)
823     {
824         gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
825             !(self->client->max_horz && self->client->max_vert);
826         gboolean topbot = !self->client->max_vert;
827         gboolean sh = self->client->shaded;
828         XSetWindowAttributes a;
829
830         /* these ones turn off when max vert, and some when shaded */
831         a.cursor = ob_cursor(r && topbot && !sh ?
832                              OB_CURSOR_NORTH : OB_CURSOR_NONE);
833         XChangeWindowAttributes(ob_display, self->topresize, CWCursor, &a);
834         XChangeWindowAttributes(ob_display, self->titletop, CWCursor, &a);
835         a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
836         XChangeWindowAttributes(ob_display, self->handle, CWCursor, &a);
837         XChangeWindowAttributes(ob_display, self->handletop, CWCursor, &a);
838         XChangeWindowAttributes(ob_display, self->handlebottom, CWCursor, &a);
839         XChangeWindowAttributes(ob_display, self->innerbottom, CWCursor, &a);
840
841         /* these ones change when shaded */
842         a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
843                              OB_CURSOR_NONE);
844         XChangeWindowAttributes(ob_display, self->titleleft, CWCursor, &a);
845         XChangeWindowAttributes(ob_display, self->tltresize, CWCursor, &a);
846         XChangeWindowAttributes(ob_display, self->tllresize, CWCursor, &a);
847         XChangeWindowAttributes(ob_display, self->titletopleft, CWCursor, &a);
848         a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
849                              OB_CURSOR_NONE);
850         XChangeWindowAttributes(ob_display, self->titleright, CWCursor, &a);
851         XChangeWindowAttributes(ob_display, self->trtresize, CWCursor, &a);
852         XChangeWindowAttributes(ob_display, self->trrresize, CWCursor, &a);
853         XChangeWindowAttributes(ob_display, self->titletopright, CWCursor, &a);
854
855         /* these ones are pretty static */
856         a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
857         XChangeWindowAttributes(ob_display, self->left, CWCursor, &a);
858         XChangeWindowAttributes(ob_display, self->innerleft, CWCursor, &a);
859         a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
860         XChangeWindowAttributes(ob_display, self->right, CWCursor, &a);
861         XChangeWindowAttributes(ob_display, self->innerright, CWCursor, &a);
862         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
863         XChangeWindowAttributes(ob_display, self->lgrip, CWCursor, &a);
864         XChangeWindowAttributes(ob_display, self->handleleft, CWCursor, &a);
865         XChangeWindowAttributes(ob_display, self->lgripleft, CWCursor, &a);
866         XChangeWindowAttributes(ob_display, self->lgriptop, CWCursor, &a);
867         XChangeWindowAttributes(ob_display, self->lgripbottom, CWCursor, &a);
868         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
869         XChangeWindowAttributes(ob_display, self->rgrip, CWCursor, &a);
870         XChangeWindowAttributes(ob_display, self->handleright, CWCursor, &a);
871         XChangeWindowAttributes(ob_display, self->rgripright, CWCursor, &a);
872         XChangeWindowAttributes(ob_display, self->rgriptop, CWCursor, &a);
873         XChangeWindowAttributes(ob_display, self->rgripbottom, CWCursor, &a);
874     }
875 }
876
877 void frame_adjust_client_area(ObFrame *self)
878 {
879     /* adjust the window which is there to prevent flashing on unmap */
880     XMoveResizeWindow(ob_display, self->backfront, 0, 0,
881                       self->client->area.width,
882                       self->client->area.height);
883 }
884
885 void frame_adjust_state(ObFrame *self)
886 {
887     self->need_render = TRUE;
888     framerender_frame(self);
889 }
890
891 void frame_adjust_focus(ObFrame *self, gboolean hilite)
892 {
893     self->focused = hilite;
894     self->need_render = TRUE;
895     framerender_frame(self);
896     XFlush(ob_display);
897 }
898
899 void frame_adjust_title(ObFrame *self)
900 {
901     self->need_render = TRUE;
902     framerender_frame(self);
903 }
904
905 void frame_adjust_icon(ObFrame *self)
906 {
907     self->need_render = TRUE;
908     framerender_frame(self);
909 }
910
911 void frame_grab_client(ObFrame *self)
912 {
913     /* DO NOT map the client window here. we used to do that, but it is bogus.
914        we need to set up the client's dimensions and everything before we
915        send a mapnotify or we create race conditions.
916     */
917
918     /* reparent the client to the frame */
919     XReparentWindow(ob_display, self->client->window, self->window, 0, 0);
920
921     /* enable the offscreen composite buffer for the client window */
922     composite_enable_for_window(self->client->window);
923
924     /*
925       When reparenting the client window, it is usually not mapped yet, since
926       this occurs from a MapRequest. However, in the case where Openbox is
927       starting up, the window is already mapped, so we'll see an unmap event
928       for it.
929     */
930     if (ob_state() == OB_STATE_STARTING)
931         ++self->client->ignore_unmaps;
932
933     /* select the event mask on the client's parent (to receive config/map
934        req's) the ButtonPress is to catch clicks on the client border */
935     XSelectInput(ob_display, self->window, FRAME_EVENTMASK);
936
937     /* set all the windows for the frame in the window_map */
938     g_hash_table_insert(window_map, &self->window, self->client);
939     g_hash_table_insert(window_map, &self->backback, self->client);
940     g_hash_table_insert(window_map, &self->backfront, self->client);
941     g_hash_table_insert(window_map, &self->innerleft, self->client);
942     g_hash_table_insert(window_map, &self->innertop, self->client);
943     g_hash_table_insert(window_map, &self->innerright, self->client);
944     g_hash_table_insert(window_map, &self->innerbottom, self->client);
945     g_hash_table_insert(window_map, &self->title, self->client);
946     g_hash_table_insert(window_map, &self->label, self->client);
947     g_hash_table_insert(window_map, &self->max, self->client);
948     g_hash_table_insert(window_map, &self->close, self->client);
949     g_hash_table_insert(window_map, &self->desk, self->client);
950     g_hash_table_insert(window_map, &self->shade, self->client);
951     g_hash_table_insert(window_map, &self->icon, self->client);
952     g_hash_table_insert(window_map, &self->iconify, self->client);
953     g_hash_table_insert(window_map, &self->handle, self->client);
954     g_hash_table_insert(window_map, &self->lgrip, self->client);
955     g_hash_table_insert(window_map, &self->rgrip, self->client);
956     g_hash_table_insert(window_map, &self->topresize, self->client);
957     g_hash_table_insert(window_map, &self->tltresize, self->client);
958     g_hash_table_insert(window_map, &self->tllresize, self->client);
959     g_hash_table_insert(window_map, &self->trtresize, self->client);
960     g_hash_table_insert(window_map, &self->trrresize, self->client);
961     g_hash_table_insert(window_map, &self->left, self->client);
962     g_hash_table_insert(window_map, &self->right, self->client);
963     g_hash_table_insert(window_map, &self->titleleft, self->client);
964     g_hash_table_insert(window_map, &self->titletop, self->client);
965     g_hash_table_insert(window_map, &self->titletopleft, self->client);
966     g_hash_table_insert(window_map, &self->titletopright, self->client);
967     g_hash_table_insert(window_map, &self->titleright, self->client);
968     g_hash_table_insert(window_map, &self->titlebottom, self->client);
969     g_hash_table_insert(window_map, &self->handleleft, self->client);
970     g_hash_table_insert(window_map, &self->handletop, self->client);
971     g_hash_table_insert(window_map, &self->handleright, self->client);
972     g_hash_table_insert(window_map, &self->handlebottom, self->client);
973     g_hash_table_insert(window_map, &self->lgripleft, self->client);
974     g_hash_table_insert(window_map, &self->lgriptop, self->client);
975     g_hash_table_insert(window_map, &self->lgripbottom, self->client);
976     g_hash_table_insert(window_map, &self->rgripright, self->client);
977     g_hash_table_insert(window_map, &self->rgriptop, self->client);
978     g_hash_table_insert(window_map, &self->rgripbottom, self->client);
979 }
980
981 void frame_release_client(ObFrame *self)
982 {
983     XEvent ev;
984     gboolean reparent = TRUE;
985
986     /* if there was any animation going on, kill it */
987     ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
988                                      self, FALSE);
989
990     /* check if the app has already reparented its window away */
991     while (XCheckTypedWindowEvent(ob_display, self->client->window,
992                                   ReparentNotify, &ev))
993     {
994         /* This check makes sure we don't catch our own reparent action to
995            our frame window. This doesn't count as the app reparenting itself
996            away of course.
997
998            Reparent events that are generated by us are just discarded here.
999            They are of no consequence to us anyhow.
1000         */
1001         if (ev.xreparent.parent != self->window) {
1002             reparent = FALSE;
1003             XPutBackEvent(ob_display, &ev);
1004             break;
1005         }
1006     }
1007
1008     if (reparent) {
1009         /* according to the ICCCM - if the client doesn't reparent itself,
1010            then we will reparent the window to root for them */
1011         XReparentWindow(ob_display, self->client->window,
1012                         RootWindow(ob_display, ob_screen),
1013                         self->client->area.x,
1014                         self->client->area.y);
1015     }
1016
1017     /* remove all the windows for the frame from the window_map */
1018     g_hash_table_remove(window_map, &self->window);
1019     g_hash_table_remove(window_map, &self->backback);
1020     g_hash_table_remove(window_map, &self->backfront);
1021     g_hash_table_remove(window_map, &self->innerleft);
1022     g_hash_table_remove(window_map, &self->innertop);
1023     g_hash_table_remove(window_map, &self->innerright);
1024     g_hash_table_remove(window_map, &self->innerbottom);
1025     g_hash_table_remove(window_map, &self->title);
1026     g_hash_table_remove(window_map, &self->label);
1027     g_hash_table_remove(window_map, &self->max);
1028     g_hash_table_remove(window_map, &self->close);
1029     g_hash_table_remove(window_map, &self->desk);
1030     g_hash_table_remove(window_map, &self->shade);
1031     g_hash_table_remove(window_map, &self->icon);
1032     g_hash_table_remove(window_map, &self->iconify);
1033     g_hash_table_remove(window_map, &self->handle);
1034     g_hash_table_remove(window_map, &self->lgrip);
1035     g_hash_table_remove(window_map, &self->rgrip);
1036     g_hash_table_remove(window_map, &self->topresize);
1037     g_hash_table_remove(window_map, &self->tltresize);
1038     g_hash_table_remove(window_map, &self->tllresize);
1039     g_hash_table_remove(window_map, &self->trtresize);
1040     g_hash_table_remove(window_map, &self->trrresize);
1041     g_hash_table_remove(window_map, &self->left);
1042     g_hash_table_remove(window_map, &self->right);
1043     g_hash_table_remove(window_map, &self->titleleft);
1044     g_hash_table_remove(window_map, &self->titletop);
1045     g_hash_table_remove(window_map, &self->titletopleft);
1046     g_hash_table_remove(window_map, &self->titletopright);
1047     g_hash_table_remove(window_map, &self->titleright);
1048     g_hash_table_remove(window_map, &self->titlebottom);
1049     g_hash_table_remove(window_map, &self->handleleft);
1050     g_hash_table_remove(window_map, &self->handletop);
1051     g_hash_table_remove(window_map, &self->handleright);
1052     g_hash_table_remove(window_map, &self->handlebottom);
1053     g_hash_table_remove(window_map, &self->lgripleft);
1054     g_hash_table_remove(window_map, &self->lgriptop);
1055     g_hash_table_remove(window_map, &self->lgripbottom);
1056     g_hash_table_remove(window_map, &self->rgripright);
1057     g_hash_table_remove(window_map, &self->rgriptop);
1058     g_hash_table_remove(window_map, &self->rgripbottom);
1059
1060     ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
1061 }
1062
1063 /* is there anything present between us and the label? */
1064 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
1065     for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
1066         if (*lc == ' ') continue; /* it was invalid */
1067         if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
1068             return TRUE;
1069         if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
1070             return TRUE;
1071         if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
1072             return TRUE;
1073         if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
1074             return TRUE;
1075         if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
1076             return TRUE;
1077         if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
1078             return TRUE;
1079         if (*lc == 'L') return FALSE;
1080     }
1081     return FALSE;
1082 }
1083
1084 static void layout_title(ObFrame *self)
1085 {
1086     gchar *lc;
1087     gint i;
1088
1089     const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
1090     /* position of the left most button */
1091     const gint left = ob_rr_theme->paddingx + 1;
1092     /* position of the right most button */
1093     const gint right = self->width;
1094
1095     /* turn them all off */
1096     self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
1097         self->max_on = self->close_on = self->label_on = FALSE;
1098     self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
1099     self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
1100
1101     /* figure out what's being show, find each element's position, and the
1102        width of the label
1103
1104        do the ones before the label, then after the label,
1105        i will be +1 the first time through when working to the left,
1106        and -1 the second time through when working to the right */
1107     for (i = 1; i >= -1; i-=2) {
1108         gint x;
1109         ObFrameContext *firstcon;
1110
1111         if (i > 0) {
1112             x = left;
1113             lc = config_title_layout;
1114             firstcon = &self->leftmost;
1115         } else {
1116             x = right;
1117             lc = config_title_layout + strlen(config_title_layout)-1;
1118             firstcon = &self->rightmost;
1119         }
1120
1121         /* stop at the end of the string (or the label, which calls break) */
1122         for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1123             if (*lc == 'L') {
1124                 if (i > 0) {
1125                     self->label_on = TRUE;
1126                     self->label_x = x;
1127                 }
1128                 break; /* break the for loop, do other side of label */
1129             } else if (*lc == 'N') {
1130                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1131                 if ((self->icon_on = is_button_present(self, lc, i))) {
1132                     /* icon is bigger than buttons */
1133                     self->label_width -= bwidth + 2;
1134                     if (i > 0) self->icon_x = x;
1135                     x += i * (bwidth + 2);
1136                     if (i < 0) self->icon_x = x;
1137                 }
1138             } else if (*lc == 'D') {
1139                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1140                 if ((self->desk_on = is_button_present(self, lc, i))) {
1141                     self->label_width -= bwidth;
1142                     if (i > 0) self->desk_x = x;
1143                     x += i * bwidth;
1144                     if (i < 0) self->desk_x = x;
1145                 }
1146             } else if (*lc == 'S') {
1147                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1148                 if ((self->shade_on = is_button_present(self, lc, i))) {
1149                     self->label_width -= bwidth;
1150                     if (i > 0) self->shade_x = x;
1151                     x += i * bwidth;
1152                     if (i < 0) self->shade_x = x;
1153                 }
1154             } else if (*lc == 'I') {
1155                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1156                 if ((self->iconify_on = is_button_present(self, lc, i))) {
1157                     self->label_width -= bwidth;
1158                     if (i > 0) self->iconify_x = x;
1159                     x += i * bwidth;
1160                     if (i < 0) self->iconify_x = x;
1161                 }
1162             } else if (*lc == 'M') {
1163                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1164                 if ((self->max_on = is_button_present(self, lc, i))) {
1165                     self->label_width -= bwidth;
1166                     if (i > 0) self->max_x = x;
1167                     x += i * bwidth;
1168                     if (i < 0) self->max_x = x;
1169                 }
1170             } else if (*lc == 'C') {
1171                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1172                 if ((self->close_on = is_button_present(self, lc, i))) {
1173                     self->label_width -= bwidth;
1174                     if (i > 0) self->close_x = x;
1175                     x += i * bwidth;
1176                     if (i < 0) self->close_x = x;
1177                 }
1178             } else
1179                 continue; /* don't set firstcon */
1180             firstcon = NULL;
1181         }
1182     }
1183
1184     /* position and map the elements */
1185     if (self->icon_on) {
1186         XMapWindow(ob_display, self->icon);
1187         XMoveWindow(ob_display, self->icon, self->icon_x,
1188                     ob_rr_theme->paddingy);
1189     } else
1190         XUnmapWindow(ob_display, self->icon);
1191
1192     if (self->desk_on) {
1193         XMapWindow(ob_display, self->desk);
1194         XMoveWindow(ob_display, self->desk, self->desk_x,
1195                     ob_rr_theme->paddingy + 1);
1196     } else
1197         XUnmapWindow(ob_display, self->desk);
1198
1199     if (self->shade_on) {
1200         XMapWindow(ob_display, self->shade);
1201         XMoveWindow(ob_display, self->shade, self->shade_x,
1202                     ob_rr_theme->paddingy + 1);
1203     } else
1204         XUnmapWindow(ob_display, self->shade);
1205
1206     if (self->iconify_on) {
1207         XMapWindow(ob_display, self->iconify);
1208         XMoveWindow(ob_display, self->iconify, self->iconify_x,
1209                     ob_rr_theme->paddingy + 1);
1210     } else
1211         XUnmapWindow(ob_display, self->iconify);
1212
1213     if (self->max_on) {
1214         XMapWindow(ob_display, self->max);
1215         XMoveWindow(ob_display, self->max, self->max_x,
1216                     ob_rr_theme->paddingy + 1);
1217     } else
1218         XUnmapWindow(ob_display, self->max);
1219
1220     if (self->close_on) {
1221         XMapWindow(ob_display, self->close);
1222         XMoveWindow(ob_display, self->close, self->close_x,
1223                     ob_rr_theme->paddingy + 1);
1224     } else
1225         XUnmapWindow(ob_display, self->close);
1226
1227     if (self->label_on) {
1228         self->label_width = MAX(1, self->label_width); /* no lower than 1 */
1229         XMapWindow(ob_display, self->label);
1230         XMoveWindow(ob_display, self->label, self->label_x,
1231                     ob_rr_theme->paddingy);
1232     } else
1233         XUnmapWindow(ob_display, self->label);
1234 }
1235
1236 ObFrameContext frame_context_from_string(const gchar *name)
1237 {
1238     if (!g_ascii_strcasecmp("Desktop", name))
1239         return OB_FRAME_CONTEXT_DESKTOP;
1240     else if (!g_ascii_strcasecmp("Root", name))
1241         return OB_FRAME_CONTEXT_ROOT;
1242     else if (!g_ascii_strcasecmp("Client", name))
1243         return OB_FRAME_CONTEXT_CLIENT;
1244     else if (!g_ascii_strcasecmp("Titlebar", name))
1245         return OB_FRAME_CONTEXT_TITLEBAR;
1246     else if (!g_ascii_strcasecmp("Frame", name))
1247         return OB_FRAME_CONTEXT_FRAME;
1248     else if (!g_ascii_strcasecmp("TLCorner", name))
1249         return OB_FRAME_CONTEXT_TLCORNER;
1250     else if (!g_ascii_strcasecmp("TRCorner", name))
1251         return OB_FRAME_CONTEXT_TRCORNER;
1252     else if (!g_ascii_strcasecmp("BLCorner", name))
1253         return OB_FRAME_CONTEXT_BLCORNER;
1254     else if (!g_ascii_strcasecmp("BRCorner", name))
1255         return OB_FRAME_CONTEXT_BRCORNER;
1256     else if (!g_ascii_strcasecmp("Top", name))
1257         return OB_FRAME_CONTEXT_TOP;
1258     else if (!g_ascii_strcasecmp("Bottom", name))
1259         return OB_FRAME_CONTEXT_BOTTOM;
1260     else if (!g_ascii_strcasecmp("Left", name))
1261         return OB_FRAME_CONTEXT_LEFT;
1262     else if (!g_ascii_strcasecmp("Right", name))
1263         return OB_FRAME_CONTEXT_RIGHT;
1264     else if (!g_ascii_strcasecmp("Maximize", name))
1265         return OB_FRAME_CONTEXT_MAXIMIZE;
1266     else if (!g_ascii_strcasecmp("AllDesktops", name))
1267         return OB_FRAME_CONTEXT_ALLDESKTOPS;
1268     else if (!g_ascii_strcasecmp("Shade", name))
1269         return OB_FRAME_CONTEXT_SHADE;
1270     else if (!g_ascii_strcasecmp("Iconify", name))
1271         return OB_FRAME_CONTEXT_ICONIFY;
1272     else if (!g_ascii_strcasecmp("Icon", name))
1273         return OB_FRAME_CONTEXT_ICON;
1274     else if (!g_ascii_strcasecmp("Close", name))
1275         return OB_FRAME_CONTEXT_CLOSE;
1276     else if (!g_ascii_strcasecmp("MoveResize", name))
1277         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1278     return OB_FRAME_CONTEXT_NONE;
1279 }
1280
1281 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1282 {
1283     ObFrame *self;
1284
1285     if (moveresize_in_progress)
1286         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1287
1288     if (win == RootWindow(ob_display, ob_screen))
1289         return OB_FRAME_CONTEXT_ROOT ;
1290     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1291     if (win == client->window) {
1292         /* conceptually, this is the desktop, as far as users are
1293            concerned */
1294         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1295             return OB_FRAME_CONTEXT_DESKTOP;
1296         return OB_FRAME_CONTEXT_CLIENT;
1297     }
1298
1299     self = client->frame;
1300
1301     /* when the user clicks in the corners of the titlebar and the client
1302        is fully maximized, then treat it like they clicked in the
1303        button that is there */
1304     if (self->max_horz && self->max_vert &&
1305         (win == self->title || win == self->titletop ||
1306          win == self->titleleft || win == self->titletopleft ||
1307          win == self->titleright || win == self->titletopright))
1308     {
1309         /* get the mouse coords in reference to the whole frame */
1310         gint fx = x;
1311         gint fy = y;
1312
1313         /* these windows are down a border width from the top of the frame */
1314         if (win == self->title ||
1315             win == self->titleleft || win == self->titleright)
1316             fy += self->bwidth;
1317
1318         /* title is a border width in from the edge */
1319         if (win == self->title)
1320             fx += self->bwidth;
1321         /* titletop is a bit to the right */
1322         else if (win == self->titletop)
1323             fx += ob_rr_theme->grip_width + self->bwidth;
1324         /* titletopright is way to the right edge */
1325         else if (win == self->titletopright)
1326             fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1327         /* titleright is even more way to the right edge */
1328         else if (win == self->titleright)
1329             fx += self->area.width - self->bwidth;
1330
1331         /* figure out if we're over the area that should be considered a
1332            button */
1333         if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1334             ob_rr_theme->button_size)
1335         {
1336             if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1337                       ob_rr_theme->button_size))
1338             {
1339                 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1340                     return self->leftmost;
1341             }
1342             else if (fx >= (self->area.width -
1343                             (self->bwidth + ob_rr_theme->paddingx + 1 +
1344                              ob_rr_theme->button_size)))
1345             {
1346                 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1347                     return self->rightmost;
1348             }
1349         }
1350
1351         /* there is no resizing maximized windows so make them the titlebar
1352            context */
1353         return OB_FRAME_CONTEXT_TITLEBAR;
1354     }
1355     else if (self->max_vert &&
1356              (win == self->titletop || win == self->topresize))
1357         /* can't resize vertically when max vert */
1358         return OB_FRAME_CONTEXT_TITLEBAR;
1359     else if (self->shaded &&
1360              (win == self->titletop || win == self->topresize))
1361         /* can't resize vertically when shaded */
1362         return OB_FRAME_CONTEXT_TITLEBAR;
1363
1364     if (win == self->window)            return OB_FRAME_CONTEXT_FRAME;
1365     if (win == self->label)             return OB_FRAME_CONTEXT_TITLEBAR;
1366     if (win == self->handle)            return OB_FRAME_CONTEXT_BOTTOM;
1367     if (win == self->handletop)         return OB_FRAME_CONTEXT_BOTTOM;
1368     if (win == self->handlebottom)      return OB_FRAME_CONTEXT_BOTTOM;
1369     if (win == self->handleleft)        return OB_FRAME_CONTEXT_BLCORNER;
1370     if (win == self->lgrip)             return OB_FRAME_CONTEXT_BLCORNER;
1371     if (win == self->lgripleft)         return OB_FRAME_CONTEXT_BLCORNER;
1372     if (win == self->lgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1373     if (win == self->lgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1374     if (win == self->handleright)       return OB_FRAME_CONTEXT_BRCORNER;
1375     if (win == self->rgrip)             return OB_FRAME_CONTEXT_BRCORNER;
1376     if (win == self->rgripright)        return OB_FRAME_CONTEXT_BLCORNER;
1377     if (win == self->rgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1378     if (win == self->rgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1379     if (win == self->title)             return OB_FRAME_CONTEXT_TITLEBAR;
1380     if (win == self->titlebottom)       return OB_FRAME_CONTEXT_TITLEBAR;
1381     if (win == self->titleleft)         return OB_FRAME_CONTEXT_TLCORNER;
1382     if (win == self->titletopleft)      return OB_FRAME_CONTEXT_TLCORNER;
1383     if (win == self->titleright)        return OB_FRAME_CONTEXT_TRCORNER;
1384     if (win == self->titletopright)     return OB_FRAME_CONTEXT_TRCORNER;
1385     if (win == self->titletop)          return OB_FRAME_CONTEXT_TOP;
1386     if (win == self->topresize)         return OB_FRAME_CONTEXT_TOP;
1387     if (win == self->tltresize)         return OB_FRAME_CONTEXT_TLCORNER;
1388     if (win == self->tllresize)         return OB_FRAME_CONTEXT_TLCORNER;
1389     if (win == self->trtresize)         return OB_FRAME_CONTEXT_TRCORNER;
1390     if (win == self->trrresize)         return OB_FRAME_CONTEXT_TRCORNER;
1391     if (win == self->left)              return OB_FRAME_CONTEXT_LEFT;
1392     if (win == self->right)             return OB_FRAME_CONTEXT_RIGHT;
1393     if (win == self->innertop)          return OB_FRAME_CONTEXT_TITLEBAR;
1394     if (win == self->innerleft)         return OB_FRAME_CONTEXT_LEFT;
1395     if (win == self->innerbottom)       return OB_FRAME_CONTEXT_BOTTOM;
1396     if (win == self->innerright)        return OB_FRAME_CONTEXT_RIGHT;
1397     if (win == self->max)               return OB_FRAME_CONTEXT_MAXIMIZE;
1398     if (win == self->iconify)           return OB_FRAME_CONTEXT_ICONIFY;
1399     if (win == self->close)             return OB_FRAME_CONTEXT_CLOSE;
1400     if (win == self->icon)              return OB_FRAME_CONTEXT_ICON;
1401     if (win == self->desk)              return OB_FRAME_CONTEXT_ALLDESKTOPS;
1402     if (win == self->shade)             return OB_FRAME_CONTEXT_SHADE;
1403
1404     return OB_FRAME_CONTEXT_NONE;
1405 }
1406
1407 void frame_client_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1408 {
1409     /* horizontal */
1410     switch (self->client->gravity) {
1411     default:
1412     case NorthWestGravity:
1413     case SouthWestGravity:
1414     case WestGravity:
1415         break;
1416
1417     case NorthGravity:
1418     case SouthGravity:
1419     case CenterGravity:
1420         /* the middle of the client will be the middle of the frame */
1421         *x -= (self->size.right - self->size.left) / 2;
1422         break;
1423
1424     case NorthEastGravity:
1425     case SouthEastGravity:
1426     case EastGravity:
1427         /* the right side of the client will be the right side of the frame */
1428         *x -= self->size.right + self->size.left -
1429             self->client->border_width * 2;
1430         break;
1431
1432     case ForgetGravity:
1433     case StaticGravity:
1434         /* the client's position won't move */
1435         *x -= self->size.left - self->client->border_width;
1436         break;
1437     }
1438
1439     /* vertical */
1440     switch (self->client->gravity) {
1441     default:
1442     case NorthWestGravity:
1443     case NorthEastGravity:
1444     case NorthGravity:
1445         break;
1446
1447     case CenterGravity:
1448     case EastGravity:
1449     case WestGravity:
1450         /* the middle of the client will be the middle of the frame */
1451         *y -= (self->size.bottom - self->size.top) / 2;
1452         break;
1453
1454     case SouthWestGravity:
1455     case SouthEastGravity:
1456     case SouthGravity:
1457         /* the bottom of the client will be the bottom of the frame */
1458         *y -= self->size.bottom + self->size.top -
1459             self->client->border_width * 2;
1460         break;
1461
1462     case ForgetGravity:
1463     case StaticGravity:
1464         /* the client's position won't move */
1465         *y -= self->size.top - self->client->border_width;
1466         break;
1467     }
1468 }
1469
1470 void frame_frame_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1471 {
1472     /* horizontal */
1473     switch (self->client->gravity) {
1474     default:
1475     case NorthWestGravity:
1476     case WestGravity:
1477     case SouthWestGravity:
1478         break;
1479     case NorthGravity:
1480     case CenterGravity:
1481     case SouthGravity:
1482         /* the middle of the client will be the middle of the frame */
1483         *x += (self->size.right - self->size.left) / 2;
1484         break;
1485     case NorthEastGravity:
1486     case EastGravity:
1487     case SouthEastGravity:
1488         /* the right side of the client will be the right side of the frame */
1489         *x += self->size.right + self->size.left -
1490             self->client->border_width * 2;
1491         break;
1492     case StaticGravity:
1493     case ForgetGravity:
1494         /* the client's position won't move */
1495         *x += self->size.left - self->client->border_width;
1496         break;
1497     }
1498
1499     /* vertical */
1500     switch (self->client->gravity) {
1501     default:
1502     case NorthWestGravity:
1503     case NorthGravity:
1504     case NorthEastGravity:
1505         break;
1506     case WestGravity:
1507     case CenterGravity:
1508     case EastGravity:
1509         /* the middle of the client will be the middle of the frame */
1510         *y += (self->size.bottom - self->size.top) / 2;
1511         break;
1512     case SouthWestGravity:
1513     case SouthGravity:
1514     case SouthEastGravity:
1515         /* the bottom of the client will be the bottom of the frame */
1516         *y += self->size.bottom + self->size.top -
1517             self->client->border_width * 2;
1518         break;
1519     case StaticGravity:
1520     case ForgetGravity:
1521         /* the client's position won't move */
1522         *y += self->size.top - self->client->border_width;
1523         break;
1524     }
1525 }
1526
1527 void frame_rect_to_frame(ObFrame *self, Rect *r)
1528 {
1529     r->width += self->size.left + self->size.right;
1530     r->height += self->size.top + self->size.bottom;
1531     frame_client_gravity(self, &r->x, &r->y, r->width, r->height);
1532 }
1533
1534 static void flash_done(gpointer data)
1535 {
1536     ObFrame *self = data;
1537
1538     if (self->focused != self->flash_on)
1539         frame_adjust_focus(self, self->focused);
1540 }
1541
1542 static gboolean flash_timeout(gpointer data)
1543 {
1544     ObFrame *self = data;
1545     GTimeVal now;
1546
1547     g_get_current_time(&now);
1548     if (now.tv_sec > self->flash_end.tv_sec ||
1549         (now.tv_sec == self->flash_end.tv_sec &&
1550          now.tv_usec >= self->flash_end.tv_usec))
1551         self->flashing = FALSE;
1552
1553     if (!self->flashing)
1554         return FALSE; /* we are done */
1555
1556     self->flash_on = !self->flash_on;
1557     if (!self->focused) {
1558         frame_adjust_focus(self, self->flash_on);
1559         self->focused = FALSE;
1560     }
1561
1562     return TRUE; /* go again */
1563 }
1564
1565 void frame_flash_start(ObFrame *self)
1566 {
1567     self->flash_on = self->focused;
1568
1569     if (!self->flashing)
1570         ob_main_loop_timeout_add(ob_main_loop,
1571                                  G_USEC_PER_SEC * 0.6,
1572                                  flash_timeout,
1573                                  self,
1574                                  g_direct_equal,
1575                                  flash_done);
1576     g_get_current_time(&self->flash_end);
1577     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1578     
1579     self->flashing = TRUE;
1580 }
1581
1582 void frame_flash_stop(ObFrame *self)
1583 {
1584     self->flashing = FALSE;
1585 }
1586
1587 static gulong frame_animate_iconify_time_left(ObFrame *self,
1588                                               const GTimeVal *now)
1589 {
1590     glong sec, usec;
1591     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1592     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1593     if (usec < 0) {
1594         usec += G_USEC_PER_SEC;
1595         sec--;
1596     }
1597     /* no negative values */
1598     return MAX(sec * G_USEC_PER_SEC + usec, 0);
1599 }
1600
1601 static gboolean frame_animate_iconify(gpointer p)
1602 {
1603     ObFrame *self = p;
1604     gint x, y, w, h;
1605     gint iconx, icony, iconw;
1606     GTimeVal now;
1607     gulong time;
1608     gboolean iconifying;
1609
1610     if (self->client->icon_geometry.width == 0) {
1611         /* there is no icon geometry set so just go straight down */
1612         Rect *a = screen_physical_area_monitor
1613             (screen_find_monitor(&self->area));
1614         iconx = self->area.x + self->area.width / 2 + 32;
1615         icony = a->y + a->width;
1616         iconw = 64;
1617         g_free(a);
1618     } else {
1619         iconx = self->client->icon_geometry.x;
1620         icony = self->client->icon_geometry.y;
1621         iconw = self->client->icon_geometry.width;
1622     }
1623
1624     iconifying = self->iconify_animation_going > 0;
1625
1626     /* how far do we have left to go ? */
1627     g_get_current_time(&now);
1628     time = frame_animate_iconify_time_left(self, &now);
1629     
1630     if (time == 0 || iconifying) {
1631         /* start where the frame is supposed to be */
1632         x = self->area.x;
1633         y = self->area.y;
1634         w = self->area.width;
1635         h = self->area.height;
1636     } else {
1637         /* start at the icon */
1638         x = iconx;
1639         y = icony;
1640         w = iconw;
1641         h = self->size.top; /* just the titlebar */
1642     }
1643
1644     if (time > 0) {
1645         glong dx, dy, dw;
1646         glong elapsed;
1647
1648         dx = self->area.x - iconx;
1649         dy = self->area.y - icony;
1650         dw = self->area.width - self->bwidth * 2 - iconw;
1651          /* if restoring, we move in the opposite direction */
1652         if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1653
1654         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1655         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1656         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1657         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1658         h = self->size.top; /* just the titlebar */
1659     }
1660
1661     if (time == 0)
1662         frame_end_iconify_animation(self);
1663     else {
1664         XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1665         XFlush(ob_display);
1666     }
1667
1668     return time > 0; /* repeat until we're out of time */
1669 }
1670
1671 void frame_end_iconify_animation(ObFrame *self)
1672 {
1673     /* see if there is an animation going */
1674     if (self->iconify_animation_going == 0) return;
1675
1676     if (!self->visible)
1677         XUnmapWindow(ob_display, self->window);
1678     else {
1679         /* Send a ConfigureNotify when the animation is done, this fixes
1680            KDE's pager showing the window in the wrong place. */
1681         client_reconfigure(self->client, TRUE);
1682
1683         /* the offscreen buffer is invalid when the window is resized */
1684         frame_get_offscreen_buffer(self);
1685     }
1686
1687     /* we're not animating any more ! */
1688     self->iconify_animation_going = 0;
1689
1690     XMoveResizeWindow(ob_display, self->window,
1691                       self->area.x, self->area.y,
1692                       self->area.width, self->area.height);
1693     /* we delay re-rendering until after we're done animating */
1694     framerender_frame(self);
1695     XFlush(ob_display);
1696 }
1697
1698 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1699 {
1700     gulong time;
1701     gboolean new_anim = FALSE;
1702     gboolean set_end = TRUE;
1703     GTimeVal now;
1704
1705     /* if there is no titlebar, just don't animate for now
1706        XXX it would be nice tho.. */
1707     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1708         return;
1709
1710     /* get the current time */
1711     g_get_current_time(&now);
1712
1713     /* get how long until the end */
1714     time = FRAME_ANIMATE_ICONIFY_TIME;
1715     if (self->iconify_animation_going) {
1716         if (!!iconifying != (self->iconify_animation_going > 0)) {
1717             /* animation was already going on in the opposite direction */
1718             time = time - frame_animate_iconify_time_left(self, &now);
1719         } else
1720             /* animation was already going in the same direction */
1721             set_end = FALSE;
1722     } else
1723         new_anim = TRUE;
1724     self->iconify_animation_going = iconifying ? 1 : -1;
1725
1726     /* set the ending time */
1727     if (set_end) {
1728         self->iconify_animation_end.tv_sec = now.tv_sec;
1729         self->iconify_animation_end.tv_usec = now.tv_usec;
1730         g_time_val_add(&self->iconify_animation_end, time);
1731     }
1732
1733     if (new_anim) {
1734         ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1735                                          self, FALSE);
1736         ob_main_loop_timeout_add(ob_main_loop,
1737                                  FRAME_ANIMATE_ICONIFY_STEP_TIME,
1738                                  frame_animate_iconify, self,
1739                                  g_direct_equal, NULL);
1740
1741         /* do the first step */
1742         frame_animate_iconify(self);
1743
1744         /* show it during the animation even if it is not "visible" */
1745         if (!self->visible)
1746             XMapWindow(ob_display, self->window);
1747     }
1748 }
1749
1750 static void frame_get_offscreen_buffer(ObFrame *self)
1751 {
1752     frame_free_offscreen_buffer(self);
1753
1754     if (self->visible || frame_iconify_animating(self)) {
1755         self->pixmap = composite_get_window_pixmap(self->client->window);
1756
1757 #ifdef SHAPE
1758         /* shape the offscreen buffer to match the window */
1759         XShapeCombineShape(ob_display, self->pixmap, ShapeBounding,
1760                            0, 0, self->client->window,
1761                            ShapeBounding, ShapeSet);
1762 #endif
1763
1764         /*
1765           self->picture = composite_create_picture(self->window,
1766           wattrib.visual,
1767           &self->has_alpha);
1768         */
1769     }
1770
1771 }
1772
1773 static void frame_free_offscreen_buffer(ObFrame *self)
1774 {
1775     if (self->pixmap) {
1776         XFreePixmap(ob_display, self->pixmap);
1777         self->pixmap = None;
1778     }
1779 }