]> icculus.org git repositories - dana/openbox.git/blob - src/frame.cc
support for pixmap icons, kwm_win_icon and the icon in wmhints
[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) {
441     // find a string that will fit inside the area for text
442     otk::ustring::size_type text_len = t.size();
443     int length;
444     int maxsize = geom.label_width - geom.bevel * 2;
445       
446     do {
447       t.resize(text_len);
448       length = font->measureString(t);// this returns an unsigned, so check < 0
449       if (length < 0) length = maxsize;// if the string's that long just adjust
450     } while (length > maxsize && text_len-- > 0);
451
452     // justify the text
453     switch (style->labelTextJustify()) {
454     case otk::RenderStyle::RightBottomJustify:
455       x += maxsize - length;
456       break;
457     case otk::RenderStyle::CenterJustify:
458       x += (maxsize - length) / 2;
459       break;
460     case otk::RenderStyle::LeftTopJustify:
461       break;
462     }
463  
464     if (text_len > 0)
465       control->drawString(*s, *font, x, 0,
466                           *(_client->focused() ? style->textFocusColor() :
467                             style->textUnfocusColor()), t);
468   }
469
470   XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
471   XClearWindow(**otk::display, _label);
472   if (_label_sur) delete _label_sur;
473   s->freePixelData();
474   _label_sur = s;
475 }
476
477 static void renderButton(int screen, bool focus, bool press, Window win,
478                          otk::Surface **sur, int butsize,
479                          const otk::PixmapMask *mask)
480 {
481   const otk::RenderStyle *style = otk::RenderStyle::style(screen);
482   const otk::RenderControl *control = otk::display->renderControl(screen);
483   otk::Surface *s = new otk::Surface(screen, otk::Size(butsize, butsize));
484
485   const otk::RenderTexture *tx = (focus ?
486                                   (press ?
487                                    style->buttonPressFocusBackground() :
488                                    style->buttonUnpressFocusBackground()) :
489                                   (press ?
490                                    style->buttonPressUnfocusBackground() :
491                                    style->buttonUnpressUnfocusBackground()));
492   const otk::RenderColor *maskcolor = (focus ?
493                                        style->buttonFocusColor() :
494                                        style->buttonUnfocusColor());
495   control->drawBackground(*s, *tx);
496   control->drawMask(*s, *maskcolor, *mask);
497
498   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
499   XClearWindow(**otk::display, win);
500   if (*sur) delete *sur;
501   s->freePixelData();
502   *sur = s;
503 }
504
505 void Frame::renderMax()
506 {
507   if (!(_decorations & Client::Decor_Maximize)) return;
508   bool press = _max_press || _client->maxVert() || _client->maxHorz();
509   renderButton(_client->screen(), _client->focused(), press, _max,
510                &_max_sur, geom.button_size,
511                otk::RenderStyle::style(_client->screen())->maximizeMask());
512 }
513
514 void Frame::renderDesk()
515 {
516   if (!(_decorations & Client::Decor_AllDesktops)) return;
517   bool press = _desk_press || _client->desktop() == 0xffffffff;
518   renderButton(_client->screen(), _client->focused(), press, _desk,
519                &_desk_sur, geom.button_size,
520                otk::RenderStyle::style(_client->screen())->alldesktopsMask());
521 }
522
523 void Frame::renderIconify()
524 {
525   if (!(_decorations & Client::Decor_Iconify)) return;
526   renderButton(_client->screen(), _client->focused(), _iconify_press, _iconify,
527                &_iconify_sur, geom.button_size,
528                otk::RenderStyle::style(_client->screen())->iconifyMask());
529 }
530
531 void Frame::renderClose()
532 {
533   if (!(_decorations & Client::Decor_Close)) return;
534   renderButton(_client->screen(), _client->focused(), _close_press, _close,
535                &_close_sur, geom.button_size,
536                otk::RenderStyle::style(_client->screen())->closeMask());
537 }
538
539 void Frame::renderIcon()
540 {
541   if (!(_decorations & Client::Decor_Icon)) return;
542   const int screen = _client->screen();
543   const otk::RenderControl *control = otk::display->renderControl(screen);
544
545   otk::Surface *s = new otk::Surface(screen, otk::Size(geom.button_size,
546                                                        geom.button_size));
547   otk::pixel32 *dest = s->pixelData(), *src;
548   int w = _title_sur->size().width();
549   
550   src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.icon_x;
551   
552   // get the background under the icon button
553   for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
554     for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
555       *dest = *src;
556   // draw the icon over it
557   const Icon *icon = _client->icon(otk::Size(geom.button_size,
558                                              geom.button_size));
559   control->drawImage(*s, icon->w, icon->h, icon->data);
560   if (!icon->data) {
561     Pixmap p = _client->pixmapIcon(), m = _client->pixmapIconMask();
562     if (p != None)
563       control->drawImage(*s, p, m);
564   }
565
566   XSetWindowBackgroundPixmap(**otk::display, _icon, s->pixmap());
567   XClearWindow(**otk::display, _icon);
568   if (_icon_sur) delete _icon_sur;
569   _icon_sur = s;
570 }
571
572 void Frame::layoutTitle()
573 {
574   // figure out whats being shown, and the width of the label
575   geom.label_width = geom.width - geom.bevel * 2;
576   bool n, d, i, t, m ,c;
577   n = d = i = t = m = c = false;
578   for (const char *l = _layout.c_str(); *l; ++l) {
579     switch (*l) {
580     case 'n':
581     case 'N':
582       if (!(_decorations & Client::Decor_Icon)) break;
583       n = true;
584       geom.label_width -= geom.button_size + geom.bevel;
585       break;
586     case 'd':
587     case 'D':
588       if (!(_decorations & Client::Decor_AllDesktops)) break;
589       d = true;
590       geom.label_width -= geom.button_size + geom.bevel;
591       break;
592     case 'i':
593     case 'I':
594       if (!(_decorations & Client::Decor_Iconify)) break;
595       i = true;
596       geom.label_width -= geom.button_size + geom.bevel;
597       break;
598     case 't':
599     case 'T':
600       t = true;
601       break;
602     case 'm':
603     case 'M':
604       if (!(_decorations & Client::Decor_Maximize)) break;
605       m = true;
606       geom.label_width -= geom.button_size + geom.bevel;
607       break;
608     case 'c':
609     case 'C':
610       if (!(_decorations & Client::Decor_Close)) break;
611       c = true;
612       geom.label_width -= geom.button_size + geom.bevel;
613       break;
614     }
615   }
616   if (geom.label_width < 1) geom.label_width = 1;
617
618   XResizeWindow(**otk::display, _label, geom.label_width, geom.font_height);
619   
620   if (!n) {
621     _decorations &= ~Client::Decor_Icon;
622     XUnmapWindow(**otk::display, _icon);
623   }
624   if (!d) {
625     _decorations &= ~Client::Decor_AllDesktops;
626     XUnmapWindow(**otk::display, _desk);
627   }
628   if (!i) {
629     _decorations &= ~Client::Decor_Iconify;
630     XUnmapWindow(**otk::display, _iconify);
631   }
632   if (!t)
633     XUnmapWindow(**otk::display, _label);
634   if (!m) {
635     _decorations &= ~Client::Decor_Maximize;
636     XUnmapWindow(**otk::display, _max);
637   }
638   if (!c) {
639     _decorations &= ~Client::Decor_Close;
640     XUnmapWindow(**otk::display, _close);
641   }
642
643   int x = geom.bevel;
644   for (const char *lc = _layout.c_str(); *lc; ++lc) {
645     switch (*lc) {
646     case 'n':
647     case 'N':
648       if (!n) break;
649       geom.icon_x = x;
650       XMapWindow(**otk::display, _icon);
651       XMoveWindow(**otk::display, _icon, x, geom.bevel + 1);
652       x += geom.button_size + geom.bevel;
653       break;
654     case 'd':
655     case 'D':
656       if (!d) break;
657       XMapWindow(**otk::display, _desk);
658       XMoveWindow(**otk::display, _desk, x, geom.bevel + 1);
659       x += geom.button_size + geom.bevel;
660       break;
661     case 'i':
662     case 'I':
663       if (!i) break;
664       XMapWindow(**otk::display, _iconify);
665       XMoveWindow(**otk::display, _iconify, x, geom.bevel + 1);
666       x += geom.button_size + geom.bevel;
667       break;
668     case 't':
669     case 'T':
670       if (!t) break;
671       XMapWindow(**otk::display, _label);
672       XMoveWindow(**otk::display, _label, x, geom.bevel);
673       x += geom.label_width + geom.bevel;
674       break;
675     case 'm':
676     case 'M':
677       if (!m) break;
678       XMapWindow(**otk::display, _max);
679       XMoveWindow(**otk::display, _max, x, geom.bevel + 1);
680       x += geom.button_size + geom.bevel;
681       break;
682     case 'c':
683     case 'C':
684       if (!c) break;
685       XMapWindow(**otk::display, _close);
686       XMoveWindow(**otk::display, _close, x, geom.bevel + 1);
687       x += geom.button_size + geom.bevel;
688       break;
689     }
690   }
691 }
692
693 void Frame::adjustPosition()
694 {
695   int x, y;
696   x = _client->area().x();
697   y = _client->area().y();
698   clientGravity(x, y);
699   XMoveWindow(**otk::display, _frame, x, y);
700   _area = otk::Rect(otk::Point(x, y), _area.size());
701 }
702
703
704 void Frame::adjustShape()
705 {
706 #ifdef SHAPE
707   if (!_client->shaped()) {
708     // clear the shape on the frame window
709     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
710                       _innersize.left,
711                       _innersize.top,
712                       None, ShapeSet);
713   } else {
714     // make the frame's shape match the clients
715     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
716                        _innersize.left,
717                        _innersize.top,
718                        _client->window(), ShapeBounding, ShapeSet);
719
720     int num = 0;
721     XRectangle xrect[2];
722
723     if (_decorations & Client::Decor_Titlebar) {
724       xrect[0].x = -geom.bevel;
725       xrect[0].y = -geom.bevel;
726       xrect[0].width = geom.width + geom.bwidth * 2;
727       xrect[0].height = geom.title_height() + geom.bwidth * 2;
728       ++num;
729     }
730
731     if (_decorations & Client::Decor_Handle) {
732       xrect[1].x = -geom.bevel;
733       xrect[1].y = geom.handle_y;
734       xrect[1].width = geom.width + geom.bwidth * 2;
735       xrect[1].height = geom.handle_height + geom.bwidth * 2;
736       ++num;
737     }
738
739     XShapeCombineRectangles(**otk::display, _frame,
740                             ShapeBounding, 0, 0, xrect, num,
741                             ShapeUnion, Unsorted);
742   }
743 #endif // SHAPE
744 }
745
746 void Frame::adjustState()
747 {
748   renderDesk();
749   renderMax();
750 }
751
752 void Frame::adjustIcon()
753 {
754   renderIcon();
755 }
756
757 void Frame::grabClient()
758 {
759   // reparent the client to the frame
760   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
761   /*
762     When reparenting the client window, it is usually not mapped yet, since
763     this occurs from a MapRequest. However, in the case where Openbox is
764     starting up, the window is already mapped, so we'll see unmap events for
765     it. There are 2 unmap events generated that we see, one with the 'event'
766     member set the root window, and one set to the client, but both get handled
767     and need to be ignored.
768   */
769   if (openbox->state() == Openbox::State_Starting)
770     _client->ignore_unmaps += 2;
771
772   // select the event mask on the client's parent (to receive config/map req's)
773   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
774
775   // map the client so it maps when the frame does
776   XMapWindow(**otk::display, _client->window());
777
778   adjustSize();
779   adjustPosition();
780 }
781
782
783 void Frame::releaseClient()
784 {
785   XEvent ev;
786
787   // check if the app has already reparented its window away
788   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
789                              ReparentNotify, &ev)) {
790     XPutBackEvent(**otk::display, &ev);
791     // re-map the window since the unmanaging process unmaps it
792     XMapWindow(**otk::display, _client->window());  
793   } else {
794     // according to the ICCCM - if the client doesn't reparent itself, then we
795     // will reparent the window to root for them
796     XReparentWindow(**otk::display, _client->window(),
797                     otk::display->screenInfo(_client->screen())->rootWindow(),
798                     _client->area().x(), _client->area().y());
799   }
800 }
801
802
803 void Frame::clientGravity(int &x, int &y)
804 {
805   // horizontal
806   switch (_client->gravity()) {
807   default:
808   case NorthWestGravity:
809   case SouthWestGravity:
810   case WestGravity:
811     break;
812
813   case NorthGravity:
814   case SouthGravity:
815   case CenterGravity:
816     x -= (_size.left + _size.right) / 2;
817     break;
818
819   case NorthEastGravity:
820   case SouthEastGravity:
821   case EastGravity:
822     x -= _size.left + _size.right;
823     break;
824
825   case ForgetGravity:
826   case StaticGravity:
827     x -= _size.left;
828     break;
829   }
830
831   // vertical
832   switch (_client->gravity()) {
833   default:
834   case NorthWestGravity:
835   case NorthEastGravity:
836   case NorthGravity:
837     break;
838
839   case CenterGravity:
840   case EastGravity:
841   case WestGravity:
842     y -= (_size.top + _size.bottom) / 2;
843     break;
844
845   case SouthWestGravity:
846   case SouthEastGravity:
847   case SouthGravity:
848     y -= _size.top + _size.bottom;
849     break;
850
851   case ForgetGravity:
852   case StaticGravity:
853     y -= _size.top;
854     break;
855   }
856 }
857
858
859 void Frame::frameGravity(int &x, int &y)
860 {
861   // horizontal
862   switch (_client->gravity()) {
863   default:
864   case NorthWestGravity:
865   case WestGravity:
866   case SouthWestGravity:
867     break;
868   case NorthGravity:
869   case CenterGravity:
870   case SouthGravity:
871     x += (_size.left + _size.right) / 2;
872     break;
873   case NorthEastGravity:
874   case EastGravity:
875   case SouthEastGravity:
876     x += _size.left + _size.right;
877     break;
878   case StaticGravity:
879   case ForgetGravity:
880     x += _size.left;
881     break;
882   }
883
884   // vertical
885   switch (_client->gravity()) {
886   default:
887   case NorthWestGravity:
888   case WestGravity:
889   case SouthWestGravity:
890     break;
891   case NorthGravity:
892   case CenterGravity:
893   case SouthGravity:
894     y += (_size.top + _size.bottom) / 2;
895     break;
896   case NorthEastGravity:
897   case EastGravity:
898   case SouthEastGravity:
899     y += _size.top + _size.bottom;
900     break;
901   case StaticGravity:
902   case ForgetGravity:
903     y += _size.top;
904     break;
905   }
906 }
907
908
909 }