]> icculus.org git repositories - mikachu/openbox.git/blob - engines/openbox/openbox.c
use rgba icons for clients
[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, 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, gboolean moved, gboolean resized)
410 {
411     if (resized) {
412         if (self->frame.client->decorations & Decor_Border) {
413             self->bwidth = s_bwidth;
414             self->cbwidth = s_cbwidth;
415         } else {
416             self->bwidth = self->cbwidth = 0;
417         }
418         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
419                   self->cbwidth, self->cbwidth);
420         self->width = self->frame.client->area.width + self->cbwidth * 2;
421         g_assert(self->width > 0);
422
423         /* set border widths */
424         XSetWindowBorderWidth(ob_display, self->frame.plate,  self->cbwidth);
425         XSetWindowBorderWidth(ob_display, self->frame.window, self->bwidth);
426         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
427         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
428         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
429         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
430   
431         /* position/size and map/unmap all the windows */
432
433         /* they all default off, they're turned on in layout_title */
434         self->icon_x = -1;
435         self->desk_x = -1;
436         self->icon_x = -1;
437         self->label_x = -1;
438         self->max_x = -1;
439         self->close_x = -1;
440
441         if (self->frame.client->decorations & Decor_Titlebar) {
442             XMoveResizeWindow(ob_display, self->title,
443                               -self->bwidth, -self->bwidth,
444                               self->width, TITLE_HEIGHT);
445             self->innersize.top += TITLE_HEIGHT + self->bwidth;
446             XMapWindow(ob_display, self->title);
447
448             /* layout the title bar elements */
449             layout_title(self);
450         } else {
451             XUnmapWindow(ob_display, self->title);
452             /* make all the titlebar stuff not render */
453             self->frame.client->decorations &= ~(Decor_Icon | Decor_Iconify |
454                                                  Decor_Maximize | Decor_Close |
455                                                  Decor_AllDesktops);
456         }
457
458         if (self->frame.client->decorations & Decor_Handle) {
459             XMoveResizeWindow(ob_display, self->handle,
460                               -self->bwidth, HANDLE_Y(self),
461                               self->width, s_handle_height);
462             XMoveWindow(ob_display, self->lgrip,
463                         -self->bwidth, -self->bwidth);
464             XMoveWindow(ob_display, self->rgrip,
465                         -self->bwidth + self->width -
466                         GRIP_WIDTH, -self->bwidth);
467             self->innersize.bottom += s_handle_height +
468                 self->bwidth;
469             XMapWindow(ob_display, self->handle);
470         } else
471             XUnmapWindow(ob_display, self->handle);
472     }
473
474     if (moved) {
475         /* find the new coordinates */
476         self->frame.area.x = self->frame.client->area.x;
477         self->frame.area.y = self->frame.client->area.y;
478         frame_client_gravity((Frame*)self,
479                              &self->frame.area.x, &self->frame.area.y);
480     }
481
482     /* move and resize the top level frame.
483        shading can change without being moved or resized */
484     XMoveResizeWindow(ob_display, self->frame.window,
485                       self->frame.area.x, self->frame.area.y,
486                       self->width,
487                       (self->frame.client->shaded ? TITLE_HEIGHT :
488                        self->innersize.top + self->innersize.bottom +
489                        self->frame.client->area.height));
490
491     if (resized) {
492         /* move and resize the plate */
493         XMoveResizeWindow(ob_display, self->frame.plate,
494                           self->innersize.left - self->cbwidth,
495                           self->innersize.top - self->cbwidth,
496                           self->frame.client->area.width,
497                           self->frame.client->area.height);
498         /* when the client has StaticGravity, it likes to move around. */
499         XMoveWindow(ob_display, self->frame.client->window, 0, 0);
500     }
501
502     if (resized) {
503         STRUT_SET(self->frame.size,
504                   self->innersize.left + self->bwidth,
505                   self->innersize.top + self->bwidth,
506                   self->innersize.right + self->bwidth,
507                   self->innersize.bottom + self->bwidth);
508     }
509
510     /* shading can change without being moved or resized */
511     RECT_SET_SIZE(self->frame.area,
512                   self->frame.client->area.width +
513                   self->frame.size.left + self->frame.size.right,
514                   (self->frame.client->shaded ? TITLE_HEIGHT + self->bwidth*2:
515                    self->frame.client->area.height +
516                    self->frame.size.top + self->frame.size.bottom));
517
518     if (resized) {
519         render(self);
520
521         frame_adjust_shape(self);
522     }
523 }
524
525 void frame_adjust_state(ObFrame *self)
526 {
527     render_max(self);
528     render_desk(self);
529 }
530
531 void frame_adjust_focus(ObFrame *self)
532 {
533     render(self);
534 }
535
536 void frame_adjust_title(ObFrame *self)
537 {
538     render_label(self);
539 }
540
541 void frame_adjust_icon(ObFrame *self)
542 {
543     render_icon(self);
544 }
545
546 void frame_grab_client(ObFrame *self, Client *client)
547 {
548     self->frame.client = client;
549
550     /* reparent the client to the frame */
551     XReparentWindow(ob_display, client->window, self->frame.plate, 0, 0);
552     /*
553       When reparenting the client window, it is usually not mapped yet, since
554       this occurs from a MapRequest. However, in the case where Openbox is
555       starting up, the window is already mapped, so we'll see unmap events for
556       it. There are 2 unmap events generated that we see, one with the 'event'
557       member set the root window, and one set to the client, but both get
558       handled and need to be ignored.
559     */
560     if (ob_state == State_Starting)
561         client->ignore_unmaps += 2;
562
563     /* select the event mask on the client's parent (to receive config/map
564        req's) the ButtonPress is to catch clicks on the client border */
565     XSelectInput(ob_display, self->frame.plate, PLATE_EVENTMASK);
566
567     /* map the client so it maps when the frame does */
568     XMapWindow(ob_display, client->window);
569
570     frame_adjust_area(self, TRUE, TRUE);
571
572     /* set all the windows for the frame in the client_map */
573     g_hash_table_insert(client_map, &self->frame.window, client);
574     g_hash_table_insert(client_map, &self->frame.plate, client);
575     g_hash_table_insert(client_map, &self->title, client);
576     g_hash_table_insert(client_map, &self->label, client);
577     g_hash_table_insert(client_map, &self->max, client);
578     g_hash_table_insert(client_map, &self->close, client);
579     g_hash_table_insert(client_map, &self->desk, client);
580     g_hash_table_insert(client_map, &self->icon, client);
581     g_hash_table_insert(client_map, &self->iconify, client);
582     g_hash_table_insert(client_map, &self->handle, client);
583     g_hash_table_insert(client_map, &self->lgrip, client);
584     g_hash_table_insert(client_map, &self->rgrip, client);
585 }
586
587 void frame_release_client(ObFrame *self, Client *client)
588 {
589     XEvent ev;
590
591     g_assert(self->frame.client == client);
592
593     /* check if the app has already reparented its window away */
594     if (XCheckTypedWindowEvent(ob_display, client->window,
595                                ReparentNotify, &ev)) {
596         XPutBackEvent(ob_display, &ev);
597         /* re-map the window since the unmanaging process unmaps it */
598         XMapWindow(ob_display, client->window);
599     } else {
600         /* according to the ICCCM - if the client doesn't reparent itself,
601            then we will reparent the window to root for them */
602         XReparentWindow(ob_display, client->window, ob_root,
603                         client->area.x,
604                         client->area.y);
605     }
606
607     /* remove all the windows for the frame from the client_map */
608     g_hash_table_remove(client_map, &self->frame.window);
609     g_hash_table_remove(client_map, &self->frame.plate);
610     g_hash_table_remove(client_map, &self->title);
611     g_hash_table_remove(client_map, &self->label);
612     g_hash_table_remove(client_map, &self->max);
613     g_hash_table_remove(client_map, &self->close);
614     g_hash_table_remove(client_map, &self->desk);
615     g_hash_table_remove(client_map, &self->icon);
616     g_hash_table_remove(client_map, &self->iconify);
617     g_hash_table_remove(client_map, &self->handle);
618     g_hash_table_remove(client_map, &self->lgrip);
619     g_hash_table_remove(client_map, &self->rgrip);
620
621     frame_free(self);
622 }
623
624 static void layout_title(ObFrame *self)
625 {
626     const char *lc;
627     int x;
628     gboolean n, d, i, l, m ,c;
629     ConfigValue layout;
630
631     n = d = i = l = m = c = FALSE;
632
633     if (!config_get("titlebar.layout", Config_String, &layout)) {
634         layout.string = "NDLIMC";
635         config_set("titlebar.layout", Config_String, layout);
636     }
637
638     /* figure out whats being shown, and the width of the label */
639     self->label_width = self->width - (s_bevel + 1) * 2;
640     for (lc = layout.string; *lc != '\0'; ++lc) {
641         switch (*lc) {
642         case 'N':
643             if (!(self->frame.client->decorations & Decor_Icon)) break;
644             n = TRUE;
645             self->label_width -= BUTTON_SIZE + s_bevel + 1;
646             break;
647         case 'D':
648             if (!(self->frame.client->decorations & Decor_AllDesktops)) break;
649             d = TRUE;
650             self->label_width -= BUTTON_SIZE + s_bevel + 1;
651             break;
652         case 'I':
653             if (!(self->frame.client->decorations & Decor_Iconify)) break;
654             i = TRUE;
655             self->label_width -= BUTTON_SIZE + s_bevel + 1;
656             break;
657         case 'L':
658             l = TRUE;
659             break;
660         case 'M':
661             if (!(self->frame.client->decorations & Decor_Maximize)) break;
662             m = TRUE;
663             self->label_width -= BUTTON_SIZE + s_bevel + 1;
664             break;
665         case 'C':
666             if (!(self->frame.client->decorations & Decor_Close)) break;
667             c = TRUE;
668             self->label_width -= BUTTON_SIZE + s_bevel + 1;
669             break;
670         }
671     }
672     if (self->label_width < 1) self->label_width = 1;
673
674     XResizeWindow(ob_display, self->label, self->label_width,
675                   LABEL_HEIGHT);
676   
677     if (!n) XUnmapWindow(ob_display, self->icon);
678     if (!d) XUnmapWindow(ob_display, self->desk);
679     if (!i) XUnmapWindow(ob_display, self->iconify);
680     if (!l) XUnmapWindow(ob_display, self->label);
681     if (!m) XUnmapWindow(ob_display, self->max);
682     if (!c) XUnmapWindow(ob_display, self->close);
683
684     x = s_bevel + 1;
685     for (lc = layout.string; *lc != '\0'; ++lc) {
686         switch (*lc) {
687         case 'N':
688             if (!n) break;
689             self->icon_x = x;
690             XMapWindow(ob_display, self->icon);
691             XMoveWindow(ob_display, self->icon, x, s_bevel + 1);
692             x += BUTTON_SIZE + s_bevel + 1;
693             break;
694         case 'D':
695             if (!d) break;
696             self->desk_x = x;
697             XMapWindow(ob_display, self->desk);
698             XMoveWindow(ob_display, self->desk, x, s_bevel + 1);
699             x += BUTTON_SIZE + s_bevel + 1;
700             break;
701         case 'I':
702             if (!i) break;
703             self->iconify_x = x;
704             XMapWindow(ob_display, self->iconify);
705             XMoveWindow(ob_display, self->iconify, x, s_bevel + 1);
706             x += BUTTON_SIZE + s_bevel + 1;
707             break;
708         case 'L':
709             if (!l) break;
710             self->label_x = x;
711             XMapWindow(ob_display, self->label);
712             XMoveWindow(ob_display, self->label, x, s_bevel);
713             x += self->label_width + s_bevel + 1;
714             break;
715         case 'M':
716             if (!m) break;
717             self->max_x = x;
718             XMapWindow(ob_display, self->max);
719             XMoveWindow(ob_display, self->max, x, s_bevel + 1);
720             x += BUTTON_SIZE + s_bevel + 1;
721             break;
722         case 'C':
723             if (!c) break;
724             self->close_x = x;
725             XMapWindow(ob_display, self->close);
726             XMoveWindow(ob_display, self->close, x, s_bevel + 1);
727             x += BUTTON_SIZE + s_bevel + 1;
728             break;
729         }
730     }
731 }
732
733 static void render(ObFrame *self)
734 {
735     if (client_focused(self->frame.client)) {
736         XSetWindowBorder(ob_display, self->frame.plate,
737                          s_cb_focused_color->pixel);
738     } else {
739         XSetWindowBorder(ob_display, self->frame.plate,
740                          s_cb_unfocused_color->pixel);
741     }
742
743     if (self->frame.client->decorations & Decor_Titlebar) {
744         paint(self->title, (client_focused(self->frame.client) ?
745                             self->a_focused_title :
746                             self->a_unfocused_title),
747               0, 0, self->width, TITLE_HEIGHT);
748         render_label(self);
749         render_max(self);
750         render_icon(self);
751         render_iconify(self);
752         render_desk(self);
753         render_close(self);
754     }
755
756     if (self->frame.client->decorations & Decor_Handle) {
757         paint(self->handle, (client_focused(self->frame.client) ?
758                              self->a_focused_handle :
759                              self->a_unfocused_handle),
760               GRIP_WIDTH + self->bwidth, 0,
761               HANDLE_WIDTH(self), s_handle_height);
762         paint(self->lgrip, (client_focused(self->frame.client) ?
763                             a_focused_grip :
764                             a_unfocused_grip),
765               0, 0, GRIP_WIDTH, s_handle_height);
766         paint(self->rgrip, (client_focused(self->frame.client) ?
767                             a_focused_grip :
768                             a_unfocused_grip),
769               0, 0, GRIP_WIDTH, s_handle_height);
770     }
771 }
772
773 static void render_label(ObFrame *self)
774 {
775     Appearance *a;
776
777     if (self->label_x < 0) return;
778
779     a = (client_focused(self->frame.client) ?
780          self->a_focused_label : self->a_unfocused_label);
781
782     /* set the texture's text! */
783     a->texture[0].data.text.string = self->frame.client->title;
784     RECT_SET(a->texture[0].position, 0, 0, self->label_width, LABEL_HEIGHT);
785
786     paint(self->label, a, 0, 0, self->label_width, LABEL_HEIGHT);
787 }
788
789 static void render_icon(ObFrame *self)
790 {
791     if (self->icon_x < 0) return;
792
793     if (self->frame.client->nicons) {
794         Icon *icon = client_icon(self->frame.client, BUTTON_SIZE, BUTTON_SIZE);
795         self->a_icon->texture[0].type = RGBA;
796         self->a_icon->texture[0].data.rgba.width = icon->width;
797         self->a_icon->texture[0].data.rgba.height = icon->height;
798         self->a_icon->texture[0].data.rgba.data = icon->data;
799         RECT_SET(self->a_icon->texture[0].position, 0, 0,
800                  BUTTON_SIZE,BUTTON_SIZE);
801     } else
802         self->a_icon->texture[0].type = NoTexture;
803
804     paint(self->icon, self->a_icon, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
805 }
806
807 static void render_max(ObFrame *self)
808 {
809     Appearance *a;
810
811     gboolean press = self->max_press ||
812         self->frame.client->max_vert || self->frame.client->max_horz;
813     
814     if (self->max_x < 0) return;
815
816     a = (client_focused(self->frame.client) ?
817          (press ? a_focused_pressed_max : a_focused_unpressed_max) :
818          (press ? a_unfocused_pressed_max : a_unfocused_unpressed_max));
819     RECT_SET(a->texture[0].position, 0, 0, BUTTON_SIZE,BUTTON_SIZE);
820     paint(self->max, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
821 }
822
823 static void render_iconify(ObFrame *self)
824 {
825     Appearance *a;
826
827     if (self->iconify_x < 0) return;
828
829     a = (client_focused(self->frame.client) ?
830          (self->iconify_press ?
831           a_focused_pressed_iconify : a_focused_unpressed_iconify) :
832          (self->iconify_press ?
833           a_unfocused_pressed_iconify : a_unfocused_unpressed_iconify));
834     RECT_SET(a->texture[0].position, 0, 0, BUTTON_SIZE,BUTTON_SIZE);
835     paint(self->iconify, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
836 }
837
838 static void render_desk(ObFrame *self)
839 {
840     Appearance *a;
841
842     gboolean press = self->desk_press ||
843         self->frame.client->desktop == DESKTOP_ALL;
844     
845     if (self->desk_x < 0) return;
846
847     a = (client_focused(self->frame.client) ?
848          (press ? a_focused_pressed_desk : a_focused_unpressed_desk) :
849          (press ? a_unfocused_pressed_desk : a_unfocused_unpressed_desk));
850     RECT_SET(a->texture[0].position, 0, 0, BUTTON_SIZE,BUTTON_SIZE);
851     paint(self->desk, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
852 }
853
854 static void render_close(ObFrame *self)
855 {
856     Appearance *a;
857
858     if (self->close_x < 0) return;
859
860     a = (client_focused(self->frame.client) ?
861          (self->close_press ?
862           a_focused_pressed_close : a_focused_unpressed_close) :
863          (self->close_press ?
864           a_unfocused_pressed_close : a_unfocused_unpressed_close));
865     RECT_SET(a->texture[0].position, 0, 0, BUTTON_SIZE,BUTTON_SIZE);
866     paint(self->close, a, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
867 }
868
869 GQuark get_context(Client *client, Window win)
870 {
871     ObFrame *self;
872
873     if (win == ob_root) return g_quark_try_string("root");
874     if (client == NULL) return g_quark_try_string("none");
875     if (win == client->window) return g_quark_try_string("client");
876
877     self = (ObFrame*) client->frame;
878     if (win == self->frame.window) return g_quark_try_string("frame");
879     if (win == self->frame.plate)  return g_quark_try_string("client");
880     if (win == self->title)  return g_quark_try_string("titlebar");
881     if (win == self->label)  return g_quark_try_string("titlebar");
882     if (win == self->handle) return g_quark_try_string("handle");
883     if (win == self->lgrip)  return g_quark_try_string("blcorner");
884     if (win == self->rgrip)  return g_quark_try_string("brcorner");
885     if (win == self->max)  return g_quark_try_string("maximize");
886     if (win == self->iconify)  return g_quark_try_string("iconify");
887     if (win == self->close)  return g_quark_try_string("close");
888     if (win == self->icon)  return g_quark_try_string("icon");
889     if (win == self->desk)  return g_quark_try_string("alldesktops");
890
891     return g_quark_try_string("none");
892 }
893
894 static void frame_mouse_press(const ObEvent *e, ObFrame *self)
895 {
896     Window win = e->data.x.e->xbutton.window;
897     if (win == self->max) {
898         self->max_press = TRUE;
899         render_max(self);
900     } else if (win == self->close) {
901         self->close_press = TRUE;
902         render_close(self);
903     } else if (win == self->iconify) {
904         self->iconify_press = TRUE;
905         render_iconify(self);
906     } else if (win == self->desk) { 
907         self->desk_press = TRUE;
908         render_desk(self);
909     }
910 }
911
912 static void frame_mouse_release(const ObEvent *e, ObFrame *self)
913 {
914     Window win = e->data.x.e->xbutton.window;
915     if (win == self->max) {
916         self->max_press = FALSE;
917         render_max(self);
918     } else if (win == self->close) {
919         self->close_press = FALSE; 
920         render_close(self);
921     } else if (win == self->iconify) {
922         self->iconify_press = FALSE;
923         render_iconify(self);
924     } else if (win == self->desk) {
925         self->desk_press = FALSE;
926         render_desk(self);
927     }
928 }