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