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