Add the "obsetroot" tool. Use it to set the root background.
[dana/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   otk::display->renderControl(screen)->drawBackground(*s, texture);
305   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
306   XClearWindow(**otk::display, win);
307   if (*surface) delete *surface;
308   if (freedata) s->freePixelData();
309   *surface = s;
310 }
311
312 void Frame::adjustSize()
313 {
314   _decorations = _client->decorations();
315   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
316
317   if (_decorations & Client::Decor_Border) {
318     geom.bwidth = style->frameBorderWidth();
319     geom.cbwidth = style->clientBorderWidth();
320   } else {
321     geom.bwidth = geom.cbwidth = 0;
322   }
323   _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
324     geom.cbwidth;
325   geom.width = _client->area().width() + geom.cbwidth * 2;
326   assert(geom.width > 0);
327
328   // set border widths
329   XSetWindowBorderWidth(**otk::display, _plate, geom.cbwidth);
330   XSetWindowBorderWidth(**otk::display, _frame, geom.bwidth);
331   XSetWindowBorderWidth(**otk::display, _title, geom.bwidth);
332   XSetWindowBorderWidth(**otk::display, _handle, geom.bwidth);
333   XSetWindowBorderWidth(**otk::display, _lgrip, geom.bwidth);
334   XSetWindowBorderWidth(**otk::display, _rgrip, geom.bwidth);
335   
336   // position/size and map/unmap all the windows
337
338   if (_decorations & Client::Decor_Titlebar) {
339     XMoveResizeWindow(**otk::display, _title, -geom.bwidth, -geom.bwidth,
340                       geom.width, geom.title_height());
341     _innersize.top += geom.title_height() + geom.bwidth;
342     XMapWindow(**otk::display, _title);
343
344     // layout the title bar elements
345     layoutTitle();
346   } else {
347     XUnmapWindow(**otk::display, _title);
348     // make all the titlebar stuff not render
349     _decorations &= ~(Client::Decor_Icon | Client::Decor_Iconify |
350                       Client::Decor_Maximize | Client::Decor_Close |
351                       Client::Decor_AllDesktops);
352   }
353
354   if (_decorations & Client::Decor_Handle) {
355     geom.handle_y = _innersize.top + _client->area().height() + geom.cbwidth;
356     XMoveResizeWindow(**otk::display, _handle, -geom.bwidth, geom.handle_y,
357                       geom.width, geom.handle_height);
358     XMoveWindow(**otk::display, _lgrip, -geom.bwidth, -geom.bwidth);
359     XMoveWindow(**otk::display, _rgrip,
360                 -geom.bwidth + geom.width - geom.grip_width(),
361                 -geom.bwidth);
362     _innersize.bottom += geom.handle_height + geom.bwidth;
363     XMapWindow(**otk::display, _handle);
364   } else
365     XUnmapWindow(**otk::display, _handle);
366   
367   XResizeWindow(**otk::display, _frame, geom.width,
368                 (_client->shaded() ? geom.title_height() :
369                  _innersize.top + _innersize.bottom +
370                  _client->area().height()));
371
372   // do this in two steps because clients whose gravity is set to
373   // 'Static' don't end up getting moved at all with an XMoveResizeWindow
374   XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
375               _innersize.top - geom.cbwidth);
376   XResizeWindow(**otk::display, _plate, _client->area().width(),
377                 _client->area().height());
378
379   _size.left   = _innersize.left + geom.bwidth;
380   _size.right  = _innersize.right + geom.bwidth;
381   _size.top    = _innersize.top + geom.bwidth;
382   _size.bottom = _innersize.bottom + geom.bwidth;
383
384   _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
385                                                 _size.left + _size.right,
386                                                 _client->area().height() +
387                                                 _size.top + _size.bottom));
388
389   // render all the elements
390   int screen = _client->screen();
391   bool focus = _client->focused();
392   if (_decorations & Client::Decor_Titlebar) {
393     render(screen, otk::Size(geom.width, geom.title_height()), _title,
394            &_title_sur, *(focus ? style->titlebarFocusBackground() :
395                           style->titlebarUnfocusBackground()), false);
396     
397     renderLabel();
398     renderMax();
399     renderDesk();
400     renderIconify();
401     renderIcon();
402     renderClose();
403   }
404
405   if (_decorations & Client::Decor_Handle) {
406     render(screen, otk::Size(geom.width, geom.handle_height), _handle,
407            &_handle_sur, *(focus ? style->handleFocusBackground() :
408                            style->handleUnfocusBackground()));
409     render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
410            &_grip_sur, *(focus ? style->gripFocusBackground() :
411                          style->gripUnfocusBackground()));
412     XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
413     XClearWindow(**otk::display, _rgrip);
414   }
415
416   XSetWindowBorder(**otk::display, _plate,
417                    focus ? style->clientBorderFocusColor()->pixel() :
418                    style->clientBorderUnfocusColor()->pixel());
419   
420   adjustShape();
421 }
422
423 void Frame::renderLabel()
424 {
425   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
426   const otk::RenderControl *control =
427     otk::display->renderControl(_client->screen());
428   const otk::Font *font = style->labelFont();
429
430   otk::Surface *s = new otk::Surface(_client->screen(),
431                                      otk::Size(geom.label_width,
432                                                geom.label_height()));
433   control->drawBackground(*s, *(_client->focused() ?
434                                 style->labelFocusBackground() :
435                                 style->labelUnfocusBackground()));
436
437   otk::ustring t = _client->title(); // the actual text to draw
438   int x = geom.bevel;                // x coord for the text
439
440   if (x * 2 > geom.label_width) return; // no room at all
441
442   // find a string that will fit inside the area for text
443   otk::ustring::size_type text_len = t.size();
444   int length;
445   int maxsize = geom.label_width - geom.bevel * 2;
446       
447   do {
448     t.resize(text_len);
449     length = font->measureString(t);  // this returns an unsigned, so check < 0
450     if (length < 0) length = maxsize; // if the string's that long just adjust
451   } while (length > maxsize && text_len-- > 0);
452
453   if (text_len <= 0) return; // won't fit anything
454
455   // justify the text
456   switch (style->labelTextJustify()) {
457   case otk::RenderStyle::RightBottomJustify:
458     x += maxsize - length;
459     break;
460   case otk::RenderStyle::CenterJustify:
461     x += (maxsize - length) / 2;
462     break;
463   case otk::RenderStyle::LeftTopJustify:
464     break;
465   }
466  
467   control->drawString(*s, *font, x, 0,
468                       *(_client->focused() ? style->textFocusColor() :
469                         style->textUnfocusColor()), t);
470
471   XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
472   XClearWindow(**otk::display, _label);
473   if (_label_sur) delete _label_sur;
474   s->freePixelData();
475   _label_sur = s;
476 }
477
478 static void renderButton(int screen, bool focus, bool press, Window win,
479                          otk::Surface **sur, int butsize,
480                          const otk::PixmapMask *mask)
481 {
482   const otk::RenderStyle *style = otk::RenderStyle::style(screen);
483   const otk::RenderControl *control = otk::display->renderControl(screen);
484   otk::Surface *s = new otk::Surface(screen, otk::Size(butsize, butsize));
485
486   const otk::RenderTexture *tx = (focus ?
487                                   (press ?
488                                    style->buttonPressFocusBackground() :
489                                    style->buttonUnpressFocusBackground()) :
490                                   (press ?
491                                    style->buttonPressUnfocusBackground() :
492                                    style->buttonUnpressUnfocusBackground()));
493   const otk::RenderColor *maskcolor = (focus ?
494                                        style->buttonFocusColor() :
495                                        style->buttonUnfocusColor());
496   control->drawBackground(*s, *tx);
497   control->drawMask(*s, *maskcolor, *mask);
498
499   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
500   XClearWindow(**otk::display, win);
501   if (*sur) delete *sur;
502   s->freePixelData();
503   *sur = s;
504 }
505
506 void Frame::renderMax()
507 {
508   if (!(_decorations & Client::Decor_Maximize)) return;
509   bool press = _max_press || _client->maxVert() || _client->maxHorz();
510   renderButton(_client->screen(), _client->focused(), press, _max,
511                &_max_sur, geom.button_size,
512                otk::RenderStyle::style(_client->screen())->maximizeMask());
513 }
514
515 void Frame::renderDesk()
516 {
517   if (!(_decorations & Client::Decor_AllDesktops)) return;
518   bool press = _desk_press || _client->desktop() == 0xffffffff;
519   renderButton(_client->screen(), _client->focused(), press, _desk,
520                &_desk_sur, geom.button_size,
521                otk::RenderStyle::style(_client->screen())->alldesktopsMask());
522 }
523
524 void Frame::renderIconify()
525 {
526   if (!(_decorations & Client::Decor_Iconify)) return;
527   renderButton(_client->screen(), _client->focused(), _iconify_press, _iconify,
528                &_iconify_sur, geom.button_size,
529                otk::RenderStyle::style(_client->screen())->iconifyMask());
530 }
531
532 void Frame::renderClose()
533 {
534   if (!(_decorations & Client::Decor_Close)) return;
535   renderButton(_client->screen(), _client->focused(), _close_press, _close,
536                &_close_sur, geom.button_size,
537                otk::RenderStyle::style(_client->screen())->closeMask());
538 }
539
540 void Frame::renderIcon()
541 {
542   if (!(_decorations & Client::Decor_Icon)) return;
543   const int screen = _client->screen();
544   const otk::RenderControl *control = otk::display->renderControl(screen);
545
546   otk::Surface *s = new otk::Surface(screen, otk::Size(geom.button_size,
547                                                        geom.button_size));
548   otk::pixel32 *dest = s->pixelData(), *src;
549   int w = _title_sur->size().width();
550   
551   src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.icon_x;
552   
553   // get the background under the icon button
554   for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
555     for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
556       *dest = *src;
557   // draw the icon over it
558   const Icon *icon = _client->icon(otk::Size(geom.button_size,
559                                              geom.button_size));
560   control->drawImage(*s, icon->w, icon->h, icon->data);
561
562   XSetWindowBackgroundPixmap(**otk::display, _icon, s->pixmap());
563   XClearWindow(**otk::display, _icon);
564   if (_icon_sur) delete _icon_sur;
565   _icon_sur = s;
566 }
567
568 void Frame::layoutTitle()
569 {
570   // figure out whats being shown, and the width of the label
571   geom.label_width = geom.width - geom.bevel * 2;
572   bool n, d, i, t, m ,c;
573   n = d = i = t = m = c = false;
574   for (const char *l = _layout.c_str(); *l; ++l) {
575     switch (*l) {
576     case 'n':
577     case 'N':
578       if (!(_decorations & Client::Decor_Icon)) break;
579       n = true;
580       geom.label_width -= geom.button_size + geom.bevel;
581       break;
582     case 'd':
583     case 'D':
584       if (!(_decorations & Client::Decor_AllDesktops)) break;
585       d = true;
586       geom.label_width -= geom.button_size + geom.bevel;
587       break;
588     case 'i':
589     case 'I':
590       if (!(_decorations & Client::Decor_Iconify)) break;
591       i = true;
592       geom.label_width -= geom.button_size + geom.bevel;
593       break;
594     case 't':
595     case 'T':
596       t = true;
597       break;
598     case 'm':
599     case 'M':
600       if (!(_decorations & Client::Decor_Maximize)) break;
601       m = true;
602       geom.label_width -= geom.button_size + geom.bevel;
603       break;
604     case 'c':
605     case 'C':
606       if (!(_decorations & Client::Decor_Close)) break;
607       c = true;
608       geom.label_width -= geom.button_size + geom.bevel;
609       break;
610     }
611   }
612   if (geom.label_width < 1) geom.label_width = 1;
613
614   XResizeWindow(**otk::display, _label, geom.label_width, geom.font_height);
615   
616   if (!n) {
617     _decorations &= ~Client::Decor_Icon;
618     XUnmapWindow(**otk::display, _icon);
619   }
620   if (!d) {
621     _decorations &= ~Client::Decor_AllDesktops;
622     XUnmapWindow(**otk::display, _desk);
623   }
624   if (!i) {
625     _decorations &= ~Client::Decor_Iconify;
626     XUnmapWindow(**otk::display, _iconify);
627   }
628   if (!t)
629     XUnmapWindow(**otk::display, _label);
630   if (!m) {
631     _decorations &= ~Client::Decor_Maximize;
632     XUnmapWindow(**otk::display, _max);
633   }
634   if (!c) {
635     _decorations &= ~Client::Decor_Close;
636     XUnmapWindow(**otk::display, _close);
637   }
638
639   int x = geom.bevel;
640   for (const char *lc = _layout.c_str(); *lc; ++lc) {
641     switch (*lc) {
642     case 'n':
643     case 'N':
644       if (!n) break;
645       geom.icon_x = x;
646       XMapWindow(**otk::display, _icon);
647       XMoveWindow(**otk::display, _icon, x, geom.bevel + 1);
648       x += geom.button_size + geom.bevel;
649       break;
650     case 'd':
651     case 'D':
652       if (!d) break;
653       XMapWindow(**otk::display, _desk);
654       XMoveWindow(**otk::display, _desk, x, geom.bevel + 1);
655       x += geom.button_size + geom.bevel;
656       break;
657     case 'i':
658     case 'I':
659       if (!i) break;
660       XMapWindow(**otk::display, _iconify);
661       XMoveWindow(**otk::display, _iconify, x, geom.bevel + 1);
662       x += geom.button_size + geom.bevel;
663       break;
664     case 't':
665     case 'T':
666       if (!t) break;
667       XMapWindow(**otk::display, _label);
668       XMoveWindow(**otk::display, _label, x, geom.bevel);
669       x += geom.label_width + geom.bevel;
670       break;
671     case 'm':
672     case 'M':
673       if (!m) break;
674       XMapWindow(**otk::display, _max);
675       XMoveWindow(**otk::display, _max, x, geom.bevel + 1);
676       x += geom.button_size + geom.bevel;
677       break;
678     case 'c':
679     case 'C':
680       if (!c) break;
681       XMapWindow(**otk::display, _close);
682       XMoveWindow(**otk::display, _close, x, geom.bevel + 1);
683       x += geom.button_size + geom.bevel;
684       break;
685     }
686   }
687 }
688
689 void Frame::adjustPosition()
690 {
691   int x, y;
692   x = _client->area().x();
693   y = _client->area().y();
694   clientGravity(x, y);
695   XMoveWindow(**otk::display, _frame, x, y);
696   _area = otk::Rect(otk::Point(x, y), _area.size());
697 }
698
699
700 void Frame::adjustShape()
701 {
702 #ifdef SHAPE
703   if (!_client->shaped()) {
704     // clear the shape on the frame window
705     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
706                       _innersize.left,
707                       _innersize.top,
708                       None, ShapeSet);
709   } else {
710     // make the frame's shape match the clients
711     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
712                        _innersize.left,
713                        _innersize.top,
714                        _client->window(), ShapeBounding, ShapeSet);
715
716     int num = 0;
717     XRectangle xrect[2];
718
719     if (_decorations & Client::Decor_Titlebar) {
720       xrect[0].x = -geom.bevel;
721       xrect[0].y = -geom.bevel;
722       xrect[0].width = geom.width + geom.bwidth * 2;
723       xrect[0].height = geom.title_height() + geom.bwidth * 2;
724       ++num;
725     }
726
727     if (_decorations & Client::Decor_Handle) {
728       xrect[1].x = -geom.bevel;
729       xrect[1].y = geom.handle_y;
730       xrect[1].width = geom.width + geom.bwidth * 2;
731       xrect[1].height = geom.handle_height + geom.bwidth * 2;
732       ++num;
733     }
734
735     XShapeCombineRectangles(**otk::display, _frame,
736                             ShapeBounding, 0, 0, xrect, num,
737                             ShapeUnion, Unsorted);
738   }
739 #endif // SHAPE
740 }
741
742 void Frame::adjustState()
743 {
744   renderDesk();
745   renderMax();
746 }
747
748 void Frame::adjustIcon()
749 {
750   renderIcon();
751 }
752
753 void Frame::grabClient()
754 {
755   // reparent the client to the frame
756   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
757   /*
758     When reparenting the client window, it is usually not mapped yet, since
759     this occurs from a MapRequest. However, in the case where Openbox is
760     starting up, the window is already mapped, so we'll see unmap events for
761     it. There are 2 unmap events generated that we see, one with the 'event'
762     member set the root window, and one set to the client, but both get handled
763     and need to be ignored.
764   */
765   if (openbox->state() == Openbox::State_Starting)
766     _client->ignore_unmaps += 2;
767
768   // select the event mask on the client's parent (to receive config/map req's)
769   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
770
771   // map the client so it maps when the frame does
772   XMapWindow(**otk::display, _client->window());
773
774   adjustSize();
775   adjustPosition();
776 }
777
778
779 void Frame::releaseClient()
780 {
781   XEvent ev;
782
783   // check if the app has already reparented its window away
784   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
785                              ReparentNotify, &ev)) {
786     XPutBackEvent(**otk::display, &ev);
787     // re-map the window since the unmanaging process unmaps it
788     XMapWindow(**otk::display, _client->window());  
789   } else {
790     // according to the ICCCM - if the client doesn't reparent itself, then we
791     // will reparent the window to root for them
792     XReparentWindow(**otk::display, _client->window(),
793                     otk::display->screenInfo(_client->screen())->rootWindow(),
794                     _client->area().x(), _client->area().y());
795   }
796 }
797
798
799 void Frame::clientGravity(int &x, int &y)
800 {
801   // horizontal
802   switch (_client->gravity()) {
803   default:
804   case NorthWestGravity:
805   case SouthWestGravity:
806   case WestGravity:
807     break;
808
809   case NorthGravity:
810   case SouthGravity:
811   case CenterGravity:
812     x -= (_size.left + _size.right) / 2;
813     break;
814
815   case NorthEastGravity:
816   case SouthEastGravity:
817   case EastGravity:
818     x -= _size.left + _size.right;
819     break;
820
821   case ForgetGravity:
822   case StaticGravity:
823     x -= _size.left;
824     break;
825   }
826
827   // vertical
828   switch (_client->gravity()) {
829   default:
830   case NorthWestGravity:
831   case NorthEastGravity:
832   case NorthGravity:
833     break;
834
835   case CenterGravity:
836   case EastGravity:
837   case WestGravity:
838     y -= (_size.top + _size.bottom) / 2;
839     break;
840
841   case SouthWestGravity:
842   case SouthEastGravity:
843   case SouthGravity:
844     y -= _size.top + _size.bottom;
845     break;
846
847   case ForgetGravity:
848   case StaticGravity:
849     y -= _size.top;
850     break;
851   }
852 }
853
854
855 void Frame::frameGravity(int &x, int &y)
856 {
857   // horizontal
858   switch (_client->gravity()) {
859   default:
860   case NorthWestGravity:
861   case WestGravity:
862   case SouthWestGravity:
863     break;
864   case NorthGravity:
865   case CenterGravity:
866   case SouthGravity:
867     x += (_size.left + _size.right) / 2;
868     break;
869   case NorthEastGravity:
870   case EastGravity:
871   case SouthEastGravity:
872     x += _size.left + _size.right;
873     break;
874   case StaticGravity:
875   case ForgetGravity:
876     x += _size.left;
877     break;
878   }
879
880   // vertical
881   switch (_client->gravity()) {
882   default:
883   case NorthWestGravity:
884   case WestGravity:
885   case SouthWestGravity:
886     break;
887   case NorthGravity:
888   case CenterGravity:
889   case SouthGravity:
890     y += (_size.top + _size.bottom) / 2;
891     break;
892   case NorthEastGravity:
893   case EastGravity:
894   case SouthEastGravity:
895     y += _size.top + _size.bottom;
896     break;
897   case StaticGravity:
898   case ForgetGravity:
899     y += _size.top;
900     break;
901   }
902 }
903
904
905 }