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