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