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