]> icculus.org git repositories - dana/openbox.git/blob - src/frame.cc
doc _layer.
[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   bool press = _max_press || _client->maxVert() || _client->maxHorz();
504   renderButton(_client->screen(), _client->focused(), press, _max,
505                &_max_sur, geom.button_size,
506                otk::RenderStyle::style(_client->screen())->maximizeMask());
507 }
508
509 void Frame::renderDesk()
510 {
511   bool press = _desk_press || _client->desktop() == 0xffffffff;
512   renderButton(_client->screen(), _client->focused(), press, _desk,
513                &_desk_sur, geom.button_size,
514                otk::RenderStyle::style(_client->screen())->alldesktopsMask());
515 }
516
517 void Frame::renderIconify()
518 {
519   renderButton(_client->screen(), _client->focused(), _iconify_press, _iconify,
520                &_iconify_sur, geom.button_size,
521                otk::RenderStyle::style(_client->screen())->iconifyMask());
522 }
523
524 void Frame::renderClose()
525 {
526   renderButton(_client->screen(), _client->focused(), _close_press, _close,
527                &_close_sur, geom.button_size,
528                otk::RenderStyle::style(_client->screen())->closeMask());
529 }
530
531 void Frame::renderIcon()
532 {
533   const int screen = _client->screen();
534   const otk::RenderControl *control = otk::display->renderControl(screen);
535
536   otk::Surface *s = new otk::Surface(screen, otk::Size(geom.button_size,
537                                                        geom.button_size));
538   otk::pixel32 *dest = s->pixelData(), *src;
539   int w = _title_sur->size().width();
540   
541   src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.icon_x;
542   
543   // get the background under the icon button
544   for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
545     for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
546       *dest = *src;
547   control->drawImage(*s, 0, 0, 0);
548
549   XSetWindowBackgroundPixmap(**otk::display, _icon, s->pixmap());
550   XClearWindow(**otk::display, _icon);
551   if (_icon_sur) delete _icon_sur;
552   _icon_sur = s;
553 }
554
555 void Frame::layoutTitle()
556 {
557   geom.label_width = geom.width - geom.bevel * 2 -
558     (geom.button_size + geom.bevel) * (_layout.size() - 1);
559   if (geom.label_width < 1) geom.label_width = 1;
560
561   XResizeWindow(**otk::display, _label, geom.label_width, geom.font_height);
562   
563   int x = geom.bevel;
564   bool n, d, i, l, m ,c;
565   n = d = i = l = m = c = false;
566   for (const char *lc = _layout.c_str(); *lc; ++lc) {
567     switch (*lc) {
568     case 'n':
569     case 'N':
570       geom.icon_x = x;
571       XMapWindow(**otk::display, _icon);
572       XMoveWindow(**otk::display, _icon, x, geom.bevel + 1);
573       n = true;
574       x += geom.button_size;
575       break;
576     case 'd':
577     case 'D':
578       XMapWindow(**otk::display, _desk);
579       XMoveWindow(**otk::display, _desk, x, geom.bevel + 1);
580       d = true;
581       x += geom.button_size;
582       break;
583     case 'i':
584     case 'I':
585       XMapWindow(**otk::display, _iconify);
586       XMoveWindow(**otk::display, _iconify, x, geom.bevel + 1);
587       i = true;
588       x += geom.button_size;
589       break;
590     case 't':
591     case 'T':
592       XMapWindow(**otk::display, _label);
593       XMoveWindow(**otk::display, _label, x, geom.bevel);
594       l = true;
595       x += geom.label_width;
596       break;
597     case 'm':
598     case 'M':
599       XMapWindow(**otk::display, _max);
600       XMoveWindow(**otk::display, _max, x, geom.bevel + 1);
601       m = true;
602       x += geom.button_size;
603       break;
604     case 'c':
605     case 'C':
606       XMapWindow(**otk::display, _close);
607       XMoveWindow(**otk::display, _close, x, geom.bevel + 1);
608       c = true;
609       x += geom.button_size;
610       break;
611     }
612     x += geom.bevel;
613   }
614   if (!n) XUnmapWindow(**otk::display, _icon);
615   if (!d) XUnmapWindow(**otk::display, _desk);
616   if (!i) XUnmapWindow(**otk::display, _iconify);
617   if (!l) XUnmapWindow(**otk::display, _label);
618   if (!m) XUnmapWindow(**otk::display, _max);
619   if (!c) XUnmapWindow(**otk::display, _close);
620 }
621
622 void Frame::adjustPosition()
623 {
624   int x, y;
625   x = _client->area().x();
626   y = _client->area().y();
627   clientGravity(x, y);
628   XMoveWindow(**otk::display, _frame, x, y);
629   _area = otk::Rect(otk::Point(x, y), _area.size());
630 }
631
632
633 void Frame::adjustShape()
634 {
635 #ifdef SHAPE
636   Client::DecorationFlags decorations = _client->decorations();
637   
638   if (!_client->shaped()) {
639     // clear the shape on the frame window
640     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
641                       _innersize.left,
642                       _innersize.top,
643                       None, ShapeSet);
644   } else {
645     // make the frame's shape match the clients
646     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
647                        _innersize.left,
648                        _innersize.top,
649                        _client->window(), ShapeBounding, ShapeSet);
650
651     int num = 0;
652     XRectangle xrect[2];
653
654     if (decorations & Client::Decor_Titlebar) {
655       xrect[0].x = -geom.bevel;
656       xrect[0].y = -geom.bevel;
657       xrect[0].width = geom.width + geom.bwidth * 2;
658       xrect[0].height = geom.title_height() + geom.bwidth * 2;
659       ++num;
660     }
661
662     if (decorations & Client::Decor_Handle) {
663       xrect[1].x = -geom.bevel;
664       xrect[1].y = geom.handle_y;
665       xrect[1].width = geom.width + geom.bwidth * 2;
666       xrect[1].height = geom.handle_height + geom.bwidth * 2;
667       ++num;
668     }
669
670     XShapeCombineRectangles(**otk::display, _frame,
671                             ShapeBounding, 0, 0, xrect, num,
672                             ShapeUnion, Unsorted);
673   }
674 #endif // SHAPE
675 }
676
677
678 void Frame::adjustState()
679 {
680   renderDesk();
681   renderMax();
682 }
683
684
685 void Frame::grabClient()
686 {
687   // reparent the client to the frame
688   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
689   /*
690     When reparenting the client window, it is usually not mapped yet, since
691     this occurs from a MapRequest. However, in the case where Openbox is
692     starting up, the window is already mapped, so we'll see unmap events for
693     it. There are 2 unmap events generated that we see, one with the 'event'
694     member set the root window, and one set to the client, but both get handled
695     and need to be ignored.
696   */
697   if (openbox->state() == Openbox::State_Starting)
698     _client->ignore_unmaps += 2;
699
700   // select the event mask on the client's parent (to receive config/map req's)
701   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
702
703   // map the client so it maps when the frame does
704   XMapWindow(**otk::display, _client->window());
705
706   adjustSize();
707   adjustPosition();
708 }
709
710
711 void Frame::releaseClient()
712 {
713   XEvent ev;
714
715   // check if the app has already reparented its window away
716   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
717                              ReparentNotify, &ev)) {
718     XPutBackEvent(**otk::display, &ev);
719     // re-map the window since the unmanaging process unmaps it
720     XMapWindow(**otk::display, _client->window());  
721   } else {
722     // according to the ICCCM - if the client doesn't reparent itself, then we
723     // will reparent the window to root for them
724     XReparentWindow(**otk::display, _client->window(),
725                     otk::display->screenInfo(_client->screen())->rootWindow(),
726                     _client->area().x(), _client->area().y());
727   }
728 }
729
730
731 void Frame::clientGravity(int &x, int &y)
732 {
733   // horizontal
734   switch (_client->gravity()) {
735   default:
736   case NorthWestGravity:
737   case SouthWestGravity:
738   case WestGravity:
739     break;
740
741   case NorthGravity:
742   case SouthGravity:
743   case CenterGravity:
744     x -= (_size.left + _size.right) / 2;
745     break;
746
747   case NorthEastGravity:
748   case SouthEastGravity:
749   case EastGravity:
750     x -= _size.left + _size.right;
751     break;
752
753   case ForgetGravity:
754   case StaticGravity:
755     x -= _size.left;
756     break;
757   }
758
759   // vertical
760   switch (_client->gravity()) {
761   default:
762   case NorthWestGravity:
763   case NorthEastGravity:
764   case NorthGravity:
765     break;
766
767   case CenterGravity:
768   case EastGravity:
769   case WestGravity:
770     y -= (_size.top + _size.bottom) / 2;
771     break;
772
773   case SouthWestGravity:
774   case SouthEastGravity:
775   case SouthGravity:
776     y -= _size.top + _size.bottom;
777     break;
778
779   case ForgetGravity:
780   case StaticGravity:
781     y -= _size.top;
782     break;
783   }
784 }
785
786
787 void Frame::frameGravity(int &x, int &y)
788 {
789   // horizontal
790   switch (_client->gravity()) {
791   default:
792   case NorthWestGravity:
793   case WestGravity:
794   case SouthWestGravity:
795     break;
796   case NorthGravity:
797   case CenterGravity:
798   case SouthGravity:
799     x += (_size.left + _size.right) / 2;
800     break;
801   case NorthEastGravity:
802   case EastGravity:
803   case SouthEastGravity:
804     x += _size.left + _size.right;
805     break;
806   case StaticGravity:
807   case ForgetGravity:
808     x += _size.left;
809     break;
810   }
811
812   // vertical
813   switch (_client->gravity()) {
814   default:
815   case NorthWestGravity:
816   case WestGravity:
817   case SouthWestGravity:
818     break;
819   case NorthGravity:
820   case CenterGravity:
821   case SouthGravity:
822     y += (_size.top + _size.bottom) / 2;
823     break;
824   case NorthEastGravity:
825   case EastGravity:
826   case SouthEastGravity:
827     y += _size.top + _size.bottom;
828     break;
829   case StaticGravity:
830   case ForgetGravity:
831     y += _size.top;
832     break;
833   }
834 }
835
836
837 }