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