fix new dimensions for maximized windows
[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->bwidth);
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("Bottom", name))
1129         return OB_FRAME_CONTEXT_BOTTOM;
1130     else if (!g_ascii_strcasecmp("Left", name))
1131         return OB_FRAME_CONTEXT_LEFT;
1132     else if (!g_ascii_strcasecmp("Right", name))
1133         return OB_FRAME_CONTEXT_RIGHT;
1134     else if (!g_ascii_strcasecmp("Maximize", name))
1135         return OB_FRAME_CONTEXT_MAXIMIZE;
1136     else if (!g_ascii_strcasecmp("AllDesktops", name))
1137         return OB_FRAME_CONTEXT_ALLDESKTOPS;
1138     else if (!g_ascii_strcasecmp("Shade", name))
1139         return OB_FRAME_CONTEXT_SHADE;
1140     else if (!g_ascii_strcasecmp("Iconify", name))
1141         return OB_FRAME_CONTEXT_ICONIFY;
1142     else if (!g_ascii_strcasecmp("Icon", name))
1143         return OB_FRAME_CONTEXT_ICON;
1144     else if (!g_ascii_strcasecmp("Close", name))
1145         return OB_FRAME_CONTEXT_CLOSE;
1146     else if (!g_ascii_strcasecmp("MoveResize", name))
1147         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1148     return OB_FRAME_CONTEXT_NONE;
1149 }
1150
1151 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1152 {
1153     ObFrame *self;
1154
1155     if (moveresize_in_progress)
1156         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1157
1158     if (win == RootWindow(ob_display, ob_screen))
1159         return OB_FRAME_CONTEXT_ROOT ;
1160     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1161     if (win == client->window) {
1162         /* conceptually, this is the desktop, as far as users are
1163            concerned */
1164         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1165             return OB_FRAME_CONTEXT_DESKTOP;
1166         return OB_FRAME_CONTEXT_CLIENT;
1167     }
1168
1169     self = client->frame;
1170     if (win == self->inner || win == self->plate) {
1171         /* conceptually, this is the desktop, as far as users are
1172            concerned */
1173         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1174             return OB_FRAME_CONTEXT_DESKTOP;
1175         return OB_FRAME_CONTEXT_CLIENT;
1176     }
1177
1178     if (win == self->title) {
1179         /* when the user clicks in the corners of the titlebar and the client
1180            is fully maximized, then treat it like they clicked in the
1181            button that is there */
1182         if (self->client->max_horz && self->client->max_vert &&
1183             y < ob_rr_theme->paddingy + 1 + ob_rr_theme->button_size)
1184         {
1185             if (x < ((ob_rr_theme->paddingx + 1) * 2 +
1186                      ob_rr_theme->button_size)) {
1187                 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1188                     return self->leftmost;
1189             }
1190             else if (x > (self->width -
1191                           (ob_rr_theme->paddingx + 1 +
1192                            ob_rr_theme->button_size)))
1193             {
1194                 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1195                     return self->rightmost;
1196             }
1197         }
1198         return OB_FRAME_CONTEXT_TITLEBAR;
1199     }
1200
1201     if (win == self->window)            return OB_FRAME_CONTEXT_FRAME;
1202     if (win == self->label)             return OB_FRAME_CONTEXT_TITLEBAR;
1203     if (win == self->handle)            return OB_FRAME_CONTEXT_BOTTOM;
1204     if (win == self->handletop)         return OB_FRAME_CONTEXT_BOTTOM;
1205     if (win == self->handlebottom)      return OB_FRAME_CONTEXT_BOTTOM;
1206     if (win == self->handleleft)        return OB_FRAME_CONTEXT_BLCORNER;
1207     if (win == self->lgrip)             return OB_FRAME_CONTEXT_BLCORNER;
1208     if (win == self->lgripleft)         return OB_FRAME_CONTEXT_BLCORNER;
1209     if (win == self->lgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1210     if (win == self->lgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1211     if (win == self->handleright)       return OB_FRAME_CONTEXT_BRCORNER;
1212     if (win == self->rgrip)             return OB_FRAME_CONTEXT_BRCORNER;
1213     if (win == self->rgripright)        return OB_FRAME_CONTEXT_BLCORNER;
1214     if (win == self->rgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1215     if (win == self->rgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1216     if (win == self->titletop)          return OB_FRAME_CONTEXT_TOP;
1217     if (win == self->topresize)         return OB_FRAME_CONTEXT_TOP;
1218     if (win == self->tltresize)         return OB_FRAME_CONTEXT_TLCORNER;
1219     if (win == self->tllresize)         return OB_FRAME_CONTEXT_TLCORNER;
1220     if (win == self->titleleft)         return OB_FRAME_CONTEXT_TLCORNER;
1221     if (win == self->titletopleft)      return OB_FRAME_CONTEXT_TLCORNER;
1222     if (win == self->trtresize)         return OB_FRAME_CONTEXT_TRCORNER;
1223     if (win == self->trrresize)         return OB_FRAME_CONTEXT_TRCORNER;
1224     if (win == self->titleright)        return OB_FRAME_CONTEXT_TRCORNER;
1225     if (win == self->titletopright)     return OB_FRAME_CONTEXT_TRCORNER;
1226     if (win == self->left)              return OB_FRAME_CONTEXT_LEFT;
1227     if (win == self->right)             return OB_FRAME_CONTEXT_RIGHT;
1228     if (win == self->max)               return OB_FRAME_CONTEXT_MAXIMIZE;
1229     if (win == self->iconify)           return OB_FRAME_CONTEXT_ICONIFY;
1230     if (win == self->close)             return OB_FRAME_CONTEXT_CLOSE;
1231     if (win == self->icon)              return OB_FRAME_CONTEXT_ICON;
1232     if (win == self->desk)              return OB_FRAME_CONTEXT_ALLDESKTOPS;
1233     if (win == self->shade)             return OB_FRAME_CONTEXT_SHADE;
1234
1235     return OB_FRAME_CONTEXT_NONE;
1236 }
1237
1238 void frame_client_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1239 {
1240     /* horizontal */
1241     switch (self->client->gravity) {
1242     default:
1243     case NorthWestGravity:
1244     case SouthWestGravity:
1245     case WestGravity:
1246         break;
1247
1248     case NorthGravity:
1249     case SouthGravity:
1250     case CenterGravity:
1251         *x -= (self->size.left + w) / 2;
1252         break;
1253
1254     case NorthEastGravity:
1255     case SouthEastGravity:
1256     case EastGravity:
1257         *x -= (self->size.left + self->size.right + w) - 1;
1258         break;
1259
1260     case ForgetGravity:
1261     case StaticGravity:
1262         *x -= self->size.left;
1263         break;
1264     }
1265
1266     /* vertical */
1267     switch (self->client->gravity) {
1268     default:
1269     case NorthWestGravity:
1270     case NorthEastGravity:
1271     case NorthGravity:
1272         break;
1273
1274     case CenterGravity:
1275     case EastGravity:
1276     case WestGravity:
1277         *y -= (self->size.top + h) / 2;
1278         break;
1279
1280     case SouthWestGravity:
1281     case SouthEastGravity:
1282     case SouthGravity:
1283         *y -= (self->size.top + self->size.bottom + h) - 1;
1284         break;
1285
1286     case ForgetGravity:
1287     case StaticGravity:
1288         *y -= self->size.top;
1289         break;
1290     }
1291 }
1292
1293 void frame_frame_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1294 {
1295     /* horizontal */
1296     switch (self->client->gravity) {
1297     default:
1298     case NorthWestGravity:
1299     case WestGravity:
1300     case SouthWestGravity:
1301         break;
1302     case NorthGravity:
1303     case CenterGravity:
1304     case SouthGravity:
1305         *x += (self->size.left + w) / 2;
1306         break;
1307     case NorthEastGravity:
1308     case EastGravity:
1309     case SouthEastGravity:
1310         *x += (self->size.left + self->size.right + w) - 1;
1311         break;
1312     case StaticGravity:
1313     case ForgetGravity:
1314         *x += self->size.left;
1315         break;
1316     }
1317
1318     /* vertical */
1319     switch (self->client->gravity) {
1320     default:
1321     case NorthWestGravity:
1322     case NorthGravity:
1323     case NorthEastGravity:
1324         break;
1325     case WestGravity:
1326     case CenterGravity:
1327     case EastGravity:
1328         *y += (self->size.top + h) / 2;
1329         break;
1330     case SouthWestGravity:
1331     case SouthGravity:
1332     case SouthEastGravity:
1333         *y += (self->size.top + self->size.bottom + h) - 1;
1334         break;
1335     case StaticGravity:
1336     case ForgetGravity:
1337         *y += self->size.top;
1338         break;
1339     }
1340 }
1341
1342 static void flash_done(gpointer data)
1343 {
1344     ObFrame *self = data;
1345
1346     if (self->focused != self->flash_on)
1347         frame_adjust_focus(self, self->focused);
1348 }
1349
1350 static gboolean flash_timeout(gpointer data)
1351 {
1352     ObFrame *self = data;
1353     GTimeVal now;
1354
1355     g_get_current_time(&now);
1356     if (now.tv_sec > self->flash_end.tv_sec ||
1357         (now.tv_sec == self->flash_end.tv_sec &&
1358          now.tv_usec >= self->flash_end.tv_usec))
1359         self->flashing = FALSE;
1360
1361     if (!self->flashing)
1362         return FALSE; /* we are done */
1363
1364     self->flash_on = !self->flash_on;
1365     if (!self->focused) {
1366         frame_adjust_focus(self, self->flash_on);
1367         self->focused = FALSE;
1368     }
1369
1370     return TRUE; /* go again */
1371 }
1372
1373 void frame_flash_start(ObFrame *self)
1374 {
1375     self->flash_on = self->focused;
1376
1377     if (!self->flashing)
1378         ob_main_loop_timeout_add(ob_main_loop,
1379                                  G_USEC_PER_SEC * 0.6,
1380                                  flash_timeout,
1381                                  self,
1382                                  g_direct_equal,
1383                                  flash_done);
1384     g_get_current_time(&self->flash_end);
1385     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1386     
1387     self->flashing = TRUE;
1388 }
1389
1390 void frame_flash_stop(ObFrame *self)
1391 {
1392     self->flashing = FALSE;
1393 }
1394
1395 static gulong frame_animate_iconify_time_left(ObFrame *self,
1396                                               const GTimeVal *now)
1397 {
1398     glong sec, usec;
1399     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1400     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1401     if (usec < 0) {
1402         usec += G_USEC_PER_SEC;
1403         sec--;
1404     }
1405     /* no negative values */
1406     return MAX(sec * G_USEC_PER_SEC + usec, 0);
1407 }
1408
1409 static gboolean frame_animate_iconify(gpointer p)
1410 {
1411     ObFrame *self = p;
1412     gint x, y, w, h;
1413     gint iconx, icony, iconw;
1414     GTimeVal now;
1415     gulong time;
1416     gboolean iconifying;
1417
1418     if (self->client->icon_geometry.width == 0) {
1419         /* there is no icon geometry set so just go straight down */
1420         Rect *a = screen_physical_area();
1421         iconx = self->area.x + self->area.width / 2 + 32;
1422         icony = a->y + a->width;
1423         iconw = 64;
1424     } else {
1425         iconx = self->client->icon_geometry.x;
1426         icony = self->client->icon_geometry.y;
1427         iconw = self->client->icon_geometry.width;
1428     }
1429
1430     iconifying = self->iconify_animation_going > 0;
1431
1432     /* how far do we have left to go ? */
1433     g_get_current_time(&now);
1434     time = frame_animate_iconify_time_left(self, &now);
1435     
1436     if (time == 0 || iconifying) {
1437         /* start where the frame is supposed to be */
1438         x = self->area.x;
1439         y = self->area.y;
1440         w = self->area.width - self->bwidth * 2;
1441         h = self->area.height - self->bwidth * 2;
1442     } else {
1443         /* start at the icon */
1444         x = iconx;
1445         y = icony;
1446         w = iconw;
1447         h = self->size.top; /* just the titlebar */
1448     }
1449
1450     if (time > 0) {
1451         glong dx, dy, dw;
1452         glong elapsed;
1453
1454         dx = self->area.x - iconx;
1455         dy = self->area.y - icony;
1456         dw = self->area.width - self->bwidth * 2 - iconw;
1457          /* if restoring, we move in the opposite direction */
1458         if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1459
1460         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1461         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1462         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1463         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1464         h = self->size.top; /* just the titlebar */
1465     }
1466
1467     if (time == 0)
1468         frame_end_iconify_animation(self);
1469     else {
1470         XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1471         XFlush(ob_display);
1472     }
1473
1474     return time > 0; /* repeat until we're out of time */
1475 }
1476
1477 void frame_end_iconify_animation(ObFrame *self)
1478 {
1479     /* see if there is an animation going */
1480     if (self->iconify_animation_going == 0) return;
1481
1482     if (!self->visible)
1483         XUnmapWindow(ob_display, self->window);
1484     else
1485         /* Send a ConfigureNotify when the animation is done, this fixes
1486            KDE's pager showing the window in the wrong place. */
1487         client_reconfigure(self->client);
1488
1489     /* we're not animating any more ! */
1490     self->iconify_animation_going = 0;
1491
1492     XMoveResizeWindow(ob_display, self->window,
1493                       self->area.x, self->area.y,
1494                       self->area.width - self->bwidth * 2,
1495                       self->area.height - self->bwidth * 2);
1496     XFlush(ob_display);
1497 }
1498
1499 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1500 {
1501     gulong time;
1502     gboolean new_anim = FALSE;
1503     gboolean set_end = TRUE;
1504     GTimeVal now;
1505
1506     /* if there is no titlebar, just don't animate for now
1507        XXX it would be nice tho.. */
1508     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1509         return;
1510
1511     /* get the current time */
1512     g_get_current_time(&now);
1513
1514     /* get how long until the end */
1515     time = FRAME_ANIMATE_ICONIFY_TIME;
1516     if (self->iconify_animation_going) {
1517         if (!!iconifying != (self->iconify_animation_going > 0)) {
1518             /* animation was already going on in the opposite direction */
1519             time = time - frame_animate_iconify_time_left(self, &now);
1520         } else
1521             /* animation was already going in the same direction */
1522             set_end = FALSE;
1523     } else
1524         new_anim = TRUE;
1525     self->iconify_animation_going = iconifying ? 1 : -1;
1526
1527     /* set the ending time */
1528     if (set_end) {
1529         self->iconify_animation_end.tv_sec = now.tv_sec;
1530         self->iconify_animation_end.tv_usec = now.tv_usec;
1531         g_time_val_add(&self->iconify_animation_end, time);
1532     }
1533
1534     if (new_anim) {
1535         ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1536                                          self, FALSE);
1537         ob_main_loop_timeout_add(ob_main_loop,
1538                                  FRAME_ANIMATE_ICONIFY_STEP_TIME,
1539                                  frame_animate_iconify, self,
1540                                  g_direct_equal, NULL);
1541
1542         /* do the first step */
1543         frame_animate_iconify(self);
1544
1545         /* show it during the animation even if it is not "visible" */
1546         if (!self->visible)
1547             XMapWindow(ob_display, self->window);
1548     }
1549 }