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