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