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