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