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