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