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