Make the dock a context and add actions LowerDock and RaiseDock
[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 (1000 / 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     /* make sure the size will be different the first time, so the extent hints
192        will be set */
193     STRUT_SET(self->oldsize, -1, -1, -1, -1);
194
195     set_theme_statics(self);
196
197     return self;
198 }
199
200 static void set_theme_statics(ObFrame *self)
201 {
202     /* set colors/appearance/sizes for stuff that doesn't change */
203     XResizeWindow(obt_display, self->max,
204                   ob_rr_theme->button_size, ob_rr_theme->button_size);
205     XResizeWindow(obt_display, self->iconify,
206                   ob_rr_theme->button_size, ob_rr_theme->button_size);
207     XResizeWindow(obt_display, self->icon,
208                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
209     XResizeWindow(obt_display, self->close,
210                   ob_rr_theme->button_size, ob_rr_theme->button_size);
211     XResizeWindow(obt_display, self->desk,
212                   ob_rr_theme->button_size, ob_rr_theme->button_size);
213     XResizeWindow(obt_display, self->shade,
214                   ob_rr_theme->button_size, ob_rr_theme->button_size);
215     XResizeWindow(obt_display, self->tltresize,
216                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
217     XResizeWindow(obt_display, self->trtresize,
218                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
219     XResizeWindow(obt_display, self->tllresize,
220                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
221     XResizeWindow(obt_display, self->trrresize,
222                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
223 }
224
225 static void free_theme_statics(ObFrame *self)
226 {
227 }
228
229 void frame_free(ObFrame *self)
230 {
231     free_theme_statics(self);
232
233     XDestroyWindow(obt_display, self->window);
234     if (self->colormap)
235         XFreeColormap(obt_display, self->colormap);
236
237     g_slice_free(ObFrame, self);
238 }
239
240 void frame_show(ObFrame *self)
241 {
242     if (!self->visible) {
243         self->visible = TRUE;
244         framerender_frame(self);
245         /* Grab the server to make sure that the frame window is mapped before
246            the client gets its MapNotify, i.e. to make sure the client is
247            _visible_ when it gets MapNotify. */
248         grab_server(TRUE);
249         XMapWindow(obt_display, self->client->window);
250         XMapWindow(obt_display, self->window);
251         grab_server(FALSE);
252     }
253 }
254
255 void frame_hide(ObFrame *self)
256 {
257     if (self->visible) {
258         self->visible = FALSE;
259         if (!frame_iconify_animating(self))
260             XUnmapWindow(obt_display, self->window);
261         /* we unmap the client itself so that we can get MapRequest
262            events, and because the ICCCM tells us to! */
263         XUnmapWindow(obt_display, self->client->window);
264         self->client->ignore_unmaps += 1;
265     }
266 }
267
268 void frame_adjust_theme(ObFrame *self)
269 {
270     free_theme_statics(self);
271     set_theme_statics(self);
272 }
273
274 #ifdef SHAPE
275 void frame_adjust_shape_kind(ObFrame *self, int kind)
276 {
277     gint num;
278     XRectangle xrect[2];
279
280     if (!((kind == ShapeBounding && self->client->shaped) ||
281           (kind == ShapeInput && self->client->shaped_input))) {
282         /* clear the shape on the frame window */
283         XShapeCombineMask(obt_display, self->window, kind,
284                           self->size.left,
285                           self->size.top,
286                           None, ShapeSet);
287     } else {
288         /* make the frame's shape match the clients */
289         XShapeCombineShape(obt_display, self->window, kind,
290                            self->size.left,
291                            self->size.top,
292                            self->client->window,
293                            kind, ShapeSet);
294
295         num = 0;
296         if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
297             xrect[0].x = 0;
298             xrect[0].y = 0;
299             xrect[0].width = self->area.width;
300             xrect[0].height = self->size.top;
301             ++num;
302         }
303
304         if (self->decorations & OB_FRAME_DECOR_HANDLE &&
305             ob_rr_theme->handle_height > 0)
306         {
307             xrect[1].x = 0;
308             xrect[1].y = FRAME_HANDLE_Y(self);
309             xrect[1].width = self->area.width;
310             xrect[1].height = ob_rr_theme->handle_height +
311                 self->bwidth * 2;
312             ++num;
313         }
314
315         XShapeCombineRectangles(obt_display, self->window,
316                                 ShapeBounding, 0, 0, xrect, num,
317                                 ShapeUnion, Unsorted);
318     }
319 }
320 #endif
321
322 void frame_adjust_shape(ObFrame *self)
323 {
324 #ifdef SHAPE
325   frame_adjust_shape_kind(self, ShapeBounding);
326   frame_adjust_shape_kind(self, ShapeInput);
327 #endif
328 }
329
330 void frame_adjust_area(ObFrame *self, gboolean moved,
331                        gboolean resized, gboolean fake)
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, self->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             self->oldsize = self->size;
859         }
860
861         /* if this occurs while we are focus cycling, the indicator needs to
862            match the changes */
863         if (focus_cycle_target == self->client)
864             focus_cycle_update_indicator(self->client);
865     }
866     if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR) &&
867         self->label_width)
868     {
869         XResizeWindow(obt_display, self->label, self->label_width,
870                       ob_rr_theme->label_height);
871     }
872 }
873
874 static void frame_adjust_cursors(ObFrame *self)
875 {
876     if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
877         (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
878         self->max_horz != self->client->max_horz ||
879         self->max_vert != self->client->max_vert ||
880         self->shaded != self->client->shaded)
881     {
882         gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
883             !(self->client->max_horz && self->client->max_vert);
884         gboolean topbot = !self->client->max_vert;
885         gboolean sh = self->client->shaded;
886         XSetWindowAttributes a;
887
888         /* these ones turn off when max vert, and some when shaded */
889         a.cursor = ob_cursor(r && topbot && !sh ?
890                              OB_CURSOR_NORTH : OB_CURSOR_NONE);
891         XChangeWindowAttributes(obt_display, self->topresize, CWCursor, &a);
892         XChangeWindowAttributes(obt_display, self->titletop, CWCursor, &a);
893         a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
894         XChangeWindowAttributes(obt_display, self->handle, CWCursor, &a);
895         XChangeWindowAttributes(obt_display, self->handletop, CWCursor, &a);
896         XChangeWindowAttributes(obt_display, self->handlebottom, CWCursor, &a);
897         XChangeWindowAttributes(obt_display, self->innerbottom, CWCursor, &a);
898
899         /* these ones change when shaded */
900         a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
901                              OB_CURSOR_NONE);
902         XChangeWindowAttributes(obt_display, self->titleleft, CWCursor, &a);
903         XChangeWindowAttributes(obt_display, self->tltresize, CWCursor, &a);
904         XChangeWindowAttributes(obt_display, self->tllresize, CWCursor, &a);
905         XChangeWindowAttributes(obt_display, self->titletopleft, CWCursor, &a);
906         a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
907                              OB_CURSOR_NONE);
908         XChangeWindowAttributes(obt_display, self->titleright, CWCursor, &a);
909         XChangeWindowAttributes(obt_display, self->trtresize, CWCursor, &a);
910         XChangeWindowAttributes(obt_display, self->trrresize, CWCursor, &a);
911         XChangeWindowAttributes(obt_display, self->titletopright, CWCursor,&a);
912
913         /* these ones are pretty static */
914         a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
915         XChangeWindowAttributes(obt_display, self->left, CWCursor, &a);
916         XChangeWindowAttributes(obt_display, self->innerleft, CWCursor, &a);
917         a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
918         XChangeWindowAttributes(obt_display, self->right, CWCursor, &a);
919         XChangeWindowAttributes(obt_display, self->innerright, CWCursor, &a);
920         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
921         XChangeWindowAttributes(obt_display, self->lgrip, CWCursor, &a);
922         XChangeWindowAttributes(obt_display, self->handleleft, CWCursor, &a);
923         XChangeWindowAttributes(obt_display, self->lgripleft, CWCursor, &a);
924         XChangeWindowAttributes(obt_display, self->lgriptop, CWCursor, &a);
925         XChangeWindowAttributes(obt_display, self->lgripbottom, CWCursor, &a);
926         XChangeWindowAttributes(obt_display, self->innerbll, CWCursor, &a);
927         XChangeWindowAttributes(obt_display, self->innerblb, CWCursor, &a);
928         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
929         XChangeWindowAttributes(obt_display, self->rgrip, CWCursor, &a);
930         XChangeWindowAttributes(obt_display, self->handleright, CWCursor, &a);
931         XChangeWindowAttributes(obt_display, self->rgripright, CWCursor, &a);
932         XChangeWindowAttributes(obt_display, self->rgriptop, CWCursor, &a);
933         XChangeWindowAttributes(obt_display, self->rgripbottom, CWCursor, &a);
934         XChangeWindowAttributes(obt_display, self->innerbrr, CWCursor, &a);
935         XChangeWindowAttributes(obt_display, self->innerbrb, CWCursor, &a);
936     }
937 }
938
939 void frame_adjust_client_area(ObFrame *self)
940 {
941     /* adjust the window which is there to prevent flashing on unmap */
942     XMoveResizeWindow(obt_display, self->backfront, 0, 0,
943                       self->client->area.width,
944                       self->client->area.height);
945 }
946
947 void frame_adjust_state(ObFrame *self)
948 {
949     self->need_render = TRUE;
950     framerender_frame(self);
951 }
952
953 void frame_adjust_focus(ObFrame *self, gboolean hilite)
954 {
955     ob_debug_type(OB_DEBUG_FOCUS,
956                   "Frame for 0x%x has focus: %d\n",
957                   self->client->window, hilite);
958     self->focused = hilite;
959     self->need_render = TRUE;
960     framerender_frame(self);
961     XFlush(obt_display);
962 }
963
964 void frame_adjust_title(ObFrame *self)
965 {
966     self->need_render = TRUE;
967     framerender_frame(self);
968 }
969
970 void frame_adjust_icon(ObFrame *self)
971 {
972     self->need_render = TRUE;
973     framerender_frame(self);
974 }
975
976 void frame_grab_client(ObFrame *self)
977 {
978     /* DO NOT map the client window here. we used to do that, but it is bogus.
979        we need to set up the client's dimensions and everything before we
980        send a mapnotify or we create race conditions.
981     */
982
983     /* reparent the client to the frame */
984     XReparentWindow(obt_display, self->client->window, self->window, 0, 0);
985
986     /*
987       When reparenting the client window, it is usually not mapped yet, since
988       this occurs from a MapRequest. However, in the case where Openbox is
989       starting up, the window is already mapped, so we'll see an unmap event
990       for it.
991     */
992     if (ob_state() == OB_STATE_STARTING)
993         ++self->client->ignore_unmaps;
994
995     /* select the event mask on the client's parent (to receive config/map
996        req's) the ButtonPress is to catch clicks on the client border */
997     XSelectInput(obt_display, self->window, FRAME_EVENTMASK);
998
999     /* set all the windows for the frame in the window_map */
1000     window_add(&self->window, CLIENT_AS_WINDOW(self->client));
1001     window_add(&self->backback, CLIENT_AS_WINDOW(self->client));
1002     window_add(&self->backfront, CLIENT_AS_WINDOW(self->client));
1003     window_add(&self->innerleft, CLIENT_AS_WINDOW(self->client));
1004     window_add(&self->innertop, CLIENT_AS_WINDOW(self->client));
1005     window_add(&self->innerright, CLIENT_AS_WINDOW(self->client));
1006     window_add(&self->innerbottom, CLIENT_AS_WINDOW(self->client));
1007     window_add(&self->innerblb, CLIENT_AS_WINDOW(self->client));
1008     window_add(&self->innerbll, CLIENT_AS_WINDOW(self->client));
1009     window_add(&self->innerbrb, CLIENT_AS_WINDOW(self->client));
1010     window_add(&self->innerbrr, CLIENT_AS_WINDOW(self->client));
1011     window_add(&self->title, CLIENT_AS_WINDOW(self->client));
1012     window_add(&self->label, CLIENT_AS_WINDOW(self->client));
1013     window_add(&self->max, CLIENT_AS_WINDOW(self->client));
1014     window_add(&self->close, CLIENT_AS_WINDOW(self->client));
1015     window_add(&self->desk, CLIENT_AS_WINDOW(self->client));
1016     window_add(&self->shade, CLIENT_AS_WINDOW(self->client));
1017     window_add(&self->icon, CLIENT_AS_WINDOW(self->client));
1018     window_add(&self->iconify, CLIENT_AS_WINDOW(self->client));
1019     window_add(&self->handle, CLIENT_AS_WINDOW(self->client));
1020     window_add(&self->lgrip, CLIENT_AS_WINDOW(self->client));
1021     window_add(&self->rgrip, CLIENT_AS_WINDOW(self->client));
1022     window_add(&self->topresize, CLIENT_AS_WINDOW(self->client));
1023     window_add(&self->tltresize, CLIENT_AS_WINDOW(self->client));
1024     window_add(&self->tllresize, CLIENT_AS_WINDOW(self->client));
1025     window_add(&self->trtresize, CLIENT_AS_WINDOW(self->client));
1026     window_add(&self->trrresize, CLIENT_AS_WINDOW(self->client));
1027     window_add(&self->left, CLIENT_AS_WINDOW(self->client));
1028     window_add(&self->right, CLIENT_AS_WINDOW(self->client));
1029     window_add(&self->titleleft, CLIENT_AS_WINDOW(self->client));
1030     window_add(&self->titletop, CLIENT_AS_WINDOW(self->client));
1031     window_add(&self->titletopleft, CLIENT_AS_WINDOW(self->client));
1032     window_add(&self->titletopright, CLIENT_AS_WINDOW(self->client));
1033     window_add(&self->titleright, CLIENT_AS_WINDOW(self->client));
1034     window_add(&self->titlebottom, CLIENT_AS_WINDOW(self->client));
1035     window_add(&self->handleleft, CLIENT_AS_WINDOW(self->client));
1036     window_add(&self->handletop, CLIENT_AS_WINDOW(self->client));
1037     window_add(&self->handleright, CLIENT_AS_WINDOW(self->client));
1038     window_add(&self->handlebottom, CLIENT_AS_WINDOW(self->client));
1039     window_add(&self->lgripleft, CLIENT_AS_WINDOW(self->client));
1040     window_add(&self->lgriptop, CLIENT_AS_WINDOW(self->client));
1041     window_add(&self->lgripbottom, CLIENT_AS_WINDOW(self->client));
1042     window_add(&self->rgripright, CLIENT_AS_WINDOW(self->client));
1043     window_add(&self->rgriptop, CLIENT_AS_WINDOW(self->client));
1044     window_add(&self->rgripbottom, CLIENT_AS_WINDOW(self->client));
1045 }
1046
1047 static gboolean find_reparent(XEvent *e, gpointer data)
1048 {
1049     const ObFrame *self = data;
1050
1051     /* Find ReparentNotify events for the window that aren't being reparented into the
1052        frame, thus the client reparenting itself off the frame. */
1053     return e->type == ReparentNotify && e->xreparent.window == self->client->window &&
1054         e->xreparent.parent != self->window;
1055 }
1056
1057 void frame_release_client(ObFrame *self)
1058 {
1059     /* if there was any animation going on, kill it */
1060     if (self->iconify_animation_timer)
1061         g_source_remove(self->iconify_animation_timer);
1062
1063     /* check if the app has already reparented its window away */
1064     if (!xqueue_exists_local(find_reparent, self)) {
1065         /* according to the ICCCM - if the client doesn't reparent itself,
1066            then we will reparent the window to root for them */
1067         XReparentWindow(obt_display, self->client->window, obt_root(ob_screen),
1068                         self->client->area.x, self->client->area.y);
1069     }
1070
1071     /* remove all the windows for the frame from the window_map */
1072     window_remove(self->window);
1073     window_remove(self->backback);
1074     window_remove(self->backfront);
1075     window_remove(self->innerleft);
1076     window_remove(self->innertop);
1077     window_remove(self->innerright);
1078     window_remove(self->innerbottom);
1079     window_remove(self->innerblb);
1080     window_remove(self->innerbll);
1081     window_remove(self->innerbrb);
1082     window_remove(self->innerbrr);
1083     window_remove(self->title);
1084     window_remove(self->label);
1085     window_remove(self->max);
1086     window_remove(self->close);
1087     window_remove(self->desk);
1088     window_remove(self->shade);
1089     window_remove(self->icon);
1090     window_remove(self->iconify);
1091     window_remove(self->handle);
1092     window_remove(self->lgrip);
1093     window_remove(self->rgrip);
1094     window_remove(self->topresize);
1095     window_remove(self->tltresize);
1096     window_remove(self->tllresize);
1097     window_remove(self->trtresize);
1098     window_remove(self->trrresize);
1099     window_remove(self->left);
1100     window_remove(self->right);
1101     window_remove(self->titleleft);
1102     window_remove(self->titletop);
1103     window_remove(self->titletopleft);
1104     window_remove(self->titletopright);
1105     window_remove(self->titleright);
1106     window_remove(self->titlebottom);
1107     window_remove(self->handleleft);
1108     window_remove(self->handletop);
1109     window_remove(self->handleright);
1110     window_remove(self->handlebottom);
1111     window_remove(self->lgripleft);
1112     window_remove(self->lgriptop);
1113     window_remove(self->lgripbottom);
1114     window_remove(self->rgripright);
1115     window_remove(self->rgriptop);
1116     window_remove(self->rgripbottom);
1117
1118     if (self->flash_timer) g_source_remove(self->flash_timer);
1119 }
1120
1121 /* is there anything present between us and the label? */
1122 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
1123     for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
1124         if (*lc == ' ') continue; /* it was invalid */
1125         if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
1126             return TRUE;
1127         if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
1128             return TRUE;
1129         if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
1130             return TRUE;
1131         if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
1132             return TRUE;
1133         if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
1134             return TRUE;
1135         if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
1136             return TRUE;
1137         if (*lc == 'L') return FALSE;
1138     }
1139     return FALSE;
1140 }
1141
1142 static void place_button(ObFrame *self, const char *lc, gint bwidth,
1143                          gint left, gint i,
1144                          gint *x, gint *button_on, gint *button_x)
1145 {
1146   if (!(*button_on = is_button_present(self, lc, i)))
1147     return;
1148
1149   self->label_width -= bwidth;
1150   if (i > 0)
1151     *button_x = *x;
1152   *x += i * bwidth;
1153   if (i < 0) {
1154     if (self->label_x <= left || *x > self->label_x) {
1155       *button_x = *x;
1156     } else {
1157       /* the button would have been drawn on top of another button */
1158       *button_on = FALSE;
1159       self->label_width += bwidth;
1160     }
1161   }
1162 }
1163
1164 static void layout_title(ObFrame *self)
1165 {
1166     gchar *lc;
1167     gint i;
1168
1169     const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
1170     /* position of the leftmost button */
1171     const gint left = ob_rr_theme->paddingx + 1;
1172     /* position of the rightmost button */
1173     const gint right = self->width;
1174
1175     /* turn them all off */
1176     self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
1177         self->max_on = self->close_on = self->label_on = FALSE;
1178     self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
1179     self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
1180
1181     /* figure out what's being shown, find each element's position, and the
1182        width of the label
1183
1184        do the ones before the label, then after the label,
1185        i will be +1 the first time through when working to the left,
1186        and -1 the second time through when working to the right */
1187     for (i = 1; i >= -1; i-=2) {
1188         gint x;
1189         ObFrameContext *firstcon;
1190
1191         if (i > 0) {
1192             x = left;
1193             lc = config_title_layout;
1194             firstcon = &self->leftmost;
1195         } else {
1196             x = right;
1197             lc = config_title_layout + strlen(config_title_layout)-1;
1198             firstcon = &self->rightmost;
1199         }
1200
1201         /* stop at the end of the string (or the label, which calls break) */
1202         for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1203             if (*lc == 'L') {
1204                 if (i > 0) {
1205                     self->label_on = TRUE;
1206                     self->label_x = x;
1207                 }
1208                 break; /* break the for loop, do other side of label */
1209             } else if (*lc == 'N') {
1210                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1211                 /* icon is bigger than buttons */
1212                 place_button(self, lc, bwidth + 2, left, i, &x, &self->icon_on, &self->icon_x);
1213             } else if (*lc == 'D') {
1214                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1215                 place_button(self, lc, bwidth, left, i, &x, &self->desk_on, &self->desk_x);
1216             } else if (*lc == 'S') {
1217                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1218                 place_button(self, lc, bwidth, left, i, &x, &self->shade_on, &self->shade_x);
1219             } else if (*lc == 'I') {
1220                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1221                 place_button(self, lc, bwidth, left, i, &x, &self->iconify_on, &self->iconify_x);
1222             } else if (*lc == 'M') {
1223                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1224                 place_button(self, lc, bwidth, left, i, &x, &self->max_on, &self->max_x);
1225             } else if (*lc == 'C') {
1226                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1227                 place_button(self, lc, bwidth, left, i, &x, &self->close_on, &self->close_x);
1228             } else
1229                 continue; /* don't set firstcon */
1230             firstcon = NULL;
1231         }
1232     }
1233
1234     /* position and map the elements */
1235     if (self->icon_on) {
1236         XMapWindow(obt_display, self->icon);
1237         XMoveWindow(obt_display, self->icon, self->icon_x,
1238                     ob_rr_theme->paddingy);
1239     } else
1240         XUnmapWindow(obt_display, self->icon);
1241
1242     if (self->desk_on) {
1243         XMapWindow(obt_display, self->desk);
1244         XMoveWindow(obt_display, self->desk, self->desk_x,
1245                     ob_rr_theme->paddingy + 1);
1246     } else
1247         XUnmapWindow(obt_display, self->desk);
1248
1249     if (self->shade_on) {
1250         XMapWindow(obt_display, self->shade);
1251         XMoveWindow(obt_display, self->shade, self->shade_x,
1252                     ob_rr_theme->paddingy + 1);
1253     } else
1254         XUnmapWindow(obt_display, self->shade);
1255
1256     if (self->iconify_on) {
1257         XMapWindow(obt_display, self->iconify);
1258         XMoveWindow(obt_display, self->iconify, self->iconify_x,
1259                     ob_rr_theme->paddingy + 1);
1260     } else
1261         XUnmapWindow(obt_display, self->iconify);
1262
1263     if (self->max_on) {
1264         XMapWindow(obt_display, self->max);
1265         XMoveWindow(obt_display, self->max, self->max_x,
1266                     ob_rr_theme->paddingy + 1);
1267     } else
1268         XUnmapWindow(obt_display, self->max);
1269
1270     if (self->close_on) {
1271         XMapWindow(obt_display, self->close);
1272         XMoveWindow(obt_display, self->close, self->close_x,
1273                     ob_rr_theme->paddingy + 1);
1274     } else
1275         XUnmapWindow(obt_display, self->close);
1276
1277     if (self->label_on && self->label_width > 0) {
1278         XMapWindow(obt_display, self->label);
1279         XMoveWindow(obt_display, self->label, self->label_x,
1280                     ob_rr_theme->paddingy);
1281     } else
1282         XUnmapWindow(obt_display, self->label);
1283 }
1284
1285 gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx)
1286 {
1287     gchar *p, *n;
1288
1289     if (!*names) /* empty string */
1290         return FALSE;
1291
1292     /* find the first space */
1293     for (p = names; *p; p = g_utf8_next_char(p)) {
1294         const gunichar c = g_utf8_get_char(p);
1295         if (g_unichar_isspace(c)) break;
1296     }
1297
1298     if (p == names) {
1299         /* leading spaces in the string */
1300         n = g_utf8_next_char(names);
1301         if (!frame_next_context_from_string(n, cx))
1302             return FALSE;
1303     } else {
1304         n = p;
1305         if (*p) {
1306             /* delete the space with null zero(s) */
1307             while (n < g_utf8_next_char(p))
1308                 *(n++) = '\0';
1309         }
1310
1311         *cx = frame_context_from_string(names);
1312
1313         /* find the next non-space */
1314         for (; *n; n = g_utf8_next_char(n)) {
1315             const gunichar c = g_utf8_get_char(n);
1316             if (!g_unichar_isspace(c)) break;
1317         }
1318     }
1319
1320     /* delete everything we just read (copy everything at n to the start of
1321        the string */
1322     for (p = names; *n; ++p, ++n)
1323         *p = *n;
1324     *p = *n;
1325
1326     return TRUE;
1327 }
1328
1329 ObFrameContext frame_context_from_string(const gchar *name)
1330 {
1331     if (!g_ascii_strcasecmp("Desktop", name))
1332         return OB_FRAME_CONTEXT_DESKTOP;
1333     else if (!g_ascii_strcasecmp("Root", name))
1334         return OB_FRAME_CONTEXT_ROOT;
1335     else if (!g_ascii_strcasecmp("Client", name))
1336         return OB_FRAME_CONTEXT_CLIENT;
1337     else if (!g_ascii_strcasecmp("Titlebar", name))
1338         return OB_FRAME_CONTEXT_TITLEBAR;
1339     else if (!g_ascii_strcasecmp("Frame", name))
1340         return OB_FRAME_CONTEXT_FRAME;
1341     else if (!g_ascii_strcasecmp("TLCorner", name))
1342         return OB_FRAME_CONTEXT_TLCORNER;
1343     else if (!g_ascii_strcasecmp("TRCorner", name))
1344         return OB_FRAME_CONTEXT_TRCORNER;
1345     else if (!g_ascii_strcasecmp("BLCorner", name))
1346         return OB_FRAME_CONTEXT_BLCORNER;
1347     else if (!g_ascii_strcasecmp("BRCorner", name))
1348         return OB_FRAME_CONTEXT_BRCORNER;
1349     else if (!g_ascii_strcasecmp("Top", name))
1350         return OB_FRAME_CONTEXT_TOP;
1351     else if (!g_ascii_strcasecmp("Bottom", name))
1352         return OB_FRAME_CONTEXT_BOTTOM;
1353     else if (!g_ascii_strcasecmp("Left", name))
1354         return OB_FRAME_CONTEXT_LEFT;
1355     else if (!g_ascii_strcasecmp("Right", name))
1356         return OB_FRAME_CONTEXT_RIGHT;
1357     else if (!g_ascii_strcasecmp("Maximize", name))
1358         return OB_FRAME_CONTEXT_MAXIMIZE;
1359     else if (!g_ascii_strcasecmp("AllDesktops", name))
1360         return OB_FRAME_CONTEXT_ALLDESKTOPS;
1361     else if (!g_ascii_strcasecmp("Shade", name))
1362         return OB_FRAME_CONTEXT_SHADE;
1363     else if (!g_ascii_strcasecmp("Iconify", name))
1364         return OB_FRAME_CONTEXT_ICONIFY;
1365     else if (!g_ascii_strcasecmp("Icon", name))
1366         return OB_FRAME_CONTEXT_ICON;
1367     else if (!g_ascii_strcasecmp("Close", name))
1368         return OB_FRAME_CONTEXT_CLOSE;
1369     else if (!g_ascii_strcasecmp("MoveResize", name))
1370         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1371     else if (!g_ascii_strcasecmp("Dock", name))
1372         return OB_FRAME_CONTEXT_DOCK;
1373
1374     return OB_FRAME_CONTEXT_NONE;
1375 }
1376
1377 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1378 {
1379     ObFrame *self;
1380     ObWindow *obwin;
1381
1382     if (moveresize_in_progress)
1383         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1384
1385     if (win == obt_root(ob_screen))
1386         return OB_FRAME_CONTEXT_ROOT;
1387     if ((obwin = window_find(win))) {
1388         if (WINDOW_IS_DOCK(obwin)) {
1389           return OB_FRAME_CONTEXT_DOCK;
1390         }
1391     }
1392     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1393     if (win == client->window) {
1394         /* conceptually, this is the desktop, as far as users are
1395            concerned */
1396         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1397             return OB_FRAME_CONTEXT_DESKTOP;
1398         return OB_FRAME_CONTEXT_CLIENT;
1399     }
1400
1401     self = client->frame;
1402
1403     /* when the user clicks in the corners of the titlebar and the client
1404        is fully maximized, then treat it like they clicked in the
1405        button that is there */
1406     if (self->max_horz && self->max_vert &&
1407         (win == self->title || win == self->titletop ||
1408          win == self->titleleft || win == self->titletopleft ||
1409          win == self->titleright || win == self->titletopright))
1410     {
1411         /* get the mouse coords in reference to the whole frame */
1412         gint fx = x;
1413         gint fy = y;
1414
1415         /* these windows are down a border width from the top of the frame */
1416         if (win == self->title ||
1417             win == self->titleleft || win == self->titleright)
1418             fy += self->bwidth;
1419
1420         /* title is a border width in from the edge */
1421         if (win == self->title)
1422             fx += self->bwidth;
1423         /* titletop is a bit to the right */
1424         else if (win == self->titletop)
1425             fx += ob_rr_theme->grip_width + self->bwidth;
1426         /* titletopright is way to the right edge */
1427         else if (win == self->titletopright)
1428             fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1429         /* titleright is even more way to the right edge */
1430         else if (win == self->titleright)
1431             fx += self->area.width - self->bwidth;
1432
1433         /* figure out if we're over the area that should be considered a
1434            button */
1435         if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1436             ob_rr_theme->button_size)
1437         {
1438             if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1439                       ob_rr_theme->button_size))
1440             {
1441                 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1442                     return self->leftmost;
1443             }
1444             else if (fx >= (self->area.width -
1445                             (self->bwidth + ob_rr_theme->paddingx + 1 +
1446                              ob_rr_theme->button_size)))
1447             {
1448                 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1449                     return self->rightmost;
1450             }
1451         }
1452
1453         /* there is no resizing maximized windows so make them the titlebar
1454            context */
1455         return OB_FRAME_CONTEXT_TITLEBAR;
1456     }
1457     else if (self->max_vert &&
1458              (win == self->titletop || win == self->topresize))
1459         /* can't resize vertically when max vert */
1460         return OB_FRAME_CONTEXT_TITLEBAR;
1461     else if (self->shaded &&
1462              (win == self->titletop || win == self->topresize))
1463         /* can't resize vertically when shaded */
1464         return OB_FRAME_CONTEXT_TITLEBAR;
1465
1466     if (win == self->window)            return OB_FRAME_CONTEXT_FRAME;
1467     if (win == self->label)             return OB_FRAME_CONTEXT_TITLEBAR;
1468     if (win == self->handle)            return OB_FRAME_CONTEXT_BOTTOM;
1469     if (win == self->handletop)         return OB_FRAME_CONTEXT_BOTTOM;
1470     if (win == self->handlebottom)      return OB_FRAME_CONTEXT_BOTTOM;
1471     if (win == self->handleleft)        return OB_FRAME_CONTEXT_BLCORNER;
1472     if (win == self->lgrip)             return OB_FRAME_CONTEXT_BLCORNER;
1473     if (win == self->lgripleft)         return OB_FRAME_CONTEXT_BLCORNER;
1474     if (win == self->lgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1475     if (win == self->lgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1476     if (win == self->handleright)       return OB_FRAME_CONTEXT_BRCORNER;
1477     if (win == self->rgrip)             return OB_FRAME_CONTEXT_BRCORNER;
1478     if (win == self->rgripright)        return OB_FRAME_CONTEXT_BRCORNER;
1479     if (win == self->rgriptop)          return OB_FRAME_CONTEXT_BRCORNER;
1480     if (win == self->rgripbottom)       return OB_FRAME_CONTEXT_BRCORNER;
1481     if (win == self->title)             return OB_FRAME_CONTEXT_TITLEBAR;
1482     if (win == self->titlebottom)       return OB_FRAME_CONTEXT_TITLEBAR;
1483     if (win == self->titleleft)         return OB_FRAME_CONTEXT_TLCORNER;
1484     if (win == self->titletopleft)      return OB_FRAME_CONTEXT_TLCORNER;
1485     if (win == self->titleright)        return OB_FRAME_CONTEXT_TRCORNER;
1486     if (win == self->titletopright)     return OB_FRAME_CONTEXT_TRCORNER;
1487     if (win == self->titletop)          return OB_FRAME_CONTEXT_TOP;
1488     if (win == self->topresize)         return OB_FRAME_CONTEXT_TOP;
1489     if (win == self->tltresize)         return OB_FRAME_CONTEXT_TLCORNER;
1490     if (win == self->tllresize)         return OB_FRAME_CONTEXT_TLCORNER;
1491     if (win == self->trtresize)         return OB_FRAME_CONTEXT_TRCORNER;
1492     if (win == self->trrresize)         return OB_FRAME_CONTEXT_TRCORNER;
1493     if (win == self->left)              return OB_FRAME_CONTEXT_LEFT;
1494     if (win == self->right)             return OB_FRAME_CONTEXT_RIGHT;
1495     if (win == self->innertop)          return OB_FRAME_CONTEXT_TITLEBAR;
1496     if (win == self->innerleft)         return OB_FRAME_CONTEXT_LEFT;
1497     if (win == self->innerbottom)       return OB_FRAME_CONTEXT_BOTTOM;
1498     if (win == self->innerright)        return OB_FRAME_CONTEXT_RIGHT;
1499     if (win == self->innerbll)          return OB_FRAME_CONTEXT_BLCORNER;
1500     if (win == self->innerblb)          return OB_FRAME_CONTEXT_BLCORNER;
1501     if (win == self->innerbrr)          return OB_FRAME_CONTEXT_BRCORNER;
1502     if (win == self->innerbrb)          return OB_FRAME_CONTEXT_BRCORNER;
1503     if (win == self->max)               return OB_FRAME_CONTEXT_MAXIMIZE;
1504     if (win == self->iconify)           return OB_FRAME_CONTEXT_ICONIFY;
1505     if (win == self->close)             return OB_FRAME_CONTEXT_CLOSE;
1506     if (win == self->icon)              return OB_FRAME_CONTEXT_ICON;
1507     if (win == self->desk)              return OB_FRAME_CONTEXT_ALLDESKTOPS;
1508     if (win == self->shade)             return OB_FRAME_CONTEXT_SHADE;
1509
1510     return OB_FRAME_CONTEXT_NONE;
1511 }
1512
1513 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
1514 {
1515     /* horizontal */
1516     switch (self->client->gravity) {
1517     default:
1518     case NorthWestGravity:
1519     case SouthWestGravity:
1520     case WestGravity:
1521         break;
1522
1523     case NorthGravity:
1524     case SouthGravity:
1525     case CenterGravity:
1526         /* the middle of the client will be the middle of the frame */
1527         *x -= (self->size.right - self->size.left) / 2;
1528         break;
1529
1530     case NorthEastGravity:
1531     case SouthEastGravity:
1532     case EastGravity:
1533         /* the right side of the client will be the right side of the frame */
1534         *x -= self->size.right + self->size.left -
1535             self->client->border_width * 2;
1536         break;
1537
1538     case ForgetGravity:
1539     case StaticGravity:
1540         /* the client's position won't move */
1541         *x -= self->size.left - self->client->border_width;
1542         break;
1543     }
1544
1545     /* vertical */
1546     switch (self->client->gravity) {
1547     default:
1548     case NorthWestGravity:
1549     case NorthEastGravity:
1550     case NorthGravity:
1551         break;
1552
1553     case CenterGravity:
1554     case EastGravity:
1555     case WestGravity:
1556         /* the middle of the client will be the middle of the frame */
1557         *y -= (self->size.bottom - self->size.top) / 2;
1558         break;
1559
1560     case SouthWestGravity:
1561     case SouthEastGravity:
1562     case SouthGravity:
1563         /* the bottom of the client will be the bottom of the frame */
1564         *y -= self->size.bottom + self->size.top -
1565             self->client->border_width * 2;
1566         break;
1567
1568     case ForgetGravity:
1569     case StaticGravity:
1570         /* the client's position won't move */
1571         *y -= self->size.top - self->client->border_width;
1572         break;
1573     }
1574 }
1575
1576 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
1577 {
1578     /* horizontal */
1579     switch (self->client->gravity) {
1580     default:
1581     case NorthWestGravity:
1582     case WestGravity:
1583     case SouthWestGravity:
1584         break;
1585     case NorthGravity:
1586     case CenterGravity:
1587     case SouthGravity:
1588         /* the middle of the client will be the middle of the frame */
1589         *x += (self->size.right - self->size.left) / 2;
1590         break;
1591     case NorthEastGravity:
1592     case EastGravity:
1593     case SouthEastGravity:
1594         /* the right side of the client will be the right side of the frame */
1595         *x += self->size.right + self->size.left -
1596             self->client->border_width * 2;
1597         break;
1598     case StaticGravity:
1599     case ForgetGravity:
1600         /* the client's position won't move */
1601         *x += self->size.left - self->client->border_width;
1602         break;
1603     }
1604
1605     /* vertical */
1606     switch (self->client->gravity) {
1607     default:
1608     case NorthWestGravity:
1609     case NorthGravity:
1610     case NorthEastGravity:
1611         break;
1612     case WestGravity:
1613     case CenterGravity:
1614     case EastGravity:
1615         /* the middle of the client will be the middle of the frame */
1616         *y += (self->size.bottom - self->size.top) / 2;
1617         break;
1618     case SouthWestGravity:
1619     case SouthGravity:
1620     case SouthEastGravity:
1621         /* the bottom of the client will be the bottom of the frame */
1622         *y += self->size.bottom + self->size.top -
1623             self->client->border_width * 2;
1624         break;
1625     case StaticGravity:
1626     case ForgetGravity:
1627         /* the client's position won't move */
1628         *y += self->size.top - self->client->border_width;
1629         break;
1630     }
1631 }
1632
1633 void frame_rect_to_frame(ObFrame *self, Rect *r)
1634 {
1635     r->width += self->size.left + self->size.right;
1636     r->height += self->size.top + self->size.bottom;
1637     frame_client_gravity(self, &r->x, &r->y);
1638 }
1639
1640 void frame_rect_to_client(ObFrame *self, Rect *r)
1641 {
1642     r->width -= self->size.left + self->size.right;
1643     r->height -= self->size.top + self->size.bottom;
1644     frame_frame_gravity(self, &r->x, &r->y);
1645 }
1646
1647 static void flash_done(gpointer data)
1648 {
1649     ObFrame *self = data;
1650
1651     if (self->focused != self->flash_on)
1652         frame_adjust_focus(self, self->focused);
1653 }
1654
1655 static gboolean flash_timeout(gpointer data)
1656 {
1657     ObFrame *self = data;
1658     GTimeVal now;
1659
1660     g_get_current_time(&now);
1661     if (now.tv_sec > self->flash_end.tv_sec ||
1662         (now.tv_sec == self->flash_end.tv_sec &&
1663          now.tv_usec >= self->flash_end.tv_usec))
1664         self->flashing = FALSE;
1665
1666     if (!self->flashing)
1667         return FALSE; /* we are done */
1668
1669     self->flash_on = !self->flash_on;
1670     if (!self->focused) {
1671         frame_adjust_focus(self, self->flash_on);
1672         self->focused = FALSE;
1673     }
1674
1675     XFlush(obt_display);
1676     return TRUE; /* go again */
1677 }
1678
1679 void frame_flash_start(ObFrame *self)
1680 {
1681     self->flash_on = self->focused;
1682
1683     if (!self->flashing)
1684         self->flash_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
1685                                                600, flash_timeout, self,
1686                                                flash_done);
1687     g_get_current_time(&self->flash_end);
1688     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1689
1690     self->flashing = TRUE;
1691 }
1692
1693 void frame_flash_stop(ObFrame *self)
1694 {
1695     self->flashing = FALSE;
1696 }
1697
1698 static gulong frame_animate_iconify_time_left(ObFrame *self,
1699                                               const GTimeVal *now)
1700 {
1701     glong sec, usec;
1702     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1703     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1704     if (usec < 0) {
1705         usec += G_USEC_PER_SEC;
1706         sec--;
1707     }
1708     /* no negative values */
1709     return MAX(sec * G_USEC_PER_SEC + usec, 0);
1710 }
1711
1712 static gboolean frame_animate_iconify(gpointer p)
1713 {
1714     ObFrame *self = p;
1715     gint x, y, w, h;
1716     gint iconx, icony, iconw;
1717     GTimeVal now;
1718     gulong time;
1719     gboolean iconifying;
1720
1721     if (self->client->icon_geometry.width == 0) {
1722         /* there is no icon geometry set so just go straight down */
1723         const Rect *a;
1724
1725         a = screen_physical_area_monitor(screen_find_monitor(&self->area));
1726         iconx = self->area.x + self->area.width / 2 + 32;
1727         icony = a->y + a->width;
1728         iconw = 64;
1729     } else {
1730         iconx = self->client->icon_geometry.x;
1731         icony = self->client->icon_geometry.y;
1732         iconw = self->client->icon_geometry.width;
1733     }
1734
1735     iconifying = self->iconify_animation_going > 0;
1736
1737     /* how far do we have left to go ? */
1738     g_get_current_time(&now);
1739     time = frame_animate_iconify_time_left(self, &now);
1740
1741     if ((time > 0 && iconifying) || (time == 0 && !iconifying)) {
1742         /* start where the frame is supposed to be */
1743         x = self->area.x;
1744         y = self->area.y;
1745         w = self->area.width;
1746         h = self->area.height;
1747     } else {
1748         /* start at the icon */
1749         x = iconx;
1750         y = icony;
1751         w = iconw;
1752         h = self->size.top; /* just the titlebar */
1753     }
1754
1755     if (time > 0) {
1756         glong dx, dy, dw;
1757         glong elapsed;
1758
1759         dx = self->area.x - iconx;
1760         dy = self->area.y - icony;
1761         dw = self->area.width - self->bwidth * 2 - iconw;
1762          /* if restoring, we move in the opposite direction */
1763         if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1764
1765         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1766         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1767         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1768         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1769         h = self->size.top; /* just the titlebar */
1770     }
1771
1772     XMoveResizeWindow(obt_display, self->window, x, y, w, h);
1773
1774     if (time == 0)
1775         frame_end_iconify_animation(self);
1776
1777     XFlush(obt_display);
1778     return time > 0; /* repeat until we're out of time */
1779 }
1780
1781 void frame_end_iconify_animation(ObFrame *self)
1782 {
1783     /* see if there is an animation going */
1784     if (self->iconify_animation_going == 0) return;
1785
1786     if (!self->visible)
1787         XUnmapWindow(obt_display, self->window);
1788     else {
1789         /* Send a ConfigureNotify when the animation is done, this fixes
1790            KDE's pager showing the window in the wrong place.  since the
1791            window is mapped at a different location and is then moved, we
1792            need to send the synthetic configurenotify, since apps may have
1793            read the position when the client mapped, apparently. */
1794         client_reconfigure(self->client, TRUE);
1795     }
1796
1797     /* we're not animating any more ! */
1798     self->iconify_animation_going = 0;
1799
1800     XMoveResizeWindow(obt_display, self->window,
1801                       self->area.x, self->area.y,
1802                       self->area.width, self->area.height);
1803     /* we delay re-rendering until after we're done animating */
1804     framerender_frame(self);
1805     XFlush(obt_display);
1806 }
1807
1808 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1809 {
1810     gulong time;
1811     gboolean new_anim = FALSE;
1812     gboolean set_end = TRUE;
1813     GTimeVal now;
1814
1815     /* if there is no titlebar, just don't animate for now
1816        XXX it would be nice tho.. */
1817     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1818         return;
1819
1820     /* get the current time */
1821     g_get_current_time(&now);
1822
1823     /* get how long until the end */
1824     time = FRAME_ANIMATE_ICONIFY_TIME;
1825     if (self->iconify_animation_going) {
1826         if (!!iconifying != (self->iconify_animation_going > 0)) {
1827             /* animation was already going on in the opposite direction */
1828             time = time - frame_animate_iconify_time_left(self, &now);
1829         } else
1830             /* animation was already going in the same direction */
1831             set_end = FALSE;
1832     } else
1833         new_anim = TRUE;
1834     self->iconify_animation_going = iconifying ? 1 : -1;
1835
1836     /* set the ending time */
1837     if (set_end) {
1838         self->iconify_animation_end.tv_sec = now.tv_sec;
1839         self->iconify_animation_end.tv_usec = now.tv_usec;
1840         g_time_val_add(&self->iconify_animation_end, time);
1841     }
1842
1843     if (new_anim) {
1844         if (self->iconify_animation_timer)
1845             g_source_remove(self->iconify_animation_timer);
1846         self->iconify_animation_timer =
1847             g_timeout_add_full(G_PRIORITY_DEFAULT,
1848                                FRAME_ANIMATE_ICONIFY_STEP_TIME,
1849                                frame_animate_iconify, self, NULL);
1850                                
1851
1852         /* do the first step */
1853         frame_animate_iconify(self);
1854
1855         /* show it during the animation even if it is not "visible" */
1856         if (!self->visible)
1857             XMapWindow(obt_display, self->window);
1858     }
1859 }