]> icculus.org git repositories - mikachu/openbox.git/blob - src/frame.cc
make parentrelative decorations work
[mikachu/openbox.git] / src / frame.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #include "config.h"
4
5 extern "C" {
6 #ifdef    SHAPE
7 #include <X11/extensions/shape.h>
8 #endif // SHAPE
9 }
10
11 #include "frame.hh"
12 #include "openbox.hh"
13 #include "otk/display.hh"
14 #include "otk/surface.hh"
15
16 #include <string>
17 #include <cassert>
18
19 namespace ob {
20
21 const long Frame::event_mask;
22
23 Window createWindow(const otk::ScreenInfo *info, Window parent, 
24                     unsigned long mask, XSetWindowAttributes *attrib)
25 {
26   return XCreateWindow(**otk::display, parent, 0, 0, 1, 1, 0,
27                        info->depth(), InputOutput, info->visual(),
28                        mask, attrib);
29                        
30 }
31
32 Frame::Frame(Client *client)
33   : _client(client),
34     _visible(false),
35     _plate(0),
36     _title(0),
37     _label(0),
38     _handle(0),
39     _lgrip(0),
40     _rgrip(0),
41     _max(0),
42     _desk(0),
43     _iconify(0),
44     _icon(0),
45     _close(0),
46     _frame_sur(0),
47     _title_sur(0),
48     _label_sur(0),
49     _handle_sur(0),
50     _grip_sur(0),
51     _max_sur(0),
52     _desk_sur(0),
53     _iconify_sur(0),
54     _icon_sur(0),
55     _close_sur(0),
56     _max_press(false),
57     _desk_press(false),
58     _iconify_press(false),
59     _icon_press(false),
60     _close_press(false),
61     _press_button(0)
62 {
63   assert(client);
64
65   XSetWindowAttributes attrib;
66   unsigned long mask;
67   const otk::ScreenInfo *info = otk::display->screenInfo(client->screen());
68
69   // create all of the decor windows (except title bar buttons)
70   mask = CWOverrideRedirect | CWEventMask;
71   attrib.event_mask = Frame::event_mask;
72   attrib.override_redirect = true;
73   _frame = createWindow(info, info->rootWindow(), mask, &attrib);
74
75   mask = 0;
76   _plate = createWindow(info, _frame, mask, &attrib);
77   mask = CWEventMask;
78   attrib.event_mask = (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
79                        ExposureMask);
80   _title = createWindow(info, _frame, mask, &attrib);
81   _label = createWindow(info, _title, mask, &attrib);
82   _max = createWindow(info, _title, mask, &attrib);
83   _close = createWindow(info, _title, mask, &attrib);
84   _desk = createWindow(info, _title, mask, &attrib);
85   _icon = createWindow(info, _title, mask, &attrib);
86   _iconify = createWindow(info, _title, mask, &attrib);
87   _handle = createWindow(info, _frame, mask, &attrib);
88   mask |= CWCursor;
89   attrib.cursor = openbox->cursors().ll_angle;
90   _lgrip = createWindow(info, _handle, mask, &attrib);
91   attrib.cursor = openbox->cursors().lr_angle;
92   _rgrip = createWindow(info, _handle, mask, &attrib);
93
94   // the other stuff is shown based on decor settings
95   XMapWindow(**otk::display, _plate);
96   XMapWindow(**otk::display, _lgrip);
97   XMapWindow(**otk::display, _rgrip);
98   XMapWindow(**otk::display, _label);
99
100   applyStyle(*otk::RenderStyle::style(_client->screen()));
101
102   _layout = openbox->screen(_client->screen())->config().titlebar_layout;
103
104   // register all of the windows with the event dispatcher
105   Window *w = allWindows();
106   for (unsigned int i = 0; w[i]; ++i)
107     openbox->registerHandler(w[i], this);
108   delete [] w;
109 }
110
111 Frame::~Frame()
112 {
113   // unregister all of the windows with the event dispatcher
114   Window *w = allWindows();
115   for (unsigned int i = 0; w[i]; ++i)
116     openbox->clearHandler(w[i]);
117   delete [] w;
118
119   XDestroyWindow(**otk::display, _rgrip);
120   XDestroyWindow(**otk::display, _lgrip);
121   XDestroyWindow(**otk::display, _handle);
122   XDestroyWindow(**otk::display, _max);
123   XDestroyWindow(**otk::display, _icon);
124   XDestroyWindow(**otk::display, _iconify);
125   XDestroyWindow(**otk::display, _desk);
126   XDestroyWindow(**otk::display, _close);
127   XDestroyWindow(**otk::display, _label);
128   XDestroyWindow(**otk::display, _title);
129   XDestroyWindow(**otk::display, _frame);
130
131   if (_frame_sur) delete _frame_sur;
132   if (_title_sur) delete _title_sur;
133   if (_label_sur) delete _label_sur;
134   if (_handle_sur) delete _handle_sur;
135   if (_grip_sur) delete _grip_sur;
136   if (_max_sur) delete _max_sur;
137   if (_desk_sur) delete _desk_sur;
138   if (_iconify_sur) delete _iconify_sur;
139   if (_icon_sur) delete _icon_sur;
140   if (_close_sur) delete _close_sur;
141 }
142
143 void Frame::show()
144 {
145   if (!_visible) {
146     _visible = true;
147     XMapWindow(**otk::display, _frame);
148   }
149 }
150
151 void Frame::hide()
152 {
153   if (_visible) {
154     _visible = false;
155     XUnmapWindow(**otk::display, _frame);
156   }
157 }
158
159 void Frame::buttonPressHandler(const XButtonEvent &e)
160 {
161   if (_press_button) return;
162   _press_button = e.button;
163   
164   if (e.window == _max) {
165     _max_press = true;
166     renderMax();
167   }
168   if (e.window == _close) {
169     _close_press = true;
170     renderClose();
171   }
172   if (e.window == _desk) {
173     _desk_press = true;
174     renderDesk();
175   }
176   if (e.window == _iconify) {
177     _iconify_press = true;
178     renderIconify();
179   }
180   if (e.window == _icon) {
181     _icon_press = true;
182     renderIcon();
183   }
184 }
185
186 void Frame::buttonReleaseHandler(const XButtonEvent &e)
187 {
188   if (e.button != _press_button) return;
189   _press_button = 0;
190
191   if (e.window == _max) {
192     _max_press = false;
193     renderMax();
194   }
195   if (e.window == _close) {
196     _close_press = false;
197     renderClose();
198   }
199   if (e.window == _desk) {
200     _desk_press = false;
201     renderDesk();
202   }
203   if (e.window == _iconify) {
204     _iconify_press = false;
205     renderIconify();
206   }
207   if (e.window == _icon) {
208     _icon_press = false;
209     renderIcon();
210   }
211 }
212
213 MouseContext::MC Frame::mouseContext(Window win) const
214 {
215   if (win == _frame)  return MouseContext::Frame;
216   if (win == _title ||
217       win == _label)  return MouseContext::Titlebar;
218   if (win == _handle) return MouseContext::Handle;
219   if (win == _plate)  return MouseContext::Window;
220   if (win == _lgrip ||
221       win == _rgrip)  return MouseContext::Grip;
222   if (win == _max)    return MouseContext::MaximizeButton;
223   if (win == _close)  return MouseContext::CloseButton;
224   if (win == _desk)   return MouseContext::AllDesktopsButton;
225   if (win == _iconify)return MouseContext::IconifyButton;
226   if (win == _icon)   return MouseContext::IconButton;
227   return (MouseContext::MC) -1;
228 }
229
230 Window *Frame::allWindows() const
231 {
232   Window *w = new Window[12 + 1];
233   unsigned int i = 0;
234   w[i++] = _frame;
235   w[i++] = _plate;
236   w[i++] = _title;
237   w[i++] = _label;
238   w[i++] = _handle;
239   w[i++] = _lgrip;
240   w[i++] = _rgrip;
241   w[i++] = _max;
242   w[i++] = _desk;
243   w[i++] = _close;
244   w[i++] = _icon;
245   w[i++] = _iconify;
246   w[i] = 0;
247   return w;
248 }
249
250 void Frame::applyStyle(const otk::RenderStyle &style)
251 {
252   // set static border colors
253   XSetWindowBorder(**otk::display, _frame, style.frameBorderColor()->pixel());
254   XSetWindowBorder(**otk::display, _title, style.frameBorderColor()->pixel());
255   XSetWindowBorder(**otk::display, _handle, style.frameBorderColor()->pixel());
256   XSetWindowBorder(**otk::display, _lgrip, style.frameBorderColor()->pixel());
257   XSetWindowBorder(**otk::display, _rgrip, style.frameBorderColor()->pixel());
258
259   // size all the fixed-size elements
260   geom.font_height = style.labelFont()->height();
261   if (geom.font_height < 1) geom.font_height = 1;
262   geom.button_size = geom.font_height - 2;
263   if (geom.button_size < 1) geom.button_size = 1;
264   geom.handle_height = style.handleWidth();
265   if (geom.handle_height < 1) geom.handle_height = 1;
266   geom.bevel = style.bevelWidth();
267   
268   XResizeWindow(**otk::display, _lgrip, geom.grip_width(), geom.handle_height);
269   XResizeWindow(**otk::display, _rgrip, geom.grip_width(), geom.handle_height);
270   
271   XResizeWindow(**otk::display, _max, geom.button_size, geom.button_size);
272   XResizeWindow(**otk::display, _close, geom.button_size, geom.button_size);
273   XResizeWindow(**otk::display, _desk, geom.button_size, geom.button_size);
274   XResizeWindow(**otk::display, _iconify, geom.button_size, geom.button_size);
275   XResizeWindow(**otk::display, _icon, geom.button_size, geom.button_size);
276 }
277
278 void Frame::styleChanged(const otk::RenderStyle &style)
279 {
280   applyStyle(style);
281   
282   // size/position everything
283   adjustSize();
284   adjustPosition();
285 }
286
287 void Frame::adjustFocus()
288 {
289   // XXX optimizations later...
290   adjustSize();
291 }
292
293 void Frame::adjustTitle()
294 {
295   // XXX optimizations later...
296   adjustSize();
297 }
298
299 static void render(int screen, const otk::Size &size, Window win,
300                    otk::Surface **surface,
301                    const otk::RenderTexture &texture, bool freedata=true)
302 {
303   otk::Surface *s = new otk::Surface(screen, size);
304   if (texture.parentRelative())
305     XSetWindowBackgroundPixmap(**otk::display, win, ParentRelative);
306   else {
307     otk::display->renderControl(screen)->drawBackground(*s, texture);
308     XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
309   }
310   XClearWindow(**otk::display, win);
311   if (*surface) delete *surface;
312   if (freedata) s->freePixelData();
313   *surface = s;
314 }
315
316 void Frame::adjustSize()
317 {
318   _decorations = _client->decorations();
319   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
320
321   if (_decorations & Client::Decor_Border) {
322     geom.bwidth = style->frameBorderWidth();
323     geom.cbwidth = style->clientBorderWidth();
324   } else {
325     geom.bwidth = geom.cbwidth = 0;
326   }
327   _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
328     geom.cbwidth;
329   geom.width = _client->area().width() + geom.cbwidth * 2;
330   assert(geom.width > 0);
331
332   // set border widths
333   XSetWindowBorderWidth(**otk::display, _plate, geom.cbwidth);
334   XSetWindowBorderWidth(**otk::display, _frame, geom.bwidth);
335   XSetWindowBorderWidth(**otk::display, _title, geom.bwidth);
336   XSetWindowBorderWidth(**otk::display, _handle, geom.bwidth);
337   XSetWindowBorderWidth(**otk::display, _lgrip, geom.bwidth);
338   XSetWindowBorderWidth(**otk::display, _rgrip, geom.bwidth);
339   
340   // position/size and map/unmap all the windows
341
342   if (_decorations & Client::Decor_Titlebar) {
343     XMoveResizeWindow(**otk::display, _title, -geom.bwidth, -geom.bwidth,
344                       geom.width, geom.title_height());
345     _innersize.top += geom.title_height() + geom.bwidth;
346     XMapWindow(**otk::display, _title);
347
348     // layout the title bar elements
349     layoutTitle();
350   } else {
351     XUnmapWindow(**otk::display, _title);
352     // make all the titlebar stuff not render
353     _decorations &= ~(Client::Decor_Icon | Client::Decor_Iconify |
354                       Client::Decor_Maximize | Client::Decor_Close |
355                       Client::Decor_AllDesktops);
356   }
357
358   if (_decorations & Client::Decor_Handle) {
359     geom.handle_y = _innersize.top + _client->area().height() + geom.cbwidth;
360     XMoveResizeWindow(**otk::display, _handle, -geom.bwidth, geom.handle_y,
361                       geom.width, geom.handle_height);
362     XMoveWindow(**otk::display, _lgrip, -geom.bwidth, -geom.bwidth);
363     XMoveWindow(**otk::display, _rgrip,
364                 -geom.bwidth + geom.width - geom.grip_width(),
365                 -geom.bwidth);
366     _innersize.bottom += geom.handle_height + geom.bwidth;
367     XMapWindow(**otk::display, _handle);
368   } else
369     XUnmapWindow(**otk::display, _handle);
370   
371   XResizeWindow(**otk::display, _frame, geom.width,
372                 (_client->shaded() ? geom.title_height() :
373                  _innersize.top + _innersize.bottom +
374                  _client->area().height()));
375
376   // do this in two steps because clients whose gravity is set to
377   // 'Static' don't end up getting moved at all with an XMoveResizeWindow
378   XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
379               _innersize.top - geom.cbwidth);
380   XResizeWindow(**otk::display, _plate, _client->area().width(),
381                 _client->area().height());
382
383   _size.left   = _innersize.left + geom.bwidth;
384   _size.right  = _innersize.right + geom.bwidth;
385   _size.top    = _innersize.top + geom.bwidth;
386   _size.bottom = _innersize.bottom + geom.bwidth;
387
388   _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
389                                                 _size.left + _size.right,
390                                                 _client->area().height() +
391                                                 _size.top + _size.bottom));
392
393   // render all the elements
394   int screen = _client->screen();
395   bool focus = _client->focused();
396   if (_decorations & Client::Decor_Titlebar) {
397     render(screen, otk::Size(geom.width, geom.title_height()), _title,
398            &_title_sur, *(focus ? style->titlebarFocusBackground() :
399                           style->titlebarUnfocusBackground()), false);
400     
401     renderLabel();
402     renderMax();
403     renderDesk();
404     renderIconify();
405     renderIcon();
406     renderClose();
407   }
408
409   if (_decorations & Client::Decor_Handle) {
410     render(screen, otk::Size(geom.width, geom.handle_height), _handle,
411            &_handle_sur, *(focus ? style->handleFocusBackground() :
412                            style->handleUnfocusBackground()));
413     render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
414            &_grip_sur, *(focus ? style->gripFocusBackground() :
415                          style->gripUnfocusBackground()));
416     if ((focus ? style->gripFocusBackground() :
417          style->gripUnfocusBackground())->parentRelative())
418       XSetWindowBackgroundPixmap(**otk::display, _rgrip, ParentRelative);
419     else {
420       XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
421     }
422     XClearWindow(**otk::display, _rgrip);
423   }
424
425   XSetWindowBorder(**otk::display, _plate,
426                    focus ? style->clientBorderFocusColor()->pixel() :
427                    style->clientBorderUnfocusColor()->pixel());
428   
429   adjustShape();
430 }
431
432 void Frame::renderLabel()
433 {
434   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
435   const otk::RenderControl *control =
436     otk::display->renderControl(_client->screen());
437   const otk::Font *font = style->labelFont();
438
439   otk::Surface *s = new otk::Surface(_client->screen(),
440                                      otk::Size(geom.label_width,
441                                                geom.label_height()));
442   const otk::RenderTexture *tx = (_client->focused() ?
443                                   style->labelFocusBackground() :
444                                   style->labelUnfocusBackground());
445   if (tx->parentRelative()) {
446     otk::pixel32 *dest = s->pixelData(), *src;
447     int w = _title_sur->size().width();
448   
449     src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.title_x;
450     
451     // get the background under the label
452     for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
453       for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
454         *dest = *src;
455     control->drawImage(*s, 0, 0, 0); // no image but draw the new background
456   } else
457     control->drawBackground(*s, *tx);
458   control->drawBackground(*s, *tx);
459
460   otk::ustring t = _client->title(); // the actual text to draw
461   int x = geom.bevel;                // x coord for the text
462
463   if (x * 2 < geom.label_width) {
464     // find a string that will fit inside the area for text
465     otk::ustring::size_type text_len = t.size();
466     int length;
467     int maxsize = geom.label_width - geom.bevel * 2;
468       
469     do {
470       t.resize(text_len);
471       length = font->measureString(t);// this returns an unsigned, so check < 0
472       if (length < 0) length = maxsize;// if the string's that long just adjust
473     } while (length > maxsize && text_len-- > 0);
474
475     // justify the text
476     switch (style->labelTextJustify()) {
477     case otk::RenderStyle::RightBottomJustify:
478       x += maxsize - length;
479       break;
480     case otk::RenderStyle::CenterJustify:
481       x += (maxsize - length) / 2;
482       break;
483     case otk::RenderStyle::LeftTopJustify:
484       break;
485     }
486  
487     if (text_len > 0)
488       control->drawString(*s, *font, x, 0,
489                           *(_client->focused() ? style->textFocusColor() :
490                             style->textUnfocusColor()), t);
491   }
492
493   XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
494   XClearWindow(**otk::display, _label);
495   if (_label_sur) delete _label_sur;
496   s->freePixelData();
497   _label_sur = s;
498 }
499
500 static void renderButton(int screen, bool focus, bool press, Window win,
501                          otk::Surface **sur, int butsize,
502                          const otk::PixmapMask *mask, int xoffset, int yoffset,
503                          otk::Surface *bgsurface)
504 {
505   const otk::RenderStyle *style = otk::RenderStyle::style(screen);
506   const otk::RenderControl *control = otk::display->renderControl(screen);
507   otk::Surface *s = new otk::Surface(screen, otk::Size(butsize, butsize));
508
509   const otk::RenderTexture *tx = (focus ?
510                                   (press ?
511                                    style->buttonPressFocusBackground() :
512                                    style->buttonUnpressFocusBackground()) :
513                                   (press ?
514                                    style->buttonPressUnfocusBackground() :
515                                    style->buttonUnpressUnfocusBackground()));
516   const otk::RenderColor *maskcolor = (focus ?
517                                        style->buttonFocusColor() :
518                                        style->buttonUnfocusColor());
519   if (tx->parentRelative()) {
520     otk::pixel32 *dest = s->pixelData(), *src;
521     int w = bgsurface->size().width();
522   
523     src = bgsurface->pixelData() + w * yoffset + xoffset;
524     
525     // get the background under the button
526     for (int y = 0; y < butsize; ++y, src += w - butsize)
527       for (int x = 0; x < butsize; ++x, ++dest, ++src)
528         *dest = *src;
529     control->drawImage(*s, 0, 0, 0); // no image but draw the new background
530   } else
531     control->drawBackground(*s, *tx);
532   control->drawMask(*s, *maskcolor, *mask);
533
534   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
535   XClearWindow(**otk::display, win);
536   if (*sur) delete *sur;
537   s->freePixelData();
538   *sur = s;
539 }
540
541 void Frame::renderMax()
542 {
543   if (!(_decorations & Client::Decor_Maximize)) return;
544   bool press = _max_press || _client->maxVert() || _client->maxHorz();
545   renderButton(_client->screen(), _client->focused(), press, _max,
546                &_max_sur, geom.button_size,
547                otk::RenderStyle::style(_client->screen())->maximizeMask(),
548                geom.max_x, (geom.bevel + 1), _title_sur);
549 }
550
551 void Frame::renderDesk()
552 {
553   if (!(_decorations & Client::Decor_AllDesktops)) return;
554   bool press = _desk_press || _client->desktop() == 0xffffffff;
555   renderButton(_client->screen(), _client->focused(), press, _desk,
556                &_desk_sur, geom.button_size,
557                otk::RenderStyle::style(_client->screen())->alldesktopsMask(),
558                geom.desktop_x, (geom.bevel + 1), _title_sur);
559 }
560
561 void Frame::renderIconify()
562 {
563   if (!(_decorations & Client::Decor_Iconify)) return;
564   renderButton(_client->screen(), _client->focused(), _iconify_press, _iconify,
565                &_iconify_sur, geom.button_size,
566                otk::RenderStyle::style(_client->screen())->iconifyMask(),
567                geom.iconify_x, (geom.bevel + 1), _title_sur);
568 }
569
570 void Frame::renderClose()
571 {
572   if (!(_decorations & Client::Decor_Close)) return;
573   renderButton(_client->screen(), _client->focused(), _close_press, _close,
574                &_close_sur, geom.button_size,
575                otk::RenderStyle::style(_client->screen())->closeMask(),
576                geom.close_x, (geom.bevel + 1), _title_sur);
577 }
578
579 void Frame::renderIcon()
580 {
581   if (!(_decorations & Client::Decor_Icon)) return;
582   const int screen = _client->screen();
583   const otk::RenderControl *control = otk::display->renderControl(screen);
584
585   otk::Surface *s = new otk::Surface(screen, otk::Size(geom.button_size,
586                                                        geom.button_size));
587   otk::pixel32 *dest = s->pixelData(), *src;
588   int w = _title_sur->size().width();
589   
590   src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.icon_x;
591   
592   // get the background under the icon button
593   for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
594     for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
595       *dest = *src;
596   // draw the icon over it
597   const Icon *icon = _client->icon(otk::Size(geom.button_size,
598                                              geom.button_size));
599   control->drawImage(*s, icon->w, icon->h, icon->data);
600   if (!icon->data) {
601     Pixmap p = _client->pixmapIcon(), m = _client->pixmapIconMask();
602     if (p != None)
603       control->drawImage(*s, p, m);
604   }
605
606   XSetWindowBackgroundPixmap(**otk::display, _icon, s->pixmap());
607   XClearWindow(**otk::display, _icon);
608   if (_icon_sur) delete _icon_sur;
609   _icon_sur = s;
610 }
611
612 void Frame::layoutTitle()
613 {
614   // figure out whats being shown, and the width of the label
615   geom.label_width = geom.width - geom.bevel * 2;
616   bool n, d, i, t, m ,c;
617   n = d = i = t = m = c = false;
618   for (const char *l = _layout.c_str(); *l; ++l) {
619     switch (*l) {
620     case 'n':
621     case 'N':
622       if (!(_decorations & Client::Decor_Icon)) break;
623       n = true;
624       geom.label_width -= geom.button_size + geom.bevel;
625       break;
626     case 'd':
627     case 'D':
628       if (!(_decorations & Client::Decor_AllDesktops)) break;
629       d = true;
630       geom.label_width -= geom.button_size + geom.bevel;
631       break;
632     case 'i':
633     case 'I':
634       if (!(_decorations & Client::Decor_Iconify)) break;
635       i = true;
636       geom.label_width -= geom.button_size + geom.bevel;
637       break;
638     case 't':
639     case 'T':
640       t = true;
641       break;
642     case 'm':
643     case 'M':
644       if (!(_decorations & Client::Decor_Maximize)) break;
645       m = true;
646       geom.label_width -= geom.button_size + geom.bevel;
647       break;
648     case 'c':
649     case 'C':
650       if (!(_decorations & Client::Decor_Close)) break;
651       c = true;
652       geom.label_width -= geom.button_size + geom.bevel;
653       break;
654     }
655   }
656   if (geom.label_width < 1) geom.label_width = 1;
657
658   XResizeWindow(**otk::display, _label, geom.label_width, geom.font_height);
659   
660   if (!n) {
661     _decorations &= ~Client::Decor_Icon;
662     XUnmapWindow(**otk::display, _icon);
663   }
664   if (!d) {
665     _decorations &= ~Client::Decor_AllDesktops;
666     XUnmapWindow(**otk::display, _desk);
667   }
668   if (!i) {
669     _decorations &= ~Client::Decor_Iconify;
670     XUnmapWindow(**otk::display, _iconify);
671   }
672   if (!t)
673     XUnmapWindow(**otk::display, _label);
674   if (!m) {
675     _decorations &= ~Client::Decor_Maximize;
676     XUnmapWindow(**otk::display, _max);
677   }
678   if (!c) {
679     _decorations &= ~Client::Decor_Close;
680     XUnmapWindow(**otk::display, _close);
681   }
682
683   int x = geom.bevel;
684   for (const char *lc = _layout.c_str(); *lc; ++lc) {
685     switch (*lc) {
686     case 'n':
687     case 'N':
688       if (!n) break;
689       geom.icon_x = x;
690       XMapWindow(**otk::display, _icon);
691       XMoveWindow(**otk::display, _icon, x, geom.bevel + 1);
692       x += geom.button_size + geom.bevel;
693       break;
694     case 'd':
695     case 'D':
696       if (!d) break;
697       geom.desktop_x = x;
698       XMapWindow(**otk::display, _desk);
699       XMoveWindow(**otk::display, _desk, x, geom.bevel + 1);
700       x += geom.button_size + geom.bevel;
701       break;
702     case 'i':
703     case 'I':
704       if (!i) break;
705       geom.iconify_x = x;
706       XMapWindow(**otk::display, _iconify);
707       XMoveWindow(**otk::display, _iconify, x, geom.bevel + 1);
708       x += geom.button_size + geom.bevel;
709       break;
710     case 't':
711     case 'T':
712       if (!t) break;
713       geom.title_x = x;
714       XMapWindow(**otk::display, _label);
715       XMoveWindow(**otk::display, _label, x, geom.bevel);
716       x += geom.label_width + geom.bevel;
717       break;
718     case 'm':
719     case 'M':
720       if (!m) break;
721       geom.max_x = x;
722       XMapWindow(**otk::display, _max);
723       XMoveWindow(**otk::display, _max, x, geom.bevel + 1);
724       x += geom.button_size + geom.bevel;
725       break;
726     case 'c':
727     case 'C':
728       if (!c) break;
729       geom.close_x = x;
730       XMapWindow(**otk::display, _close);
731       XMoveWindow(**otk::display, _close, x, geom.bevel + 1);
732       x += geom.button_size + geom.bevel;
733       break;
734     }
735   }
736 }
737
738 void Frame::adjustPosition()
739 {
740   int x, y;
741   x = _client->area().x();
742   y = _client->area().y();
743   clientGravity(x, y);
744   XMoveWindow(**otk::display, _frame, x, y);
745   _area = otk::Rect(otk::Point(x, y), _area.size());
746 }
747
748
749 void Frame::adjustShape()
750 {
751 #ifdef SHAPE
752   if (!_client->shaped()) {
753     // clear the shape on the frame window
754     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
755                       _innersize.left,
756                       _innersize.top,
757                       None, ShapeSet);
758   } else {
759     // make the frame's shape match the clients
760     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
761                        _innersize.left,
762                        _innersize.top,
763                        _client->window(), ShapeBounding, ShapeSet);
764
765     int num = 0;
766     XRectangle xrect[2];
767
768     if (_decorations & Client::Decor_Titlebar) {
769       xrect[0].x = -geom.bevel;
770       xrect[0].y = -geom.bevel;
771       xrect[0].width = geom.width + geom.bwidth * 2;
772       xrect[0].height = geom.title_height() + geom.bwidth * 2;
773       ++num;
774     }
775
776     if (_decorations & Client::Decor_Handle) {
777       xrect[1].x = -geom.bevel;
778       xrect[1].y = geom.handle_y;
779       xrect[1].width = geom.width + geom.bwidth * 2;
780       xrect[1].height = geom.handle_height + geom.bwidth * 2;
781       ++num;
782     }
783
784     XShapeCombineRectangles(**otk::display, _frame,
785                             ShapeBounding, 0, 0, xrect, num,
786                             ShapeUnion, Unsorted);
787   }
788 #endif // SHAPE
789 }
790
791 void Frame::adjustState()
792 {
793   renderDesk();
794   renderMax();
795 }
796
797 void Frame::adjustIcon()
798 {
799   renderIcon();
800 }
801
802 void Frame::grabClient()
803 {
804   // reparent the client to the frame
805   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
806   /*
807     When reparenting the client window, it is usually not mapped yet, since
808     this occurs from a MapRequest. However, in the case where Openbox is
809     starting up, the window is already mapped, so we'll see unmap events for
810     it. There are 2 unmap events generated that we see, one with the 'event'
811     member set the root window, and one set to the client, but both get handled
812     and need to be ignored.
813   */
814   if (openbox->state() == Openbox::State_Starting)
815     _client->ignore_unmaps += 2;
816
817   // select the event mask on the client's parent (to receive config/map req's)
818   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
819
820   // map the client so it maps when the frame does
821   XMapWindow(**otk::display, _client->window());
822
823   adjustSize();
824   adjustPosition();
825 }
826
827
828 void Frame::releaseClient()
829 {
830   XEvent ev;
831
832   // check if the app has already reparented its window away
833   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
834                              ReparentNotify, &ev)) {
835     XPutBackEvent(**otk::display, &ev);
836     // re-map the window since the unmanaging process unmaps it
837     XMapWindow(**otk::display, _client->window());  
838   } else {
839     // according to the ICCCM - if the client doesn't reparent itself, then we
840     // will reparent the window to root for them
841     XReparentWindow(**otk::display, _client->window(),
842                     otk::display->screenInfo(_client->screen())->rootWindow(),
843                     _client->area().x(), _client->area().y());
844   }
845 }
846
847
848 void Frame::clientGravity(int &x, int &y)
849 {
850   // horizontal
851   switch (_client->gravity()) {
852   default:
853   case NorthWestGravity:
854   case SouthWestGravity:
855   case WestGravity:
856     break;
857
858   case NorthGravity:
859   case SouthGravity:
860   case CenterGravity:
861     x -= (_size.left + _size.right) / 2;
862     break;
863
864   case NorthEastGravity:
865   case SouthEastGravity:
866   case EastGravity:
867     x -= _size.left + _size.right;
868     break;
869
870   case ForgetGravity:
871   case StaticGravity:
872     x -= _size.left;
873     break;
874   }
875
876   // vertical
877   switch (_client->gravity()) {
878   default:
879   case NorthWestGravity:
880   case NorthEastGravity:
881   case NorthGravity:
882     break;
883
884   case CenterGravity:
885   case EastGravity:
886   case WestGravity:
887     y -= (_size.top + _size.bottom) / 2;
888     break;
889
890   case SouthWestGravity:
891   case SouthEastGravity:
892   case SouthGravity:
893     y -= _size.top + _size.bottom;
894     break;
895
896   case ForgetGravity:
897   case StaticGravity:
898     y -= _size.top;
899     break;
900   }
901 }
902
903
904 void Frame::frameGravity(int &x, int &y)
905 {
906   // horizontal
907   switch (_client->gravity()) {
908   default:
909   case NorthWestGravity:
910   case WestGravity:
911   case SouthWestGravity:
912     break;
913   case NorthGravity:
914   case CenterGravity:
915   case SouthGravity:
916     x += (_size.left + _size.right) / 2;
917     break;
918   case NorthEastGravity:
919   case EastGravity:
920   case SouthEastGravity:
921     x += _size.left + _size.right;
922     break;
923   case StaticGravity:
924   case ForgetGravity:
925     x += _size.left;
926     break;
927   }
928
929   // vertical
930   switch (_client->gravity()) {
931   default:
932   case NorthWestGravity:
933   case WestGravity:
934   case SouthWestGravity:
935     break;
936   case NorthGravity:
937   case CenterGravity:
938   case SouthGravity:
939     y += (_size.top + _size.bottom) / 2;
940     break;
941   case NorthEastGravity:
942   case EastGravity:
943   case SouthEastGravity:
944     y += _size.top + _size.bottom;
945     break;
946   case StaticGravity:
947   case ForgetGravity:
948     y += _size.top;
949     break;
950   }
951 }
952
953
954 }