]> icculus.org git repositories - mikachu/openbox.git/blob - engines/openbox/openbox.c
proper height for shaded windows
[mikachu/openbox.git] / engines / openbox / openbox.c
1 #include "theme.h"
2 #include "../../kernel/openbox.h"
3 #include "../../kernel/screen.h"
4 #include "../../kernel/extensions.h"
5 #include "../../kernel/dispatch.h"
6 #include "../../kernel/config.h"
7 #include "../../kernel/frame.h"
8 #include "../../render/render.h"
9 #include "../../render/color.h"
10 #include "../../render/font.h"
11 #include "../../render/mask.h"
12
13 #include <X11/Xlib.h>
14 #include <glib.h>
15
16 #define LABEL_HEIGHT    (s_winfont_height + 2)
17 #define TITLE_HEIGHT    (LABEL_HEIGHT + s_bevel * 2)
18 #define HANDLE_Y(f)     (f->innersize.top + f->frame.client->area.height + \
19                          f->cbwidth)
20 #define BUTTON_SIZE     (LABEL_HEIGHT - 2)
21 #define GRIP_WIDTH      (BUTTON_SIZE * 2)
22 #define HANDLE_WIDTH(f) (f->width - (GRIP_WIDTH + f->bwidth) * 2)
23
24 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
25 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask)
26 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
27                            ButtonMotionMask | ExposureMask)
28
29 /* style settings - geometry */
30 int s_bevel;
31 int s_handle_height;
32 int s_bwidth;
33 int s_cbwidth;
34 /* style settings - colors */
35 color_rgb *s_b_color;
36 color_rgb *s_cb_focused_color;
37 color_rgb *s_cb_unfocused_color;
38 color_rgb *s_title_focused_color;
39 color_rgb *s_title_unfocused_color;
40 color_rgb *s_titlebut_focused_color;
41 color_rgb *s_titlebut_unfocused_color;
42 /* style settings - fonts */
43 int s_winfont_height;
44 int s_winfont_shadow;
45 int s_winfont_shadow_offset;
46 ObFont *s_winfont;
47 /* style settings - masks */
48 pixmap_mask *s_max_mask;
49 pixmap_mask *s_icon_mask;
50 pixmap_mask *s_desk_mask;
51 pixmap_mask *s_close_mask;
52
53 /* global appearances */
54 Appearance *a_focused_unpressed_max;
55 Appearance *a_focused_pressed_max;
56 Appearance *a_unfocused_unpressed_max;
57 Appearance *a_unfocused_pressed_max;
58 Appearance *a_focused_unpressed_close;
59 Appearance *a_focused_pressed_close;
60 Appearance *a_unfocused_unpressed_close;
61 Appearance *a_unfocused_pressed_close;
62 Appearance *a_focused_unpressed_desk;
63 Appearance *a_focused_pressed_desk;
64 Appearance *a_unfocused_unpressed_desk;
65 Appearance *a_unfocused_pressed_desk;
66 Appearance *a_focused_unpressed_iconify;
67 Appearance *a_focused_pressed_iconify;
68 Appearance *a_unfocused_unpressed_iconify;
69 Appearance *a_unfocused_pressed_iconify;
70 Appearance *a_focused_grip;
71 Appearance *a_unfocused_grip;
72 Appearance *a_focused_title;
73 Appearance *a_unfocused_title;
74 Appearance *a_focused_label;
75 Appearance *a_unfocused_label;
76 Appearance *a_icon; /* always parentrelative, so no focused/unfocused */
77 Appearance *a_focused_handle;
78 Appearance *a_unfocused_handle;
79
80 typedef struct ObFrame {
81     Frame frame;
82
83     Window title;
84     Window label;
85     Window max;
86     Window close;
87     Window desk;
88     Window icon;
89     Window iconify;
90     Window handle;
91     Window lgrip;
92     Window rgrip;
93
94     Appearance *a_unfocused_title;
95     Appearance *a_focused_title;
96     Appearance *a_unfocused_label;
97     Appearance *a_focused_label;
98     Appearance *a_icon;
99     Appearance *a_unfocused_handle;
100     Appearance *a_focused_handle;
101
102     Strut  innersize;
103
104     GSList *clients;
105
106     int width; /* title and handle */
107     int label_width;
108     int icon_x;        /* x-position of the window icon button */
109     int label_x;       /* x-position of the window title */
110     int iconify_x;     /* x-position of the window iconify button */
111     int desk_x;         /* x-position of the window all-desktops button */
112     int max_x;         /* x-position of the window maximize button */
113     int close_x;       /* x-position of the window close button */
114     int bwidth;        /* border width */
115     int cbwidth;       /* client border width */
116
117     gboolean max_press;
118     gboolean close_press;
119     gboolean desk_press;
120     gboolean iconify_press;
121 } ObFrame;
122
123 static void layout_title(ObFrame *self);
124 static void render(ObFrame *self);
125 static void render_label(ObFrame *self);
126 static void render_max(ObFrame *self);
127 static void render_icon(ObFrame *self);
128 static void render_iconify(ObFrame *self);
129 static void render_desk(ObFrame *self);
130 static void render_close(ObFrame *self);
131
132 static void frame_mouse_press(const ObEvent *e, ObFrame *self);
133 static void frame_mouse_release(const ObEvent *e, ObFrame *self);
134
135 gboolean startup()
136 {
137     g_quark_from_string("none");
138     g_quark_from_string("root");
139     g_quark_from_string("client");
140     g_quark_from_string("titlebar");
141     g_quark_from_string("handle");
142     g_quark_from_string("frame");
143     g_quark_from_string("blcorner");
144     g_quark_from_string("brcorner");
145     g_quark_from_string("maximize");
146     g_quark_from_string("alldesktops");
147     g_quark_from_string("iconify");
148     g_quark_from_string("icon");
149     g_quark_from_string("close");
150
151     s_b_color = s_cb_unfocused_color = s_cb_focused_color = 
152         s_title_unfocused_color = s_title_focused_color = 
153         s_titlebut_unfocused_color = s_titlebut_focused_color = NULL;
154     s_winfont = NULL;
155     s_max_mask = s_icon_mask = s_desk_mask = s_close_mask = NULL;
156
157     a_focused_unpressed_max = appearance_new(Surface_Planar, 1);
158     a_focused_pressed_max = appearance_new(Surface_Planar, 1);
159     a_unfocused_unpressed_max = appearance_new(Surface_Planar, 1);
160     a_unfocused_pressed_max = appearance_new(Surface_Planar, 1);
161     a_focused_unpressed_close = NULL;
162     a_focused_pressed_close = NULL;
163     a_unfocused_unpressed_close = NULL;
164     a_unfocused_pressed_close = NULL;
165     a_focused_unpressed_desk = NULL;
166     a_focused_pressed_desk = NULL;
167     a_unfocused_unpressed_desk = NULL;
168     a_unfocused_pressed_desk = NULL;
169     a_focused_unpressed_iconify = NULL;
170     a_focused_pressed_iconify = NULL;
171     a_unfocused_unpressed_iconify = NULL;
172     a_unfocused_pressed_iconify = NULL;
173     a_focused_grip = appearance_new(Surface_Planar, 0);
174     a_unfocused_grip = appearance_new(Surface_Planar, 0);
175     a_focused_title = appearance_new(Surface_Planar, 0);
176     a_unfocused_title = appearance_new(Surface_Planar, 0);
177     a_focused_label = appearance_new(Surface_Planar, 1);
178     a_unfocused_label = appearance_new(Surface_Planar, 1);
179     a_icon = appearance_new(Surface_Planar, 0);/*1);*/
180     a_focused_handle = appearance_new(Surface_Planar, 0);
181     a_unfocused_handle = appearance_new(Surface_Planar, 0);
182
183     return load();
184 }
185
186 void shutdown()
187 {
188     if (s_b_color != NULL) color_free(s_b_color);
189     if (s_cb_unfocused_color != NULL) color_free(s_cb_unfocused_color);
190     if (s_cb_focused_color != NULL) color_free(s_cb_focused_color);
191     if (s_title_unfocused_color != NULL) color_free(s_title_unfocused_color);
192     if (s_title_focused_color != NULL) color_free(s_title_focused_color);
193     if (s_titlebut_unfocused_color != NULL)
194         color_free(s_titlebut_unfocused_color);
195     if (s_titlebut_focused_color != NULL)
196         color_free(s_titlebut_focused_color);
197
198     if (s_max_mask != NULL) pixmap_mask_free(s_max_mask);
199     if (s_desk_mask != NULL) pixmap_mask_free(s_desk_mask);
200     if (s_icon_mask != NULL) pixmap_mask_free(s_icon_mask);
201     if (s_close_mask != NULL) pixmap_mask_free(s_close_mask);
202
203     if (s_winfont != NULL) font_close(s_winfont);
204
205     appearance_free(a_focused_unpressed_max);
206     appearance_free(a_focused_pressed_max);
207     appearance_free(a_unfocused_unpressed_max);
208     appearance_free(a_unfocused_pressed_max);
209     if (a_focused_unpressed_close != NULL)
210         appearance_free(a_focused_unpressed_close);
211     if (a_focused_pressed_close != NULL)
212         appearance_free(a_focused_pressed_close);
213     if (a_unfocused_unpressed_close != NULL)
214         appearance_free(a_unfocused_unpressed_close);
215     if (a_unfocused_pressed_close != NULL)
216         appearance_free(a_unfocused_pressed_close);
217     if (a_focused_unpressed_desk != NULL)
218         appearance_free(a_focused_unpressed_desk);
219     if (a_focused_pressed_desk != NULL)
220         appearance_free(a_focused_pressed_desk);
221     if (a_unfocused_unpressed_desk != NULL)
222         appearance_free(a_unfocused_unpressed_desk);
223     if (a_unfocused_pressed_desk != NULL)
224         appearance_free(a_unfocused_pressed_desk);
225     if (a_focused_unpressed_iconify != NULL)
226         appearance_free(a_focused_unpressed_iconify);
227     if (a_focused_pressed_iconify != NULL)
228         appearance_free(a_focused_pressed_iconify);
229     if (a_unfocused_unpressed_iconify != NULL)
230         appearance_free(a_unfocused_unpressed_iconify);
231     if (a_unfocused_pressed_iconify != NULL)
232         appearance_free(a_unfocused_pressed_iconify);
233     appearance_free(a_focused_grip);
234     appearance_free(a_unfocused_grip);
235     appearance_free(a_focused_title);
236     appearance_free(a_unfocused_title);
237     appearance_free(a_focused_label);
238     appearance_free(a_unfocused_label);
239     appearance_free(a_icon);
240     appearance_free(a_focused_handle);
241     appearance_free(a_unfocused_handle);
242 }
243
244 static Window createWindow(Window parent, unsigned long mask,
245                            XSetWindowAttributes *attrib)
246 {
247     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
248                          render_depth, InputOutput, render_visual,
249                          mask, attrib);
250                        
251 }
252
253 Frame *frame_new()
254 {
255     XSetWindowAttributes attrib;
256     unsigned long mask;
257     ObFrame *self;
258
259     self = g_new(ObFrame, 1);
260
261     self->frame.visible = FALSE;
262
263     /* create all of the decor windows */
264     mask = CWOverrideRedirect | CWEventMask;
265     attrib.event_mask = FRAME_EVENTMASK;
266     attrib.override_redirect = TRUE;
267     self->frame.window = createWindow(ob_root, mask, &attrib);
268
269     mask = 0;
270     self->frame.plate = createWindow(self->frame.window, mask, &attrib);
271
272     mask = CWEventMask;
273     attrib.event_mask = ELEMENT_EVENTMASK;
274     self->title = createWindow(self->frame.window, mask, &attrib);
275     self->label = createWindow(self->title, mask, &attrib);
276     self->max = createWindow(self->title, mask, &attrib);
277     self->close = createWindow(self->title, mask, &attrib);
278     self->desk = createWindow(self->title, mask, &attrib);
279     self->icon = createWindow(self->title, mask, &attrib);
280     self->iconify = createWindow(self->title, mask, &attrib);
281     self->handle = createWindow(self->frame.window, mask, &attrib);
282     mask |= CWCursor;
283     attrib.cursor = ob_cursors.ll_angle;
284     self->lgrip = createWindow(self->handle, mask, &attrib);
285     attrib.cursor = ob_cursors.lr_angle;
286     self->rgrip = createWindow(self->handle, mask, &attrib);
287
288     /* the other stuff is shown based on decor settings */
289     XMapWindow(ob_display, self->frame.plate);
290     XMapWindow(ob_display, self->lgrip);
291     XMapWindow(ob_display, self->rgrip);
292     XMapWindow(ob_display, self->label);
293
294     /* set colors/appearance/sizes for stuff that doesn't change */
295     XSetWindowBorder(ob_display, self->frame.window, s_b_color->pixel);
296     XSetWindowBorder(ob_display, self->label, s_b_color->pixel);
297     XSetWindowBorder(ob_display, self->rgrip, s_b_color->pixel);
298     XSetWindowBorder(ob_display, self->lgrip, s_b_color->pixel);
299
300     XResizeWindow(ob_display, self->max, BUTTON_SIZE, BUTTON_SIZE);
301     XResizeWindow(ob_display, self->iconify, BUTTON_SIZE, BUTTON_SIZE);
302     XResizeWindow(ob_display, self->icon, BUTTON_SIZE, BUTTON_SIZE);
303     XResizeWindow(ob_display, self->close, BUTTON_SIZE, BUTTON_SIZE);
304     XResizeWindow(ob_display, self->desk, BUTTON_SIZE, BUTTON_SIZE);
305     XResizeWindow(ob_display, self->lgrip, GRIP_WIDTH, s_handle_height);
306     XResizeWindow(ob_display, self->rgrip, GRIP_WIDTH, s_handle_height);
307
308     /* set up the dynamic appearances */
309     self->a_unfocused_title = appearance_copy(a_unfocused_title);
310     self->a_focused_title = appearance_copy(a_focused_title);
311     self->a_unfocused_label = appearance_copy(a_unfocused_label);
312     self->a_focused_label = appearance_copy(a_focused_label);
313     self->a_unfocused_handle = appearance_copy(a_unfocused_handle);
314     self->a_focused_handle = appearance_copy(a_focused_handle);
315     self->a_icon = appearance_copy(a_icon);
316
317     self->max_press = self->close_press = self->desk_press = 
318         self->iconify_press = FALSE;
319
320     dispatch_register(Event_X_ButtonPress, (EventHandler)frame_mouse_press,
321                       self);
322     dispatch_register(Event_X_ButtonRelease, (EventHandler)frame_mouse_release,
323                       self);
324
325     return (Frame*)self;
326 }
327
328 static void frame_free(ObFrame *self)
329 {
330     appearance_free(self->a_unfocused_title); 
331     appearance_free(self->a_focused_title);
332     appearance_free(self->a_unfocused_label);
333     appearance_free(self->a_focused_label);
334     appearance_free(self->a_unfocused_handle);
335     appearance_free(self->a_focused_handle);
336     appearance_free(self->a_icon);
337
338     XDestroyWindow(ob_display, self->frame.window);
339
340     dispatch_register(0, (EventHandler)frame_mouse_press, self);
341     dispatch_register(0, (EventHandler)frame_mouse_release, self);
342
343     g_free(self);
344 }
345
346 void frame_show(ObFrame *self)
347 {
348     if (!self->frame.visible) {
349         self->frame.visible = TRUE;
350         XMapWindow(ob_display, self->frame.window);
351     }
352 }
353
354 void frame_hide(ObFrame *self)
355 {
356     if (self->frame.visible) {
357         self->frame.visible = FALSE;
358         self->frame.client->ignore_unmaps++;
359         XUnmapWindow(ob_display, self->frame.window);
360     }
361 }
362
363 void frame_adjust_shape(ObFrame *self)
364 {
365 #ifdef SHAPE
366     int num;
367     XRectangle xrect[2];
368
369     if (!self->frame.client->shaped) {
370         /* clear the shape on the frame window */
371         XShapeCombineMask(ob_display, self->frame.window, ShapeBounding,
372                           self->innersize.left,
373                           self->innersize.top,
374                           None, ShapeSet);
375     } else {
376         /* make the frame's shape match the clients */
377         XShapeCombineShape(ob_display, self->frame.window, ShapeBounding,
378                            self->innersize.left,
379                            self->innersize.top,
380                            self->frame.client->window,
381                            ShapeBounding, ShapeSet);
382
383         num = 0;
384         if (self->frame.client->decorations & Decor_Titlebar) {
385             xrect[0].x = -s_bevel;
386             xrect[0].y = -s_bevel;
387             xrect[0].width = self->width + self->bwidth * 2;
388             xrect[0].height = TITLE_HEIGHT +
389                 self->bwidth * 2;
390             ++num;
391         }
392
393         if (self->frame.client->decorations & Decor_Handle) {
394             xrect[1].x = -s_bevel;
395             xrect[1].y = HANDLE_Y(self);
396             xrect[1].width = self->width + self->bwidth * 2;
397             xrect[1].height = s_handle_height +
398                 self->bwidth * 2;
399             ++num;
400         }
401
402         XShapeCombineRectangles(ob_display, self->frame.window,
403                                 ShapeBounding, 0, 0, xrect, num,
404                                 ShapeUnion, Unsorted);
405     }
406 #endif
407 }
408
409 void frame_adjust_area(ObFrame *self, gboolean moved, gboolean resized)
410 {
411     g_message("adjust_area: %d %d", moved, resized);
412     if (resized) {
413         if (self->frame.client->decorations & Decor_Border) {
414             self->bwidth = s_bwidth;
415             self->cbwidth = s_cbwidth;
416         } else {
417             self->bwidth = self->cbwidth = 0;
418         }
419         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
420                   self->cbwidth, self->cbwidth);
421         self->width = self->frame.client->area.width + self->cbwidth * 2;
422         g_assert(self->width > 0);
423
424         /* set border widths */
425         XSetWindowBorderWidth(ob_display, self->frame.plate,  self->cbwidth);
426         XSetWindowBorderWidth(ob_display, self->frame.window, self->bwidth);
427         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
428         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
429         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
430         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
431   
432         /* position/size and map/unmap all the windows */
433
434         /* they all default off, they're turned on in layout_title */
435         self->icon_x = -1;
436         self->desk_x = -1;
437         self->icon_x = -1;
438         self->label_x = -1;
439         self->max_x = -1;
440         self->close_x = -1;
441
442         if (self->frame.client->decorations & Decor_Titlebar) {
443             XMoveResizeWindow(ob_display, self->title,
444                               -self->bwidth, -self->bwidth,
445                               self->width, TITLE_HEIGHT);
446             self->innersize.top += TITLE_HEIGHT + self->bwidth;
447             XMapWindow(ob_display, self->title);
448
449             /* layout the title bar elements */
450             layout_title(self);
451         } else {
452             XUnmapWindow(ob_display, self->title);
453             /* make all the titlebar stuff not render */
454             self->frame.client->decorations &= ~(Decor_Icon | Decor_Iconify |
455                                                  Decor_Maximize | Decor_Close |
456                                                  Decor_AllDesktops);
457         }
458
459         if (self->frame.client->decorations & Decor_Handle) {
460             XMoveResizeWindow(ob_display, self->handle,
461                               -self->bwidth, HANDLE_Y(self),
462                               self->width, s_handle_height);
463             XMoveWindow(ob_display, self->lgrip,
464                         -self->bwidth, -self->bwidth);
465             XMoveWindow(ob_display, self->rgrip,
466                         -self->bwidth + self->width -
467                         GRIP_WIDTH, -self->bwidth);
468             self->innersize.bottom += s_handle_height +
469                 self->bwidth;
470             XMapWindow(ob_display, self->handle);
471         } else
472             XUnmapWindow(ob_display, self->handle);
473     }
474
475     if (moved) {
476         /* find the new coordinates */
477         self->frame.area.x = self->frame.client->area.x;
478         self->frame.area.y = self->frame.client->area.y;
479         frame_client_gravity((Frame*)self,
480                              &self->frame.area.x, &self->frame.area.y);
481     }
482
483     /* move and resize the top level frame.
484        shading can change without being moved or resized */
485     XMoveResizeWindow(ob_display, self->frame.window,
486                       self->frame.area.x, self->frame.area.y,
487                       self->width,
488                       (self->frame.client->shaded ? TITLE_HEIGHT :
489                        self->innersize.top + self->innersize.bottom +
490                        self->frame.client->area.height));
491
492     if (resized) {
493         /* move and resize the plate */
494         XMoveResizeWindow(ob_display, self->frame.plate,
495                           self->innersize.left - self->cbwidth,
496                           self->innersize.top - self->cbwidth,
497                           self->frame.client->area.width,
498                           self->frame.client->area.height);
499         /* when the client has StaticGravity, it likes to move around. */
500         XMoveWindow(ob_display, self->frame.client->window, 0, 0);
501     }
502
503     if (resized) {
504         STRUT_SET(self->frame.size,
505                   self->innersize.left + self->bwidth,
506                   self->innersize.top + self->bwidth,
507                   self->innersize.right + self->bwidth,
508                   self->innersize.bottom + self->bwidth);
509     }
510
511     /* shading can change without being moved or resized */
512     RECT_SET_SIZE(self->frame.area,
513                   self->frame.client->area.width +
514                   self->frame.size.left + self->frame.size.right,
515                   (self->frame.client->shaded ? TITLE_HEIGHT + self->bwidth*2:
516                    self->frame.client->area.height +
517                    self->frame.size.top + self->frame.size.bottom));
518
519     if (resized) {
520         render(self);
521
522         frame_adjust_shape(self);
523     }
524 }
525
526 void frame_adjust_state(ObFrame *self)
527 {
528     render_max(self);
529     render_desk(self);
530 }
531
532 void frame_adjust_focus(ObFrame *self)
533 {
534     render(self);
535 }
536
537 void frame_adjust_title(ObFrame *self)
538 {
539     render_label(self);
540 }
541
542 void frame_adjust_icon(ObFrame *self)
543 {
544     render_icon(self);
545 }
546
547 void frame_grab_client(ObFrame *self, Client *client)
548 {
549     self->frame.client = client;
550
551     /* reparent the client to the frame */
552     XReparentWindow(ob_display, client->window, self->frame.plate, 0, 0);
553     /*
554       When reparenting the client window, it is usually not mapped yet, since
555       this occurs from a MapRequest. However, in the case where Openbox is
556       starting up, the window is already mapped, so we'll see unmap events for
557       it. There are 2 unmap events generated that we see, one with the 'event'
558       member set the root window, and one set to the client, but both get
559       handled and need to be ignored.
560     */
561     if (ob_state == State_Starting)
562         client->ignore_unmaps += 2;
563
564     /* select the event mask on the client's parent (to receive config/map
565        req's) the ButtonPress is to catch clicks on the client border */
566     XSelectInput(ob_display, self->frame.plate, PLATE_EVENTMASK);
567
568     /* map the client so it maps when the frame does */
569     XMapWindow(ob_display, client->window);
570
571     frame_adjust_area(self, TRUE, TRUE);
572
573     /* set all the windows for the frame in the client_map */
574     g_hash_table_insert(client_map, (gpointer)self->frame.window, client);
575     g_hash_table_insert(client_map, (gpointer)self->frame.plate, client);
576     g_hash_table_insert(client_map, (gpointer)self->title, client);
577     g_hash_table_insert(client_map, (gpointer)self->label, client);
578     g_hash_table_insert(client_map, (gpointer)self->max, client);
579     g_hash_table_insert(client_map, (gpointer)self->close, client);
580     g_hash_table_insert(client_map, (gpointer)self->desk, client);
581     g_hash_table_insert(client_map, (gpointer)self->icon, client);
582     g_hash_table_insert(client_map, (gpointer)self->iconify, client);
583     g_hash_table_insert(client_map, (gpointer)self->handle, client);
584     g_hash_table_insert(client_map, (gpointer)self->lgrip, client);
585     g_hash_table_insert(client_map, (gpointer)self->rgrip, client);
586 }
587
588 void frame_release_client(ObFrame *self, Client *client)
589 {
590     XEvent ev;
591
592     g_assert(self->frame.client == client);
593
594     /* check if the app has already reparented its window away */
595     if (XCheckTypedWindowEvent(ob_display, client->window,
596                                ReparentNotify, &ev)) {
597         XPutBackEvent(ob_display, &ev);
598         /* re-map the window since the unmanaging process unmaps it */
599         XMapWindow(ob_display, client->window);
600     } else {
601         /* according to the ICCCM - if the client doesn't reparent itself,
602            then we will reparent the window to root for them */
603         XReparentWindow(ob_display, client->window, ob_root,
604                         client->area.x,
605                         client->area.y);
606     }
607
608     /* remove all the windows for the frame from the client_map */
609     g_hash_table_remove(client_map, (gpointer)self->frame.window);
610     g_hash_table_remove(client_map, (gpointer)self->frame.plate);
611     g_hash_table_remove(client_map, (gpointer)self->title);
612     g_hash_table_remove(client_map, (gpointer)self->label);
613     g_hash_table_remove(client_map, (gpointer)self->max);
614     g_hash_table_remove(client_map, (gpointer)self->close);
615     g_hash_table_remove(client_map, (gpointer)self->desk);
616     g_hash_table_remove(client_map, (gpointer)self->icon);
617     g_hash_table_remove(client_map, (gpointer)self->iconify);
618     g_hash_table_remove(client_map, (gpointer)self->handle);
619     g_hash_table_remove(client_map, (gpointer)self->lgrip);
620     g_hash_table_remove(client_map, (gpointer)self->rgrip);
621
622     frame_free(self);
623 }
624
625 static void layout_title(ObFrame *self)
626 {
627     const char *lc;
628     int x;
629     gboolean n, d, i, l, m ,c;
630     ConfigValue layout;
631
632     n = d = i = l = m = c = FALSE;
633
634     if (!config_get("titlebar.layout", Config_String, &layout)) {
635         layout.string = "NDLIMC";
636         config_set("titlebar.layout", Config_String, layout);
637     }
638
639     /* figure out whats being shown, and the width of the label */
640     self->label_width = self->width - (s_bevel + 1) * 2;
641     for (lc = layout.string; *lc != '\0'; ++lc) {
642         switch (*lc) {
643         case 'N':
644             if (!(self->frame.client->decorations & Decor_Icon)) break;
645             n = TRUE;
646             self->label_width -= BUTTON_SIZE + s_bevel + 1;
647             break;
648         case 'D':
649             if (!(self->frame.client->decorations & Decor_AllDesktops)) break;
650             d = TRUE;
651             self->label_width -= BUTTON_SIZE + s_bevel + 1;
652             break;
653         case 'I':
654             if (!(self->frame.client->decorations & Decor_Iconify)) break;
655             i = TRUE;
656             self->label_width -= BUTTON_SIZE + s_bevel + 1;
657             break;
658         case 'L':
659             l = TRUE;
660             break;
661         case 'M':
662             if (!(self->frame.client->decorations & Decor_Maximize)) break;
663             m = TRUE;
664             self->label_width -= BUTTON_SIZE + s_bevel + 1;
665             break;
666         case 'C':
667             if (!(self->frame.client->decorations & Decor_Close)) break;
668             c = TRUE;
669             self->label_width -= BUTTON_SIZE + s_bevel + 1;
670             break;
671         }
672     }
673     if (self->label_width < 1) self->label_width = 1;
674
675     XResizeWindow(ob_display, self->label, self->label_width,
676                   LABEL_HEIGHT);
677   
678     if (!n) XUnmapWindow(ob_display, self->icon);
679     if (!d) XUnmapWindow(ob_display, self->desk);
680     if (!i) XUnmapWindow(ob_display, self->iconify);
681     if (!l) XUnmapWindow(ob_display, self->label);
682     if (!m) XUnmapWindow(ob_display, self->max);
683     if (!c) XUnmapWindow(ob_display, self->close);
684
685     x = s_bevel + 1;
686     for (lc = layout.string; *lc != '\0'; ++lc) {
687         switch (*lc) {
688         case 'N':
689             if (!n) break;
690             self->icon_x = x;
691             XMapWindow(ob_display, self->icon);
692             XMoveWindow(ob_display, self->icon, x, s_bevel + 1);
693             x += BUTTON_SIZE + s_bevel + 1;
694             break;
695         case 'D':
696             if (!d) break;
697             self->desk_x = x;
698             XMapWindow(ob_display, self->desk);
699             XMoveWindow(ob_display, self->desk, x, s_bevel + 1);
700             x += BUTTON_SIZE + s_bevel + 1;
701             break;
702         case 'I':
703             if (!i) break;
704             self->iconify_x = x;
705             XMapWindow(ob_display, self->iconify);
706             XMoveWindow(ob_display, self->iconify, x, s_bevel + 1);
707             x += BUTTON_SIZE + s_bevel + 1;
708             break;
709         case 'L':
710             if (!l) break;
711             self->label_x = x;
712             XMapWindow(ob_display, self->label);
713             XMoveWindow(ob_display, self->label, x, s_bevel);
714             x += self->label_width + s_bevel + 1;
715             break;
716         case 'M':
717             if (!m) break;
718             self->max_x = x;
719             XMapWindow(ob_display, self->max);
720             XMoveWindow(ob_display, self->max, x, s_bevel + 1);
721             x += BUTTON_SIZE + s_bevel + 1;
722             break;
723         case 'C':
724             if (!c) break;
725             self->close_x = x;
726             XMapWindow(ob_display, self->close);
727             XMoveWindow(ob_display, self->close, x, s_bevel + 1);
728             x += BUTTON_SIZE + s_bevel + 1;
729             break;
730         }
731     }
732 }
733
734 static void render(ObFrame *self)
735 {
736     if (client_focused(self->frame.client)) {
737         XSetWindowBorder(ob_display, self->frame.plate,
738                          s_cb_focused_color->pixel);
739     } else {
740         XSetWindowBorder(ob_display, self->frame.plate,
741                          s_cb_unfocused_color->pixel);
742     }
743
744     if (self->frame.client->decorations & Decor_Titlebar) {
745         paint(self->title, (client_focused(self->frame.client) ?
746                             self->a_focused_title :
747                             self->a_unfocused_title),
748               0, 0, self->width, TITLE_HEIGHT);
749         render_label(self);
750         render_max(self);
751         render_icon(self);
752         render_iconify(self);
753         render_desk(self);
754         render_close(self);
755     }
756
757     if (self->frame.client->decorations & Decor_Handle) {
758         paint(self->handle, (client_focused(self->frame.client) ?
759                              self->a_focused_handle :
760                              self->a_unfocused_handle),
761               GRIP_WIDTH + self->bwidth, 0,
762               HANDLE_WIDTH(self), s_handle_height);
763         paint(self->lgrip, (client_focused(self->frame.client) ?
764                             a_focused_grip :
765                             a_unfocused_grip),
766               0, 0, GRIP_WIDTH, s_handle_height);
767         paint(self->rgrip, (client_focused(self->frame.client) ?
768                             a_focused_grip :
769                             a_unfocused_grip),
770               0, 0, GRIP_WIDTH, s_handle_height);
771     }
772 }
773
774 static void render_label(ObFrame *self)
775 {
776     Appearance *a;
777
778     if (self->label_x < 0) return;
779
780     a = (client_focused(self->frame.client) ?
781          self->a_focused_label : self->a_unfocused_label);
782
783     /* set the texture's text! */
784     a->texture[0].data.text.string = self->frame.client->title;
785
786     paint(self->label, a, 0, 0, self->label_width, LABEL_HEIGHT);
787 }
788
789 static void render_icon(ObFrame *self)
790 {
791     if (self->icon_x < 0) return;
792
793     /* XXX set the texture's icon picture! */
794     paint(self->icon, self->a_icon, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
795 }
796
797 static void render_max(ObFrame *self)
798 {
799     gboolean press = self->max_press ||
800         self->frame.client->max_vert || self->frame.client->max_horz;
801     
802     if (self->max_x < 0) return;
803
804     paint(self->max, (client_focused(self->frame.client) ?
805                       (press ?
806                        a_focused_pressed_max :
807                        a_focused_unpressed_max) :
808                       (press ?
809                        a_unfocused_pressed_max :
810                        a_unfocused_unpressed_max)),
811           0, 0, BUTTON_SIZE, BUTTON_SIZE);
812 }
813
814 static void render_iconify(ObFrame *self)
815 {
816     if (self->iconify_x < 0) return;
817
818     paint(self->iconify, (client_focused(self->frame.client) ?
819                           (self->iconify_press ?
820                            a_focused_pressed_iconify :
821                            a_focused_unpressed_iconify) :
822                           (self->iconify_press ?
823                            a_unfocused_pressed_iconify :
824                            a_unfocused_unpressed_iconify)),
825           0, 0, BUTTON_SIZE, BUTTON_SIZE);
826 }
827
828 static void render_desk(ObFrame *self)
829 {
830     gboolean press = self->desk_press ||
831         self->frame.client->desktop == DESKTOP_ALL;
832     
833     if (self->desk_x < 0) return;
834
835     paint(self->desk, (client_focused(self->frame.client) ?
836                        (press ?
837                         a_focused_pressed_desk :
838                         a_focused_unpressed_desk) :
839                        (press ?
840                         a_unfocused_pressed_desk :
841                         a_unfocused_unpressed_desk)),
842           0, 0, BUTTON_SIZE, BUTTON_SIZE);
843 }
844
845 static void render_close(ObFrame *self)
846 {
847     if (self->close_x < 0) return;
848
849     paint(self->close, (client_focused(self->frame.client) ?
850                           (self->close_press ?
851                            a_focused_pressed_close :
852                            a_focused_unpressed_close) :
853                           (self->close_press ?
854                            a_unfocused_pressed_close :
855                            a_unfocused_unpressed_close)),
856           0, 0, BUTTON_SIZE, BUTTON_SIZE);
857 }
858
859 GQuark get_context(Client *client, Window win)
860 {
861     ObFrame *self;
862
863     if (win == ob_root) return g_quark_try_string("root");
864     if (client == NULL) return g_quark_try_string("none");
865     if (win == client->window) return g_quark_try_string("client");
866
867     self = (ObFrame*) client->frame;
868     if (win == self->frame.window) return g_quark_try_string("frame");
869     if (win == self->frame.plate)  return g_quark_try_string("client");
870     if (win == self->title)  return g_quark_try_string("titlebar");
871     if (win == self->label)  return g_quark_try_string("titlebar");
872     if (win == self->handle) return g_quark_try_string("handle");
873     if (win == self->lgrip)  return g_quark_try_string("blcorner");
874     if (win == self->rgrip)  return g_quark_try_string("brcorner");
875     if (win == self->max)  return g_quark_try_string("maximize");
876     if (win == self->iconify)  return g_quark_try_string("iconify");
877     if (win == self->close)  return g_quark_try_string("close");
878     if (win == self->icon)  return g_quark_try_string("icon");
879     if (win == self->desk)  return g_quark_try_string("alldesktops");
880
881     return g_quark_try_string("none");
882 }
883
884 static void frame_mouse_press(const ObEvent *e, ObFrame *self)
885 {
886     Window win = e->data.x.e->xbutton.window;
887     if (win == self->max) {
888         self->max_press = TRUE;
889         render_max(self);
890     } else if (win == self->close) {
891         self->close_press = TRUE;
892         render_close(self);
893     } else if (win == self->iconify) {
894         self->iconify_press = TRUE;
895         render_iconify(self);
896     } else if (win == self->desk) { 
897         self->desk_press = TRUE;
898         render_desk(self);
899     }
900 }
901
902 static void frame_mouse_release(const ObEvent *e, ObFrame *self)
903 {
904     Window win = e->data.x.e->xbutton.window;
905     if (win == self->max) {
906         self->max_press = FALSE;
907         render_max(self);
908     } else if (win == self->close) {
909         self->close_press = FALSE; 
910         render_close(self);
911     } else if (win == self->iconify) {
912         self->iconify_press = FALSE;
913         render_iconify(self);
914     } else if (win == self->desk) {
915         self->desk_press = FALSE;
916         render_desk(self);
917     }
918 }