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