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