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