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