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