]> icculus.org git repositories - dana/openbox.git/blob - engines/openbox/obengine.c
add support for foo.pressed.mask to load a different mask for pressed buttons
[dana/openbox.git] / engines / openbox / obengine.c
1 #include "obtheme.h"
2 #include "obrender.h"
3 #include "obengine.h"
4 #include "../../kernel/openbox.h"
5 #include "../../kernel/extensions.h"
6 #include "../../kernel/dispatch.h"
7 #include "../../kernel/config.h"
8
9 #ifdef HAVE_SYS_STAT_H
10 #  include <sys/stat.h>
11 #  include <sys/types.h>
12 #endif
13 #include <X11/Xlib.h>
14 #include <glib.h>
15
16 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
17 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask)
18 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
19                            ButtonMotionMask | ExposureMask)
20
21 /* style settings - geometry */
22 int ob_s_bevel;
23 int ob_s_handle_height;
24 int ob_s_bwidth;
25 int ob_s_cbwidth;
26 /* style settings - colors */
27 color_rgb *ob_s_b_color;
28 color_rgb *ob_s_cb_focused_color;
29 color_rgb *ob_s_cb_unfocused_color;
30 color_rgb *ob_s_title_focused_color;
31 color_rgb *ob_s_title_unfocused_color;
32 color_rgb *ob_s_titlebut_focused_color;
33 color_rgb *ob_s_titlebut_unfocused_color;
34 /* style settings - fonts */
35 int ob_s_winfont_height;
36 int ob_s_winfont_shadow;
37 int ob_s_winfont_shadow_offset;
38 ObFont *ob_s_winfont;
39 /* style settings - masks */
40 pixmap_mask *ob_s_max_pressed_mask;
41 pixmap_mask *ob_s_max_unpressed_mask;
42 pixmap_mask *ob_s_iconify_pressed_mask;
43 pixmap_mask *ob_s_iconify_unpressed_mask;
44 pixmap_mask *ob_s_desk_pressed_mask;
45 pixmap_mask *ob_s_desk_unpressed_mask;
46 pixmap_mask *ob_s_close_pressed_mask;
47 pixmap_mask *ob_s_close_unpressed_mask;
48
49 /* global appearances */
50 Appearance *ob_a_focused_unpressed_max;
51 Appearance *ob_a_focused_pressed_max;
52 Appearance *ob_a_unfocused_unpressed_max;
53 Appearance *ob_a_unfocused_pressed_max;
54 Appearance *ob_a_focused_unpressed_close;
55 Appearance *ob_a_focused_pressed_close;
56 Appearance *ob_a_unfocused_unpressed_close;
57 Appearance *ob_a_unfocused_pressed_close;
58 Appearance *ob_a_focused_unpressed_desk;
59 Appearance *ob_a_focused_pressed_desk;
60 Appearance *ob_a_unfocused_unpressed_desk;
61 Appearance *ob_a_unfocused_pressed_desk;
62 Appearance *ob_a_focused_unpressed_iconify;
63 Appearance *ob_a_focused_pressed_iconify;
64 Appearance *ob_a_unfocused_unpressed_iconify;
65 Appearance *ob_a_unfocused_pressed_iconify;
66 Appearance *ob_a_focused_grip;
67 Appearance *ob_a_unfocused_grip;
68 Appearance *ob_a_focused_title;
69 Appearance *ob_a_unfocused_title;
70 Appearance *ob_a_focused_label;
71 Appearance *ob_a_unfocused_label;
72 Appearance *ob_a_icon; /* always parentrelative, so no focused/unfocused */
73 Appearance *ob_a_focused_handle;
74 Appearance *ob_a_unfocused_handle;
75
76 static void layout_title(ObFrame *self);
77 static void mouse_event(const ObEvent *e, ObFrame *self);
78
79 gboolean startup()
80 {
81     char *path;
82
83     g_quark_from_string("none");
84     g_quark_from_string("root");
85     g_quark_from_string("client");
86     g_quark_from_string("titlebar");
87     g_quark_from_string("handle");
88     g_quark_from_string("frame");
89     g_quark_from_string("blcorner");
90     g_quark_from_string("brcorner");
91     g_quark_from_string("maximize");
92     g_quark_from_string("alldesktops");
93     g_quark_from_string("iconify");
94     g_quark_from_string("icon");
95     g_quark_from_string("close");
96
97     /* create the ~/.openbox/themes/openbox dir */
98     path = g_build_filename(g_get_home_dir(), ".openbox", "themes", "openbox",
99                             NULL);
100     mkdir(path, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
101                  S_IROTH | S_IWOTH | S_IXOTH));
102     g_free(path);
103
104     ob_s_b_color = ob_s_cb_unfocused_color = ob_s_cb_focused_color = 
105         ob_s_title_unfocused_color = ob_s_title_focused_color = 
106         ob_s_titlebut_unfocused_color = ob_s_titlebut_focused_color = NULL;
107     ob_s_winfont = NULL;
108     ob_s_max_pressed_mask = ob_s_max_unpressed_mask = NULL;
109     ob_s_iconify_pressed_mask = ob_s_iconify_unpressed_mask = NULL;
110     ob_s_desk_pressed_mask = ob_s_desk_unpressed_mask = NULL;
111     ob_s_close_pressed_mask = ob_s_close_unpressed_mask = NULL;
112
113     ob_a_focused_unpressed_max = appearance_new(Surface_Planar, 1);
114     ob_a_focused_pressed_max = appearance_new(Surface_Planar, 1);
115     ob_a_unfocused_unpressed_max = appearance_new(Surface_Planar, 1);
116     ob_a_unfocused_pressed_max = appearance_new(Surface_Planar, 1);
117     ob_a_focused_unpressed_close = NULL;
118     ob_a_focused_pressed_close = NULL;
119     ob_a_unfocused_unpressed_close = NULL;
120     ob_a_unfocused_pressed_close = NULL;
121     ob_a_focused_unpressed_desk = NULL;
122     ob_a_focused_pressed_desk = NULL;
123     ob_a_unfocused_unpressed_desk = NULL;
124     ob_a_unfocused_pressed_desk = NULL;
125     ob_a_focused_unpressed_iconify = NULL;
126     ob_a_focused_pressed_iconify = NULL;
127     ob_a_unfocused_unpressed_iconify = NULL;
128     ob_a_unfocused_pressed_iconify = NULL;
129     ob_a_focused_grip = appearance_new(Surface_Planar, 0);
130     ob_a_unfocused_grip = appearance_new(Surface_Planar, 0);
131     ob_a_focused_title = appearance_new(Surface_Planar, 0);
132     ob_a_unfocused_title = appearance_new(Surface_Planar, 0);
133     ob_a_focused_label = appearance_new(Surface_Planar, 1);
134     ob_a_unfocused_label = appearance_new(Surface_Planar, 1);
135     ob_a_icon = appearance_new(Surface_Planar, 1);
136     ob_a_focused_handle = appearance_new(Surface_Planar, 0);
137     ob_a_unfocused_handle = appearance_new(Surface_Planar, 0);
138
139     if (obtheme_load()) {
140         RECT_SET(ob_a_focused_pressed_desk->area, 0, 0,
141                  BUTTON_SIZE, BUTTON_SIZE);
142         RECT_SET(ob_a_focused_unpressed_desk->area, 0, 0,
143                  BUTTON_SIZE, BUTTON_SIZE);
144         RECT_SET(ob_a_unfocused_pressed_desk->area, 0, 0,
145                  BUTTON_SIZE, BUTTON_SIZE);
146         RECT_SET(ob_a_unfocused_unpressed_desk->area, 0, 0,
147                  BUTTON_SIZE, BUTTON_SIZE);
148         RECT_SET(ob_a_focused_pressed_iconify->area, 0, 0,
149                  BUTTON_SIZE, BUTTON_SIZE);
150         RECT_SET(ob_a_focused_unpressed_iconify->area, 0, 0,
151                  BUTTON_SIZE, BUTTON_SIZE);
152         RECT_SET(ob_a_unfocused_pressed_iconify->area, 0, 0,
153                  BUTTON_SIZE, BUTTON_SIZE);
154         RECT_SET(ob_a_unfocused_unpressed_iconify->area, 0, 0,
155                  BUTTON_SIZE, BUTTON_SIZE);
156         RECT_SET(ob_a_unfocused_unpressed_iconify->area, 0, 0,
157                  BUTTON_SIZE, BUTTON_SIZE);
158         RECT_SET(ob_a_focused_pressed_max->area, 0, 0,
159                  BUTTON_SIZE, BUTTON_SIZE);
160         RECT_SET(ob_a_focused_unpressed_max->area, 0, 0,
161                  BUTTON_SIZE, BUTTON_SIZE);
162         RECT_SET(ob_a_unfocused_pressed_max->area, 0, 0,
163                  BUTTON_SIZE, BUTTON_SIZE);
164         RECT_SET(ob_a_unfocused_unpressed_max->area, 0, 0,
165                  BUTTON_SIZE, BUTTON_SIZE);
166         RECT_SET(ob_a_focused_pressed_close->area, 0, 0,
167                  BUTTON_SIZE, BUTTON_SIZE);
168         RECT_SET(ob_a_focused_unpressed_close->area, 0, 0,
169                  BUTTON_SIZE, BUTTON_SIZE);
170         RECT_SET(ob_a_unfocused_pressed_close->area, 0, 0,
171                  BUTTON_SIZE, BUTTON_SIZE);
172         RECT_SET(ob_a_unfocused_unpressed_close->area, 0, 0,
173                  BUTTON_SIZE, BUTTON_SIZE);
174
175         RECT_SET(ob_a_focused_grip->area, 0, 0,
176                  GRIP_WIDTH, ob_s_handle_height);
177         RECT_SET(ob_a_unfocused_grip->area, 0, 0,
178                  GRIP_WIDTH, ob_s_handle_height);
179         return TRUE;
180     } else
181         return FALSE;
182 }
183
184 void shutdown()
185 {
186     if (ob_s_b_color != NULL) color_free(ob_s_b_color);
187     if (ob_s_cb_unfocused_color != NULL) color_free(ob_s_cb_unfocused_color);
188     if (ob_s_cb_focused_color != NULL) color_free(ob_s_cb_focused_color);
189     if (ob_s_title_unfocused_color != NULL) color_free(ob_s_title_unfocused_color);
190     if (ob_s_title_focused_color != NULL) color_free(ob_s_title_focused_color);
191     if (ob_s_titlebut_unfocused_color != NULL)
192         color_free(ob_s_titlebut_unfocused_color);
193     if (ob_s_titlebut_focused_color != NULL)
194         color_free(ob_s_titlebut_focused_color);
195
196     if (ob_s_max_pressed_mask != NULL)
197         pixmap_mask_free(ob_s_max_pressed_mask);
198     if (ob_s_max_unpressed_mask != NULL)
199         pixmap_mask_free(ob_s_max_unpressed_mask);
200     if (ob_s_desk_pressed_mask != NULL)
201         pixmap_mask_free(ob_s_desk_pressed_mask);
202     if (ob_s_desk_unpressed_mask != NULL)
203         pixmap_mask_free(ob_s_desk_unpressed_mask);
204     if (ob_s_iconify_pressed_mask != NULL)
205         pixmap_mask_free(ob_s_iconify_pressed_mask);
206     if (ob_s_iconify_unpressed_mask != NULL)
207         pixmap_mask_free(ob_s_iconify_unpressed_mask);
208     if (ob_s_close_pressed_mask != NULL)
209         pixmap_mask_free(ob_s_close_pressed_mask);
210     if (ob_s_close_unpressed_mask != NULL)
211         pixmap_mask_free(ob_s_close_unpressed_mask);
212
213     if (ob_s_winfont != NULL) font_close(ob_s_winfont);
214
215     appearance_free(ob_a_focused_unpressed_max);
216     appearance_free(ob_a_focused_pressed_max);
217     appearance_free(ob_a_unfocused_unpressed_max);
218     appearance_free(ob_a_unfocused_pressed_max);
219     if (ob_a_focused_unpressed_close != NULL)
220         appearance_free(ob_a_focused_unpressed_close);
221     if (ob_a_focused_pressed_close != NULL)
222         appearance_free(ob_a_focused_pressed_close);
223     if (ob_a_unfocused_unpressed_close != NULL)
224         appearance_free(ob_a_unfocused_unpressed_close);
225     if (ob_a_unfocused_pressed_close != NULL)
226         appearance_free(ob_a_unfocused_pressed_close);
227     if (ob_a_focused_unpressed_desk != NULL)
228         appearance_free(ob_a_focused_unpressed_desk);
229     if (ob_a_focused_pressed_desk != NULL)
230         appearance_free(ob_a_focused_pressed_desk);
231     if (ob_a_unfocused_unpressed_desk != NULL)
232         appearance_free(ob_a_unfocused_unpressed_desk);
233     if (ob_a_unfocused_pressed_desk != NULL)
234         appearance_free(ob_a_unfocused_pressed_desk);
235     if (ob_a_focused_unpressed_iconify != NULL)
236         appearance_free(ob_a_focused_unpressed_iconify);
237     if (ob_a_focused_pressed_iconify != NULL)
238         appearance_free(ob_a_focused_pressed_iconify);
239     if (ob_a_unfocused_unpressed_iconify != NULL)
240         appearance_free(ob_a_unfocused_unpressed_iconify);
241     if (ob_a_unfocused_pressed_iconify != NULL)
242         appearance_free(ob_a_unfocused_pressed_iconify);
243     appearance_free(ob_a_focused_grip);
244     appearance_free(ob_a_unfocused_grip);
245     appearance_free(ob_a_focused_title);
246     appearance_free(ob_a_unfocused_title);
247     appearance_free(ob_a_focused_label);
248     appearance_free(ob_a_unfocused_label);
249     appearance_free(ob_a_icon);
250     appearance_free(ob_a_focused_handle);
251     appearance_free(ob_a_unfocused_handle);
252 }
253
254 static Window createWindow(Window parent, unsigned long mask,
255                            XSetWindowAttributes *attrib)
256 {
257     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
258                          render_depth, InputOutput, render_visual,
259                          mask, attrib);
260                        
261 }
262
263 Frame *frame_new()
264 {
265     XSetWindowAttributes attrib;
266     unsigned long mask;
267     ObFrame *self;
268
269     self = g_new(ObFrame, 1);
270
271     self->frame.visible = FALSE;
272
273     /* create all of the decor windows */
274     mask = CWOverrideRedirect | CWEventMask;
275     attrib.event_mask = FRAME_EVENTMASK;
276     attrib.override_redirect = TRUE;
277     self->frame.window = createWindow(ob_root, mask, &attrib);
278
279     mask = 0;
280     self->frame.plate = createWindow(self->frame.window, mask, &attrib);
281
282     mask = CWEventMask;
283     attrib.event_mask = ELEMENT_EVENTMASK;
284     self->title = createWindow(self->frame.window, mask, &attrib);
285     self->label = createWindow(self->title, mask, &attrib);
286     self->max = createWindow(self->title, mask, &attrib);
287     self->close = createWindow(self->title, mask, &attrib);
288     self->desk = createWindow(self->title, mask, &attrib);
289     self->icon = createWindow(self->title, mask, &attrib);
290     self->iconify = createWindow(self->title, mask, &attrib);
291     self->handle = createWindow(self->frame.window, mask, &attrib);
292     mask |= CWCursor;
293     attrib.cursor = ob_cursors.ll_angle;
294     self->lgrip = createWindow(self->handle, mask, &attrib);
295     attrib.cursor = ob_cursors.lr_angle;
296     self->rgrip = createWindow(self->handle, mask, &attrib);
297
298     /* the other stuff is shown based on decor settings */
299     XMapWindow(ob_display, self->frame.plate);
300     XMapWindow(ob_display, self->lgrip);
301     XMapWindow(ob_display, self->rgrip);
302     XMapWindow(ob_display, self->label);
303
304     /* set colors/appearance/sizes for stuff that doesn't change */
305     XSetWindowBorder(ob_display, self->frame.window, ob_s_b_color->pixel);
306     XSetWindowBorder(ob_display, self->label, ob_s_b_color->pixel);
307     XSetWindowBorder(ob_display, self->rgrip, ob_s_b_color->pixel);
308     XSetWindowBorder(ob_display, self->lgrip, ob_s_b_color->pixel);
309
310     XResizeWindow(ob_display, self->max, BUTTON_SIZE, BUTTON_SIZE);
311     XResizeWindow(ob_display, self->iconify, BUTTON_SIZE, BUTTON_SIZE);
312     XResizeWindow(ob_display, self->icon, BUTTON_SIZE, BUTTON_SIZE);
313     XResizeWindow(ob_display, self->close, BUTTON_SIZE, BUTTON_SIZE);
314     XResizeWindow(ob_display, self->desk, BUTTON_SIZE, BUTTON_SIZE);
315     XResizeWindow(ob_display, self->lgrip, GRIP_WIDTH, ob_s_handle_height);
316     XResizeWindow(ob_display, self->rgrip, GRIP_WIDTH, ob_s_handle_height);
317
318     /* set up the dynamic appearances */
319     self->a_unfocused_title = appearance_copy(ob_a_unfocused_title);
320     self->a_focused_title = appearance_copy(ob_a_focused_title);
321     self->a_unfocused_label = appearance_copy(ob_a_unfocused_label);
322     self->a_focused_label = appearance_copy(ob_a_focused_label);
323     self->a_unfocused_handle = appearance_copy(ob_a_unfocused_handle);
324     self->a_focused_handle = appearance_copy(ob_a_focused_handle);
325     self->a_icon = appearance_copy(ob_a_icon);
326
327     self->max_press = self->close_press = self->desk_press = 
328         self->iconify_press = FALSE;
329
330     dispatch_register(Event_X_ButtonPress | Event_X_ButtonRelease,
331                       (EventHandler)mouse_event, self);
332
333     return (Frame*)self;
334 }
335
336 static void frame_free(ObFrame *self)
337 {
338     appearance_free(self->a_unfocused_title); 
339     appearance_free(self->a_focused_title);
340     appearance_free(self->a_unfocused_label);
341     appearance_free(self->a_focused_label);
342     appearance_free(self->a_unfocused_handle);
343     appearance_free(self->a_focused_handle);
344     appearance_free(self->a_icon);
345
346     XDestroyWindow(ob_display, self->frame.window);
347
348     dispatch_register(0, (EventHandler)mouse_event, self);
349
350     g_free(self);
351 }
352
353 void frame_show(ObFrame *self)
354 {
355     if (!self->frame.visible) {
356         self->frame.visible = TRUE;
357         XMapWindow(ob_display, self->frame.window);
358     }
359 }
360
361 void frame_hide(ObFrame *self)
362 {
363     if (self->frame.visible) {
364         self->frame.visible = FALSE;
365         self->frame.client->ignore_unmaps++;
366         XUnmapWindow(ob_display, self->frame.window);
367     }
368 }
369
370 void frame_adjust_shape(ObFrame *self)
371 {
372 #ifdef SHAPE
373     int num;
374     XRectangle xrect[2];
375
376     if (!self->frame.client->shaped) {
377         /* clear the shape on the frame window */
378         XShapeCombineMask(ob_display, self->frame.window, ShapeBounding,
379                           self->innersize.left,
380                           self->innersize.top,
381                           None, ShapeSet);
382     } else {
383         /* make the frame's shape match the clients */
384         XShapeCombineShape(ob_display, self->frame.window, ShapeBounding,
385                            self->innersize.left,
386                            self->innersize.top,
387                            self->frame.client->window,
388                            ShapeBounding, ShapeSet);
389
390         num = 0;
391         if (self->frame.client->decorations & Decor_Titlebar) {
392             xrect[0].x = -ob_s_bevel;
393             xrect[0].y = -ob_s_bevel;
394             xrect[0].width = self->width + self->bwidth * 2;
395             xrect[0].height = TITLE_HEIGHT +
396                 self->bwidth * 2;
397             ++num;
398         }
399
400         if (self->frame.client->decorations & Decor_Handle) {
401             xrect[1].x = -ob_s_bevel;
402             xrect[1].y = HANDLE_Y(self);
403             xrect[1].width = self->width + self->bwidth * 2;
404             xrect[1].height = ob_s_handle_height +
405                 self->bwidth * 2;
406             ++num;
407         }
408
409         XShapeCombineRectangles(ob_display, self->frame.window,
410                                 ShapeBounding, 0, 0, xrect, num,
411                                 ShapeUnion, Unsorted);
412     }
413 #endif
414 }
415
416 void frame_adjust_area(ObFrame *self, gboolean moved, gboolean resized)
417 {
418     if (resized) {
419         if (self->frame.client->decorations & Decor_Border) {
420             self->bwidth = ob_s_bwidth;
421             self->cbwidth = ob_s_cbwidth;
422         } else {
423             self->bwidth = self->cbwidth = 0;
424         }
425         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
426                   self->cbwidth, self->cbwidth);
427         self->width = self->frame.client->area.width + self->cbwidth * 2;
428         g_assert(self->width > 0);
429
430         /* set border widths */
431         XSetWindowBorderWidth(ob_display, self->frame.plate,  self->cbwidth);
432         XSetWindowBorderWidth(ob_display, self->frame.window, self->bwidth);
433         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
434         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
435         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
436         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
437   
438         /* position/size and map/unmap all the windows */
439
440         /* they all default off, they're turned on in layout_title */
441         self->icon_x = -1;
442         self->desk_x = -1;
443         self->icon_x = -1;
444         self->label_x = -1;
445         self->max_x = -1;
446         self->close_x = -1;
447
448         if (self->frame.client->decorations & Decor_Titlebar) {
449             XMoveResizeWindow(ob_display, self->title,
450                               -self->bwidth, -self->bwidth,
451                               self->width, TITLE_HEIGHT);
452             self->innersize.top += TITLE_HEIGHT + self->bwidth;
453             XMapWindow(ob_display, self->title);
454
455             RECT_SET(self->a_focused_title->area, 0, 0,
456                      self->width, TITLE_HEIGHT);
457             RECT_SET(self->a_unfocused_title->area, 0, 0,
458                      self->width, TITLE_HEIGHT);
459
460             /* layout the title bar elements */
461             layout_title(self);
462         } else {
463             XUnmapWindow(ob_display, self->title);
464             /* make all the titlebar stuff not render */
465             self->frame.client->decorations &= ~(Decor_Icon | Decor_Iconify |
466                                                  Decor_Maximize | Decor_Close |
467                                                  Decor_AllDesktops);
468         }
469
470         if (self->frame.client->decorations & Decor_Handle) {
471             XMoveResizeWindow(ob_display, self->handle,
472                               -self->bwidth, HANDLE_Y(self),
473                               self->width, ob_s_handle_height);
474             XMoveWindow(ob_display, self->lgrip,
475                         -self->bwidth, -self->bwidth);
476             XMoveWindow(ob_display, self->rgrip,
477                         -self->bwidth + self->width -
478                         GRIP_WIDTH, -self->bwidth);
479             self->innersize.bottom += ob_s_handle_height +
480                 self->bwidth;
481             XMapWindow(ob_display, self->handle);
482
483             if (self->a_focused_handle->surface.data.planar.grad ==
484                 Background_ParentRelative)
485                 RECT_SET(self->a_focused_handle->area, 0, 0,
486                          self->width, ob_s_handle_height);
487             else
488                 RECT_SET(self->a_focused_handle->area,
489                          GRIP_WIDTH + self->bwidth, 0,
490                          self->width - (GRIP_WIDTH + self->bwidth) * 2,
491                          ob_s_handle_height);
492             if (self->a_unfocused_handle->surface.data.planar.grad ==
493                 Background_ParentRelative)
494                 RECT_SET(self->a_unfocused_handle->area, 0, 0,
495                          self->width, ob_s_handle_height);
496             else
497                 RECT_SET(self->a_unfocused_handle->area,
498                          GRIP_WIDTH + self->bwidth, 0,
499                          self->width - (GRIP_WIDTH + self->bwidth) * 2,
500                          ob_s_handle_height);
501
502         } else
503             XUnmapWindow(ob_display, self->handle);
504     }
505
506     if (resized) {
507         /* move and resize the plate */
508         XMoveResizeWindow(ob_display, self->frame.plate,
509                           self->innersize.left - self->cbwidth,
510                           self->innersize.top - self->cbwidth,
511                           self->frame.client->area.width,
512                           self->frame.client->area.height);
513         /* when the client has StaticGravity, it likes to move around. */
514         XMoveWindow(ob_display, self->frame.client->window, 0, 0);
515     }
516
517     if (resized) {
518         STRUT_SET(self->frame.size,
519                   self->innersize.left + self->bwidth,
520                   self->innersize.top + self->bwidth,
521                   self->innersize.right + self->bwidth,
522                   self->innersize.bottom + self->bwidth);
523     }
524
525     /* shading can change without being moved or resized */
526     RECT_SET_SIZE(self->frame.area,
527                   self->frame.client->area.width +
528                   self->frame.size.left + self->frame.size.right,
529                   (self->frame.client->shaded ? TITLE_HEIGHT + self->bwidth*2:
530                    self->frame.client->area.height +
531                    self->frame.size.top + self->frame.size.bottom));
532
533     if (moved) {
534         /* find the new coordinates, done after setting the frame.size, for
535            frame_client_gravity. */
536         self->frame.area.x = self->frame.client->area.x;
537         self->frame.area.y = self->frame.client->area.y;
538         frame_client_gravity((Frame*)self,
539                              &self->frame.area.x, &self->frame.area.y);
540     }
541
542     /* move and resize the top level frame.
543        shading can change without being moved or resized */
544     XMoveResizeWindow(ob_display, self->frame.window,
545                       self->frame.area.x, self->frame.area.y,
546                       self->width,
547                       self->frame.area.height - self->bwidth * 2);
548
549     if (resized) {
550         obrender_frame(self);
551
552         frame_adjust_shape(self);
553     }
554 }
555
556 void frame_adjust_state(ObFrame *self)
557 {
558     obrender_frame(self);
559 }
560
561 void frame_adjust_focus(ObFrame *self)
562 {
563     obrender_frame(self);
564 }
565
566 void frame_adjust_title(ObFrame *self)
567 {
568     obrender_frame(self);
569 }
570
571 void frame_adjust_icon(ObFrame *self)
572 {
573     obrender_frame(self);
574 }
575
576 void frame_grab_client(ObFrame *self, Client *client)
577 {
578     self->frame.client = client;
579
580     /* reparent the client to the frame */
581     XReparentWindow(ob_display, client->window, self->frame.plate, 0, 0);
582     /*
583       When reparenting the client window, it is usually not mapped yet, since
584       this occurs from a MapRequest. However, in the case where Openbox is
585       starting up, the window is already mapped, so we'll see unmap events for
586       it. There are 2 unmap events generated that we see, one with the 'event'
587       member set the root window, and one set to the client, but both get
588       handled and need to be ignored.
589     */
590     if (ob_state == State_Starting)
591         client->ignore_unmaps += 2;
592
593     /* select the event mask on the client's parent (to receive config/map
594        req's) the ButtonPress is to catch clicks on the client border */
595     XSelectInput(ob_display, self->frame.plate, PLATE_EVENTMASK);
596
597     /* map the client so it maps when the frame does */
598     XMapWindow(ob_display, client->window);
599
600     frame_adjust_area(self, TRUE, TRUE);
601
602     /* set all the windows for the frame in the client_map */
603     g_hash_table_insert(client_map, &self->frame.window, client);
604     g_hash_table_insert(client_map, &self->frame.plate, client);
605     g_hash_table_insert(client_map, &self->title, client);
606     g_hash_table_insert(client_map, &self->label, client);
607     g_hash_table_insert(client_map, &self->max, client);
608     g_hash_table_insert(client_map, &self->close, client);
609     g_hash_table_insert(client_map, &self->desk, client);
610     g_hash_table_insert(client_map, &self->icon, client);
611     g_hash_table_insert(client_map, &self->iconify, client);
612     g_hash_table_insert(client_map, &self->handle, client);
613     g_hash_table_insert(client_map, &self->lgrip, client);
614     g_hash_table_insert(client_map, &self->rgrip, client);
615 }
616
617 void frame_release_client(ObFrame *self, Client *client)
618 {
619     XEvent ev;
620
621     g_assert(self->frame.client == client);
622
623     /* check if the app has already reparented its window away */
624     if (XCheckTypedWindowEvent(ob_display, client->window,
625                                ReparentNotify, &ev)) {
626         XPutBackEvent(ob_display, &ev);
627         /* re-map the window since the unmanaging process unmaps it */
628         XMapWindow(ob_display, client->window);
629     } else {
630         /* according to the ICCCM - if the client doesn't reparent itself,
631            then we will reparent the window to root for them */
632         XReparentWindow(ob_display, client->window, ob_root,
633                         client->area.x,
634                         client->area.y);
635     }
636
637     /* remove all the windows for the frame from the client_map */
638     g_hash_table_remove(client_map, &self->frame.window);
639     g_hash_table_remove(client_map, &self->frame.plate);
640     g_hash_table_remove(client_map, &self->title);
641     g_hash_table_remove(client_map, &self->label);
642     g_hash_table_remove(client_map, &self->max);
643     g_hash_table_remove(client_map, &self->close);
644     g_hash_table_remove(client_map, &self->desk);
645     g_hash_table_remove(client_map, &self->icon);
646     g_hash_table_remove(client_map, &self->iconify);
647     g_hash_table_remove(client_map, &self->handle);
648     g_hash_table_remove(client_map, &self->lgrip);
649     g_hash_table_remove(client_map, &self->rgrip);
650
651     frame_free(self);
652 }
653
654 static void layout_title(ObFrame *self)
655 {
656     char *lc;
657     int x;
658     gboolean n, d, i, l, m ,c;
659     ConfigValue layout;
660
661     n = d = i = l = m = c = FALSE;
662
663     if (!config_get("titlebar.layout", Config_String, &layout)) {
664         layout.string = "NDLIMC";
665         config_set("titlebar.layout", Config_String, layout);
666     }
667
668     /* figure out whats being shown, and the width of the label */
669     self->label_width = self->width - (ob_s_bevel + 1) * 2;
670     for (lc = layout.string; *lc != '\0'; ++lc) {
671         switch (*lc) {
672         case 'N':
673             if (!(self->frame.client->decorations & Decor_Icon)) break;
674             if (n) { *lc = ' '; break; } /* rm duplicates */
675             n = TRUE;
676             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
677             break;
678         case 'D':
679             if (!(self->frame.client->decorations & Decor_AllDesktops)) break;
680             if (d) { *lc = ' '; break; } /* rm duplicates */
681             d = TRUE;
682             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
683             break;
684         case 'I':
685             if (!(self->frame.client->decorations & Decor_Iconify)) break;
686             if (i) { *lc = ' '; break; } /* rm duplicates */
687             i = TRUE;
688             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
689             break;
690         case 'L':
691             if (l) { *lc = ' '; break; } /* rm duplicates */
692             l = TRUE;
693             break;
694         case 'M':
695             if (!(self->frame.client->decorations & Decor_Maximize)) break;
696             if (m) { *lc = ' '; break; } /* rm duplicates */
697             m = TRUE;
698             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
699             break;
700         case 'C':
701             if (!(self->frame.client->decorations & Decor_Close)) break;
702             if (c) { *lc = ' '; break; } /* rm duplicates */
703             c = TRUE;
704             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
705             break;
706         }
707     }
708     if (self->label_width < 1) self->label_width = 1;
709
710     XResizeWindow(ob_display, self->label, self->label_width,
711                   LABEL_HEIGHT);
712   
713     if (!n) XUnmapWindow(ob_display, self->icon);
714     if (!d) XUnmapWindow(ob_display, self->desk);
715     if (!i) XUnmapWindow(ob_display, self->iconify);
716     if (!l) XUnmapWindow(ob_display, self->label);
717     if (!m) XUnmapWindow(ob_display, self->max);
718     if (!c) XUnmapWindow(ob_display, self->close);
719
720     x = ob_s_bevel + 1;
721     for (lc = layout.string; *lc != '\0'; ++lc) {
722         switch (*lc) {
723         case 'N':
724             if (!n) break;
725             self->icon_x = x;
726             RECT_SET(self->a_icon->area, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
727             XMapWindow(ob_display, self->icon);
728             XMoveWindow(ob_display, self->icon, x, ob_s_bevel + 1);
729             x += BUTTON_SIZE + ob_s_bevel + 1;
730             break;
731         case 'D':
732             if (!d) break;
733             self->desk_x = x;
734             XMapWindow(ob_display, self->desk);
735             XMoveWindow(ob_display, self->desk, x, ob_s_bevel + 1);
736             x += BUTTON_SIZE + ob_s_bevel + 1;
737             break;
738         case 'I':
739             if (!i) break;
740             self->iconify_x = x;
741             XMapWindow(ob_display, self->iconify);
742             XMoveWindow(ob_display, self->iconify, x, ob_s_bevel + 1);
743             x += BUTTON_SIZE + ob_s_bevel + 1;
744             break;
745         case 'L':
746             if (!l) break;
747             self->label_x = x;
748             XMapWindow(ob_display, self->label);
749             XMoveWindow(ob_display, self->label, x, ob_s_bevel);
750             x += self->label_width + ob_s_bevel + 1;
751             break;
752         case 'M':
753             if (!m) break;
754             self->max_x = x;
755             XMapWindow(ob_display, self->max);
756             XMoveWindow(ob_display, self->max, x, ob_s_bevel + 1);
757             x += BUTTON_SIZE + ob_s_bevel + 1;
758             break;
759         case 'C':
760             if (!c) break;
761             self->close_x = x;
762             XMapWindow(ob_display, self->close);
763             XMoveWindow(ob_display, self->close, x, ob_s_bevel + 1);
764             x += BUTTON_SIZE + ob_s_bevel + 1;
765             break;
766         }
767     }
768
769     RECT_SET(self->a_focused_label->area, 0, 0,
770              self->label_width, LABEL_HEIGHT);
771     RECT_SET(self->a_unfocused_label->area, 0, 0,
772              self->label_width, LABEL_HEIGHT);
773 }
774
775 static void mouse_event(const ObEvent *e, ObFrame *self)
776 {
777     Window win;
778     gboolean press = e->type == Event_X_ButtonPress;
779
780     win = e->data.x.e->xbutton.window;
781     if (win == self->max) {
782         self->max_press = press;
783         obrender_frame(self);
784     } else if (win == self->close) {
785         self->close_press = press;
786         obrender_frame(self);
787     } else if (win == self->iconify) {
788         self->iconify_press = press;
789         obrender_frame(self);
790     } else if (win == self->desk) { 
791         self->desk_press = press;
792         obrender_frame(self);
793     }
794 }
795
796 GQuark get_context(Client *client, Window win)
797 {
798     ObFrame *self;
799
800     if (win == ob_root) return g_quark_try_string("root");
801     if (client == NULL) return g_quark_try_string("none");
802     if (win == client->window) return g_quark_try_string("client");
803
804     self = (ObFrame*) client->frame;
805     if (win == self->frame.window) return g_quark_try_string("frame");
806     if (win == self->frame.plate)  return g_quark_try_string("client");
807     if (win == self->title)  return g_quark_try_string("titlebar");
808     if (win == self->label)  return g_quark_try_string("titlebar");
809     if (win == self->handle) return g_quark_try_string("handle");
810     if (win == self->lgrip)  return g_quark_try_string("blcorner");
811     if (win == self->rgrip)  return g_quark_try_string("brcorner");
812     if (win == self->max)  return g_quark_try_string("maximize");
813     if (win == self->iconify)  return g_quark_try_string("iconify");
814     if (win == self->close)  return g_quark_try_string("close");
815     if (win == self->icon)  return g_quark_try_string("icon");
816     if (win == self->desk)  return g_quark_try_string("alldesktops");
817
818     return g_quark_try_string("none");
819 }