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