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