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