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