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