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