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