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