]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/frame.c
make rendering the shortcuts much faster
[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.h"
29 #include "moveresize.h"
30 #include "render/theme.h"
31
32 #define PLATE_EVENTMASK (SubstructureRedirectMask | FocusChangeMask)
33 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
34                          ButtonPressMask | ButtonReleaseMask)
35 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
36                            ButtonMotionMask | \
37                            EnterWindowMask | LeaveWindowMask)
38 /* The inner window does not need enter/leave events.
39    If it does get them, then it needs its own context for enter events
40    because sloppy focus will focus the window when you enter the inner window
41    from the frame. */
42 #define INNER_EVENTMASK (ButtonPressMask)
43
44 #define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \
45                            f->cbwidth_y)
46
47 static void layout_title(ObFrame *self);
48 static void flash_done(gpointer data);
49 static gboolean flash_timeout(gpointer data);
50
51 static void set_theme_statics(ObFrame *self);
52 static void free_theme_statics(ObFrame *self);
53
54 static Window createWindow(Window parent, Visual *visual,
55                            gulong mask, XSetWindowAttributes *attrib)
56 {
57     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
58                          (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
59                          (visual ? visual : RrVisual(ob_rr_inst)),
60                          mask, attrib);
61                        
62 }
63
64 static Visual *check_32bit_client(ObClient *c)
65 {
66     XWindowAttributes wattrib;
67     Status ret;
68
69     ret = XGetWindowAttributes(ob_display, c->window, &wattrib);
70     g_assert(ret != BadDrawable);
71     g_assert(ret != BadWindow);
72
73     if (wattrib.depth == 32)
74         return wattrib.visual;
75     return NULL;
76 }
77
78 ObFrame *frame_new(ObClient *client)
79 {
80     XSetWindowAttributes attrib;
81     gulong mask;
82     ObFrame *self;
83     Visual *visual;
84
85     self = g_new0(ObFrame, 1);
86
87     visual = check_32bit_client(client);
88
89     /* create the non-visible decor windows */
90
91     mask = CWEventMask;
92     if (visual) {
93         /* client has a 32-bit visual */
94         mask |= CWColormap | CWBackPixel | CWBorderPixel;
95         /* create a colormap with the visual */
96         self->colormap = attrib.colormap =
97             XCreateColormap(ob_display,
98                             RootWindow(ob_display, ob_screen),
99                             visual, AllocNone);
100         attrib.background_pixel = BlackPixel(ob_display, 0);
101         attrib.border_pixel = BlackPixel(ob_display, 0);
102     }
103     attrib.event_mask = FRAME_EVENTMASK;
104     self->window = createWindow(RootWindow(ob_display, ob_screen), visual,
105                                 mask, &attrib);
106
107     attrib.event_mask = INNER_EVENTMASK;
108     self->inner = createWindow(self->window, visual, mask, &attrib);
109
110     mask &= ~CWEventMask;
111     self->plate = createWindow(self->inner, visual, mask, &attrib);
112
113     /* create the visible decor windows */
114
115     mask = CWEventMask;
116     if (visual) {
117         /* client has a 32-bit visual */
118         mask |= CWColormap | CWBackPixel | CWBorderPixel;
119         attrib.colormap = RrColormap(ob_rr_inst);
120     }
121     attrib.event_mask = ELEMENT_EVENTMASK;
122     self->title = createWindow(self->window, NULL, mask, &attrib);
123
124     mask |= CWCursor;
125     attrib.cursor = ob_cursor(OB_CURSOR_NORTHWEST);
126     self->tltresize = createWindow(self->title, NULL, mask, &attrib);
127     self->tllresize = createWindow(self->title, NULL, mask, &attrib);
128     attrib.cursor = ob_cursor(OB_CURSOR_NORTHEAST);
129     self->trtresize = createWindow(self->title, NULL, mask, &attrib);
130     self->trrresize = createWindow(self->title, NULL, mask, &attrib);
131
132     mask &= ~CWCursor;
133     self->label = createWindow(self->title, NULL, mask, &attrib);
134     self->max = createWindow(self->title, NULL, mask, &attrib);
135     self->close = createWindow(self->title, NULL, mask, &attrib);
136     self->desk = createWindow(self->title, NULL, mask, &attrib);
137     self->shade = createWindow(self->title, NULL, mask, &attrib);
138     self->icon = createWindow(self->title, NULL, mask, &attrib);
139     self->iconify = createWindow(self->title, NULL, mask, &attrib);
140     self->handle = createWindow(self->window, NULL, mask, &attrib);
141
142     mask |= CWCursor;
143     attrib.cursor = ob_cursor(OB_CURSOR_SOUTHWEST);
144     self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
145     attrib.cursor = ob_cursor(OB_CURSOR_SOUTHEAST);
146     self->rgrip = createWindow(self->handle, NULL, mask, &attrib); 
147
148     self->focused = FALSE;
149
150     /* the other stuff is shown based on decor settings */
151     XMapWindow(ob_display, self->plate);
152     XMapWindow(ob_display, self->inner);
153     XMapWindow(ob_display, self->lgrip);
154     XMapWindow(ob_display, self->rgrip);
155     XMapWindow(ob_display, self->label);
156
157     self->max_press = self->close_press = self->desk_press = 
158         self->iconify_press = self->shade_press = FALSE;
159     self->max_hover = self->close_hover = self->desk_hover = 
160         self->iconify_hover = self->shade_hover = FALSE;
161
162     set_theme_statics(self);
163
164     return (ObFrame*)self;
165 }
166
167 static void set_theme_statics(ObFrame *self)
168 {
169     /* set colors/appearance/sizes for stuff that doesn't change */
170     XSetWindowBorder(ob_display, self->window,
171                      RrColorPixel(ob_rr_theme->frame_b_color));
172     XSetWindowBorder(ob_display, self->inner,
173                      RrColorPixel(ob_rr_theme->frame_b_color));
174     XSetWindowBorder(ob_display, self->title,
175                      RrColorPixel(ob_rr_theme->frame_b_color));
176     XSetWindowBorder(ob_display, self->handle,
177                      RrColorPixel(ob_rr_theme->frame_b_color));
178     XSetWindowBorder(ob_display, self->rgrip,
179                      RrColorPixel(ob_rr_theme->frame_b_color));
180     XSetWindowBorder(ob_display, self->lgrip,
181                      RrColorPixel(ob_rr_theme->frame_b_color));
182
183     XResizeWindow(ob_display, self->max,
184                   ob_rr_theme->button_size, ob_rr_theme->button_size);
185     XResizeWindow(ob_display, self->iconify,
186                   ob_rr_theme->button_size, ob_rr_theme->button_size);
187     XResizeWindow(ob_display, self->icon,
188                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
189     XResizeWindow(ob_display, self->close,
190                   ob_rr_theme->button_size, ob_rr_theme->button_size);
191     XResizeWindow(ob_display, self->desk,
192                   ob_rr_theme->button_size, ob_rr_theme->button_size);
193     XResizeWindow(ob_display, self->shade,
194                   ob_rr_theme->button_size, ob_rr_theme->button_size);
195     if (ob_rr_theme->handle_height > 0) {
196         XResizeWindow(ob_display, self->lgrip,
197                       ob_rr_theme->grip_width, ob_rr_theme->handle_height);
198         XResizeWindow(ob_display, self->rgrip,
199                       ob_rr_theme->grip_width, ob_rr_theme->handle_height);
200     }
201     XResizeWindow(ob_display, self->tltresize,
202                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
203     XResizeWindow(ob_display, self->trtresize,
204                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
205     XResizeWindow(ob_display, self->tllresize,
206                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
207     XResizeWindow(ob_display, self->trrresize,
208                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
209
210     /* set up the dynamic appearances */
211     self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
212     self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
213     self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
214     self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
215     self->a_unfocused_handle =
216         RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
217     self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
218     self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
219 }
220
221 static void free_theme_statics(ObFrame *self)
222 {
223     RrAppearanceFree(self->a_unfocused_title); 
224     RrAppearanceFree(self->a_focused_title);
225     RrAppearanceFree(self->a_unfocused_label);
226     RrAppearanceFree(self->a_focused_label);
227     RrAppearanceFree(self->a_unfocused_handle);
228     RrAppearanceFree(self->a_focused_handle);
229     RrAppearanceFree(self->a_icon);
230 }
231
232 static void frame_free(ObFrame *self)
233 {
234     free_theme_statics(self);
235
236     XDestroyWindow(ob_display, self->window);
237     if (self->colormap)
238         XFreeColormap(ob_display, self->colormap);
239
240     g_free(self);
241 }
242
243 void frame_show(ObFrame *self)
244 {
245     if (!self->visible) {
246         self->visible = TRUE;
247         XMapWindow(ob_display, self->client->window);
248         XMapWindow(ob_display, self->window);
249     }
250 }
251
252 void frame_hide(ObFrame *self)
253 {
254     if (self->visible) {
255         self->visible = FALSE;
256         self->client->ignore_unmaps += 1;
257         /* we unmap the client itself so that we can get MapRequest
258            events, and because the ICCCM tells us to! */
259         XUnmapWindow(ob_display, self->window);
260         XUnmapWindow(ob_display, self->client->window);
261     }
262 }
263
264 void frame_adjust_theme(ObFrame *self)
265 {
266     free_theme_statics(self);
267     set_theme_statics(self);
268 }
269
270 void frame_adjust_shape(ObFrame *self)
271 {
272 #ifdef SHAPE
273     gint num;
274     XRectangle xrect[2];
275
276     if (!self->client->shaped) {
277         /* clear the shape on the frame window */
278         XShapeCombineMask(ob_display, self->window, ShapeBounding,
279                           self->innersize.left,
280                           self->innersize.top,
281                           None, ShapeSet);
282     } else {
283         /* make the frame's shape match the clients */
284         XShapeCombineShape(ob_display, self->window, ShapeBounding,
285                            self->innersize.left,
286                            self->innersize.top,
287                            self->client->window,
288                            ShapeBounding, ShapeSet);
289
290         num = 0;
291         if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
292             xrect[0].x = -ob_rr_theme->fbwidth;
293             xrect[0].y = -ob_rr_theme->fbwidth;
294             xrect[0].width = self->width + self->rbwidth * 2;
295             xrect[0].height = ob_rr_theme->title_height +
296                 self->bwidth * 2;
297             ++num;
298         }
299
300         if (self->decorations & OB_FRAME_DECOR_HANDLE) {
301             xrect[1].x = -ob_rr_theme->fbwidth;
302             xrect[1].y = FRAME_HANDLE_Y(self);
303             xrect[1].width = self->width + self->rbwidth * 2;
304             xrect[1].height = ob_rr_theme->handle_height +
305                 self->bwidth * 2;
306             ++num;
307         }
308
309         XShapeCombineRectangles(ob_display, self->window,
310                                 ShapeBounding, 0, 0, xrect, num,
311                                 ShapeUnion, Unsorted);
312     }
313 #endif
314 }
315
316 void frame_adjust_area(ObFrame *self, gboolean moved,
317                        gboolean resized, gboolean fake)
318 {
319     Strut oldsize;
320
321     oldsize = self->size;
322
323     if (resized) {
324         self->decorations = self->client->decorations;
325         self->max_horz = self->client->max_horz;
326
327         if (self->decorations & OB_FRAME_DECOR_BORDER) {
328             self->bwidth = ob_rr_theme->fbwidth;
329             self->cbwidth_x = ob_rr_theme->cbwidthx;
330             self->cbwidth_y = ob_rr_theme->cbwidthy;
331         } else {
332             self->bwidth = self->cbwidth_x = self->cbwidth_y = 0;
333         }
334         self->rbwidth = self->bwidth;
335
336         if (self->max_horz)
337             self->bwidth = self->cbwidth_x = 0;
338
339         STRUT_SET(self->innersize,
340                   self->cbwidth_x,
341                   self->cbwidth_y,
342                   self->cbwidth_x,
343                   self->cbwidth_y);
344         self->width = self->client->area.width + self->cbwidth_x * 2 -
345             (self->max_horz ? self->rbwidth * 2 : 0);
346         self->width = MAX(self->width, 1); /* no lower than 1 */
347
348         /* set border widths */
349         if (!fake) {
350             XSetWindowBorderWidth(ob_display, self->window, self->bwidth);
351             XSetWindowBorderWidth(ob_display, self->inner, self->bwidth);
352             XSetWindowBorderWidth(ob_display, self->title,  self->rbwidth);
353             XSetWindowBorderWidth(ob_display, self->handle, self->rbwidth);
354             XSetWindowBorderWidth(ob_display, self->lgrip,  self->rbwidth);
355             XSetWindowBorderWidth(ob_display, self->rgrip,  self->rbwidth);
356         }
357
358         if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
359             self->innersize.top += ob_rr_theme->title_height + self->rbwidth +
360                 (self->rbwidth - self->bwidth);
361         if (self->decorations & OB_FRAME_DECOR_HANDLE &&
362             ob_rr_theme->handle_height > 0)
363             self->innersize.bottom += ob_rr_theme->handle_height +
364                 self->rbwidth + (self->rbwidth - self->bwidth);
365   
366         /* they all default off, they're turned on in layout_title */
367         self->icon_x = -1;
368         self->desk_x = -1;
369         self->shade_x = -1;
370         self->iconify_x = -1;
371         self->label_x = -1;
372         self->max_x = -1;
373         self->close_x = -1;
374
375         /* position/size and map/unmap all the windows */
376
377         if (!fake) {
378             if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
379                 XMoveResizeWindow(ob_display, self->title,
380                                   -self->bwidth, -self->bwidth,
381                                   self->width, ob_rr_theme->title_height);
382                 XMapWindow(ob_display, self->title);
383
384                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
385                     XMoveWindow(ob_display, self->tltresize, 0, 0);
386                     XMoveWindow(ob_display, self->tllresize, 0, 0);
387                     XMoveWindow(ob_display, self->trtresize,
388                                 self->width - ob_rr_theme->grip_width, 0);
389                     XMoveWindow(ob_display, self->trrresize,
390                                 self->width - ob_rr_theme->paddingx - 1, 0);
391                     XMapWindow(ob_display, self->tltresize);
392                     XMapWindow(ob_display, self->tllresize);
393                     XMapWindow(ob_display, self->trtresize);
394                     XMapWindow(ob_display, self->trrresize);
395                 } else {
396                     XUnmapWindow(ob_display, self->tltresize);
397                     XUnmapWindow(ob_display, self->tllresize);
398                     XUnmapWindow(ob_display, self->trtresize);
399                     XUnmapWindow(ob_display, self->trrresize);
400                 }
401             } else
402                 XUnmapWindow(ob_display, self->title);
403         }
404
405         if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
406             /* layout the title bar elements */
407             layout_title(self);
408
409         if (!fake) {
410             if (self->decorations & OB_FRAME_DECOR_HANDLE &&
411                 ob_rr_theme->handle_height > 0)
412             {
413                 XMoveResizeWindow(ob_display, self->handle,
414                                   -self->bwidth, FRAME_HANDLE_Y(self),
415                                   self->width, ob_rr_theme->handle_height);
416                 XMapWindow(ob_display, self->handle);
417
418                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
419                     XMoveWindow(ob_display, self->lgrip,
420                                 -self->rbwidth, -self->rbwidth);
421                     XMoveWindow(ob_display, self->rgrip,
422                                 -self->rbwidth + self->width -
423                                 ob_rr_theme->grip_width, -self->rbwidth);
424                     XMapWindow(ob_display, self->lgrip);
425                     XMapWindow(ob_display, self->rgrip);
426                 } else {
427                     XUnmapWindow(ob_display, self->lgrip);
428                     XUnmapWindow(ob_display, self->rgrip);
429                 }
430             } else
431                 XUnmapWindow(ob_display, self->handle);
432
433             /* move and resize the inner border window which contains the plate
434              */
435             XMoveResizeWindow(ob_display, self->inner,
436                               self->innersize.left - self->cbwidth_x -
437                               self->bwidth,
438                               self->innersize.top - self->cbwidth_y -
439                               self->bwidth,
440                               self->client->area.width +
441                               self->cbwidth_x * 2,
442                               self->client->area.height +
443                               self->cbwidth_y * 2);
444
445             /* move the plate */
446             XMoveWindow(ob_display, self->plate,
447                         self->cbwidth_x, self->cbwidth_y);
448
449             /* when the client has StaticGravity, it likes to move around. */
450             XMoveWindow(ob_display, self->client->window, 0, 0);
451         }
452
453         STRUT_SET(self->size,
454                   self->innersize.left + self->bwidth,
455                   self->innersize.top + self->bwidth,
456                   self->innersize.right + self->bwidth,
457                   self->innersize.bottom + self->bwidth);
458     }
459
460     /* shading can change without being moved or resized */
461     RECT_SET_SIZE(self->area,
462                   self->client->area.width +
463                   self->size.left + self->size.right,
464                   (self->client->shaded ?
465                    ob_rr_theme->title_height + self->rbwidth * 2:
466                    self->client->area.height +
467                    self->size.top + self->size.bottom));
468
469     if (moved) {
470         /* find the new coordinates, done after setting the frame.size, for
471            frame_client_gravity. */
472         self->area.x = self->client->area.x;
473         self->area.y = self->client->area.y;
474         frame_client_gravity(self, &self->area.x, &self->area.y);
475     }
476
477     if (!fake) {
478         /* move and resize the top level frame.
479            shading can change without being moved or resized */
480         XMoveResizeWindow(ob_display, self->window,
481                           self->area.x, self->area.y,
482                           self->area.width - self->bwidth * 2,
483                           self->area.height - self->bwidth * 2);
484
485         if (resized) {
486             framerender_frame(self);
487             frame_adjust_shape(self);
488         }
489
490         if (!STRUT_EQUAL(self->size, oldsize)) {
491             gulong vals[4];
492             vals[0] = self->size.left;
493             vals[1] = self->size.right;
494             vals[2] = self->size.top;
495             vals[3] = self->size.bottom;
496             PROP_SETA32(self->client->window, net_frame_extents,
497                         cardinal, vals, 4);
498         }
499
500         /* if this occurs while we are focus cycling, the indicator needs to
501            match the changes */
502         if (focus_cycle_target == self->client)
503             focus_cycle_draw_indicator();
504     }
505     if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR))
506         XResizeWindow(ob_display, self->label, self->label_width,
507                       ob_rr_theme->label_height);
508 }
509
510 void frame_adjust_state(ObFrame *self)
511 {
512     framerender_frame(self);
513 }
514
515 void frame_adjust_focus(ObFrame *self, gboolean hilite)
516 {
517     self->focused = hilite;
518     framerender_frame(self);
519     XFlush(ob_display);
520 }
521
522 void frame_adjust_client_area(ObFrame *self)
523 {
524     /* resize the plate */
525     XResizeWindow(ob_display, self->plate,
526                   self->client->area.width, self->client->area.height);
527 }
528
529 void frame_adjust_title(ObFrame *self)
530 {
531     framerender_frame(self);
532 }
533
534 void frame_adjust_icon(ObFrame *self)
535 {
536     framerender_frame(self);
537 }
538
539 void frame_grab_client(ObFrame *self, ObClient *client)
540 {
541     self->client = client;
542
543     /* reparent the client to the frame */
544     XReparentWindow(ob_display, client->window, self->plate, 0, 0);
545     /*
546       When reparenting the client window, it is usually not mapped yet, since
547       this occurs from a MapRequest. However, in the case where Openbox is
548       starting up, the window is already mapped, so we'll see unmap events for
549       it. There are 2 unmap events generated that we see, one with the 'event'
550       member set the root window, and one set to the client, but both get
551       handled and need to be ignored.
552     */
553     if (ob_state() == OB_STATE_STARTING)
554         client->ignore_unmaps += 2;
555
556     /* select the event mask on the client's parent (to receive config/map
557        req's) the ButtonPress is to catch clicks on the client border */
558     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
559
560     frame_adjust_area(self, TRUE, TRUE, FALSE);
561
562     /* map the client so it maps when the frame does */
563     XMapWindow(ob_display, client->window);
564
565     /* set all the windows for the frame in the window_map */
566     g_hash_table_insert(window_map, &self->window, client);
567     g_hash_table_insert(window_map, &self->plate, client);
568     g_hash_table_insert(window_map, &self->inner, client);
569     g_hash_table_insert(window_map, &self->title, client);
570     g_hash_table_insert(window_map, &self->label, client);
571     g_hash_table_insert(window_map, &self->max, client);
572     g_hash_table_insert(window_map, &self->close, client);
573     g_hash_table_insert(window_map, &self->desk, client);
574     g_hash_table_insert(window_map, &self->shade, client);
575     g_hash_table_insert(window_map, &self->icon, client);
576     g_hash_table_insert(window_map, &self->iconify, client);
577     g_hash_table_insert(window_map, &self->handle, client);
578     g_hash_table_insert(window_map, &self->lgrip, client);
579     g_hash_table_insert(window_map, &self->rgrip, client);
580     g_hash_table_insert(window_map, &self->tltresize, client);
581     g_hash_table_insert(window_map, &self->tllresize, client);
582     g_hash_table_insert(window_map, &self->trtresize, client);
583     g_hash_table_insert(window_map, &self->trrresize, client);
584 }
585
586 void frame_release_client(ObFrame *self, ObClient *client)
587 {
588     XEvent ev;
589     gboolean reparent = TRUE;
590
591     g_assert(self->client == client);
592
593     /* check if the app has already reparented its window away */
594     while (XCheckTypedWindowEvent(ob_display, client->window,
595                                   ReparentNotify, &ev))
596     {
597         /* This check makes sure we don't catch our own reparent action to
598            our frame window. This doesn't count as the app reparenting itself
599            away of course.
600
601            Reparent events that are generated by us are just discarded here.
602            They are of no consequence to us anyhow.
603         */
604         if (ev.xreparent.parent != self->plate) {
605             reparent = FALSE;
606             XPutBackEvent(ob_display, &ev);
607             break;
608         }
609     }
610
611     if (reparent) {
612         /* according to the ICCCM - if the client doesn't reparent itself,
613            then we will reparent the window to root for them */
614         XReparentWindow(ob_display, client->window,
615                         RootWindow(ob_display, ob_screen),
616                         client->area.x,
617                         client->area.y);
618     }
619
620     /* remove all the windows for the frame from the window_map */
621     g_hash_table_remove(window_map, &self->window);
622     g_hash_table_remove(window_map, &self->plate);
623     g_hash_table_remove(window_map, &self->inner);
624     g_hash_table_remove(window_map, &self->title);
625     g_hash_table_remove(window_map, &self->label);
626     g_hash_table_remove(window_map, &self->max);
627     g_hash_table_remove(window_map, &self->close);
628     g_hash_table_remove(window_map, &self->desk);
629     g_hash_table_remove(window_map, &self->shade);
630     g_hash_table_remove(window_map, &self->icon);
631     g_hash_table_remove(window_map, &self->iconify);
632     g_hash_table_remove(window_map, &self->handle);
633     g_hash_table_remove(window_map, &self->lgrip);
634     g_hash_table_remove(window_map, &self->rgrip);
635     g_hash_table_remove(window_map, &self->tltresize);
636     g_hash_table_remove(window_map, &self->tllresize);
637     g_hash_table_remove(window_map, &self->trtresize);
638     g_hash_table_remove(window_map, &self->trrresize);
639
640     ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
641
642     frame_free(self);
643 }
644
645 static void layout_title(ObFrame *self)
646 {
647     gchar *lc;
648     gint x;
649     gboolean n, d, i, l, m, c, s;
650
651     n = d = i = l = m = c = s = FALSE;
652
653     /* figure out whats being shown, and the width of the label */
654     self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
655     for (lc = config_title_layout; *lc != '\0'; ++lc) {
656         switch (*lc) {
657         case 'N':
658             if (n) { *lc = ' '; break; } /* rm duplicates */
659             n = TRUE;
660             self->label_width -= (ob_rr_theme->button_size + 2 +
661                                   ob_rr_theme->paddingx + 1);
662             break;
663         case 'D':
664             if (d) { *lc = ' '; break; }
665             if (!(self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
666                 && config_theme_hidedisabled)
667                 break;
668             d = TRUE;
669             self->label_width -= (ob_rr_theme->button_size +
670                                   ob_rr_theme->paddingx + 1);
671             break;
672         case 'S':
673             if (s) { *lc = ' '; break; }
674             if (!(self->decorations & OB_FRAME_DECOR_SHADE)
675                 && config_theme_hidedisabled)
676                 break;
677             s = TRUE;
678             self->label_width -= (ob_rr_theme->button_size +
679                                   ob_rr_theme->paddingx + 1);
680             break;
681         case 'I':
682             if (i) { *lc = ' '; break; }
683             if (!(self->decorations & OB_FRAME_DECOR_ICONIFY)
684                 && config_theme_hidedisabled)
685                 break;
686             i = TRUE;
687             self->label_width -= (ob_rr_theme->button_size +
688                                   ob_rr_theme->paddingx + 1);
689             break;
690         case 'L':
691             if (l) { *lc = ' '; break; }
692             l = TRUE;
693             break;
694         case 'M':
695             if (m) { *lc = ' '; break; }
696             if (!(self->decorations & OB_FRAME_DECOR_MAXIMIZE)
697                 && config_theme_hidedisabled)
698                 break;
699             m = TRUE;
700             self->label_width -= (ob_rr_theme->button_size +
701                                   ob_rr_theme->paddingx + 1);
702             break;
703         case 'C':
704             if (c) { *lc = ' '; break; }
705             if (!(self->decorations & OB_FRAME_DECOR_CLOSE)
706                 && config_theme_hidedisabled)
707                 break;
708             c = TRUE;
709             self->label_width -= (ob_rr_theme->button_size +
710                                   ob_rr_theme->paddingx + 1);
711             break;
712         }
713     }
714     if (self->label_width < 1) self->label_width = 1;
715
716     if (!n) XUnmapWindow(ob_display, self->icon);
717     if (!d) XUnmapWindow(ob_display, self->desk);
718     if (!s) XUnmapWindow(ob_display, self->shade);
719     if (!i) XUnmapWindow(ob_display, self->iconify);
720     if (!l) XUnmapWindow(ob_display, self->label);
721     if (!m) XUnmapWindow(ob_display, self->max);
722     if (!c) XUnmapWindow(ob_display, self->close);
723
724     x = ob_rr_theme->paddingx + 1;
725     for (lc = config_title_layout; *lc != '\0'; ++lc) {
726         switch (*lc) {
727         case 'N':
728             if (!n) break;
729             self->icon_x = x;
730             XMapWindow(ob_display, self->icon);
731             XMoveWindow(ob_display, self->icon, x, ob_rr_theme->paddingy);
732             x += ob_rr_theme->button_size + 2 + ob_rr_theme->paddingx + 1;
733             break;
734         case 'D':
735             if (!d) break;
736             self->desk_x = x;
737             XMapWindow(ob_display, self->desk);
738             XMoveWindow(ob_display, self->desk, x, ob_rr_theme->paddingy + 1);
739             x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
740             break;
741         case 'S':
742             if (!s) break;
743             self->shade_x = x;
744             XMapWindow(ob_display, self->shade);
745             XMoveWindow(ob_display, self->shade, x, ob_rr_theme->paddingy + 1);
746             x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
747             break;
748         case 'I':
749             if (!i) break;
750             self->iconify_x = x;
751             XMapWindow(ob_display, self->iconify);
752             XMoveWindow(ob_display,self->iconify, x, ob_rr_theme->paddingy + 1);
753             x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
754             break;
755         case 'L':
756             if (!l) break;
757             self->label_x = x;
758             XMapWindow(ob_display, self->label);
759             XMoveWindow(ob_display, self->label, x, ob_rr_theme->paddingy);
760             x += self->label_width + ob_rr_theme->paddingx + 1;
761             break;
762         case 'M':
763             if (!m) break;
764             self->max_x = x;
765             XMapWindow(ob_display, self->max);
766             XMoveWindow(ob_display, self->max, x, ob_rr_theme->paddingy + 1);
767             x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
768             break;
769         case 'C':
770             if (!c) break;
771             self->close_x = x;
772             XMapWindow(ob_display, self->close);
773             XMoveWindow(ob_display, self->close, x, ob_rr_theme->paddingy + 1);
774             x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
775             break;
776         }
777     }
778 }
779
780 ObFrameContext frame_context_from_string(const gchar *name)
781 {
782     if (!g_ascii_strcasecmp("Desktop", name))
783         return OB_FRAME_CONTEXT_DESKTOP;
784     else if (!g_ascii_strcasecmp("Client", name))
785         return OB_FRAME_CONTEXT_CLIENT;
786     else if (!g_ascii_strcasecmp("Titlebar", name))
787         return OB_FRAME_CONTEXT_TITLEBAR;
788     else if (!g_ascii_strcasecmp("Handle", name))
789         return OB_FRAME_CONTEXT_HANDLE;
790     else if (!g_ascii_strcasecmp("Frame", name))
791         return OB_FRAME_CONTEXT_FRAME;
792     else if (!g_ascii_strcasecmp("TLCorner", name))
793         return OB_FRAME_CONTEXT_TLCORNER;
794     else if (!g_ascii_strcasecmp("TRCorner", name))
795         return OB_FRAME_CONTEXT_TRCORNER;
796     else if (!g_ascii_strcasecmp("BLCorner", name))
797         return OB_FRAME_CONTEXT_BLCORNER;
798     else if (!g_ascii_strcasecmp("BRCorner", name))
799         return OB_FRAME_CONTEXT_BRCORNER;
800     else if (!g_ascii_strcasecmp("Maximize", name))
801         return OB_FRAME_CONTEXT_MAXIMIZE;
802     else if (!g_ascii_strcasecmp("AllDesktops", name))
803         return OB_FRAME_CONTEXT_ALLDESKTOPS;
804     else if (!g_ascii_strcasecmp("Shade", name))
805         return OB_FRAME_CONTEXT_SHADE;
806     else if (!g_ascii_strcasecmp("Iconify", name))
807         return OB_FRAME_CONTEXT_ICONIFY;
808     else if (!g_ascii_strcasecmp("Icon", name))
809         return OB_FRAME_CONTEXT_ICON;
810     else if (!g_ascii_strcasecmp("Close", name))
811         return OB_FRAME_CONTEXT_CLOSE;
812     else if (!g_ascii_strcasecmp("MoveResize", name))
813         return OB_FRAME_CONTEXT_MOVE_RESIZE;
814     return OB_FRAME_CONTEXT_NONE;
815 }
816
817 ObFrameContext frame_context(ObClient *client, Window win)
818 {
819     ObFrame *self;
820
821     if (moveresize_in_progress)
822         return OB_FRAME_CONTEXT_MOVE_RESIZE;
823
824     if (win == RootWindow(ob_display, ob_screen))
825         return OB_FRAME_CONTEXT_DESKTOP;
826     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
827     if (win == client->window) {
828         /* conceptually, this is the desktop, as far as users are
829            concerned */
830         if (client->type == OB_CLIENT_TYPE_DESKTOP)
831             return OB_FRAME_CONTEXT_DESKTOP;
832         return OB_FRAME_CONTEXT_CLIENT;
833     }
834
835     self = client->frame;
836     if (win == self->inner || win == self->plate) {
837         /* conceptually, this is the desktop, as far as users are
838            concerned */
839         if (client->type == OB_CLIENT_TYPE_DESKTOP)
840             return OB_FRAME_CONTEXT_DESKTOP;
841         return OB_FRAME_CONTEXT_CLIENT;
842     }
843
844     if (win == self->window)    return OB_FRAME_CONTEXT_FRAME;
845     if (win == self->title)     return OB_FRAME_CONTEXT_TITLEBAR;
846     if (win == self->label)     return OB_FRAME_CONTEXT_TITLEBAR;
847     if (win == self->handle)    return OB_FRAME_CONTEXT_HANDLE;
848     if (win == self->lgrip)     return OB_FRAME_CONTEXT_BLCORNER;
849     if (win == self->rgrip)     return OB_FRAME_CONTEXT_BRCORNER;
850     if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER;
851     if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER;
852     if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER;
853     if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER;
854     if (win == self->max)       return OB_FRAME_CONTEXT_MAXIMIZE;
855     if (win == self->iconify)   return OB_FRAME_CONTEXT_ICONIFY;
856     if (win == self->close)     return OB_FRAME_CONTEXT_CLOSE;
857     if (win == self->icon)      return OB_FRAME_CONTEXT_ICON;
858     if (win == self->desk)      return OB_FRAME_CONTEXT_ALLDESKTOPS;
859     if (win == self->shade)     return OB_FRAME_CONTEXT_SHADE;
860
861     return OB_FRAME_CONTEXT_NONE;
862 }
863
864 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
865 {
866     /* horizontal */
867     switch (self->client->gravity) {
868     default:
869     case NorthWestGravity:
870     case SouthWestGravity:
871     case WestGravity:
872         break;
873
874     case NorthGravity:
875     case SouthGravity:
876     case CenterGravity:
877         *x -= (self->size.left + self->size.right) / 2;
878         break;
879
880     case NorthEastGravity:
881     case SouthEastGravity:
882     case EastGravity:
883         *x -= self->size.left + self->size.right;
884         break;
885
886     case ForgetGravity:
887     case StaticGravity:
888         *x -= self->size.left;
889         break;
890     }
891
892     /* vertical */
893     switch (self->client->gravity) {
894     default:
895     case NorthWestGravity:
896     case NorthEastGravity:
897     case NorthGravity:
898         break;
899
900     case CenterGravity:
901     case EastGravity:
902     case WestGravity:
903         *y -= (self->size.top + self->size.bottom) / 2;
904         break;
905
906     case SouthWestGravity:
907     case SouthEastGravity:
908     case SouthGravity:
909         *y -= self->size.top + self->size.bottom;
910         break;
911
912     case ForgetGravity:
913     case StaticGravity:
914         *y -= self->size.top;
915         break;
916     }
917 }
918
919 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
920 {
921     /* horizontal */
922     switch (self->client->gravity) {
923     default:
924     case NorthWestGravity:
925     case WestGravity:
926     case SouthWestGravity:
927         break;
928     case NorthGravity:
929     case CenterGravity:
930     case SouthGravity:
931         *x += (self->size.left + self->size.right) / 2;
932         break;
933     case NorthEastGravity:
934     case EastGravity:
935     case SouthEastGravity:
936         *x += self->size.left + self->size.right;
937         break;
938     case StaticGravity:
939     case ForgetGravity:
940         *x += self->size.left;
941         break;
942     }
943
944     /* vertical */
945     switch (self->client->gravity) {
946     default:
947     case NorthWestGravity:
948     case NorthGravity:
949     case NorthEastGravity:
950         break;
951     case WestGravity:
952     case CenterGravity:
953     case EastGravity:
954         *y += (self->size.top + self->size.bottom) / 2;
955         break;
956     case SouthWestGravity:
957     case SouthGravity:
958     case SouthEastGravity:
959         *y += self->size.top + self->size.bottom;
960         break;
961     case StaticGravity:
962     case ForgetGravity:
963         *y += self->size.top;
964         break;
965     }
966 }
967
968 static void flash_done(gpointer data)
969 {
970     ObFrame *self = data;
971
972     if (self->focused != self->flash_on)
973         frame_adjust_focus(self, self->focused);
974 }
975
976 static gboolean flash_timeout(gpointer data)
977 {
978     ObFrame *self = data;
979     GTimeVal now;
980
981     g_get_current_time(&now);
982     if (now.tv_sec > self->flash_end.tv_sec ||
983         (now.tv_sec == self->flash_end.tv_sec &&
984          now.tv_usec >= self->flash_end.tv_usec))
985         self->flashing = FALSE;
986
987     if (!self->flashing)
988         return FALSE; /* we are done */
989
990     self->flash_on = !self->flash_on;
991     if (!self->focused) {
992         frame_adjust_focus(self, self->flash_on);
993         self->focused = FALSE;
994     }
995
996     return TRUE; /* go again */
997 }
998
999 void frame_flash_start(ObFrame *self)
1000 {
1001     self->flash_on = self->focused;
1002
1003     if (!self->flashing)
1004         ob_main_loop_timeout_add(ob_main_loop,
1005                                  G_USEC_PER_SEC * 0.6,
1006                                  flash_timeout,
1007                                  self,
1008                                  g_direct_equal,
1009                                  flash_done);
1010     g_get_current_time(&self->flash_end);
1011     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1012     
1013     self->flashing = TRUE;
1014 }
1015
1016 void frame_flash_stop(ObFrame *self)
1017 {
1018     self->flashing = FALSE;
1019 }