fix for shaped windows
[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->titletop);
425                 XUnmapWindow(ob_display, self->titletopleft);
426                 XUnmapWindow(ob_display, self->titletopright);
427                 XUnmapWindow(ob_display, self->titleleft);
428                 XUnmapWindow(ob_display, self->titleright);
429             }
430
431             if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
432                 XMoveResizeWindow(ob_display, self->title,
433                                   self->bwidth, self->bwidth,
434                                   self->width, ob_rr_theme->title_height);
435
436                 XMapWindow(ob_display, self->title);
437
438                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
439                     XMoveResizeWindow(ob_display, self->topresize,
440                                       ob_rr_theme->grip_width + self->bwidth,
441                                       0,
442                                       self->width - (ob_rr_theme->grip_width +
443                                                      self->bwidth) * 2,
444                                       ob_rr_theme->paddingy + 1);
445
446                     XMoveWindow(ob_display, self->tltresize, 0, 0);
447                     XMoveWindow(ob_display, self->tllresize, 0, 0);
448                     XMoveWindow(ob_display, self->trtresize,
449                                 self->width - ob_rr_theme->grip_width, 0);
450                     XMoveWindow(ob_display, self->trrresize,
451                                 self->width - ob_rr_theme->paddingx - 1, 0);
452
453                     XMapWindow(ob_display, self->topresize);
454                     XMapWindow(ob_display, self->tltresize);
455                     XMapWindow(ob_display, self->tllresize);
456                     XMapWindow(ob_display, self->trtresize);
457                     XMapWindow(ob_display, self->trrresize);
458                 } else {
459                     XUnmapWindow(ob_display, self->topresize);
460                     XUnmapWindow(ob_display, self->tltresize);
461                     XUnmapWindow(ob_display, self->tllresize);
462                     XUnmapWindow(ob_display, self->trtresize);
463                     XUnmapWindow(ob_display, self->trrresize);
464                 }
465             } else
466                 XUnmapWindow(ob_display, self->title);
467         }
468
469         if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
470             /* layout the title bar elements */
471             layout_title(self);
472
473         if (!fake) {
474             if (self->bwidth) {
475                 XMoveResizeWindow(ob_display, self->handlebottom,
476                                   ob_rr_theme->grip_width +
477                                   self->bwidth * 2,
478                                   self->size.top + self->client->area.height +
479                                   self->size.bottom - self->bwidth,
480                                   self->width - (ob_rr_theme->grip_width +
481                                                  self->bwidth) * 2,
482                                   self->bwidth);
483
484                 XMoveResizeWindow(ob_display, self->lgripleft,
485                                   0,
486                                   self->size.top + self->client->area.height +
487                                   self->size.bottom -
488                                   (self->leftb ?
489                                    ob_rr_theme->grip_width :
490                                    self->size.bottom),
491                                   self->bwidth,
492                                   (self->leftb ?
493                                    ob_rr_theme->grip_width :
494                                    self->size.bottom));
495                 XMoveResizeWindow(ob_display, self->rgripright,
496                                   self->size.left + self->client->area.width +
497                                   self->size.right - self->bwidth,
498                                   self->size.top + self->client->area.height +
499                                   self->size.bottom -
500                                   (self->leftb ?
501                                    ob_rr_theme->grip_width :
502                                    self->size.bottom),
503                                   self->bwidth,
504                                   (self->rightb ?
505                                    ob_rr_theme->grip_width :
506                                    self->size.bottom));
507
508                 XMoveResizeWindow(ob_display, self->lgripbottom,
509                                   self->bwidth,
510                                   self->size.top + self->client->area.height +
511                                   self->size.bottom - self->bwidth,
512                                   ob_rr_theme->grip_width + self->bwidth,
513                                   self->bwidth);
514                 XMoveResizeWindow(ob_display, self->rgripbottom,
515                                   self->size.left + self->client->area.width +
516                                   self->size.right - self->bwidth * 2 -
517                                   ob_rr_theme->grip_width,
518                                   self->size.top + self->client->area.height +
519                                   self->size.bottom - self->bwidth,
520                                   ob_rr_theme->grip_width + self->bwidth,
521                                   self->bwidth);
522
523                 XMapWindow(ob_display, self->handlebottom);
524                 XMapWindow(ob_display, self->lgripleft);
525                 XMapWindow(ob_display, self->rgripright);
526                 XMapWindow(ob_display, self->lgripbottom);
527                 XMapWindow(ob_display, self->rgripbottom);
528
529                 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
530                     ob_rr_theme->handle_height > 0)
531                 {
532                     XMoveResizeWindow(ob_display, self->handletop,
533                                       ob_rr_theme->grip_width +
534                                       self->bwidth * 2,
535                                       FRAME_HANDLE_Y(self),
536                                       self->width - (ob_rr_theme->grip_width +
537                                                      self->bwidth) * 2,
538                                       self->bwidth);
539                     XMapWindow(ob_display, self->handletop);
540
541                     if (self->decorations & OB_FRAME_DECOR_GRIPS) {
542                         XMoveResizeWindow(ob_display, self->handleleft,
543                                           ob_rr_theme->grip_width,
544                                           0,
545                                           self->bwidth,
546                                           ob_rr_theme->handle_height);
547                         XMoveResizeWindow(ob_display, self->handleright,
548                                           self->width -
549                                           ob_rr_theme->grip_width -
550                                           self->bwidth,
551                                           0,
552                                           self->bwidth,
553                                           ob_rr_theme->handle_height);
554
555                         XMoveResizeWindow(ob_display, self->lgriptop,
556                                           self->bwidth,
557                                           FRAME_HANDLE_Y(self),
558                                           ob_rr_theme->grip_width +
559                                           self->bwidth,
560                                           self->bwidth);
561                         XMoveResizeWindow(ob_display, self->rgriptop,
562                                           self->size.left +
563                                           self->client->area.width +
564                                           self->size.right - self->bwidth * 2 -
565                                           ob_rr_theme->grip_width,
566                                           FRAME_HANDLE_Y(self),
567                                           ob_rr_theme->grip_width +
568                                           self->bwidth,
569                                           self->bwidth);
570
571                         XMapWindow(ob_display, self->handleleft);
572                         XMapWindow(ob_display, self->handleright);
573                         XMapWindow(ob_display, self->lgriptop);
574                         XMapWindow(ob_display, self->rgriptop);
575                     } else {
576                         XUnmapWindow(ob_display, self->handleleft);
577                         XUnmapWindow(ob_display, self->handleright);
578                         XUnmapWindow(ob_display, self->lgriptop);
579                         XUnmapWindow(ob_display, self->rgriptop);
580                     }
581                 } else
582                     XUnmapWindow(ob_display, self->handletop);
583             } else {
584                 XUnmapWindow(ob_display, self->handlebottom);
585                 XUnmapWindow(ob_display, self->lgripleft);
586                 XUnmapWindow(ob_display, self->rgripright);
587                 XUnmapWindow(ob_display, self->lgripbottom);
588                 XUnmapWindow(ob_display, self->rgripbottom);
589             }
590
591             if (self->decorations & OB_FRAME_DECOR_HANDLE &&
592                 ob_rr_theme->handle_height > 0)
593             {
594                 XMoveResizeWindow(ob_display, self->handle,
595                                   self->bwidth,
596                                   FRAME_HANDLE_Y(self) + self->bwidth,
597                                   self->width, ob_rr_theme->handle_height);
598                 XMapWindow(ob_display, self->handle);
599
600                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
601                     XMoveResizeWindow(ob_display, self->lgrip,
602                                       0, 0,
603                                       ob_rr_theme->grip_width,
604                                       ob_rr_theme->handle_height);
605                     XMoveResizeWindow(ob_display, self->rgrip,
606                                       self->width - ob_rr_theme->grip_width,
607                                       0,
608                                       ob_rr_theme->grip_width,
609                                       ob_rr_theme->handle_height);
610
611                     XMapWindow(ob_display, self->lgrip);
612                     XMapWindow(ob_display, self->rgrip);
613                 } else {
614                     XUnmapWindow(ob_display, self->lgrip);
615                     XUnmapWindow(ob_display, self->rgrip);
616                 }
617             } else
618                 XUnmapWindow(ob_display, self->handle);
619
620             if (self->bwidth && !self->max_horz) {
621                 XMoveResizeWindow(ob_display, self->left,
622                                   0,
623                                   self->bwidth + ob_rr_theme->grip_width,
624                                   self->bwidth,
625                                   self->client->area.height +
626                                   self->size.top + self->size.bottom -
627                                   ob_rr_theme->grip_width * 2);
628                 XMoveResizeWindow(ob_display, self->right,
629                                   self->client->area.width +
630                                   self->cbwidth_x * 2 + self->bwidth,
631                                   self->bwidth + ob_rr_theme->grip_width,
632                                   self->bwidth,
633                                   self->client->area.height +
634                                   self->size.top + self->size.bottom -
635                                   ob_rr_theme->grip_width * 2);
636
637                 XMapWindow(ob_display, self->left);
638                 XMapWindow(ob_display, self->right);
639             } else {
640                 XUnmapWindow(ob_display, self->left);
641                 XUnmapWindow(ob_display, self->right);
642             }
643
644             /* move and resize the inner border window which contains the plate
645              */
646             XMoveResizeWindow(ob_display, self->inner,
647                               0,
648                               self->size.top - self->cbwidth_y,
649                               self->client->area.width +
650                               self->cbwidth_x * 2 +
651                               (self->leftb ? self->bwidth : 0) +
652                               (self->rightb ? self->bwidth : 0),
653                               self->client->area.height +
654                               self->cbwidth_y * 2);
655
656             /* move the plate */
657             XMoveWindow(ob_display, self->plate,
658                         (self->leftb ? self->bwidth : 0) + self->cbwidth_x,
659                         self->cbwidth_y);
660
661             /* when the client has StaticGravity, it likes to move around. */
662             XMoveWindow(ob_display, self->client->window, 0, 0);
663         }
664     }
665
666     /* shading can change without being moved or resized */
667     RECT_SET_SIZE(self->area,
668                   self->client->area.width +
669                   self->size.left + self->size.right,
670                   (self->client->shaded ?
671                    ob_rr_theme->title_height + self->bwidth * 2:
672                    self->client->area.height +
673                    self->size.top + self->size.bottom));
674
675     if (moved || resized) {
676         /* find the new coordinates, done after setting the frame.size, for
677            frame_client_gravity. */
678         self->area.x = self->client->area.x;
679         self->area.y = self->client->area.y;
680         frame_client_gravity(self, &self->area.x, &self->area.y,
681                              self->client->area.width,
682                              self->client->area.height);
683     }
684
685     if (!fake) {
686         if (!frame_iconify_animating(self))
687             /* move and resize the top level frame.
688                shading can change without being moved or resized.
689                
690                but don't do this during an iconify animation. it will be
691                reflected afterwards.
692             */
693             XMoveResizeWindow(ob_display, self->window,
694                               self->area.x,
695                               self->area.y,
696                               self->area.width,
697                               self->area.height);
698
699         if (resized) {
700             framerender_frame(self);
701             frame_adjust_shape(self);
702         }
703
704         if (!STRUT_EQUAL(self->size, oldsize)) {
705             gulong vals[4];
706             vals[0] = self->size.left;
707             vals[1] = self->size.right;
708             vals[2] = self->size.top;
709             vals[3] = self->size.bottom;
710             PROP_SETA32(self->client->window, net_frame_extents,
711                         cardinal, vals, 4);
712             PROP_SETA32(self->client->window, kde_net_wm_frame_strut,
713                         cardinal, vals, 4);
714         }
715
716         /* if this occurs while we are focus cycling, the indicator needs to
717            match the changes */
718         if (focus_cycle_target == self->client)
719             focus_cycle_draw_indicator(self->client);
720     }
721     if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR))
722         XResizeWindow(ob_display, self->label, self->label_width,
723                       ob_rr_theme->label_height);
724
725     /* set up cursors */
726     if (!fake &&
727         (self->functions & OB_CLIENT_FUNC_RESIZE) !=
728         (self->client->functions & OB_CLIENT_FUNC_RESIZE))
729     {
730         gboolean r = self->client->functions & OB_CLIENT_FUNC_RESIZE;
731         XSetWindowAttributes a;
732
733         a.cursor = ob_cursor(r ? OB_CURSOR_NORTH : OB_CURSOR_NONE);
734         XChangeWindowAttributes(ob_display, self->topresize, CWCursor, &a);
735         XChangeWindowAttributes(ob_display, self->titletop, CWCursor, &a);
736         a.cursor = ob_cursor(r ? OB_CURSOR_NORTHWEST : OB_CURSOR_NONE);
737         XChangeWindowAttributes(ob_display, self->tltresize, CWCursor, &a);
738         XChangeWindowAttributes(ob_display, self->tllresize, CWCursor, &a);
739         XChangeWindowAttributes(ob_display, self->titletopleft, CWCursor, &a);
740         XChangeWindowAttributes(ob_display, self->titleleft, CWCursor, &a);
741         a.cursor = ob_cursor(r ? OB_CURSOR_NORTHEAST : OB_CURSOR_NONE);
742         XChangeWindowAttributes(ob_display, self->trtresize, CWCursor, &a);
743         XChangeWindowAttributes(ob_display, self->trrresize, CWCursor, &a);
744         XChangeWindowAttributes(ob_display, self->titletopright, CWCursor, &a);
745         XChangeWindowAttributes(ob_display, self->titleright, CWCursor, &a);
746         a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
747         XChangeWindowAttributes(ob_display, self->left, CWCursor, &a);
748         a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
749         XChangeWindowAttributes(ob_display, self->right, CWCursor, &a);
750         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
751         XChangeWindowAttributes(ob_display, self->handle, CWCursor, &a);
752         XChangeWindowAttributes(ob_display, self->handletop, CWCursor, &a);
753         XChangeWindowAttributes(ob_display, self->handlebottom, CWCursor, &a);
754         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
755         XChangeWindowAttributes(ob_display, self->lgrip, CWCursor, &a);
756         XChangeWindowAttributes(ob_display, self->handleleft, CWCursor, &a);
757         XChangeWindowAttributes(ob_display, self->lgripleft, CWCursor, &a);
758         XChangeWindowAttributes(ob_display, self->lgriptop, CWCursor, &a);
759         XChangeWindowAttributes(ob_display, self->lgripbottom, CWCursor, &a);
760         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
761         XChangeWindowAttributes(ob_display, self->rgrip, CWCursor, &a);
762         XChangeWindowAttributes(ob_display, self->handleright, CWCursor, &a);
763         XChangeWindowAttributes(ob_display, self->rgripright, CWCursor, &a);
764         XChangeWindowAttributes(ob_display, self->rgriptop, CWCursor, &a);
765         XChangeWindowAttributes(ob_display, self->rgripbottom, CWCursor, &a);
766
767         self->functions = self->client->functions;
768     }
769 }
770
771 void frame_adjust_client_area(ObFrame *self)
772 {
773     /* resize the plate */
774     XResizeWindow(ob_display, self->plate,
775                   self->client->area.width, self->client->area.height);
776 }
777
778 void frame_adjust_state(ObFrame *self)
779 {
780     framerender_frame(self);
781 }
782
783 void frame_adjust_focus(ObFrame *self, gboolean hilite)
784 {
785     self->focused = hilite;
786     framerender_frame(self);
787     XFlush(ob_display);
788 }
789
790 void frame_adjust_title(ObFrame *self)
791 {
792     framerender_frame(self);
793 }
794
795 void frame_adjust_icon(ObFrame *self)
796 {
797     framerender_frame(self);
798 }
799
800 void frame_grab_client(ObFrame *self)
801 {
802     /* reparent the client to the frame */
803     XReparentWindow(ob_display, self->client->window, self->plate, 0, 0);
804
805     /*
806       When reparenting the client window, it is usually not mapped yet, since
807       this occurs from a MapRequest. However, in the case where Openbox is
808       starting up, the window is already mapped, so we'll see unmap events for
809       it. There are 2 unmap events generated that we see, one with the 'event'
810       member set the root window, and one set to the client, but both get
811       handled and need to be ignored.
812     */
813     if (ob_state() == OB_STATE_STARTING)
814         self->client->ignore_unmaps += 2;
815
816     /* select the event mask on the client's parent (to receive config/map
817        req's) the ButtonPress is to catch clicks on the client border */
818     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
819
820     /* map the client so it maps when the frame does */
821     XMapWindow(ob_display, self->client->window);
822
823     /* set all the windows for the frame in the window_map */
824     g_hash_table_insert(window_map, &self->window, self->client);
825     g_hash_table_insert(window_map, &self->plate, self->client);
826     g_hash_table_insert(window_map, &self->inner, self->client);
827     g_hash_table_insert(window_map, &self->title, self->client);
828     g_hash_table_insert(window_map, &self->label, self->client);
829     g_hash_table_insert(window_map, &self->max, self->client);
830     g_hash_table_insert(window_map, &self->close, self->client);
831     g_hash_table_insert(window_map, &self->desk, self->client);
832     g_hash_table_insert(window_map, &self->shade, self->client);
833     g_hash_table_insert(window_map, &self->icon, self->client);
834     g_hash_table_insert(window_map, &self->iconify, self->client);
835     g_hash_table_insert(window_map, &self->handle, self->client);
836     g_hash_table_insert(window_map, &self->lgrip, self->client);
837     g_hash_table_insert(window_map, &self->rgrip, self->client);
838     g_hash_table_insert(window_map, &self->topresize, self->client);
839     g_hash_table_insert(window_map, &self->tltresize, self->client);
840     g_hash_table_insert(window_map, &self->tllresize, self->client);
841     g_hash_table_insert(window_map, &self->trtresize, self->client);
842     g_hash_table_insert(window_map, &self->trrresize, self->client);
843     g_hash_table_insert(window_map, &self->left, self->client);
844     g_hash_table_insert(window_map, &self->right, self->client);
845     g_hash_table_insert(window_map, &self->titleleft, self->client);
846     g_hash_table_insert(window_map, &self->titletop, self->client);
847     g_hash_table_insert(window_map, &self->titletopleft, self->client);
848     g_hash_table_insert(window_map, &self->titletopright, self->client);
849     g_hash_table_insert(window_map, &self->titleright, self->client);
850     g_hash_table_insert(window_map, &self->titlebottom, self->client);
851     g_hash_table_insert(window_map, &self->handleleft, self->client);
852     g_hash_table_insert(window_map, &self->handletop, self->client);
853     g_hash_table_insert(window_map, &self->handleright, self->client);
854     g_hash_table_insert(window_map, &self->handlebottom, self->client);
855     g_hash_table_insert(window_map, &self->lgripleft, self->client);
856     g_hash_table_insert(window_map, &self->lgriptop, self->client);
857     g_hash_table_insert(window_map, &self->lgripbottom, self->client);
858     g_hash_table_insert(window_map, &self->rgripright, self->client);
859     g_hash_table_insert(window_map, &self->rgriptop, self->client);
860     g_hash_table_insert(window_map, &self->rgripbottom, self->client);
861 }
862
863 void frame_release_client(ObFrame *self)
864 {
865     XEvent ev;
866     gboolean reparent = TRUE;
867
868     /* if there was any animation going on, kill it */
869     ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
870                                      self, FALSE);
871
872     /* check if the app has already reparented its window away */
873     while (XCheckTypedWindowEvent(ob_display, self->client->window,
874                                   ReparentNotify, &ev))
875     {
876         /* This check makes sure we don't catch our own reparent action to
877            our frame window. This doesn't count as the app reparenting itself
878            away of course.
879
880            Reparent events that are generated by us are just discarded here.
881            They are of no consequence to us anyhow.
882         */
883         if (ev.xreparent.parent != self->plate) {
884             reparent = FALSE;
885             XPutBackEvent(ob_display, &ev);
886             break;
887         }
888     }
889
890     if (reparent) {
891         /* according to the ICCCM - if the client doesn't reparent itself,
892            then we will reparent the window to root for them */
893         XReparentWindow(ob_display, self->client->window,
894                         RootWindow(ob_display, ob_screen),
895                         self->client->area.x,
896                         self->client->area.y);
897     }
898
899     /* remove all the windows for the frame from the window_map */
900     g_hash_table_remove(window_map, &self->window);
901     g_hash_table_remove(window_map, &self->plate);
902     g_hash_table_remove(window_map, &self->inner);
903     g_hash_table_remove(window_map, &self->title);
904     g_hash_table_remove(window_map, &self->label);
905     g_hash_table_remove(window_map, &self->max);
906     g_hash_table_remove(window_map, &self->close);
907     g_hash_table_remove(window_map, &self->desk);
908     g_hash_table_remove(window_map, &self->shade);
909     g_hash_table_remove(window_map, &self->icon);
910     g_hash_table_remove(window_map, &self->iconify);
911     g_hash_table_remove(window_map, &self->handle);
912     g_hash_table_remove(window_map, &self->lgrip);
913     g_hash_table_remove(window_map, &self->rgrip);
914     g_hash_table_remove(window_map, &self->topresize);
915     g_hash_table_remove(window_map, &self->tltresize);
916     g_hash_table_remove(window_map, &self->tllresize);
917     g_hash_table_remove(window_map, &self->trtresize);
918     g_hash_table_remove(window_map, &self->trrresize);
919     g_hash_table_remove(window_map, &self->left);
920     g_hash_table_remove(window_map, &self->right);
921     g_hash_table_remove(window_map, &self->titleleft);
922     g_hash_table_remove(window_map, &self->titletop);
923     g_hash_table_remove(window_map, &self->titletopleft);
924     g_hash_table_remove(window_map, &self->titletopright);
925     g_hash_table_remove(window_map, &self->titleright);
926     g_hash_table_remove(window_map, &self->titlebottom);
927     g_hash_table_remove(window_map, &self->handleleft);
928     g_hash_table_remove(window_map, &self->handletop);
929     g_hash_table_remove(window_map, &self->handleright);
930     g_hash_table_remove(window_map, &self->handlebottom);
931     g_hash_table_remove(window_map, &self->lgripleft);
932     g_hash_table_remove(window_map, &self->lgriptop);
933     g_hash_table_remove(window_map, &self->lgripbottom);
934     g_hash_table_remove(window_map, &self->rgripright);
935     g_hash_table_remove(window_map, &self->rgriptop);
936     g_hash_table_remove(window_map, &self->rgripbottom);
937
938     ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
939 }
940
941 /* is there anything present between us and the label? */
942 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
943     for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
944         if (*lc == ' ') continue; /* it was invalid */
945         if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
946             return TRUE;
947         if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
948             return TRUE;
949         if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
950             return TRUE;
951         if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
952             return TRUE;
953         if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
954             return TRUE;
955         if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
956             return TRUE;
957         if (*lc == 'L') return FALSE;
958     }
959     return FALSE;
960 }
961
962 static void layout_title(ObFrame *self)
963 {
964     gchar *lc;
965     gint i;
966
967     const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
968     /* position of the left most button */
969     const gint left = ob_rr_theme->paddingx + 1;
970     /* position of the right most button */
971     const gint right = self->width - bwidth;
972
973     /* turn them all off */
974     self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
975         self->max_on = self->close_on = self->label_on = FALSE;
976     self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
977     self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
978
979     /* figure out what's being show, find each element's position, and the
980        width of the label
981
982        do the ones before the label, then after the label,
983        i will be +1 the first time through when working to the left,
984        and -1 the second time through when working to the right */
985     for (i = 1; i >= -1; i-=2) {
986         gint x;
987         ObFrameContext *firstcon;
988
989         if (i > 0) {
990             x = left;
991             lc = config_title_layout;
992             firstcon = &self->leftmost;
993         } else {
994             x = right;
995             lc = config_title_layout + strlen(config_title_layout)-1;
996             firstcon = &self->rightmost;
997         }
998
999         /* stop at the end of the string (or the label, which calls break) */
1000         for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1001             if (*lc == 'L') {
1002                 if (i > 0) {
1003                     self->label_on = TRUE;
1004                     self->label_x = x;
1005                 }
1006                 break; /* break the for loop, do other side of label */
1007             } else if (*lc == 'N') {
1008                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1009                 if ((self->icon_on = is_button_present(self, lc, i))) {
1010                     /* icon is bigger than buttons */
1011                     self->label_width -= bwidth + 2;
1012                     self->icon_x = x;
1013                     x += i * (bwidth + 2);
1014                 }
1015             } else if (*lc == 'D') {
1016                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1017                 if ((self->desk_on = is_button_present(self, lc, i))) {
1018                     self->label_width -= bwidth;
1019                     self->desk_x = x;
1020                     x += i * bwidth;
1021                 }
1022             } else if (*lc == 'S') {
1023                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1024                 if ((self->shade_on = is_button_present(self, lc, i))) {
1025                     self->label_width -= bwidth;
1026                     self->shade_x = x;
1027                     x += i * bwidth;
1028                 }
1029             } else if (*lc == 'I') {
1030                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1031                 if ((self->iconify_on = is_button_present(self, lc, i))) {
1032                     self->label_width -= bwidth;
1033                     self->iconify_x = x;
1034                     x += i * bwidth;
1035                 }
1036             } else if (*lc == 'M') {
1037                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1038                 if ((self->max_on = is_button_present(self, lc, i))) {
1039                     self->label_width -= bwidth;
1040                     self->max_x = x;
1041                     x += i * bwidth;
1042                 }
1043             } else if (*lc == 'C') {
1044                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1045                 if ((self->close_on = is_button_present(self, lc, i))) {
1046                     self->label_width -= bwidth;
1047                     self->close_x = x;
1048                     x += i * bwidth;
1049                 }
1050             } else
1051                 continue; /* don't set firstcon */
1052             firstcon = NULL;
1053         }
1054     }
1055
1056     /* position and map the elements */
1057     if (self->icon_on) {
1058         XMapWindow(ob_display, self->icon);
1059         XMoveWindow(ob_display, self->icon, self->icon_x,
1060                     ob_rr_theme->paddingy);
1061     } else
1062         XUnmapWindow(ob_display, self->icon);
1063
1064     if (self->desk_on) {
1065         XMapWindow(ob_display, self->desk);
1066         XMoveWindow(ob_display, self->desk, self->desk_x,
1067                     ob_rr_theme->paddingy + 1);
1068     } else
1069         XUnmapWindow(ob_display, self->desk);
1070
1071     if (self->shade_on) {
1072         XMapWindow(ob_display, self->shade);
1073         XMoveWindow(ob_display, self->shade, self->shade_x,
1074                     ob_rr_theme->paddingy + 1);
1075     } else
1076         XUnmapWindow(ob_display, self->shade);
1077
1078     if (self->iconify_on) {
1079         XMapWindow(ob_display, self->iconify);
1080         XMoveWindow(ob_display, self->iconify, self->iconify_x,
1081                     ob_rr_theme->paddingy + 1);
1082     } else
1083         XUnmapWindow(ob_display, self->iconify);
1084
1085     if (self->max_on) {
1086         XMapWindow(ob_display, self->max);
1087         XMoveWindow(ob_display, self->max, self->max_x,
1088                     ob_rr_theme->paddingy + 1);
1089     } else
1090         XUnmapWindow(ob_display, self->max);
1091
1092     if (self->close_on) {
1093         XMapWindow(ob_display, self->close);
1094         XMoveWindow(ob_display, self->close, self->close_x,
1095                     ob_rr_theme->paddingy + 1);
1096     } else
1097         XUnmapWindow(ob_display, self->close);
1098
1099     if (self->label_on) {
1100         self->label_width = MAX(1, self->label_width); /* no lower than 1 */
1101         XMapWindow(ob_display, self->label);
1102         XMoveWindow(ob_display, self->label, self->label_x,
1103                     ob_rr_theme->paddingy);
1104     } else
1105         XUnmapWindow(ob_display, self->label);
1106 }
1107
1108 ObFrameContext frame_context_from_string(const gchar *name)
1109 {
1110     if (!g_ascii_strcasecmp("Desktop", name))
1111         return OB_FRAME_CONTEXT_DESKTOP;
1112     else if (!g_ascii_strcasecmp("Root", name))
1113         return OB_FRAME_CONTEXT_ROOT;
1114     else if (!g_ascii_strcasecmp("Client", name))
1115         return OB_FRAME_CONTEXT_CLIENT;
1116     else if (!g_ascii_strcasecmp("Titlebar", name))
1117         return OB_FRAME_CONTEXT_TITLEBAR;
1118     else if (!g_ascii_strcasecmp("Frame", name))
1119         return OB_FRAME_CONTEXT_FRAME;
1120     else if (!g_ascii_strcasecmp("TLCorner", name))
1121         return OB_FRAME_CONTEXT_TLCORNER;
1122     else if (!g_ascii_strcasecmp("TRCorner", name))
1123         return OB_FRAME_CONTEXT_TRCORNER;
1124     else if (!g_ascii_strcasecmp("BLCorner", name))
1125         return OB_FRAME_CONTEXT_BLCORNER;
1126     else if (!g_ascii_strcasecmp("BRCorner", name))
1127         return OB_FRAME_CONTEXT_BRCORNER;
1128     else if (!g_ascii_strcasecmp("Top", name))
1129         return OB_FRAME_CONTEXT_TOP;
1130     else if (!g_ascii_strcasecmp("Bottom", name))
1131         return OB_FRAME_CONTEXT_BOTTOM;
1132     else if (!g_ascii_strcasecmp("Left", name))
1133         return OB_FRAME_CONTEXT_LEFT;
1134     else if (!g_ascii_strcasecmp("Right", name))
1135         return OB_FRAME_CONTEXT_RIGHT;
1136     else if (!g_ascii_strcasecmp("Maximize", name))
1137         return OB_FRAME_CONTEXT_MAXIMIZE;
1138     else if (!g_ascii_strcasecmp("AllDesktops", name))
1139         return OB_FRAME_CONTEXT_ALLDESKTOPS;
1140     else if (!g_ascii_strcasecmp("Shade", name))
1141         return OB_FRAME_CONTEXT_SHADE;
1142     else if (!g_ascii_strcasecmp("Iconify", name))
1143         return OB_FRAME_CONTEXT_ICONIFY;
1144     else if (!g_ascii_strcasecmp("Icon", name))
1145         return OB_FRAME_CONTEXT_ICON;
1146     else if (!g_ascii_strcasecmp("Close", name))
1147         return OB_FRAME_CONTEXT_CLOSE;
1148     else if (!g_ascii_strcasecmp("MoveResize", name))
1149         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1150     return OB_FRAME_CONTEXT_NONE;
1151 }
1152
1153 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1154 {
1155     ObFrame *self;
1156
1157     if (moveresize_in_progress)
1158         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1159
1160     if (win == RootWindow(ob_display, ob_screen))
1161         return OB_FRAME_CONTEXT_ROOT ;
1162     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1163     if (win == client->window) {
1164         /* conceptually, this is the desktop, as far as users are
1165            concerned */
1166         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1167             return OB_FRAME_CONTEXT_DESKTOP;
1168         return OB_FRAME_CONTEXT_CLIENT;
1169     }
1170
1171     self = client->frame;
1172     if (win == self->inner || win == self->plate) {
1173         /* conceptually, this is the desktop, as far as users are
1174            concerned */
1175         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1176             return OB_FRAME_CONTEXT_DESKTOP;
1177         return OB_FRAME_CONTEXT_CLIENT;
1178     }
1179
1180     if (win == self->title) {
1181         /* when the user clicks in the corners of the titlebar and the client
1182            is fully maximized, then treat it like they clicked in the
1183            button that is there */
1184         if (self->client->max_horz && self->client->max_vert &&
1185             y < ob_rr_theme->paddingy + 1 + ob_rr_theme->button_size)
1186         {
1187             if (x < ((ob_rr_theme->paddingx + 1) * 2 +
1188                      ob_rr_theme->button_size)) {
1189                 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1190                     return self->leftmost;
1191             }
1192             else if (x > (self->width -
1193                           (ob_rr_theme->paddingx + 1 +
1194                            ob_rr_theme->button_size)))
1195             {
1196                 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1197                     return self->rightmost;
1198             }
1199         }
1200         return OB_FRAME_CONTEXT_TITLEBAR;
1201     }
1202
1203     if (win == self->window)            return OB_FRAME_CONTEXT_FRAME;
1204     if (win == self->label)             return OB_FRAME_CONTEXT_TITLEBAR;
1205     if (win == self->handle)            return OB_FRAME_CONTEXT_BOTTOM;
1206     if (win == self->handletop)         return OB_FRAME_CONTEXT_BOTTOM;
1207     if (win == self->handlebottom)      return OB_FRAME_CONTEXT_BOTTOM;
1208     if (win == self->handleleft)        return OB_FRAME_CONTEXT_BLCORNER;
1209     if (win == self->lgrip)             return OB_FRAME_CONTEXT_BLCORNER;
1210     if (win == self->lgripleft)         return OB_FRAME_CONTEXT_BLCORNER;
1211     if (win == self->lgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1212     if (win == self->lgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1213     if (win == self->handleright)       return OB_FRAME_CONTEXT_BRCORNER;
1214     if (win == self->rgrip)             return OB_FRAME_CONTEXT_BRCORNER;
1215     if (win == self->rgripright)        return OB_FRAME_CONTEXT_BLCORNER;
1216     if (win == self->rgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1217     if (win == self->rgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1218     if (win == self->titletop)          return OB_FRAME_CONTEXT_TOP;
1219     if (win == self->topresize)         return OB_FRAME_CONTEXT_TOP;
1220     if (win == self->tltresize)         return OB_FRAME_CONTEXT_TLCORNER;
1221     if (win == self->tllresize)         return OB_FRAME_CONTEXT_TLCORNER;
1222     if (win == self->titleleft)         return OB_FRAME_CONTEXT_TLCORNER;
1223     if (win == self->titletopleft)      return OB_FRAME_CONTEXT_TLCORNER;
1224     if (win == self->trtresize)         return OB_FRAME_CONTEXT_TRCORNER;
1225     if (win == self->trrresize)         return OB_FRAME_CONTEXT_TRCORNER;
1226     if (win == self->titleright)        return OB_FRAME_CONTEXT_TRCORNER;
1227     if (win == self->titletopright)     return OB_FRAME_CONTEXT_TRCORNER;
1228     if (win == self->left)              return OB_FRAME_CONTEXT_LEFT;
1229     if (win == self->right)             return OB_FRAME_CONTEXT_RIGHT;
1230     if (win == self->max)               return OB_FRAME_CONTEXT_MAXIMIZE;
1231     if (win == self->iconify)           return OB_FRAME_CONTEXT_ICONIFY;
1232     if (win == self->close)             return OB_FRAME_CONTEXT_CLOSE;
1233     if (win == self->icon)              return OB_FRAME_CONTEXT_ICON;
1234     if (win == self->desk)              return OB_FRAME_CONTEXT_ALLDESKTOPS;
1235     if (win == self->shade)             return OB_FRAME_CONTEXT_SHADE;
1236
1237     return OB_FRAME_CONTEXT_NONE;
1238 }
1239
1240 void frame_client_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1241 {
1242     /* horizontal */
1243     switch (self->client->gravity) {
1244     default:
1245     case NorthWestGravity:
1246     case SouthWestGravity:
1247     case WestGravity:
1248         break;
1249
1250     case NorthGravity:
1251     case SouthGravity:
1252     case CenterGravity:
1253         *x -= (self->size.left + w) / 2;
1254         break;
1255
1256     case NorthEastGravity:
1257     case SouthEastGravity:
1258     case EastGravity:
1259         *x -= (self->size.left + self->size.right + w) - 1;
1260         break;
1261
1262     case ForgetGravity:
1263     case StaticGravity:
1264         *x -= self->size.left;
1265         break;
1266     }
1267
1268     /* vertical */
1269     switch (self->client->gravity) {
1270     default:
1271     case NorthWestGravity:
1272     case NorthEastGravity:
1273     case NorthGravity:
1274         break;
1275
1276     case CenterGravity:
1277     case EastGravity:
1278     case WestGravity:
1279         *y -= (self->size.top + h) / 2;
1280         break;
1281
1282     case SouthWestGravity:
1283     case SouthEastGravity:
1284     case SouthGravity:
1285         *y -= (self->size.top + self->size.bottom + h) - 1;
1286         break;
1287
1288     case ForgetGravity:
1289     case StaticGravity:
1290         *y -= self->size.top;
1291         break;
1292     }
1293 }
1294
1295 void frame_frame_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1296 {
1297     /* horizontal */
1298     switch (self->client->gravity) {
1299     default:
1300     case NorthWestGravity:
1301     case WestGravity:
1302     case SouthWestGravity:
1303         break;
1304     case NorthGravity:
1305     case CenterGravity:
1306     case SouthGravity:
1307         *x += (self->size.left + w) / 2;
1308         break;
1309     case NorthEastGravity:
1310     case EastGravity:
1311     case SouthEastGravity:
1312         *x += (self->size.left + self->size.right + w) - 1;
1313         break;
1314     case StaticGravity:
1315     case ForgetGravity:
1316         *x += self->size.left;
1317         break;
1318     }
1319
1320     /* vertical */
1321     switch (self->client->gravity) {
1322     default:
1323     case NorthWestGravity:
1324     case NorthGravity:
1325     case NorthEastGravity:
1326         break;
1327     case WestGravity:
1328     case CenterGravity:
1329     case EastGravity:
1330         *y += (self->size.top + h) / 2;
1331         break;
1332     case SouthWestGravity:
1333     case SouthGravity:
1334     case SouthEastGravity:
1335         *y += (self->size.top + self->size.bottom + h) - 1;
1336         break;
1337     case StaticGravity:
1338     case ForgetGravity:
1339         *y += self->size.top;
1340         break;
1341     }
1342 }
1343
1344 static void flash_done(gpointer data)
1345 {
1346     ObFrame *self = data;
1347
1348     if (self->focused != self->flash_on)
1349         frame_adjust_focus(self, self->focused);
1350 }
1351
1352 static gboolean flash_timeout(gpointer data)
1353 {
1354     ObFrame *self = data;
1355     GTimeVal now;
1356
1357     g_get_current_time(&now);
1358     if (now.tv_sec > self->flash_end.tv_sec ||
1359         (now.tv_sec == self->flash_end.tv_sec &&
1360          now.tv_usec >= self->flash_end.tv_usec))
1361         self->flashing = FALSE;
1362
1363     if (!self->flashing)
1364         return FALSE; /* we are done */
1365
1366     self->flash_on = !self->flash_on;
1367     if (!self->focused) {
1368         frame_adjust_focus(self, self->flash_on);
1369         self->focused = FALSE;
1370     }
1371
1372     return TRUE; /* go again */
1373 }
1374
1375 void frame_flash_start(ObFrame *self)
1376 {
1377     self->flash_on = self->focused;
1378
1379     if (!self->flashing)
1380         ob_main_loop_timeout_add(ob_main_loop,
1381                                  G_USEC_PER_SEC * 0.6,
1382                                  flash_timeout,
1383                                  self,
1384                                  g_direct_equal,
1385                                  flash_done);
1386     g_get_current_time(&self->flash_end);
1387     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1388     
1389     self->flashing = TRUE;
1390 }
1391
1392 void frame_flash_stop(ObFrame *self)
1393 {
1394     self->flashing = FALSE;
1395 }
1396
1397 static gulong frame_animate_iconify_time_left(ObFrame *self,
1398                                               const GTimeVal *now)
1399 {
1400     glong sec, usec;
1401     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1402     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1403     if (usec < 0) {
1404         usec += G_USEC_PER_SEC;
1405         sec--;
1406     }
1407     /* no negative values */
1408     return MAX(sec * G_USEC_PER_SEC + usec, 0);
1409 }
1410
1411 static gboolean frame_animate_iconify(gpointer p)
1412 {
1413     ObFrame *self = p;
1414     gint x, y, w, h;
1415     gint iconx, icony, iconw;
1416     GTimeVal now;
1417     gulong time;
1418     gboolean iconifying;
1419
1420     if (self->client->icon_geometry.width == 0) {
1421         /* there is no icon geometry set so just go straight down */
1422         Rect *a = screen_physical_area();
1423         iconx = self->area.x + self->area.width / 2 + 32;
1424         icony = a->y + a->width;
1425         iconw = 64;
1426     } else {
1427         iconx = self->client->icon_geometry.x;
1428         icony = self->client->icon_geometry.y;
1429         iconw = self->client->icon_geometry.width;
1430     }
1431
1432     iconifying = self->iconify_animation_going > 0;
1433
1434     /* how far do we have left to go ? */
1435     g_get_current_time(&now);
1436     time = frame_animate_iconify_time_left(self, &now);
1437     
1438     if (time == 0 || iconifying) {
1439         /* start where the frame is supposed to be */
1440         x = self->area.x;
1441         y = self->area.y;
1442         w = self->area.width - self->bwidth * 2;
1443         h = self->area.height - self->bwidth * 2;
1444     } else {
1445         /* start at the icon */
1446         x = iconx;
1447         y = icony;
1448         w = iconw;
1449         h = self->size.top; /* just the titlebar */
1450     }
1451
1452     if (time > 0) {
1453         glong dx, dy, dw;
1454         glong elapsed;
1455
1456         dx = self->area.x - iconx;
1457         dy = self->area.y - icony;
1458         dw = self->area.width - self->bwidth * 2 - iconw;
1459          /* if restoring, we move in the opposite direction */
1460         if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1461
1462         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1463         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1464         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1465         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1466         h = self->size.top; /* just the titlebar */
1467     }
1468
1469     if (time == 0)
1470         frame_end_iconify_animation(self);
1471     else {
1472         XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1473         XFlush(ob_display);
1474     }
1475
1476     return time > 0; /* repeat until we're out of time */
1477 }
1478
1479 void frame_end_iconify_animation(ObFrame *self)
1480 {
1481     /* see if there is an animation going */
1482     if (self->iconify_animation_going == 0) return;
1483
1484     if (!self->visible)
1485         XUnmapWindow(ob_display, self->window);
1486     else
1487         /* Send a ConfigureNotify when the animation is done, this fixes
1488            KDE's pager showing the window in the wrong place. */
1489         client_reconfigure(self->client);
1490
1491     /* we're not animating any more ! */
1492     self->iconify_animation_going = 0;
1493
1494     XMoveResizeWindow(ob_display, self->window,
1495                       self->area.x, self->area.y,
1496                       self->area.width - self->bwidth * 2,
1497                       self->area.height - self->bwidth * 2);
1498     XFlush(ob_display);
1499 }
1500
1501 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1502 {
1503     gulong time;
1504     gboolean new_anim = FALSE;
1505     gboolean set_end = TRUE;
1506     GTimeVal now;
1507
1508     /* if there is no titlebar, just don't animate for now
1509        XXX it would be nice tho.. */
1510     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1511         return;
1512
1513     /* get the current time */
1514     g_get_current_time(&now);
1515
1516     /* get how long until the end */
1517     time = FRAME_ANIMATE_ICONIFY_TIME;
1518     if (self->iconify_animation_going) {
1519         if (!!iconifying != (self->iconify_animation_going > 0)) {
1520             /* animation was already going on in the opposite direction */
1521             time = time - frame_animate_iconify_time_left(self, &now);
1522         } else
1523             /* animation was already going in the same direction */
1524             set_end = FALSE;
1525     } else
1526         new_anim = TRUE;
1527     self->iconify_animation_going = iconifying ? 1 : -1;
1528
1529     /* set the ending time */
1530     if (set_end) {
1531         self->iconify_animation_end.tv_sec = now.tv_sec;
1532         self->iconify_animation_end.tv_usec = now.tv_usec;
1533         g_time_val_add(&self->iconify_animation_end, time);
1534     }
1535
1536     if (new_anim) {
1537         ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1538                                          self, FALSE);
1539         ob_main_loop_timeout_add(ob_main_loop,
1540                                  FRAME_ANIMATE_ICONIFY_STEP_TIME,
1541                                  frame_animate_iconify, self,
1542                                  g_direct_equal, NULL);
1543
1544         /* do the first step */
1545         frame_animate_iconify(self);
1546
1547         /* show it during the animation even if it is not "visible" */
1548         if (!self->visible)
1549             XMapWindow(ob_display, self->window);
1550     }
1551 }