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