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