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