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