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