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