support parent relative titlebar elements
[dana/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, Appearance *a);
126 static void render_max(ObFrame *self, Appearance *a);
127 static void render_icon(ObFrame *self, Appearance *a);
128 static void render_iconify(ObFrame *self, Appearance *a);
129 static void render_desk(ObFrame *self, Appearance *a);
130 static void render_close(ObFrame *self, Appearance *a);
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, 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     if (resized) {
412         if (self->frame.client->decorations & Decor_Border) {
413             self->bwidth = s_bwidth;
414             self->cbwidth = s_cbwidth;
415         } else {
416             self->bwidth = self->cbwidth = 0;
417         }
418         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
419                   self->cbwidth, self->cbwidth);
420         self->width = self->frame.client->area.width + self->cbwidth * 2;
421         g_assert(self->width > 0);
422
423         /* set border widths */
424         XSetWindowBorderWidth(ob_display, self->frame.plate,  self->cbwidth);
425         XSetWindowBorderWidth(ob_display, self->frame.window, self->bwidth);
426         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
427         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
428         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
429         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
430   
431         /* position/size and map/unmap all the windows */
432
433         /* they all default off, they're turned on in layout_title */
434         self->icon_x = -1;
435         self->desk_x = -1;
436         self->icon_x = -1;
437         self->label_x = -1;
438         self->max_x = -1;
439         self->close_x = -1;
440
441         if (self->frame.client->decorations & Decor_Titlebar) {
442             XMoveResizeWindow(ob_display, self->title,
443                               -self->bwidth, -self->bwidth,
444                               self->width, TITLE_HEIGHT);
445             self->innersize.top += TITLE_HEIGHT + self->bwidth;
446             XMapWindow(ob_display, self->title);
447
448             /* layout the title bar elements */
449             layout_title(self);
450         } else {
451             XUnmapWindow(ob_display, self->title);
452             /* make all the titlebar stuff not render */
453             self->frame.client->decorations &= ~(Decor_Icon | Decor_Iconify |
454                                                  Decor_Maximize | Decor_Close |
455                                                  Decor_AllDesktops);
456         }
457
458         if (self->frame.client->decorations & Decor_Handle) {
459             XMoveResizeWindow(ob_display, self->handle,
460                               -self->bwidth, HANDLE_Y(self),
461                               self->width, s_handle_height);
462             XMoveWindow(ob_display, self->lgrip,
463                         -self->bwidth, -self->bwidth);
464             XMoveWindow(ob_display, self->rgrip,
465                         -self->bwidth + self->width -
466                         GRIP_WIDTH, -self->bwidth);
467             self->innersize.bottom += s_handle_height +
468                 self->bwidth;
469             XMapWindow(ob_display, self->handle);
470         } else
471             XUnmapWindow(ob_display, self->handle);
472     }
473
474     if (resized) {
475         /* move and resize the plate */
476         XMoveResizeWindow(ob_display, self->frame.plate,
477                           self->innersize.left - self->cbwidth,
478                           self->innersize.top - self->cbwidth,
479                           self->frame.client->area.width,
480                           self->frame.client->area.height);
481         /* when the client has StaticGravity, it likes to move around. */
482         XMoveWindow(ob_display, self->frame.client->window, 0, 0);
483     }
484
485     if (resized) {
486         STRUT_SET(self->frame.size,
487                   self->innersize.left + self->bwidth,
488                   self->innersize.top + self->bwidth,
489                   self->innersize.right + self->bwidth,
490                   self->innersize.bottom + self->bwidth);
491     }
492
493     /* shading can change without being moved or resized */
494     RECT_SET_SIZE(self->frame.area,
495                   self->frame.client->area.width +
496                   self->frame.size.left + self->frame.size.right,
497                   (self->frame.client->shaded ? TITLE_HEIGHT + self->bwidth*2:
498                    self->frame.client->area.height +
499                    self->frame.size.top + self->frame.size.bottom));
500
501     if (moved) {
502         /* find the new coordinates, done after setting the frame.size, for
503            frame_client_gravity. */
504         self->frame.area.x = self->frame.client->area.x;
505         self->frame.area.y = self->frame.client->area.y;
506         frame_client_gravity((Frame*)self,
507                              &self->frame.area.x, &self->frame.area.y);
508     }
509
510     /* move and resize the top level frame.
511        shading can change without being moved or resized */
512     XMoveResizeWindow(ob_display, self->frame.window,
513                       self->frame.area.x, self->frame.area.y,
514                       self->width, self->frame.area.height - self->bwidth * 2);
515
516     if (resized) {
517         render(self);
518
519         frame_adjust_shape(self);
520     }
521 }
522
523 void frame_adjust_state(ObFrame *self)
524 {
525     render(self);
526 }
527
528 void frame_adjust_focus(ObFrame *self)
529 {
530     render(self);
531 }
532
533 void frame_adjust_title(ObFrame *self)
534 {
535     render(self);
536 }
537
538 void frame_adjust_icon(ObFrame *self)
539 {
540     render(self);
541 }
542
543 void frame_grab_client(ObFrame *self, Client *client)
544 {
545     self->frame.client = client;
546
547     /* reparent the client to the frame */
548     XReparentWindow(ob_display, client->window, self->frame.plate, 0, 0);
549     /*
550       When reparenting the client window, it is usually not mapped yet, since
551       this occurs from a MapRequest. However, in the case where Openbox is
552       starting up, the window is already mapped, so we'll see unmap events for
553       it. There are 2 unmap events generated that we see, one with the 'event'
554       member set the root window, and one set to the client, but both get
555       handled and need to be ignored.
556     */
557     if (ob_state == State_Starting)
558         client->ignore_unmaps += 2;
559
560     /* select the event mask on the client's parent (to receive config/map
561        req's) the ButtonPress is to catch clicks on the client border */
562     XSelectInput(ob_display, self->frame.plate, PLATE_EVENTMASK);
563
564     /* map the client so it maps when the frame does */
565     XMapWindow(ob_display, client->window);
566
567     frame_adjust_area(self, TRUE, TRUE);
568
569     /* set all the windows for the frame in the client_map */
570     g_hash_table_insert(client_map, &self->frame.window, client);
571     g_hash_table_insert(client_map, &self->frame.plate, client);
572     g_hash_table_insert(client_map, &self->title, client);
573     g_hash_table_insert(client_map, &self->label, client);
574     g_hash_table_insert(client_map, &self->max, client);
575     g_hash_table_insert(client_map, &self->close, client);
576     g_hash_table_insert(client_map, &self->desk, client);
577     g_hash_table_insert(client_map, &self->icon, client);
578     g_hash_table_insert(client_map, &self->iconify, client);
579     g_hash_table_insert(client_map, &self->handle, client);
580     g_hash_table_insert(client_map, &self->lgrip, client);
581     g_hash_table_insert(client_map, &self->rgrip, client);
582 }
583
584 void frame_release_client(ObFrame *self, Client *client)
585 {
586     XEvent ev;
587
588     g_assert(self->frame.client == client);
589
590     /* check if the app has already reparented its window away */
591     if (XCheckTypedWindowEvent(ob_display, client->window,
592                                ReparentNotify, &ev)) {
593         XPutBackEvent(ob_display, &ev);
594         /* re-map the window since the unmanaging process unmaps it */
595         XMapWindow(ob_display, client->window);
596     } else {
597         /* according to the ICCCM - if the client doesn't reparent itself,
598            then we will reparent the window to root for them */
599         XReparentWindow(ob_display, client->window, ob_root,
600                         client->area.x,
601                         client->area.y);
602     }
603
604     /* remove all the windows for the frame from the client_map */
605     g_hash_table_remove(client_map, &self->frame.window);
606     g_hash_table_remove(client_map, &self->frame.plate);
607     g_hash_table_remove(client_map, &self->title);
608     g_hash_table_remove(client_map, &self->label);
609     g_hash_table_remove(client_map, &self->max);
610     g_hash_table_remove(client_map, &self->close);
611     g_hash_table_remove(client_map, &self->desk);
612     g_hash_table_remove(client_map, &self->icon);
613     g_hash_table_remove(client_map, &self->iconify);
614     g_hash_table_remove(client_map, &self->handle);
615     g_hash_table_remove(client_map, &self->lgrip);
616     g_hash_table_remove(client_map, &self->rgrip);
617
618     frame_free(self);
619 }
620
621 static void layout_title(ObFrame *self)
622 {
623     const char *lc;
624     int x;
625     gboolean n, d, i, l, m ,c;
626     ConfigValue layout;
627
628     n = d = i = l = m = c = FALSE;
629
630     if (!config_get("titlebar.layout", Config_String, &layout)) {
631         layout.string = "NDLIMC";
632         config_set("titlebar.layout", Config_String, layout);
633     }
634
635     /* figure out whats being shown, and the width of the label */
636     self->label_width = self->width - (s_bevel + 1) * 2;
637     for (lc = layout.string; *lc != '\0'; ++lc) {
638         switch (*lc) {
639         case 'N':
640             if (!(self->frame.client->decorations & Decor_Icon)) break;
641             n = TRUE;
642             self->label_width -= BUTTON_SIZE + s_bevel + 1;
643             break;
644         case 'D':
645             if (!(self->frame.client->decorations & Decor_AllDesktops)) break;
646             d = TRUE;
647             self->label_width -= BUTTON_SIZE + s_bevel + 1;
648             break;
649         case 'I':
650             if (!(self->frame.client->decorations & Decor_Iconify)) break;
651             i = TRUE;
652             self->label_width -= BUTTON_SIZE + s_bevel + 1;
653             break;
654         case 'L':
655             l = TRUE;
656             break;
657         case 'M':
658             if (!(self->frame.client->decorations & Decor_Maximize)) break;
659             m = TRUE;
660             self->label_width -= BUTTON_SIZE + s_bevel + 1;
661             break;
662         case 'C':
663             if (!(self->frame.client->decorations & Decor_Close)) break;
664             c = TRUE;
665             self->label_width -= BUTTON_SIZE + s_bevel + 1;
666             break;
667         }
668     }
669     if (self->label_width < 1) self->label_width = 1;
670
671     XResizeWindow(ob_display, self->label, self->label_width,
672                   LABEL_HEIGHT);
673   
674     if (!n) XUnmapWindow(ob_display, self->icon);
675     if (!d) XUnmapWindow(ob_display, self->desk);
676     if (!i) XUnmapWindow(ob_display, self->iconify);
677     if (!l) XUnmapWindow(ob_display, self->label);
678     if (!m) XUnmapWindow(ob_display, self->max);
679     if (!c) XUnmapWindow(ob_display, self->close);
680
681     x = s_bevel + 1;
682     for (lc = layout.string; *lc != '\0'; ++lc) {
683         switch (*lc) {
684         case 'N':
685             if (!n) break;
686             self->icon_x = x;
687             XMapWindow(ob_display, self->icon);
688             XMoveWindow(ob_display, self->icon, x, s_bevel + 1);
689             x += BUTTON_SIZE + s_bevel + 1;
690             break;
691         case 'D':
692             if (!d) break;
693             self->desk_x = x;
694             XMapWindow(ob_display, self->desk);
695             XMoveWindow(ob_display, self->desk, x, s_bevel + 1);
696             x += BUTTON_SIZE + s_bevel + 1;
697             break;
698         case 'I':
699             if (!i) break;
700             self->iconify_x = x;
701             XMapWindow(ob_display, self->iconify);
702             XMoveWindow(ob_display, self->iconify, x, s_bevel + 1);
703             x += BUTTON_SIZE + s_bevel + 1;
704             break;
705         case 'L':
706             if (!l) break;
707             self->label_x = x;
708             XMapWindow(ob_display, self->label);
709             XMoveWindow(ob_display, self->label, x, s_bevel);
710             x += self->label_width + s_bevel + 1;
711             break;
712         case 'M':
713             if (!m) break;
714             self->max_x = x;
715             XMapWindow(ob_display, self->max);
716             XMoveWindow(ob_display, self->max, x, s_bevel + 1);
717             x += BUTTON_SIZE + s_bevel + 1;
718             break;
719         case 'C':
720             if (!c) break;
721             self->close_x = x;
722             XMapWindow(ob_display, self->close);
723             XMoveWindow(ob_display, self->close, x, s_bevel + 1);
724             x += BUTTON_SIZE + s_bevel + 1;
725             break;
726         }
727     }
728 }
729
730 static void render(ObFrame *self)
731 {
732     if (client_focused(self->frame.client)) {
733         XSetWindowBorder(ob_display, self->frame.plate,
734                          s_cb_focused_color->pixel);
735     } else {
736         XSetWindowBorder(ob_display, self->frame.plate,
737                          s_cb_unfocused_color->pixel);
738     }
739
740     if (self->frame.client->decorations & Decor_Titlebar) {
741         Appearance *t, *l, *m, *n, *i, *d, *c;
742
743         t = (client_focused(self->frame.client) ?
744              self->a_focused_title : self->a_unfocused_title);
745         l = (client_focused(self->frame.client) ?
746              self->a_focused_label : self->a_unfocused_label);
747         m = (client_focused(self->frame.client) ?
748              ((self->max_press ||
749               self->frame.client->max_vert || self->frame.client->max_horz) ?
750               a_focused_pressed_max : a_focused_unpressed_max) :
751              ((self->max_press ||
752               self->frame.client->max_vert || self->frame.client->max_horz) ?
753               a_unfocused_pressed_max : a_unfocused_unpressed_max));
754         n = self->a_icon;
755         i = (client_focused(self->frame.client) ?
756              (self->iconify_press ?
757               a_focused_pressed_iconify : a_focused_unpressed_iconify) :
758              (self->iconify_press ?
759               a_unfocused_pressed_iconify : a_unfocused_unpressed_iconify));
760         d = (client_focused(self->frame.client) ?
761              (self->desk_press || self->frame.client->desktop == DESKTOP_ALL ?
762               a_focused_pressed_desk : a_focused_unpressed_desk) :
763              (self->desk_press || self->frame.client->desktop == DESKTOP_ALL ?
764               a_unfocused_pressed_desk : a_unfocused_unpressed_desk));
765         c = (client_focused(self->frame.client) ?
766              (self->close_press ?
767               a_focused_pressed_close : a_focused_unpressed_close) :
768              (self->close_press ?
769               a_unfocused_pressed_close : a_unfocused_unpressed_close));
770
771         paint(self->title, t, 0, 0, self->width, TITLE_HEIGHT);
772
773         /* set parents for any parent relative guys */
774         l->surface.data.planar.parent = t;
775         l->surface.data.planar.parentx = self->label_x;
776         l->surface.data.planar.parenty = s_bevel;
777
778         m->surface.data.planar.parent = t;
779         m->surface.data.planar.parentx = self->max_x;
780         m->surface.data.planar.parenty = s_bevel + 1;
781
782         n->surface.data.planar.parent = t;
783         n->surface.data.planar.parentx = self->icon_x;
784         n->surface.data.planar.parenty = s_bevel + 1;
785
786         i->surface.data.planar.parent = t;
787         i->surface.data.planar.parentx = self->iconify_x;
788         i->surface.data.planar.parenty = s_bevel + 1;
789
790         d->surface.data.planar.parent = t;
791         d->surface.data.planar.parentx = self->desk_x;
792         d->surface.data.planar.parenty = s_bevel + 1;
793
794         c->surface.data.planar.parent = t;
795         c->surface.data.planar.parentx = self->close_x;
796         c->surface.data.planar.parenty = s_bevel + 1;
797
798         render_label(self, l);
799         render_max(self, m);
800         render_icon(self, n);
801         render_iconify(self, i);
802         render_desk(self, d);
803         render_close(self, c);
804     }
805
806     if (self->frame.client->decorations & Decor_Handle) {
807         paint(self->handle, (client_focused(self->frame.client) ?
808                              self->a_focused_handle :
809                              self->a_unfocused_handle),
810               GRIP_WIDTH + self->bwidth, 0,
811               HANDLE_WIDTH(self), s_handle_height);
812         paint(self->lgrip, (client_focused(self->frame.client) ?
813                             a_focused_grip :
814                             a_unfocused_grip),
815               0, 0, GRIP_WIDTH, s_handle_height);
816         paint(self->rgrip, (client_focused(self->frame.client) ?
817                             a_focused_grip :
818                             a_unfocused_grip),
819               0, 0, GRIP_WIDTH, s_handle_height);
820     }
821 }
822
823 static void render_label(ObFrame *self, Appearance *a)
824 {
825     if (self->label_x < 0) return;
826
827
828     /* set the texture's text! */
829     a->texture[0].data.text.string = self->frame.client->title;
830     RECT_SET(a->texture[0].position, 0, 0, self->label_width, LABEL_HEIGHT);
831
832     paint(self->label, a, 0, 0, self->label_width, LABEL_HEIGHT);
833 }
834
835 static void render_icon(ObFrame *self, Appearance *a)
836 {
837     if (self->icon_x < 0) return;
838
839     if (self->frame.client->nicons) {
840         Icon *icon = client_icon(self->frame.client, BUTTON_SIZE, BUTTON_SIZE);
841         a->texture[0].type = RGBA;
842         a->texture[0].data.rgba.width = icon->width;
843         a->texture[0].data.rgba.height = icon->height;
844         a->texture[0].data.rgba.data = icon->data;
845         RECT_SET(self->a_icon->texture[0].position, 0, 0,
846                  BUTTON_SIZE,BUTTON_SIZE);
847     } else
848         a->texture[0].type = NoTexture;
849
850     paint(self->icon, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
851 }
852
853 static void render_max(ObFrame *self, Appearance *a)
854 {
855     if (self->max_x < 0) return;
856
857     RECT_SET(a->texture[0].position, 0, 0, BUTTON_SIZE,BUTTON_SIZE);
858     paint(self->max, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
859 }
860
861 static void render_iconify(ObFrame *self, Appearance *a)
862 {
863     if (self->iconify_x < 0) return;
864
865     RECT_SET(a->texture[0].position, 0, 0, BUTTON_SIZE,BUTTON_SIZE);
866     paint(self->iconify, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
867 }
868
869 static void render_desk(ObFrame *self, Appearance *a)
870 {
871     if (self->desk_x < 0) return;
872
873     RECT_SET(a->texture[0].position, 0, 0, BUTTON_SIZE,BUTTON_SIZE);
874     paint(self->desk, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
875 }
876
877 static void render_close(ObFrame *self, Appearance *a)
878 {
879     if (self->close_x < 0) return;
880
881     RECT_SET(a->texture[0].position, 0, 0, BUTTON_SIZE,BUTTON_SIZE);
882     paint(self->close, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
883 }
884
885 GQuark get_context(Client *client, Window win)
886 {
887     ObFrame *self;
888
889     if (win == ob_root) return g_quark_try_string("root");
890     if (client == NULL) return g_quark_try_string("none");
891     if (win == client->window) return g_quark_try_string("client");
892
893     self = (ObFrame*) client->frame;
894     if (win == self->frame.window) return g_quark_try_string("frame");
895     if (win == self->frame.plate)  return g_quark_try_string("client");
896     if (win == self->title)  return g_quark_try_string("titlebar");
897     if (win == self->label)  return g_quark_try_string("titlebar");
898     if (win == self->handle) return g_quark_try_string("handle");
899     if (win == self->lgrip)  return g_quark_try_string("blcorner");
900     if (win == self->rgrip)  return g_quark_try_string("brcorner");
901     if (win == self->max)  return g_quark_try_string("maximize");
902     if (win == self->iconify)  return g_quark_try_string("iconify");
903     if (win == self->close)  return g_quark_try_string("close");
904     if (win == self->icon)  return g_quark_try_string("icon");
905     if (win == self->desk)  return g_quark_try_string("alldesktops");
906
907     return g_quark_try_string("none");
908 }
909
910 static void frame_mouse_press(const ObEvent *e, ObFrame *self)
911 {
912     Window win = e->data.x.e->xbutton.window;
913     if (win == self->max) {
914         self->max_press = TRUE;
915         render(self);
916     } else if (win == self->close) {
917         self->close_press = TRUE;
918         render(self);
919     } else if (win == self->iconify) {
920         self->iconify_press = TRUE;
921         render(self);
922     } else if (win == self->desk) { 
923         self->desk_press = TRUE;
924         render(self);
925     }
926 }
927
928 static void frame_mouse_release(const ObEvent *e, ObFrame *self)
929 {
930     Window win = e->data.x.e->xbutton.window;
931     if (win == self->max) {
932         self->max_press = FALSE;
933         render(self);
934     } else if (win == self->close) {
935         self->close_press = FALSE; 
936         render(self);
937     } else if (win == self->iconify) {
938         self->iconify_press = FALSE;
939         render(self);
940     } else if (win == self->desk) {
941         self->desk_press = FALSE;
942         render(self);
943     }
944 }