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