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