67adb40535e113a1f23676746d93efeb5852e39a
[dana/openbox.git] / openbox / frame.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    frame.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "frame.h"
21 #include "client.h"
22 #include "openbox.h"
23 #include "extensions.h"
24 #include "prop.h"
25 #include "grab.h"
26 #include "debug.h"
27 #include "config.h"
28 #include "framerender.h"
29 #include "mainloop.h"
30 #include "focus_cycle.h"
31 #include "focus_cycle_indicator.h"
32 #include "moveresize.h"
33 #include "screen.h"
34 #include "render/theme.h"
35
36 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
37                          ButtonPressMask | ButtonReleaseMask | \
38                          SubstructureRedirectMask | FocusChangeMask)
39 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
40                            ButtonMotionMask | PointerMotionMask | \
41                            EnterWindowMask | LeaveWindowMask)
42
43 #define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
44 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (G_USEC_PER_SEC / 60) /* 60 Hz */
45
46 #define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b)
47
48 static void flash_done(gpointer data);
49 static gboolean flash_timeout(gpointer data);
50
51 static void layout_title(ObFrame *self);
52 static void set_theme_statics(ObFrame *self);
53 static void free_theme_statics(ObFrame *self);
54 static gboolean frame_animate_iconify(gpointer self);
55 static void frame_adjust_cursors(ObFrame *self);
56
57 static Window createWindow(Window parent, Visual *visual,
58                            gulong mask, XSetWindowAttributes *attrib)
59 {
60     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
61                          (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
62                          (visual ? visual : RrVisual(ob_rr_inst)),
63                          mask, attrib);
64
65 }
66
67 static Visual *check_32bit_client(ObClient *c)
68 {
69     XWindowAttributes wattrib;
70     Status ret;
71
72     /* we're already running at 32 bit depth, yay. we don't need to use their
73        visual */
74     if (RrDepth(ob_rr_inst) == 32)
75         return NULL;
76
77     ret = XGetWindowAttributes(ob_display, c->window, &wattrib);
78     g_assert(ret != BadDrawable);
79     g_assert(ret != BadWindow);
80
81     if (wattrib.depth == 32)
82         return wattrib.visual;
83     return NULL;
84 }
85
86 ObFrame *frame_new(ObClient *client)
87 {
88     XSetWindowAttributes attrib;
89     gulong mask;
90     ObFrame *self;
91     Visual *visual;
92
93     self = g_new0(ObFrame, 1);
94     self->client = client;
95
96     visual = check_32bit_client(client);
97
98     /* create the non-visible decor windows */
99
100     mask = 0;
101     if (visual) {
102         /* client has a 32-bit visual */
103         mask = CWColormap | CWBackPixel | CWBorderPixel;
104         /* create a colormap with the visual */
105         self->colormap = attrib.colormap =
106             XCreateColormap(ob_display,
107                             RootWindow(ob_display, ob_screen),
108                             visual, AllocNone);
109         attrib.background_pixel = BlackPixel(ob_display, ob_screen);
110         attrib.border_pixel = BlackPixel(ob_display, ob_screen);
111     }
112     self->window = createWindow(RootWindow(ob_display, ob_screen), visual,
113                                 mask, &attrib);
114
115     /* create the visible decor windows */
116
117     mask = 0;
118     if (visual) {
119         /* client has a 32-bit visual */
120         mask = CWColormap | CWBackPixel | CWBorderPixel;
121         attrib.colormap = RrColormap(ob_rr_inst);
122     }
123
124     self->backback = createWindow(self->window, NULL, mask, &attrib);
125     self->backfront = createWindow(self->backback, NULL, mask, &attrib);
126
127     mask |= CWEventMask;
128     attrib.event_mask = ELEMENT_EVENTMASK;
129     self->innerleft = createWindow(self->window, NULL, mask, &attrib);
130     self->innertop = createWindow(self->window, NULL, mask, &attrib);
131     self->innerright = createWindow(self->window, NULL, mask, &attrib);
132     self->innerbottom = createWindow(self->window, NULL, mask, &attrib);
133
134     self->innerblb = createWindow(self->innerbottom, NULL, mask, &attrib);
135     self->innerbrb = createWindow(self->innerbottom, NULL, mask, &attrib);
136     self->innerbll = createWindow(self->innerleft, NULL, mask, &attrib);
137     self->innerbrr = createWindow(self->innerright, NULL, mask, &attrib);
138
139     self->title = createWindow(self->window, NULL, mask, &attrib);
140     self->titleleft = createWindow(self->window, NULL, mask, &attrib);
141     self->titletop = createWindow(self->window, NULL, mask, &attrib);
142     self->titletopleft = createWindow(self->window, NULL, mask, &attrib);
143     self->titletopright = createWindow(self->window, NULL, mask, &attrib);
144     self->titleright = createWindow(self->window, NULL, mask, &attrib);
145     self->titlebottom = createWindow(self->window, NULL, mask, &attrib);
146
147     self->topresize = createWindow(self->title, NULL, mask, &attrib);
148     self->tltresize = createWindow(self->title, NULL, mask, &attrib);
149     self->tllresize = createWindow(self->title, NULL, mask, &attrib);
150     self->trtresize = createWindow(self->title, NULL, mask, &attrib);
151     self->trrresize = createWindow(self->title, NULL, mask, &attrib);
152
153     self->left = createWindow(self->window, NULL, mask, &attrib);
154     self->right = createWindow(self->window, NULL, mask, &attrib);
155
156     self->label = createWindow(self->title, NULL, mask, &attrib);
157     self->max = createWindow(self->title, NULL, mask, &attrib);
158     self->close = createWindow(self->title, NULL, mask, &attrib);
159     self->desk = createWindow(self->title, NULL, mask, &attrib);
160     self->shade = createWindow(self->title, NULL, mask, &attrib);
161     self->icon = createWindow(self->title, NULL, mask, &attrib);
162     self->iconify = createWindow(self->title, NULL, mask, &attrib);
163
164     self->handle = createWindow(self->window, NULL, mask, &attrib);
165     self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
166     self->rgrip = createWindow(self->handle, NULL, mask, &attrib);
167
168     self->handleleft = createWindow(self->handle, NULL, mask, &attrib);
169     self->handleright = createWindow(self->handle, NULL, mask, &attrib);
170
171     self->handletop = createWindow(self->window, NULL, mask, &attrib);
172     self->handlebottom = createWindow(self->window, NULL, mask, &attrib);
173     self->lgripleft = createWindow(self->window, NULL, mask, &attrib);
174     self->lgriptop = createWindow(self->window, NULL, mask, &attrib);
175     self->lgripbottom = createWindow(self->window, NULL, mask, &attrib);
176     self->rgripright = createWindow(self->window, NULL, mask, &attrib);
177     self->rgriptop = createWindow(self->window, NULL, mask, &attrib);
178     self->rgripbottom = createWindow(self->window, NULL, mask, &attrib);
179
180     self->focused = FALSE;
181
182     /* the other stuff is shown based on decor settings */
183     XMapWindow(ob_display, self->label);
184     XMapWindow(ob_display, self->backback);
185     XMapWindow(ob_display, self->backfront);
186
187     self->max_press = self->close_press = self->desk_press =
188         self->iconify_press = self->shade_press = FALSE;
189     self->max_hover = self->close_hover = self->desk_hover =
190         self->iconify_hover = self->shade_hover = FALSE;
191
192     set_theme_statics(self);
193
194     return self;
195 }
196
197 static void set_theme_statics(ObFrame *self)
198 {
199     /* set colors/appearance/sizes for stuff that doesn't change */
200     XResizeWindow(ob_display, self->max,
201                   ob_rr_theme->button_size, ob_rr_theme->button_size);
202     XResizeWindow(ob_display, self->iconify,
203                   ob_rr_theme->button_size, ob_rr_theme->button_size);
204     XResizeWindow(ob_display, self->icon,
205                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
206     XResizeWindow(ob_display, self->close,
207                   ob_rr_theme->button_size, ob_rr_theme->button_size);
208     XResizeWindow(ob_display, self->desk,
209                   ob_rr_theme->button_size, ob_rr_theme->button_size);
210     XResizeWindow(ob_display, self->shade,
211                   ob_rr_theme->button_size, ob_rr_theme->button_size);
212     XResizeWindow(ob_display, self->tltresize,
213                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
214     XResizeWindow(ob_display, self->trtresize,
215                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
216     XResizeWindow(ob_display, self->tllresize,
217                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
218     XResizeWindow(ob_display, self->trrresize,
219                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
220 }
221
222 static void free_theme_statics(ObFrame *self)
223 {
224 }
225
226 void frame_free(ObFrame *self)
227 {
228     free_theme_statics(self);
229
230     XDestroyWindow(ob_display, self->window);
231     if (self->colormap)
232         XFreeColormap(ob_display, self->colormap);
233
234     g_free(self);
235 }
236
237 void frame_show(ObFrame *self)
238 {
239     if (!self->visible) {
240         self->visible = TRUE;
241         framerender_frame(self);
242         /* Grab the server to make sure that the frame window is mapped before
243            the client gets its MapNotify, i.e. to make sure the client is
244            _visible_ when it gets MapNotify. */
245         grab_server(TRUE);
246         XMapWindow(ob_display, self->client->window);
247         XMapWindow(ob_display, self->window);
248         grab_server(FALSE);
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         /* we unmap the client itself so that we can get MapRequest
259            events, and because the ICCCM tells us to! */
260         XUnmapWindow(ob_display, self->client->window);
261         self->client->ignore_unmaps += 1;
262     }
263 }
264
265 void frame_adjust_theme(ObFrame *self)
266 {
267     free_theme_statics(self);
268     set_theme_statics(self);
269 }
270
271 #ifdef SHAPE
272 void frame_adjust_shape_kind(ObFrame *self, int kind)
273 {
274     gint num;
275     XRectangle xrect[2];
276
277     if (!((kind == ShapeBounding && self->client->shaped) ||
278           (kind == ShapeInput && self->client->shaped_input))) {
279         /* clear the shape on the frame window */
280         XShapeCombineMask(ob_display, self->window, kind,
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, kind,
287                            self->size.left,
288                            self->size.top,
289                            self->client->window,
290                            kind, 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                                 kind, 0, 0, xrect, num,
314                                 ShapeUnion, Unsorted);
315     }
316 }
317 #endif
318
319 void frame_adjust_shape(ObFrame *self)
320 {
321 #ifdef SHAPE
322   frame_adjust_shape_kind(self, ShapeBounding);
323   frame_adjust_shape_kind(self, ShapeInput);
324 #endif
325 }
326
327 void frame_adjust_area(ObFrame *self, gboolean moved,
328                        gboolean resized, gboolean fake)
329 {
330     Strut oldsize;
331
332     oldsize = self->size;
333
334     if (resized) {
335         /* do this before changing the frame's status like max_horz max_vert */
336         frame_adjust_cursors(self);
337
338         self->functions = self->client->functions;
339         self->decorations = self->client->decorations;
340         self->max_horz = self->client->max_horz;
341         self->max_vert = self->client->max_vert;
342         self->shaded = self->client->shaded;
343
344         if (self->decorations & OB_FRAME_DECOR_BORDER)
345             self->bwidth = ob_rr_theme->fbwidth;
346         else
347             self->bwidth = 0;
348
349         if (self->decorations & OB_FRAME_DECOR_BORDER &&
350             !self->client->undecorated)
351         {
352             self->cbwidth_l = self->cbwidth_r = ob_rr_theme->cbwidthx;
353             self->cbwidth_t = self->cbwidth_b = ob_rr_theme->cbwidthy;
354         } else
355             self->cbwidth_l = self->cbwidth_t =
356                 self->cbwidth_r = self->cbwidth_b = 0;
357
358         if (self->max_horz) {
359             self->cbwidth_l = self->cbwidth_r = 0;
360             self->width = self->client->area.width;
361             if (self->max_vert)
362                 self->cbwidth_b = 0;
363         } else
364             self->width = self->client->area.width +
365                 self->cbwidth_l + self->cbwidth_r;
366
367         /* some elements are sized based of the width, so don't let them have
368            negative values */
369         self->width = MAX(self->width,
370                           (ob_rr_theme->grip_width + self->bwidth) * 2 + 1);
371
372         STRUT_SET(self->size,
373                   self->cbwidth_l + (!self->max_horz ? self->bwidth : 0),
374                   self->cbwidth_t + self->bwidth,
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         self->label_width)
867     {
868         XResizeWindow(ob_display, self->label, self->label_width,
869                       ob_rr_theme->label_height);
870     }
871 }
872
873 static void frame_adjust_cursors(ObFrame *self)
874 {
875     if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
876         (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
877         self->max_horz != self->client->max_horz ||
878         self->max_vert != self->client->max_vert ||
879         self->shaded != self->client->shaded)
880     {
881         gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
882             !(self->client->max_horz && self->client->max_vert);
883         gboolean topbot = !self->client->max_vert;
884         gboolean sh = self->client->shaded;
885         XSetWindowAttributes a;
886
887         /* these ones turn off when max vert, and some when shaded */
888         a.cursor = ob_cursor(r && topbot && !sh ?
889                              OB_CURSOR_NORTH : OB_CURSOR_NONE);
890         XChangeWindowAttributes(ob_display, self->topresize, CWCursor, &a);
891         XChangeWindowAttributes(ob_display, self->titletop, CWCursor, &a);
892         a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
893         XChangeWindowAttributes(ob_display, self->handle, CWCursor, &a);
894         XChangeWindowAttributes(ob_display, self->handletop, CWCursor, &a);
895         XChangeWindowAttributes(ob_display, self->handlebottom, CWCursor, &a);
896         XChangeWindowAttributes(ob_display, self->innerbottom, CWCursor, &a);
897
898         /* these ones change when shaded */
899         a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
900                              OB_CURSOR_NONE);
901         XChangeWindowAttributes(ob_display, self->titleleft, CWCursor, &a);
902         XChangeWindowAttributes(ob_display, self->tltresize, CWCursor, &a);
903         XChangeWindowAttributes(ob_display, self->tllresize, CWCursor, &a);
904         XChangeWindowAttributes(ob_display, self->titletopleft, CWCursor, &a);
905         a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
906                              OB_CURSOR_NONE);
907         XChangeWindowAttributes(ob_display, self->titleright, CWCursor, &a);
908         XChangeWindowAttributes(ob_display, self->trtresize, CWCursor, &a);
909         XChangeWindowAttributes(ob_display, self->trrresize, CWCursor, &a);
910         XChangeWindowAttributes(ob_display, self->titletopright, CWCursor, &a);
911
912         /* these ones are pretty static */
913         a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
914         XChangeWindowAttributes(ob_display, self->left, CWCursor, &a);
915         XChangeWindowAttributes(ob_display, self->innerleft, CWCursor, &a);
916         a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
917         XChangeWindowAttributes(ob_display, self->right, CWCursor, &a);
918         XChangeWindowAttributes(ob_display, self->innerright, CWCursor, &a);
919         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
920         XChangeWindowAttributes(ob_display, self->lgrip, CWCursor, &a);
921         XChangeWindowAttributes(ob_display, self->handleleft, CWCursor, &a);
922         XChangeWindowAttributes(ob_display, self->lgripleft, CWCursor, &a);
923         XChangeWindowAttributes(ob_display, self->lgriptop, CWCursor, &a);
924         XChangeWindowAttributes(ob_display, self->lgripbottom, CWCursor, &a);
925         XChangeWindowAttributes(ob_display, self->innerbll, CWCursor, &a);
926         XChangeWindowAttributes(ob_display, self->innerblb, CWCursor, &a);
927         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
928         XChangeWindowAttributes(ob_display, self->rgrip, CWCursor, &a);
929         XChangeWindowAttributes(ob_display, self->handleright, CWCursor, &a);
930         XChangeWindowAttributes(ob_display, self->rgripright, CWCursor, &a);
931         XChangeWindowAttributes(ob_display, self->rgriptop, CWCursor, &a);
932         XChangeWindowAttributes(ob_display, self->rgripbottom, CWCursor, &a);
933         XChangeWindowAttributes(ob_display, self->innerbrr, CWCursor, &a);
934         XChangeWindowAttributes(ob_display, self->innerbrb, CWCursor, &a);
935     }
936 }
937
938 void frame_adjust_client_area(ObFrame *self)
939 {
940     /* adjust the window which is there to prevent flashing on unmap */
941     XMoveResizeWindow(ob_display, self->backfront, 0, 0,
942                       self->client->area.width,
943                       self->client->area.height);
944 }
945
946 void frame_adjust_state(ObFrame *self)
947 {
948     self->need_render = TRUE;
949     framerender_frame(self);
950 }
951
952 void frame_adjust_focus(ObFrame *self, gboolean hilite)
953 {
954     ob_debug_type(OB_DEBUG_FOCUS,
955                   "Frame for 0x%x has focus: %d\n",
956                   self->client->window, hilite);
957     self->focused = hilite;
958     self->need_render = TRUE;
959     framerender_frame(self);
960     XFlush(ob_display);
961 }
962
963 void frame_adjust_title(ObFrame *self)
964 {
965     self->need_render = TRUE;
966     framerender_frame(self);
967 }
968
969 void frame_adjust_icon(ObFrame *self)
970 {
971     self->need_render = TRUE;
972     framerender_frame(self);
973 }
974
975 void frame_grab_client(ObFrame *self)
976 {
977     /* DO NOT map the client window here. we used to do that, but it is bogus.
978        we need to set up the client's dimensions and everything before we
979        send a mapnotify or we create race conditions.
980     */
981
982     /* reparent the client to the frame */
983     XReparentWindow(ob_display, self->client->window, self->window, 0, 0);
984
985     /*
986       When reparenting the client window, it is usually not mapped yet, since
987       this occurs from a MapRequest. However, in the case where Openbox is
988       starting up, the window is already mapped, so we'll see an unmap event
989       for it.
990     */
991     if (ob_state() == OB_STATE_STARTING)
992         ++self->client->ignore_unmaps;
993
994     /* select the event mask on the client's parent (to receive config/map
995        req's) the ButtonPress is to catch clicks on the client border */
996     XSelectInput(ob_display, self->window, FRAME_EVENTMASK);
997
998     /* set all the windows for the frame in the window_map */
999     g_hash_table_insert(window_map, &self->window, self->client);
1000     g_hash_table_insert(window_map, &self->backback, self->client);
1001     g_hash_table_insert(window_map, &self->backfront, self->client);
1002     g_hash_table_insert(window_map, &self->innerleft, self->client);
1003     g_hash_table_insert(window_map, &self->innertop, self->client);
1004     g_hash_table_insert(window_map, &self->innerright, self->client);
1005     g_hash_table_insert(window_map, &self->innerbottom, self->client);
1006     g_hash_table_insert(window_map, &self->innerblb, self->client);
1007     g_hash_table_insert(window_map, &self->innerbll, self->client);
1008     g_hash_table_insert(window_map, &self->innerbrb, self->client);
1009     g_hash_table_insert(window_map, &self->innerbrr, self->client);
1010     g_hash_table_insert(window_map, &self->title, self->client);
1011     g_hash_table_insert(window_map, &self->label, self->client);
1012     g_hash_table_insert(window_map, &self->max, self->client);
1013     g_hash_table_insert(window_map, &self->close, self->client);
1014     g_hash_table_insert(window_map, &self->desk, self->client);
1015     g_hash_table_insert(window_map, &self->shade, self->client);
1016     g_hash_table_insert(window_map, &self->icon, self->client);
1017     g_hash_table_insert(window_map, &self->iconify, self->client);
1018     g_hash_table_insert(window_map, &self->handle, self->client);
1019     g_hash_table_insert(window_map, &self->lgrip, self->client);
1020     g_hash_table_insert(window_map, &self->rgrip, self->client);
1021     g_hash_table_insert(window_map, &self->topresize, self->client);
1022     g_hash_table_insert(window_map, &self->tltresize, self->client);
1023     g_hash_table_insert(window_map, &self->tllresize, self->client);
1024     g_hash_table_insert(window_map, &self->trtresize, self->client);
1025     g_hash_table_insert(window_map, &self->trrresize, self->client);
1026     g_hash_table_insert(window_map, &self->left, self->client);
1027     g_hash_table_insert(window_map, &self->right, self->client);
1028     g_hash_table_insert(window_map, &self->titleleft, self->client);
1029     g_hash_table_insert(window_map, &self->titletop, self->client);
1030     g_hash_table_insert(window_map, &self->titletopleft, self->client);
1031     g_hash_table_insert(window_map, &self->titletopright, self->client);
1032     g_hash_table_insert(window_map, &self->titleright, self->client);
1033     g_hash_table_insert(window_map, &self->titlebottom, self->client);
1034     g_hash_table_insert(window_map, &self->handleleft, self->client);
1035     g_hash_table_insert(window_map, &self->handletop, self->client);
1036     g_hash_table_insert(window_map, &self->handleright, self->client);
1037     g_hash_table_insert(window_map, &self->handlebottom, self->client);
1038     g_hash_table_insert(window_map, &self->lgripleft, self->client);
1039     g_hash_table_insert(window_map, &self->lgriptop, self->client);
1040     g_hash_table_insert(window_map, &self->lgripbottom, self->client);
1041     g_hash_table_insert(window_map, &self->rgripright, self->client);
1042     g_hash_table_insert(window_map, &self->rgriptop, self->client);
1043     g_hash_table_insert(window_map, &self->rgripbottom, self->client);
1044 }
1045
1046 void frame_release_client(ObFrame *self)
1047 {
1048     XEvent ev;
1049     gboolean reparent = TRUE;
1050
1051     /* if there was any animation going on, kill it */
1052     ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1053                                      self, FALSE);
1054
1055     /* check if the app has already reparented its window away */
1056     while (XCheckTypedWindowEvent(ob_display, self->client->window,
1057                                   ReparentNotify, &ev))
1058     {
1059         /* This check makes sure we don't catch our own reparent action to
1060            our frame window. This doesn't count as the app reparenting itself
1061            away of course.
1062
1063            Reparent events that are generated by us are just discarded here.
1064            They are of no consequence to us anyhow.
1065         */
1066         if (ev.xreparent.parent != self->window) {
1067             reparent = FALSE;
1068             XPutBackEvent(ob_display, &ev);
1069             break;
1070         }
1071     }
1072
1073     if (reparent) {
1074         /* according to the ICCCM - if the client doesn't reparent itself,
1075            then we will reparent the window to root for them */
1076         XReparentWindow(ob_display, self->client->window,
1077                         RootWindow(ob_display, ob_screen),
1078                         self->client->area.x,
1079                         self->client->area.y);
1080     }
1081
1082     /* remove all the windows for the frame from the window_map */
1083     g_hash_table_remove(window_map, &self->window);
1084     g_hash_table_remove(window_map, &self->backback);
1085     g_hash_table_remove(window_map, &self->backfront);
1086     g_hash_table_remove(window_map, &self->innerleft);
1087     g_hash_table_remove(window_map, &self->innertop);
1088     g_hash_table_remove(window_map, &self->innerright);
1089     g_hash_table_remove(window_map, &self->innerbottom);
1090     g_hash_table_remove(window_map, &self->innerblb);
1091     g_hash_table_remove(window_map, &self->innerbll);
1092     g_hash_table_remove(window_map, &self->innerbrb);
1093     g_hash_table_remove(window_map, &self->innerbrr);
1094     g_hash_table_remove(window_map, &self->title);
1095     g_hash_table_remove(window_map, &self->label);
1096     g_hash_table_remove(window_map, &self->max);
1097     g_hash_table_remove(window_map, &self->close);
1098     g_hash_table_remove(window_map, &self->desk);
1099     g_hash_table_remove(window_map, &self->shade);
1100     g_hash_table_remove(window_map, &self->icon);
1101     g_hash_table_remove(window_map, &self->iconify);
1102     g_hash_table_remove(window_map, &self->handle);
1103     g_hash_table_remove(window_map, &self->lgrip);
1104     g_hash_table_remove(window_map, &self->rgrip);
1105     g_hash_table_remove(window_map, &self->topresize);
1106     g_hash_table_remove(window_map, &self->tltresize);
1107     g_hash_table_remove(window_map, &self->tllresize);
1108     g_hash_table_remove(window_map, &self->trtresize);
1109     g_hash_table_remove(window_map, &self->trrresize);
1110     g_hash_table_remove(window_map, &self->left);
1111     g_hash_table_remove(window_map, &self->right);
1112     g_hash_table_remove(window_map, &self->titleleft);
1113     g_hash_table_remove(window_map, &self->titletop);
1114     g_hash_table_remove(window_map, &self->titletopleft);
1115     g_hash_table_remove(window_map, &self->titletopright);
1116     g_hash_table_remove(window_map, &self->titleright);
1117     g_hash_table_remove(window_map, &self->titlebottom);
1118     g_hash_table_remove(window_map, &self->handleleft);
1119     g_hash_table_remove(window_map, &self->handletop);
1120     g_hash_table_remove(window_map, &self->handleright);
1121     g_hash_table_remove(window_map, &self->handlebottom);
1122     g_hash_table_remove(window_map, &self->lgripleft);
1123     g_hash_table_remove(window_map, &self->lgriptop);
1124     g_hash_table_remove(window_map, &self->lgripbottom);
1125     g_hash_table_remove(window_map, &self->rgripright);
1126     g_hash_table_remove(window_map, &self->rgriptop);
1127     g_hash_table_remove(window_map, &self->rgripbottom);
1128
1129     ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
1130 }
1131
1132 /* is there anything present between us and the label? */
1133 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
1134     for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
1135         if (*lc == ' ') continue; /* it was invalid */
1136         if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
1137             return TRUE;
1138         if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
1139             return TRUE;
1140         if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
1141             return TRUE;
1142         if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
1143             return TRUE;
1144         if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
1145             return TRUE;
1146         if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
1147             return TRUE;
1148         if (*lc == 'L') return FALSE;
1149     }
1150     return FALSE;
1151 }
1152
1153 static void place_button(ObFrame *self, const char *lc, gint bwidth,
1154                          gint left, gint i,
1155                          gint *x, gint *button_on, gint *button_x)
1156 {
1157   if (!(*button_on = is_button_present(self, lc, i)))
1158     return;
1159
1160   self->label_width -= bwidth;
1161   if (i > 0)
1162     *button_x = *x;
1163   *x += i * bwidth;
1164   if (i < 0) {
1165     if (self->label_x <= left || *x > self->label_x) {
1166       *button_x = *x;
1167     } else {
1168       /* the button would have been drawn on top of another button */
1169       *button_on = FALSE;
1170       self->label_width += bwidth;
1171     }
1172   }
1173 }
1174
1175 static void layout_title(ObFrame *self)
1176 {
1177     gchar *lc;
1178     gint i;
1179
1180     const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
1181     /* position of the leftmost button */
1182     const gint left = ob_rr_theme->paddingx + 1;
1183     /* position of the rightmost button */
1184     const gint right = self->width;
1185
1186     /* turn them all off */
1187     self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
1188         self->max_on = self->close_on = self->label_on = FALSE;
1189     self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
1190     self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
1191
1192     /* figure out what's being shown, find each element's position, and the
1193        width of the label
1194
1195        do the ones before the label, then after the label,
1196        i will be +1 the first time through when working to the left,
1197        and -1 the second time through when working to the right */
1198     for (i = 1; i >= -1; i-=2) {
1199         gint x;
1200         ObFrameContext *firstcon;
1201
1202         if (i > 0) {
1203             x = left;
1204             lc = config_title_layout;
1205             firstcon = &self->leftmost;
1206         } else {
1207             x = right;
1208             lc = config_title_layout + strlen(config_title_layout)-1;
1209             firstcon = &self->rightmost;
1210         }
1211
1212         /* stop at the end of the string (or the label, which calls break) */
1213         for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1214             if (*lc == 'L') {
1215                 if (i > 0) {
1216                     self->label_on = TRUE;
1217                     self->label_x = x;
1218                 }
1219                 break; /* break the for loop, do other side of label */
1220             } else if (*lc == 'N') {
1221                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1222                 /* icon is bigger than buttons */
1223                 place_button(self, lc, bwidth + 2, left, i, &x, &self->icon_on, &self->icon_x);
1224             } else if (*lc == 'D') {
1225                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1226                 place_button(self, lc, bwidth, left, i, &x, &self->desk_on, &self->desk_x);
1227             } else if (*lc == 'S') {
1228                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1229                 place_button(self, lc, bwidth, left, i, &x, &self->shade_on, &self->shade_x);
1230             } else if (*lc == 'I') {
1231                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1232                 place_button(self, lc, bwidth, left, i, &x, &self->iconify_on, &self->iconify_x);
1233             } else if (*lc == 'M') {
1234                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1235                 place_button(self, lc, bwidth, left, i, &x, &self->max_on, &self->max_x);
1236             } else if (*lc == 'C') {
1237                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1238                 place_button(self, lc, bwidth, left, i, &x, &self->close_on, &self->close_x);
1239             } else
1240                 continue; /* don't set firstcon */
1241             firstcon = NULL;
1242         }
1243     }
1244
1245     /* position and map the elements */
1246     if (self->icon_on) {
1247         XMapWindow(ob_display, self->icon);
1248         XMoveWindow(ob_display, self->icon, self->icon_x,
1249                     ob_rr_theme->paddingy);
1250     } else
1251         XUnmapWindow(ob_display, self->icon);
1252
1253     if (self->desk_on) {
1254         XMapWindow(ob_display, self->desk);
1255         XMoveWindow(ob_display, self->desk, self->desk_x,
1256                     ob_rr_theme->paddingy + 1);
1257     } else
1258         XUnmapWindow(ob_display, self->desk);
1259
1260     if (self->shade_on) {
1261         XMapWindow(ob_display, self->shade);
1262         XMoveWindow(ob_display, self->shade, self->shade_x,
1263                     ob_rr_theme->paddingy + 1);
1264     } else
1265         XUnmapWindow(ob_display, self->shade);
1266
1267     if (self->iconify_on) {
1268         XMapWindow(ob_display, self->iconify);
1269         XMoveWindow(ob_display, self->iconify, self->iconify_x,
1270                     ob_rr_theme->paddingy + 1);
1271     } else
1272         XUnmapWindow(ob_display, self->iconify);
1273
1274     if (self->max_on) {
1275         XMapWindow(ob_display, self->max);
1276         XMoveWindow(ob_display, self->max, self->max_x,
1277                     ob_rr_theme->paddingy + 1);
1278     } else
1279         XUnmapWindow(ob_display, self->max);
1280
1281     if (self->close_on) {
1282         XMapWindow(ob_display, self->close);
1283         XMoveWindow(ob_display, self->close, self->close_x,
1284                     ob_rr_theme->paddingy + 1);
1285     } else
1286         XUnmapWindow(ob_display, self->close);
1287
1288     if (self->label_on && self->label_width > 0) {
1289         XMapWindow(ob_display, self->label);
1290         XMoveWindow(ob_display, self->label, self->label_x,
1291                     ob_rr_theme->paddingy);
1292     } else
1293         XUnmapWindow(ob_display, self->label);
1294 }
1295
1296 ObFrameContext frame_context_from_string(const gchar *name)
1297 {
1298     if (!g_ascii_strcasecmp("Desktop", name))
1299         return OB_FRAME_CONTEXT_DESKTOP;
1300     else if (!g_ascii_strcasecmp("Root", name))
1301         return OB_FRAME_CONTEXT_ROOT;
1302     else if (!g_ascii_strcasecmp("Client", name))
1303         return OB_FRAME_CONTEXT_CLIENT;
1304     else if (!g_ascii_strcasecmp("Titlebar", name))
1305         return OB_FRAME_CONTEXT_TITLEBAR;
1306     else if (!g_ascii_strcasecmp("Frame", name))
1307         return OB_FRAME_CONTEXT_FRAME;
1308     else if (!g_ascii_strcasecmp("TLCorner", name))
1309         return OB_FRAME_CONTEXT_TLCORNER;
1310     else if (!g_ascii_strcasecmp("TRCorner", name))
1311         return OB_FRAME_CONTEXT_TRCORNER;
1312     else if (!g_ascii_strcasecmp("BLCorner", name))
1313         return OB_FRAME_CONTEXT_BLCORNER;
1314     else if (!g_ascii_strcasecmp("BRCorner", name))
1315         return OB_FRAME_CONTEXT_BRCORNER;
1316     else if (!g_ascii_strcasecmp("Top", name))
1317         return OB_FRAME_CONTEXT_TOP;
1318     else if (!g_ascii_strcasecmp("Bottom", name))
1319         return OB_FRAME_CONTEXT_BOTTOM;
1320     else if (!g_ascii_strcasecmp("Left", name))
1321         return OB_FRAME_CONTEXT_LEFT;
1322     else if (!g_ascii_strcasecmp("Right", name))
1323         return OB_FRAME_CONTEXT_RIGHT;
1324     else if (!g_ascii_strcasecmp("Maximize", name))
1325         return OB_FRAME_CONTEXT_MAXIMIZE;
1326     else if (!g_ascii_strcasecmp("AllDesktops", name))
1327         return OB_FRAME_CONTEXT_ALLDESKTOPS;
1328     else if (!g_ascii_strcasecmp("Shade", name))
1329         return OB_FRAME_CONTEXT_SHADE;
1330     else if (!g_ascii_strcasecmp("Iconify", name))
1331         return OB_FRAME_CONTEXT_ICONIFY;
1332     else if (!g_ascii_strcasecmp("Icon", name))
1333         return OB_FRAME_CONTEXT_ICON;
1334     else if (!g_ascii_strcasecmp("Close", name))
1335         return OB_FRAME_CONTEXT_CLOSE;
1336     else if (!g_ascii_strcasecmp("MoveResize", name))
1337         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1338     return OB_FRAME_CONTEXT_NONE;
1339 }
1340
1341 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1342 {
1343     ObFrame *self;
1344
1345     if (moveresize_in_progress)
1346         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1347
1348     if (win == RootWindow(ob_display, ob_screen))
1349         return OB_FRAME_CONTEXT_ROOT ;
1350     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1351     if (win == client->window) {
1352         /* conceptually, this is the desktop, as far as users are
1353            concerned */
1354         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1355             return OB_FRAME_CONTEXT_DESKTOP;
1356         return OB_FRAME_CONTEXT_CLIENT;
1357     }
1358
1359     self = client->frame;
1360
1361     /* when the user clicks in the corners of the titlebar and the client
1362        is fully maximized, then treat it like they clicked in the
1363        button that is there */
1364     if (self->max_horz && self->max_vert &&
1365         (win == self->title || win == self->titletop ||
1366          win == self->titleleft || win == self->titletopleft ||
1367          win == self->titleright || win == self->titletopright))
1368     {
1369         /* get the mouse coords in reference to the whole frame */
1370         gint fx = x;
1371         gint fy = y;
1372
1373         /* these windows are down a border width from the top of the frame */
1374         if (win == self->title ||
1375             win == self->titleleft || win == self->titleright)
1376             fy += self->bwidth;
1377
1378         /* title is a border width in from the edge */
1379         if (win == self->title)
1380             fx += self->bwidth;
1381         /* titletop is a bit to the right */
1382         else if (win == self->titletop)
1383             fx += ob_rr_theme->grip_width + self->bwidth;
1384         /* titletopright is way to the right edge */
1385         else if (win == self->titletopright)
1386             fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1387         /* titleright is even more way to the right edge */
1388         else if (win == self->titleright)
1389             fx += self->area.width - self->bwidth;
1390
1391         /* figure out if we're over the area that should be considered a
1392            button */
1393         if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1394             ob_rr_theme->button_size)
1395         {
1396             if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1397                       ob_rr_theme->button_size))
1398             {
1399                 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1400                     return self->leftmost;
1401             }
1402             else if (fx >= (self->area.width -
1403                             (self->bwidth + ob_rr_theme->paddingx + 1 +
1404                              ob_rr_theme->button_size)))
1405             {
1406                 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1407                     return self->rightmost;
1408             }
1409         }
1410
1411         /* there is no resizing maximized windows so make them the titlebar
1412            context */
1413         return OB_FRAME_CONTEXT_TITLEBAR;
1414     }
1415     else if (self->max_vert &&
1416              (win == self->titletop || win == self->topresize))
1417         /* can't resize vertically when max vert */
1418         return OB_FRAME_CONTEXT_TITLEBAR;
1419     else if (self->shaded &&
1420              (win == self->titletop || win == self->topresize))
1421         /* can't resize vertically when shaded */
1422         return OB_FRAME_CONTEXT_TITLEBAR;
1423
1424     if (win == self->window)            return OB_FRAME_CONTEXT_FRAME;
1425     if (win == self->label)             return OB_FRAME_CONTEXT_TITLEBAR;
1426     if (win == self->handle)            return OB_FRAME_CONTEXT_BOTTOM;
1427     if (win == self->handletop)         return OB_FRAME_CONTEXT_BOTTOM;
1428     if (win == self->handlebottom)      return OB_FRAME_CONTEXT_BOTTOM;
1429     if (win == self->handleleft)        return OB_FRAME_CONTEXT_BLCORNER;
1430     if (win == self->lgrip)             return OB_FRAME_CONTEXT_BLCORNER;
1431     if (win == self->lgripleft)         return OB_FRAME_CONTEXT_BLCORNER;
1432     if (win == self->lgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1433     if (win == self->lgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1434     if (win == self->handleright)       return OB_FRAME_CONTEXT_BRCORNER;
1435     if (win == self->rgrip)             return OB_FRAME_CONTEXT_BRCORNER;
1436     if (win == self->rgripright)        return OB_FRAME_CONTEXT_BRCORNER;
1437     if (win == self->rgriptop)          return OB_FRAME_CONTEXT_BRCORNER;
1438     if (win == self->rgripbottom)       return OB_FRAME_CONTEXT_BRCORNER;
1439     if (win == self->title)             return OB_FRAME_CONTEXT_TITLEBAR;
1440     if (win == self->titlebottom)       return OB_FRAME_CONTEXT_TITLEBAR;
1441     if (win == self->titleleft)         return OB_FRAME_CONTEXT_TLCORNER;
1442     if (win == self->titletopleft)      return OB_FRAME_CONTEXT_TLCORNER;
1443     if (win == self->titleright)        return OB_FRAME_CONTEXT_TRCORNER;
1444     if (win == self->titletopright)     return OB_FRAME_CONTEXT_TRCORNER;
1445     if (win == self->titletop)          return OB_FRAME_CONTEXT_TOP;
1446     if (win == self->topresize)         return OB_FRAME_CONTEXT_TOP;
1447     if (win == self->tltresize)         return OB_FRAME_CONTEXT_TLCORNER;
1448     if (win == self->tllresize)         return OB_FRAME_CONTEXT_TLCORNER;
1449     if (win == self->trtresize)         return OB_FRAME_CONTEXT_TRCORNER;
1450     if (win == self->trrresize)         return OB_FRAME_CONTEXT_TRCORNER;
1451     if (win == self->left)              return OB_FRAME_CONTEXT_LEFT;
1452     if (win == self->right)             return OB_FRAME_CONTEXT_RIGHT;
1453     if (win == self->innertop)          return OB_FRAME_CONTEXT_TITLEBAR;
1454     if (win == self->innerleft)         return OB_FRAME_CONTEXT_LEFT;
1455     if (win == self->innerbottom)       return OB_FRAME_CONTEXT_BOTTOM;
1456     if (win == self->innerright)        return OB_FRAME_CONTEXT_RIGHT;
1457     if (win == self->innerbll)          return OB_FRAME_CONTEXT_BLCORNER;
1458     if (win == self->innerblb)          return OB_FRAME_CONTEXT_BLCORNER;
1459     if (win == self->innerbrr)          return OB_FRAME_CONTEXT_BRCORNER;
1460     if (win == self->innerbrb)          return OB_FRAME_CONTEXT_BRCORNER;
1461     if (win == self->max)               return OB_FRAME_CONTEXT_MAXIMIZE;
1462     if (win == self->iconify)           return OB_FRAME_CONTEXT_ICONIFY;
1463     if (win == self->close)             return OB_FRAME_CONTEXT_CLOSE;
1464     if (win == self->icon)              return OB_FRAME_CONTEXT_ICON;
1465     if (win == self->desk)              return OB_FRAME_CONTEXT_ALLDESKTOPS;
1466     if (win == self->shade)             return OB_FRAME_CONTEXT_SHADE;
1467
1468     return OB_FRAME_CONTEXT_NONE;
1469 }
1470
1471 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
1472 {
1473     /* horizontal */
1474     switch (self->client->gravity) {
1475     default:
1476     case NorthWestGravity:
1477     case SouthWestGravity:
1478     case WestGravity:
1479         break;
1480
1481     case NorthGravity:
1482     case SouthGravity:
1483     case CenterGravity:
1484         /* the middle of the client will be the middle of the frame */
1485         *x -= (self->size.right - self->size.left) / 2;
1486         break;
1487
1488     case NorthEastGravity:
1489     case SouthEastGravity:
1490     case EastGravity:
1491         /* the right side of the client will be the right side of the frame */
1492         *x -= self->size.right + self->size.left -
1493             self->client->border_width * 2;
1494         break;
1495
1496     case ForgetGravity:
1497     case StaticGravity:
1498         /* the client's position won't move */
1499         *x -= self->size.left - self->client->border_width;
1500         break;
1501     }
1502
1503     /* vertical */
1504     switch (self->client->gravity) {
1505     default:
1506     case NorthWestGravity:
1507     case NorthEastGravity:
1508     case NorthGravity:
1509         break;
1510
1511     case CenterGravity:
1512     case EastGravity:
1513     case WestGravity:
1514         /* the middle of the client will be the middle of the frame */
1515         *y -= (self->size.bottom - self->size.top) / 2;
1516         break;
1517
1518     case SouthWestGravity:
1519     case SouthEastGravity:
1520     case SouthGravity:
1521         /* the bottom of the client will be the bottom of the frame */
1522         *y -= self->size.bottom + self->size.top -
1523             self->client->border_width * 2;
1524         break;
1525
1526     case ForgetGravity:
1527     case StaticGravity:
1528         /* the client's position won't move */
1529         *y -= self->size.top - self->client->border_width;
1530         break;
1531     }
1532 }
1533
1534 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
1535 {
1536     /* horizontal */
1537     switch (self->client->gravity) {
1538     default:
1539     case NorthWestGravity:
1540     case WestGravity:
1541     case SouthWestGravity:
1542         break;
1543     case NorthGravity:
1544     case CenterGravity:
1545     case SouthGravity:
1546         /* the middle of the client will be the middle of the frame */
1547         *x += (self->size.right - self->size.left) / 2;
1548         break;
1549     case NorthEastGravity:
1550     case EastGravity:
1551     case SouthEastGravity:
1552         /* the right side of the client will be the right side of the frame */
1553         *x += self->size.right + self->size.left -
1554             self->client->border_width * 2;
1555         break;
1556     case StaticGravity:
1557     case ForgetGravity:
1558         /* the client's position won't move */
1559         *x += self->size.left - self->client->border_width;
1560         break;
1561     }
1562
1563     /* vertical */
1564     switch (self->client->gravity) {
1565     default:
1566     case NorthWestGravity:
1567     case NorthGravity:
1568     case NorthEastGravity:
1569         break;
1570     case WestGravity:
1571     case CenterGravity:
1572     case EastGravity:
1573         /* the middle of the client will be the middle of the frame */
1574         *y += (self->size.bottom - self->size.top) / 2;
1575         break;
1576     case SouthWestGravity:
1577     case SouthGravity:
1578     case SouthEastGravity:
1579         /* the bottom of the client will be the bottom of the frame */
1580         *y += self->size.bottom + self->size.top -
1581             self->client->border_width * 2;
1582         break;
1583     case StaticGravity:
1584     case ForgetGravity:
1585         /* the client's position won't move */
1586         *y += self->size.top - self->client->border_width;
1587         break;
1588     }
1589 }
1590
1591 void frame_rect_to_frame(ObFrame *self, Rect *r)
1592 {
1593     r->width += self->size.left + self->size.right;
1594     r->height += self->size.top + self->size.bottom;
1595     frame_client_gravity(self, &r->x, &r->y);
1596 }
1597
1598 void frame_rect_to_client(ObFrame *self, Rect *r)
1599 {
1600     r->width -= self->size.left + self->size.right;
1601     r->height -= self->size.top + self->size.bottom;
1602     frame_frame_gravity(self, &r->x, &r->y);
1603 }
1604
1605 static void flash_done(gpointer data)
1606 {
1607     ObFrame *self = data;
1608
1609     if (self->focused != self->flash_on)
1610         frame_adjust_focus(self, self->focused);
1611 }
1612
1613 static gboolean flash_timeout(gpointer data)
1614 {
1615     ObFrame *self = data;
1616     GTimeVal now;
1617
1618     g_get_current_time(&now);
1619     if (now.tv_sec > self->flash_end.tv_sec ||
1620         (now.tv_sec == self->flash_end.tv_sec &&
1621          now.tv_usec >= self->flash_end.tv_usec))
1622         self->flashing = FALSE;
1623
1624     if (!self->flashing)
1625         return FALSE; /* we are done */
1626
1627     self->flash_on = !self->flash_on;
1628     if (!self->focused) {
1629         frame_adjust_focus(self, self->flash_on);
1630         self->focused = FALSE;
1631     }
1632
1633     return TRUE; /* go again */
1634 }
1635
1636 void frame_flash_start(ObFrame *self)
1637 {
1638     self->flash_on = self->focused;
1639
1640     if (!self->flashing)
1641         ob_main_loop_timeout_add(ob_main_loop,
1642                                  G_USEC_PER_SEC * 0.6,
1643                                  flash_timeout,
1644                                  self,
1645                                  g_direct_equal,
1646                                  flash_done);
1647     g_get_current_time(&self->flash_end);
1648     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1649
1650     self->flashing = TRUE;
1651 }
1652
1653 void frame_flash_stop(ObFrame *self)
1654 {
1655     self->flashing = FALSE;
1656 }
1657
1658 static gulong frame_animate_iconify_time_left(ObFrame *self,
1659                                               const GTimeVal *now)
1660 {
1661     glong sec, usec;
1662     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1663     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1664     if (usec < 0) {
1665         usec += G_USEC_PER_SEC;
1666         sec--;
1667     }
1668     /* no negative values */
1669     return MAX(sec * G_USEC_PER_SEC + usec, 0);
1670 }
1671
1672 static gboolean frame_animate_iconify(gpointer p)
1673 {
1674     ObFrame *self = p;
1675     gint x, y, w, h;
1676     gint iconx, icony, iconw;
1677     GTimeVal now;
1678     gulong time;
1679     gboolean iconifying;
1680
1681     if (self->client->icon_geometry.width == 0) {
1682         /* there is no icon geometry set so just go straight down */
1683         Rect *a = screen_physical_area_monitor
1684             (screen_find_monitor(&self->area));
1685         iconx = self->area.x + self->area.width / 2 + 32;
1686         icony = a->y + a->width;
1687         iconw = 64;
1688         g_free(a);
1689     } else {
1690         iconx = self->client->icon_geometry.x;
1691         icony = self->client->icon_geometry.y;
1692         iconw = self->client->icon_geometry.width;
1693     }
1694
1695     iconifying = self->iconify_animation_going > 0;
1696
1697     /* how far do we have left to go ? */
1698     g_get_current_time(&now);
1699     time = frame_animate_iconify_time_left(self, &now);
1700
1701     if ((time > 0 && iconifying) || (time == 0 && !iconifying)) {
1702         /* start where the frame is supposed to be */
1703         x = self->area.x;
1704         y = self->area.y;
1705         w = self->area.width;
1706         h = self->area.height;
1707     } else {
1708         /* start at the icon */
1709         x = iconx;
1710         y = icony;
1711         w = iconw;
1712         h = self->size.top; /* just the titlebar */
1713     }
1714
1715     if (time > 0) {
1716         glong dx, dy, dw;
1717         glong elapsed;
1718
1719         dx = self->area.x - iconx;
1720         dy = self->area.y - icony;
1721         dw = self->area.width - self->bwidth * 2 - iconw;
1722          /* if restoring, we move in the opposite direction */
1723         if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1724
1725         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1726         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1727         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1728         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1729         h = self->size.top; /* just the titlebar */
1730     }
1731
1732     XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1733     XFlush(ob_display);
1734
1735     if (time == 0)
1736         frame_end_iconify_animation(self);
1737
1738     return time > 0; /* repeat until we're out of time */
1739 }
1740
1741 void frame_end_iconify_animation(ObFrame *self)
1742 {
1743     /* see if there is an animation going */
1744     if (self->iconify_animation_going == 0) return;
1745
1746     if (!self->visible)
1747         XUnmapWindow(ob_display, self->window);
1748     else {
1749         /* Send a ConfigureNotify when the animation is done, this fixes
1750            KDE's pager showing the window in the wrong place.  since the
1751            window is mapped at a different location and is then moved, we
1752            need to send the synthetic configurenotify, since apps may have
1753            read the position when the client mapped, apparently. */
1754         client_reconfigure(self->client, TRUE);
1755     }
1756
1757     /* we're not animating any more ! */
1758     self->iconify_animation_going = 0;
1759
1760     XMoveResizeWindow(ob_display, self->window,
1761                       self->area.x, self->area.y,
1762                       self->area.width, self->area.height);
1763     /* we delay re-rendering until after we're done animating */
1764     framerender_frame(self);
1765     XFlush(ob_display);
1766 }
1767
1768 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1769 {
1770     gulong time;
1771     gboolean new_anim = FALSE;
1772     gboolean set_end = TRUE;
1773     GTimeVal now;
1774
1775     /* if there is no titlebar, just don't animate for now
1776        XXX it would be nice tho.. */
1777     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1778         return;
1779
1780     /* get the current time */
1781     g_get_current_time(&now);
1782
1783     /* get how long until the end */
1784     time = FRAME_ANIMATE_ICONIFY_TIME;
1785     if (self->iconify_animation_going) {
1786         if (!!iconifying != (self->iconify_animation_going > 0)) {
1787             /* animation was already going on in the opposite direction */
1788             time = time - frame_animate_iconify_time_left(self, &now);
1789         } else
1790             /* animation was already going in the same direction */
1791             set_end = FALSE;
1792     } else
1793         new_anim = TRUE;
1794     self->iconify_animation_going = iconifying ? 1 : -1;
1795
1796     /* set the ending time */
1797     if (set_end) {
1798         self->iconify_animation_end.tv_sec = now.tv_sec;
1799         self->iconify_animation_end.tv_usec = now.tv_usec;
1800         g_time_val_add(&self->iconify_animation_end, time);
1801     }
1802
1803     if (new_anim) {
1804         ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1805                                          self, FALSE);
1806         ob_main_loop_timeout_add(ob_main_loop,
1807                                  FRAME_ANIMATE_ICONIFY_STEP_TIME,
1808                                  frame_animate_iconify, self,
1809                                  g_direct_equal, NULL);
1810
1811         /* do the first step */
1812         frame_animate_iconify(self);
1813
1814         /* show it during the animation even if it is not "visible" */
1815         if (!self->visible)
1816             XMapWindow(ob_display, self->window);
1817     }
1818 }