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