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