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