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