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