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