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