]> icculus.org git repositories - mikachu/openbox.git/blob - engines/openbox/openbox.c
add strict ansi compliance
[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/themerc.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     /* do this in two steps because clients whose gravity is set to
479        'Static' don't end up getting moved at all with an XMoveResizeWindow */
480     XMoveWindow(ob_display, self->frame.plate,
481                 self->innersize.left - self->cbwidth,
482                 self->innersize.top - self->cbwidth);
483     XResizeWindow(ob_display, self->frame.plate,
484                   self->frame.client->area.width,
485                   self->frame.client->area.height);
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
609     n = d = i = l = m = c = FALSE;
610
611     /* figure out whats being shown, and the width of the label */
612     self->label_width = self->width - (s_bevel + 1) * 2;
613     for (lc = themerc_titlebar_layout; *lc != '\0'; ++lc) {
614         switch (*lc) {
615         case 'N':
616             if (!(self->frame.client->decorations & Decor_Icon)) break;
617             n = TRUE;
618             self->label_width -= BUTTON_SIZE + s_bevel + 1;
619             break;
620         case 'D':
621             if (!(self->frame.client->decorations & Decor_AllDesktops)) break;
622             d = TRUE;
623             self->label_width -= BUTTON_SIZE + s_bevel + 1;
624             break;
625         case 'I':
626             if (!(self->frame.client->decorations & Decor_Iconify)) break;
627             i = TRUE;
628             self->label_width -= BUTTON_SIZE + s_bevel + 1;
629             break;
630         case 'L':
631             l = TRUE;
632             break;
633         case 'M':
634             if (!(self->frame.client->decorations & Decor_Maximize)) break;
635             m = TRUE;
636             self->label_width -= BUTTON_SIZE + s_bevel + 1;
637             break;
638         case 'C':
639             if (!(self->frame.client->decorations & Decor_Close)) break;
640             c = TRUE;
641             self->label_width -= BUTTON_SIZE + s_bevel + 1;
642             break;
643         }
644     }
645     if (self->label_width < 1) self->label_width = 1;
646
647     XResizeWindow(ob_display, self->label, self->label_width,
648                   LABEL_HEIGHT);
649   
650     if (!n) {
651         self->frame.client->decorations &= ~Decor_Icon;
652         XUnmapWindow(ob_display, self->icon);
653         self->icon_x = -1;
654     }
655     if (!d) {
656         self->frame.client->decorations &= ~Decor_AllDesktops;
657         XUnmapWindow(ob_display, self->desk);
658         self->desk_x = -1;
659     }
660     if (!i) {
661         self->frame.client->decorations &= ~Decor_Iconify;
662         XUnmapWindow(ob_display, self->iconify);
663         self->icon_x = -1;
664     }
665     if (!l) {
666         XUnmapWindow(ob_display, self->label);
667         self->label_x = -1;
668     }
669     if (!m) {
670         self->frame.client->decorations &= ~Decor_Maximize;
671         XUnmapWindow(ob_display, self->max);
672         self->max_x = -1;
673     }
674     if (!c) {
675         self->frame.client->decorations &= ~Decor_Close;
676         XUnmapWindow(ob_display, self->close);
677         self->close_x = -1;
678     }
679
680     x = s_bevel + 1;
681     for (lc = themerc_titlebar_layout; *lc != '\0'; ++lc) {
682         switch (*lc) {
683         case 'N':
684             if (!n) break;
685             self->icon_x = x;
686             XMapWindow(ob_display, self->icon);
687             XMoveWindow(ob_display, self->icon, x, s_bevel + 1);
688             x += BUTTON_SIZE + s_bevel + 1;
689             break;
690         case 'D':
691             if (!d) break;
692             self->desk_x = x;
693             XMapWindow(ob_display, self->desk);
694             XMoveWindow(ob_display, self->desk, x, s_bevel + 1);
695             x += BUTTON_SIZE + s_bevel + 1;
696             break;
697         case 'I':
698             if (!i) break;
699             self->iconify_x = x;
700             XMapWindow(ob_display, self->iconify);
701             XMoveWindow(ob_display, self->iconify, x, s_bevel + 1);
702             x += BUTTON_SIZE + s_bevel + 1;
703             break;
704         case 'L':
705             if (!l) break;
706             self->label_x = x;
707             XMapWindow(ob_display, self->label);
708             XMoveWindow(ob_display, self->label, x, s_bevel);
709             x += self->label_width + s_bevel + 1;
710             break;
711         case 'M':
712             if (!m) break;
713             self->max_x = x;
714             XMapWindow(ob_display, self->max);
715             XMoveWindow(ob_display, self->max, x, s_bevel + 1);
716             x += BUTTON_SIZE + s_bevel + 1;
717             break;
718         case 'C':
719             if (!c) break;
720             self->close_x = x;
721             XMapWindow(ob_display, self->close);
722             XMoveWindow(ob_display, self->close, x, s_bevel + 1);
723             x += BUTTON_SIZE + s_bevel + 1;
724             break;
725         }
726     }
727 }
728
729 static void render(ObFrame *self)
730 {
731     if (client_focused(self->frame.client)) {
732         XSetWindowBorder(ob_display, self->frame.plate,
733                          s_cb_focused_color->pixel);
734     } else {
735         XSetWindowBorder(ob_display, self->frame.plate,
736                          s_cb_unfocused_color->pixel);
737     }
738
739     if (self->frame.client->decorations & Decor_Titlebar) {
740         paint(self->title, (client_focused(self->frame.client) ?
741                             self->a_focused_title :
742                             self->a_unfocused_title),
743               0, 0, self->width, TITLE_HEIGHT);
744         render_label(self);
745         render_max(self);
746         render_icon(self);
747         render_iconify(self);
748         render_desk(self);
749         render_close(self);
750     }
751
752     if (self->frame.client->decorations & Decor_Handle) {
753         paint(self->handle, (client_focused(self->frame.client) ?
754                              self->a_focused_handle :
755                              self->a_unfocused_handle),
756               GRIP_WIDTH + self->bwidth, 0,
757               HANDLE_WIDTH(self), s_handle_height);
758         paint(self->lgrip, (client_focused(self->frame.client) ?
759                             a_focused_grip :
760                             a_unfocused_grip),
761               0, 0, GRIP_WIDTH, s_handle_height);
762         paint(self->rgrip, (client_focused(self->frame.client) ?
763                             a_focused_grip :
764                             a_unfocused_grip),
765               0, 0, GRIP_WIDTH, s_handle_height);
766     }
767 }
768
769 static void render_label(ObFrame *self)
770 {
771     Appearance *a;
772
773     if (self->label_x < 0) return;
774
775     a = (client_focused(self->frame.client) ?
776          self->a_focused_label : self->a_unfocused_label);
777
778     /* set the texture's text! */
779     a->texture[0].data.text.string = self->frame.client->title;
780
781     paint(self->label, a, 0, 0, self->label_width, LABEL_HEIGHT);
782 }
783
784 static void render_icon(ObFrame *self)
785 {
786     if (self->icon_x < 0) return;
787
788     /* XXX set the texture's icon picture! */
789     paint(self->icon, self->a_icon, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
790 }
791
792 static void render_max(ObFrame *self)
793 {
794     gboolean press = self->max_press ||
795         self->frame.client->max_vert || self->frame.client->max_horz;
796     
797     if (self->max_x < 0) return;
798
799     paint(self->max, (client_focused(self->frame.client) ?
800                       (press ?
801                        a_focused_pressed_max :
802                        a_focused_unpressed_max) :
803                       (press ?
804                        a_unfocused_pressed_max :
805                        a_unfocused_unpressed_max)),
806           0, 0, BUTTON_SIZE, BUTTON_SIZE);
807 }
808
809 static void render_iconify(ObFrame *self)
810 {
811     if (self->iconify_x < 0) return;
812
813     paint(self->iconify, (client_focused(self->frame.client) ?
814                           (self->iconify_press ?
815                            a_focused_pressed_iconify :
816                            a_focused_unpressed_iconify) :
817                           (self->iconify_press ?
818                            a_unfocused_pressed_iconify :
819                            a_unfocused_unpressed_iconify)),
820           0, 0, BUTTON_SIZE, BUTTON_SIZE);
821 }
822
823 static void render_desk(ObFrame *self)
824 {
825     gboolean press = self->desk_press ||
826         self->frame.client->desktop == DESKTOP_ALL;
827     
828     if (self->desk_x < 0) return;
829
830     paint(self->desk, (client_focused(self->frame.client) ?
831                        (press ?
832                         a_focused_pressed_desk :
833                         a_focused_unpressed_desk) :
834                        (press ?
835                         a_unfocused_pressed_desk :
836                         a_unfocused_unpressed_desk)),
837           0, 0, BUTTON_SIZE, BUTTON_SIZE);
838 }
839
840 static void render_close(ObFrame *self)
841 {
842     if (self->close_x < 0) return;
843
844     paint(self->close, (client_focused(self->frame.client) ?
845                           (self->close_press ?
846                            a_focused_pressed_close :
847                            a_focused_unpressed_close) :
848                           (self->close_press ?
849                            a_unfocused_pressed_close :
850                            a_unfocused_unpressed_close)),
851           0, 0, BUTTON_SIZE, BUTTON_SIZE);
852 }
853
854 GQuark get_context(Client *client, Window win)
855 {
856     ObFrame *self;
857
858     if (win == ob_root) return g_quark_try_string("root");
859     if (client == NULL) return g_quark_try_string("none");
860     if (win == client->window) return g_quark_try_string("client");
861
862     self = (ObFrame*) client->frame;
863     if (win == self->frame.window) return g_quark_try_string("frame");
864     if (win == self->frame.plate)  return g_quark_try_string("client");
865     if (win == self->title)  return g_quark_try_string("titlebar");
866     if (win == self->label)  return g_quark_try_string("titlebar");
867     if (win == self->handle) return g_quark_try_string("handle");
868     if (win == self->lgrip)  return g_quark_try_string("blcorner");
869     if (win == self->rgrip)  return g_quark_try_string("brcorner");
870     if (win == self->max)  return g_quark_try_string("maximize");
871     if (win == self->iconify)  return g_quark_try_string("iconify");
872     if (win == self->close)  return g_quark_try_string("close");
873     if (win == self->icon)  return g_quark_try_string("icon");
874     if (win == self->desk)  return g_quark_try_string("alldesktops");
875
876     return g_quark_try_string("none");
877 }
878
879 static void frame_mouse_press(const ObEvent *e, ObFrame *self)
880 {
881     Window win = e->data.x.e->xbutton.window;
882     if (win == self->max) {
883         self->max_press = TRUE;
884         render_max(self);
885     } else if (win == self->close) {
886         self->close_press = TRUE;
887         render_close(self);
888     } else if (win == self->iconify) {
889         self->iconify_press = TRUE;
890         render_iconify(self);
891     } else if (win == self->desk) { 
892         self->desk_press = TRUE;
893         render_desk(self);
894     }
895 }
896
897 static void frame_mouse_release(const ObEvent *e, ObFrame *self)
898 {
899     Window win = e->data.x.e->xbutton.window;
900     if (win == self->max) {
901         self->max_press = FALSE;
902         render_max(self);
903     } else if (win == self->close) {
904         self->close_press = FALSE; 
905         render_close(self);
906     } else if (win == self->iconify) {
907         self->iconify_press = FALSE;
908         render_iconify(self);
909     } else if (win == self->desk) {
910         self->desk_press = FALSE;
911         render_desk(self);
912     }
913 }