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