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