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