]> icculus.org git repositories - dana/openbox.git/blob - openbox/frame.c
Unused variables in screen_area()
[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("Handle", name))
1321         return OB_FRAME_CONTEXT_BOTTOM;
1322     else if (!g_ascii_strcasecmp("Left", name))
1323         return OB_FRAME_CONTEXT_LEFT;
1324     else if (!g_ascii_strcasecmp("Right", name))
1325         return OB_FRAME_CONTEXT_RIGHT;
1326     else if (!g_ascii_strcasecmp("Maximize", name))
1327         return OB_FRAME_CONTEXT_MAXIMIZE;
1328     else if (!g_ascii_strcasecmp("AllDesktops", name))
1329         return OB_FRAME_CONTEXT_ALLDESKTOPS;
1330     else if (!g_ascii_strcasecmp("Shade", name))
1331         return OB_FRAME_CONTEXT_SHADE;
1332     else if (!g_ascii_strcasecmp("Iconify", name))
1333         return OB_FRAME_CONTEXT_ICONIFY;
1334     else if (!g_ascii_strcasecmp("Icon", name))
1335         return OB_FRAME_CONTEXT_ICON;
1336     else if (!g_ascii_strcasecmp("Close", name))
1337         return OB_FRAME_CONTEXT_CLOSE;
1338     else if (!g_ascii_strcasecmp("MoveResize", name))
1339         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1340     return OB_FRAME_CONTEXT_NONE;
1341 }
1342
1343 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1344 {
1345     ObFrame *self;
1346
1347     if (moveresize_in_progress)
1348         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1349
1350     if (win == RootWindow(ob_display, ob_screen))
1351         return OB_FRAME_CONTEXT_ROOT ;
1352     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1353     if (win == client->window) {
1354         /* conceptually, this is the desktop, as far as users are
1355            concerned */
1356         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1357             return OB_FRAME_CONTEXT_DESKTOP;
1358         return OB_FRAME_CONTEXT_CLIENT;
1359     }
1360
1361     self = client->frame;
1362
1363     /* when the user clicks in the corners of the titlebar and the client
1364        is fully maximized, then treat it like they clicked in the
1365        button that is there */
1366     if (self->max_horz && self->max_vert &&
1367         (win == self->title || win == self->titletop ||
1368          win == self->titleleft || win == self->titletopleft ||
1369          win == self->titleright || win == self->titletopright))
1370     {
1371         /* get the mouse coords in reference to the whole frame */
1372         gint fx = x;
1373         gint fy = y;
1374
1375         /* these windows are down a border width from the top of the frame */
1376         if (win == self->title ||
1377             win == self->titleleft || win == self->titleright)
1378             fy += self->bwidth;
1379
1380         /* title is a border width in from the edge */
1381         if (win == self->title)
1382             fx += self->bwidth;
1383         /* titletop is a bit to the right */
1384         else if (win == self->titletop)
1385             fx += ob_rr_theme->grip_width + self->bwidth;
1386         /* titletopright is way to the right edge */
1387         else if (win == self->titletopright)
1388             fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1389         /* titleright is even more way to the right edge */
1390         else if (win == self->titleright)
1391             fx += self->area.width - self->bwidth;
1392
1393         /* figure out if we're over the area that should be considered a
1394            button */
1395         if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1396             ob_rr_theme->button_size)
1397         {
1398             if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1399                       ob_rr_theme->button_size))
1400             {
1401                 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1402                     return self->leftmost;
1403             }
1404             else if (fx >= (self->area.width -
1405                             (self->bwidth + ob_rr_theme->paddingx + 1 +
1406                              ob_rr_theme->button_size)))
1407             {
1408                 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1409                     return self->rightmost;
1410             }
1411         }
1412
1413         /* there is no resizing maximized windows so make them the titlebar
1414            context */
1415         return OB_FRAME_CONTEXT_TITLEBAR;
1416     }
1417     else if (self->max_vert &&
1418              (win == self->titletop || win == self->topresize))
1419         /* can't resize vertically when max vert */
1420         return OB_FRAME_CONTEXT_TITLEBAR;
1421     else if (self->shaded &&
1422              (win == self->titletop || win == self->topresize))
1423         /* can't resize vertically when shaded */
1424         return OB_FRAME_CONTEXT_TITLEBAR;
1425
1426     if (win == self->window)            return OB_FRAME_CONTEXT_FRAME;
1427     if (win == self->label)             return OB_FRAME_CONTEXT_TITLEBAR;
1428     if (win == self->handle)            return OB_FRAME_CONTEXT_BOTTOM;
1429     if (win == self->handletop)         return OB_FRAME_CONTEXT_BOTTOM;
1430     if (win == self->handlebottom)      return OB_FRAME_CONTEXT_BOTTOM;
1431     if (win == self->handleleft)        return OB_FRAME_CONTEXT_BLCORNER;
1432     if (win == self->lgrip)             return OB_FRAME_CONTEXT_BLCORNER;
1433     if (win == self->lgripleft)         return OB_FRAME_CONTEXT_BLCORNER;
1434     if (win == self->lgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1435     if (win == self->lgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1436     if (win == self->handleright)       return OB_FRAME_CONTEXT_BRCORNER;
1437     if (win == self->rgrip)             return OB_FRAME_CONTEXT_BRCORNER;
1438     if (win == self->rgripright)        return OB_FRAME_CONTEXT_BRCORNER;
1439     if (win == self->rgriptop)          return OB_FRAME_CONTEXT_BRCORNER;
1440     if (win == self->rgripbottom)       return OB_FRAME_CONTEXT_BRCORNER;
1441     if (win == self->title)             return OB_FRAME_CONTEXT_TITLEBAR;
1442     if (win == self->titlebottom)       return OB_FRAME_CONTEXT_TITLEBAR;
1443     if (win == self->titleleft)         return OB_FRAME_CONTEXT_TLCORNER;
1444     if (win == self->titletopleft)      return OB_FRAME_CONTEXT_TLCORNER;
1445     if (win == self->titleright)        return OB_FRAME_CONTEXT_TRCORNER;
1446     if (win == self->titletopright)     return OB_FRAME_CONTEXT_TRCORNER;
1447     if (win == self->titletop)          return OB_FRAME_CONTEXT_TOP;
1448     if (win == self->topresize)         return OB_FRAME_CONTEXT_TOP;
1449     if (win == self->tltresize)         return OB_FRAME_CONTEXT_TLCORNER;
1450     if (win == self->tllresize)         return OB_FRAME_CONTEXT_TLCORNER;
1451     if (win == self->trtresize)         return OB_FRAME_CONTEXT_TRCORNER;
1452     if (win == self->trrresize)         return OB_FRAME_CONTEXT_TRCORNER;
1453     if (win == self->left)              return OB_FRAME_CONTEXT_LEFT;
1454     if (win == self->right)             return OB_FRAME_CONTEXT_RIGHT;
1455     if (win == self->innertop)          return OB_FRAME_CONTEXT_TITLEBAR;
1456     if (win == self->innerleft)         return OB_FRAME_CONTEXT_LEFT;
1457     if (win == self->innerbottom)       return OB_FRAME_CONTEXT_BOTTOM;
1458     if (win == self->innerright)        return OB_FRAME_CONTEXT_RIGHT;
1459     if (win == self->innerbll)          return OB_FRAME_CONTEXT_BLCORNER;
1460     if (win == self->innerblb)          return OB_FRAME_CONTEXT_BLCORNER;
1461     if (win == self->innerbrr)          return OB_FRAME_CONTEXT_BRCORNER;
1462     if (win == self->innerbrb)          return OB_FRAME_CONTEXT_BRCORNER;
1463     if (win == self->max)               return OB_FRAME_CONTEXT_MAXIMIZE;
1464     if (win == self->iconify)           return OB_FRAME_CONTEXT_ICONIFY;
1465     if (win == self->close)             return OB_FRAME_CONTEXT_CLOSE;
1466     if (win == self->icon)              return OB_FRAME_CONTEXT_ICON;
1467     if (win == self->desk)              return OB_FRAME_CONTEXT_ALLDESKTOPS;
1468     if (win == self->shade)             return OB_FRAME_CONTEXT_SHADE;
1469
1470     return OB_FRAME_CONTEXT_NONE;
1471 }
1472
1473 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
1474 {
1475     /* horizontal */
1476     switch (self->client->gravity) {
1477     default:
1478     case NorthWestGravity:
1479     case SouthWestGravity:
1480     case WestGravity:
1481         break;
1482
1483     case NorthGravity:
1484     case SouthGravity:
1485     case CenterGravity:
1486         /* the middle of the client will be the middle of the frame */
1487         *x -= (self->size.right - self->size.left) / 2;
1488         break;
1489
1490     case NorthEastGravity:
1491     case SouthEastGravity:
1492     case EastGravity:
1493         /* the right side of the client will be the right side of the frame */
1494         *x -= self->size.right + self->size.left -
1495             self->client->border_width * 2;
1496         break;
1497
1498     case ForgetGravity:
1499     case StaticGravity:
1500         /* the client's position won't move */
1501         *x -= self->size.left - self->client->border_width;
1502         break;
1503     }
1504
1505     /* vertical */
1506     switch (self->client->gravity) {
1507     default:
1508     case NorthWestGravity:
1509     case NorthEastGravity:
1510     case NorthGravity:
1511         break;
1512
1513     case CenterGravity:
1514     case EastGravity:
1515     case WestGravity:
1516         /* the middle of the client will be the middle of the frame */
1517         *y -= (self->size.bottom - self->size.top) / 2;
1518         break;
1519
1520     case SouthWestGravity:
1521     case SouthEastGravity:
1522     case SouthGravity:
1523         /* the bottom of the client will be the bottom of the frame */
1524         *y -= self->size.bottom + self->size.top -
1525             self->client->border_width * 2;
1526         break;
1527
1528     case ForgetGravity:
1529     case StaticGravity:
1530         /* the client's position won't move */
1531         *y -= self->size.top - self->client->border_width;
1532         break;
1533     }
1534 }
1535
1536 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
1537 {
1538     /* horizontal */
1539     switch (self->client->gravity) {
1540     default:
1541     case NorthWestGravity:
1542     case WestGravity:
1543     case SouthWestGravity:
1544         break;
1545     case NorthGravity:
1546     case CenterGravity:
1547     case SouthGravity:
1548         /* the middle of the client will be the middle of the frame */
1549         *x += (self->size.right - self->size.left) / 2;
1550         break;
1551     case NorthEastGravity:
1552     case EastGravity:
1553     case SouthEastGravity:
1554         /* the right side of the client will be the right side of the frame */
1555         *x += self->size.right + self->size.left -
1556             self->client->border_width * 2;
1557         break;
1558     case StaticGravity:
1559     case ForgetGravity:
1560         /* the client's position won't move */
1561         *x += self->size.left - self->client->border_width;
1562         break;
1563     }
1564
1565     /* vertical */
1566     switch (self->client->gravity) {
1567     default:
1568     case NorthWestGravity:
1569     case NorthGravity:
1570     case NorthEastGravity:
1571         break;
1572     case WestGravity:
1573     case CenterGravity:
1574     case EastGravity:
1575         /* the middle of the client will be the middle of the frame */
1576         *y += (self->size.bottom - self->size.top) / 2;
1577         break;
1578     case SouthWestGravity:
1579     case SouthGravity:
1580     case SouthEastGravity:
1581         /* the bottom of the client will be the bottom of the frame */
1582         *y += self->size.bottom + self->size.top -
1583             self->client->border_width * 2;
1584         break;
1585     case StaticGravity:
1586     case ForgetGravity:
1587         /* the client's position won't move */
1588         *y += self->size.top - self->client->border_width;
1589         break;
1590     }
1591 }
1592
1593 void frame_rect_to_frame(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_client_gravity(self, &r->x, &r->y);
1598 }
1599
1600 void frame_rect_to_client(ObFrame *self, Rect *r)
1601 {
1602     r->width -= self->size.left + self->size.right;
1603     r->height -= self->size.top + self->size.bottom;
1604     frame_frame_gravity(self, &r->x, &r->y);
1605 }
1606
1607 static void flash_done(gpointer data)
1608 {
1609     ObFrame *self = data;
1610
1611     if (self->focused != self->flash_on)
1612         frame_adjust_focus(self, self->focused);
1613 }
1614
1615 static gboolean flash_timeout(gpointer data)
1616 {
1617     ObFrame *self = data;
1618     GTimeVal now;
1619
1620     g_get_current_time(&now);
1621     if (now.tv_sec > self->flash_end.tv_sec ||
1622         (now.tv_sec == self->flash_end.tv_sec &&
1623          now.tv_usec >= self->flash_end.tv_usec))
1624         self->flashing = FALSE;
1625
1626     if (!self->flashing)
1627         return FALSE; /* we are done */
1628
1629     self->flash_on = !self->flash_on;
1630     if (!self->focused) {
1631         frame_adjust_focus(self, self->flash_on);
1632         self->focused = FALSE;
1633     }
1634
1635     return TRUE; /* go again */
1636 }
1637
1638 void frame_flash_start(ObFrame *self)
1639 {
1640     self->flash_on = self->focused;
1641
1642     if (!self->flashing)
1643         ob_main_loop_timeout_add(ob_main_loop,
1644                                  G_USEC_PER_SEC * 0.6,
1645                                  flash_timeout,
1646                                  self,
1647                                  g_direct_equal,
1648                                  flash_done);
1649     g_get_current_time(&self->flash_end);
1650     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1651
1652     self->flashing = TRUE;
1653 }
1654
1655 void frame_flash_stop(ObFrame *self)
1656 {
1657     self->flashing = FALSE;
1658 }
1659
1660 static gulong frame_animate_iconify_time_left(ObFrame *self,
1661                                               const GTimeVal *now)
1662 {
1663     glong sec, usec;
1664     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1665     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1666     if (usec < 0) {
1667         usec += G_USEC_PER_SEC;
1668         sec--;
1669     }
1670     /* no negative values */
1671     return MAX(sec * G_USEC_PER_SEC + usec, 0);
1672 }
1673
1674 static gboolean frame_animate_iconify(gpointer p)
1675 {
1676     ObFrame *self = p;
1677     gint x, y, w, h;
1678     gint iconx, icony, iconw;
1679     GTimeVal now;
1680     gulong time;
1681     gboolean iconifying;
1682
1683     if (self->client->icon_geometry.width == 0) {
1684         /* there is no icon geometry set so just go straight down */
1685         Rect *a = screen_physical_area_monitor
1686             (screen_find_monitor(&self->area));
1687         iconx = self->area.x + self->area.width / 2 + 32;
1688         icony = a->y + a->width;
1689         iconw = 64;
1690         g_free(a);
1691     } else {
1692         iconx = self->client->icon_geometry.x;
1693         icony = self->client->icon_geometry.y;
1694         iconw = self->client->icon_geometry.width;
1695     }
1696
1697     iconifying = self->iconify_animation_going > 0;
1698
1699     /* how far do we have left to go ? */
1700     g_get_current_time(&now);
1701     time = frame_animate_iconify_time_left(self, &now);
1702
1703     if ((time > 0 && iconifying) || (time == 0 && !iconifying)) {
1704         /* start where the frame is supposed to be */
1705         x = self->area.x;
1706         y = self->area.y;
1707         w = self->area.width;
1708         h = self->area.height;
1709     } else {
1710         /* start at the icon */
1711         x = iconx;
1712         y = icony;
1713         w = iconw;
1714         h = self->size.top; /* just the titlebar */
1715     }
1716
1717     if (time > 0) {
1718         glong dx, dy, dw;
1719         glong elapsed;
1720
1721         dx = self->area.x - iconx;
1722         dy = self->area.y - icony;
1723         dw = self->area.width - self->bwidth * 2 - iconw;
1724          /* if restoring, we move in the opposite direction */
1725         if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1726
1727         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1728         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1729         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1730         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1731         h = self->size.top; /* just the titlebar */
1732     }
1733
1734     XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1735     XFlush(ob_display);
1736
1737     if (time == 0)
1738         frame_end_iconify_animation(self);
1739
1740     return time > 0; /* repeat until we're out of time */
1741 }
1742
1743 void frame_end_iconify_animation(ObFrame *self)
1744 {
1745     /* see if there is an animation going */
1746     if (self->iconify_animation_going == 0) return;
1747
1748     if (!self->visible)
1749         XUnmapWindow(ob_display, self->window);
1750     else {
1751         /* Send a ConfigureNotify when the animation is done, this fixes
1752            KDE's pager showing the window in the wrong place.  since the
1753            window is mapped at a different location and is then moved, we
1754            need to send the synthetic configurenotify, since apps may have
1755            read the position when the client mapped, apparently. */
1756         client_reconfigure(self->client, TRUE);
1757     }
1758
1759     /* we're not animating any more ! */
1760     self->iconify_animation_going = 0;
1761
1762     XMoveResizeWindow(ob_display, self->window,
1763                       self->area.x, self->area.y,
1764                       self->area.width, self->area.height);
1765     /* we delay re-rendering until after we're done animating */
1766     framerender_frame(self);
1767     XFlush(ob_display);
1768 }
1769
1770 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1771 {
1772     gulong time;
1773     gboolean new_anim = FALSE;
1774     gboolean set_end = TRUE;
1775     GTimeVal now;
1776
1777     /* if there is no titlebar, just don't animate for now
1778        XXX it would be nice tho.. */
1779     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1780         return;
1781
1782     /* get the current time */
1783     g_get_current_time(&now);
1784
1785     /* get how long until the end */
1786     time = FRAME_ANIMATE_ICONIFY_TIME;
1787     if (self->iconify_animation_going) {
1788         if (!!iconifying != (self->iconify_animation_going > 0)) {
1789             /* animation was already going on in the opposite direction */
1790             time = time - frame_animate_iconify_time_left(self, &now);
1791         } else
1792             /* animation was already going in the same direction */
1793             set_end = FALSE;
1794     } else
1795         new_anim = TRUE;
1796     self->iconify_animation_going = iconifying ? 1 : -1;
1797
1798     /* set the ending time */
1799     if (set_end) {
1800         self->iconify_animation_end.tv_sec = now.tv_sec;
1801         self->iconify_animation_end.tv_usec = now.tv_usec;
1802         g_time_val_add(&self->iconify_animation_end, time);
1803     }
1804
1805     if (new_anim) {
1806         ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1807                                          self, FALSE);
1808         ob_main_loop_timeout_add(ob_main_loop,
1809                                  FRAME_ANIMATE_ICONIFY_STEP_TIME,
1810                                  frame_animate_iconify, self,
1811                                  g_direct_equal, NULL);
1812
1813         /* do the first step */
1814         frame_animate_iconify(self);
1815
1816         /* show it during the animation even if it is not "visible" */
1817         if (!self->visible)
1818             XMapWindow(ob_display, self->window);
1819     }
1820 }