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