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