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