]> icculus.org git repositories - dana/openbox.git/blob - src/frame.cc
remove includes for widgetbase.hh. fix bug with circular modal pointer.
[dana/openbox.git] / src / frame.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 extern "C" {
8 #ifdef    SHAPE
9 #include <X11/extensions/shape.h>
10 #endif // SHAPE
11 }
12
13 #include "frame.hh"
14 #include "client.hh"
15 #include "openbox.hh"
16 #include "otk/display.hh"
17
18 #include <string>
19 #include <cassert>
20
21 namespace ob {
22
23 const long Frame::event_mask;
24
25 Window createWindow(const otk::ScreenInfo *info, Window parent, 
26                     unsigned long mask, XSetWindowAttributes *attrib)
27 {
28   return XCreateWindow(**otk::display, parent, 0, 0, 1, 1, 0,
29                        info->depth(), InputOutput, info->visual(),
30                        mask, attrib);
31                        
32 }
33
34 Frame::Frame(Client *client)
35   : _client(client),
36     _visible(false),
37     _plate(0),
38     _title(0),
39     _label(0),
40     _handle(0),
41     _lgrip(0),
42     _rgrip(0),
43     _buttons(0),
44     _numbuttons(0),
45     _titleorder(0),
46     _frame_sur(0),
47     _title_sur(0),
48     _label_sur(0),
49     _handle_sur(0),
50     _grip_sur(0),
51     _buttons_sur(0)
52 {
53   assert(client);
54
55   XSetWindowAttributes attrib;
56   unsigned long mask;
57   const otk::ScreenInfo *info = otk::display->screenInfo(client->screen());
58
59   // create all of the decor windows (except title bar buttons)
60   mask = CWOverrideRedirect | CWEventMask;
61   attrib.event_mask = Frame::event_mask;
62   attrib.override_redirect = true;
63   _frame = createWindow(info, info->rootWindow(), mask, &attrib);
64
65   mask = 0;
66   _plate = createWindow(info, _frame, mask, &attrib);
67   mask = CWEventMask;
68   attrib.event_mask = (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
69                        ExposureMask);
70   _title = createWindow(info, _frame, mask, &attrib);
71   _label = createWindow(info, _title, mask, &attrib);
72   _handle = createWindow(info, _frame, mask, &attrib);
73   mask |= CWCursor;
74   attrib.cursor = openbox->cursors().ll_angle;
75   _lgrip = createWindow(info, _handle, mask, &attrib);
76   attrib.cursor = openbox->cursors().lr_angle;
77   _rgrip = createWindow(info, _handle, mask, &attrib);
78
79   // the other stuff is shown based on decor settings
80   XMapWindow(**otk::display, _plate);
81   XMapWindow(**otk::display, _lgrip);
82   XMapWindow(**otk::display, _rgrip);
83   XMapWindow(**otk::display, _label);
84
85   applyStyle(*otk::RenderStyle::style(_client->screen()));
86
87   // XXX load buttons
88   _numbuttons = 0;
89   _buttons = new Window[0];
90   _buttons_sur = new otk::Surface*[0];
91   _titleorder = new unsigned int[1];
92   _titleorder[0] = (unsigned)-1;
93
94   // register all of the windows with the event dispatcher
95   Window *w = allWindows();
96   for (unsigned int i = 0; w[i]; ++i)
97     openbox->registerHandler(w[i], this);
98   delete [] w;
99 }
100
101 Frame::~Frame()
102 {
103   // unregister all of the windows with the event dispatcher
104   Window *w = allWindows();
105   for (unsigned int i = 0; w[i]; ++i)
106     openbox->clearHandler(w[i]);
107   delete [] w;
108
109   for (unsigned int i = 0; i < _numbuttons; ++i) {
110     XDestroyWindow(**otk::display, _buttons[i]);
111     delete _buttons_sur[i];
112   }
113   XDestroyWindow(**otk::display, _rgrip);
114   XDestroyWindow(**otk::display, _lgrip);
115   XDestroyWindow(**otk::display, _handle);
116   XDestroyWindow(**otk::display, _label);
117   XDestroyWindow(**otk::display, _title);
118   XDestroyWindow(**otk::display, _frame);
119
120   if (_frame_sur) delete _frame_sur;
121   if (_title_sur) delete _title_sur;
122   if (_label_sur) delete _label_sur;
123   if (_handle_sur) delete _handle_sur;
124   if (_grip_sur) delete _grip_sur;
125
126   delete [] _buttons;
127   delete [] _titleorder;
128   delete [] _buttons_sur;
129 }
130
131 void Frame::show()
132 {
133   if (!_visible) {
134     _visible = true;
135     XMapWindow(**otk::display, _frame);
136   }
137 }
138
139 void Frame::hide()
140 {
141   if (_visible) {
142     _visible = false;
143     XUnmapWindow(**otk::display, _frame);
144   }
145 }
146
147 MouseContext::MC Frame::mouseContext(Window win) const
148 {
149   if (win == _frame)  return MouseContext::Frame;
150   if (win == _title ||
151       win == _label)  return MouseContext::Titlebar;
152   if (win == _handle) return MouseContext::Handle;
153   if (win == _plate)  return MouseContext::Window;
154   if (win == _lgrip ||
155       win == _rgrip)  return MouseContext::Grip;
156   return (MouseContext::MC) -1;
157 }
158
159 Window *Frame::allWindows() const
160 {
161   Window *w = new Window[7 + _numbuttons + 1];
162   unsigned int i = 0;
163   w[i++] = _frame;
164   w[i++] = _plate;
165   w[i++] = _title;
166   w[i++] = _label;
167   w[i++] = _handle;
168   w[i++] = _lgrip;
169   w[i++] = _rgrip;
170   for (unsigned int j = 0; j < _numbuttons; ++j)
171     w[j + i++] = _buttons[j];
172   w[i] = 0;
173   return w;
174 }
175
176 void Frame::applyStyle(const otk::RenderStyle &style)
177 {
178   // set static border colors
179   XSetWindowBorder(**otk::display, _frame, style.frameBorderColor()->pixel());
180   XSetWindowBorder(**otk::display, _title, style.frameBorderColor()->pixel());
181   XSetWindowBorder(**otk::display, _handle, style.frameBorderColor()->pixel());
182   XSetWindowBorder(**otk::display, _lgrip, style.frameBorderColor()->pixel());
183   XSetWindowBorder(**otk::display, _rgrip, style.frameBorderColor()->pixel());
184
185   // size all the fixed-size elements
186   geom.font_height = style.labelFont()->height();
187   if (geom.font_height < 1) geom.font_height = 1;
188   geom.button_size = geom.font_height - 2;
189   if (geom.button_size < 1) geom.button_size = 1;
190   geom.handle_height = style.handleWidth();
191   if (geom.handle_height < 1) geom.handle_height = 1;
192   geom.bevel = style.bevelWidth();
193   
194   XResizeWindow(**otk::display, _lgrip, geom.grip_width(), geom.handle_height);
195   XResizeWindow(**otk::display, _rgrip, geom.grip_width(), geom.handle_height);
196   
197   for (unsigned int i = 0; i < _numbuttons; ++i)
198     XResizeWindow(**otk::display, _buttons[i],
199                   geom.button_size, geom.button_size);
200 }
201
202 void Frame::styleChanged(const otk::RenderStyle &style)
203 {
204   applyStyle(style);
205   
206   // size/position everything
207   adjustSize();
208   adjustPosition();
209 }
210
211 void Frame::adjustFocus()
212 {
213   // XXX optimizations later...
214   adjustSize();
215 }
216
217 void Frame::adjustTitle()
218 {
219   // XXX optimizations later...
220   adjustSize();
221 }
222
223 static void render(int screen, const otk::Size &size, Window win,
224                    otk::Surface **surface,
225                    const otk::RenderTexture &texture)
226 {
227   otk::Surface *s = new otk::Surface(screen, size);
228   otk::display->renderControl(screen)->drawBackground(*s, texture);
229   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
230   XClearWindow(**otk::display, win);
231   if (*surface) delete *surface;
232   *surface = s;
233 }
234
235 void Frame::adjustSize()
236 {
237   Client::DecorationFlags decorations = _client->decorations();
238   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
239
240   if (decorations & Client::Decor_Border) {
241     geom.bwidth = style->frameBorderWidth();
242     geom.cbwidth = style->clientBorderWidth();
243   } else {
244     geom.bwidth = geom.cbwidth = 0;
245   }
246   _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
247     geom.cbwidth;
248   geom.width = _client->area().width() + geom.cbwidth * 2;
249   assert(geom.width > 0);
250
251   // set border widths
252   XSetWindowBorderWidth(**otk::display, _plate, geom.cbwidth);
253   XSetWindowBorderWidth(**otk::display, _frame, geom.bwidth);
254   XSetWindowBorderWidth(**otk::display, _title, geom.bwidth);
255   XSetWindowBorderWidth(**otk::display, _handle, geom.bwidth);
256   XSetWindowBorderWidth(**otk::display, _lgrip, geom.bwidth);
257   XSetWindowBorderWidth(**otk::display, _rgrip, geom.bwidth);
258   
259   // position/size and map/unmap all the windows
260
261   if (decorations & Client::Decor_Titlebar) {
262     XMoveResizeWindow(**otk::display, _title, -geom.bwidth, -geom.bwidth,
263                       geom.width, geom.title_height());
264     _innersize.top += geom.title_height() + geom.bwidth;
265     XMapWindow(**otk::display, _title);
266
267     // layout the title bar elements
268     layoutTitle();
269   } else
270     XUnmapWindow(**otk::display, _title);
271
272   if (decorations & Client::Decor_Handle) {
273     geom.handle_y = _innersize.top + _client->area().height() + geom.cbwidth;
274     XMoveResizeWindow(**otk::display, _handle, -geom.bwidth, geom.handle_y,
275                       geom.width, geom.handle_height);
276     XMoveWindow(**otk::display, _lgrip, -geom.bwidth, -geom.bwidth);
277     XMoveWindow(**otk::display, _rgrip,
278                 -geom.bwidth + geom.width - geom.grip_width(),
279                 -geom.bwidth);
280     _innersize.bottom += geom.handle_height + geom.bwidth;
281     XMapWindow(**otk::display, _handle);
282   } else
283     XUnmapWindow(**otk::display, _handle);
284   
285   XResizeWindow(**otk::display, _frame, geom.width,
286                 (_client->shaded() ? geom.title_height() :
287                  _innersize.top + _innersize.bottom +
288                  _client->area().height()));
289
290   // do this in two steps because clients whose gravity is set to
291   // 'Static' don't end up getting moved at all with an XMoveResizeWindow
292   XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
293               _innersize.top - geom.cbwidth);
294   XResizeWindow(**otk::display, _plate, _client->area().width(),
295                 _client->area().height());
296
297   _size.left   = _innersize.left + geom.bwidth;
298   _size.right  = _innersize.right + geom.bwidth;
299   _size.top    = _innersize.top + geom.bwidth;
300   _size.bottom = _innersize.bottom + geom.bwidth;
301
302   _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
303                                                 _size.left + _size.right,
304                                                 _client->area().height() +
305                                                 _size.top + _size.bottom));
306
307   // render all the elements
308   int screen = _client->screen();
309   bool focus = _client->focused();
310   if (decorations & Client::Decor_Titlebar) {
311     render(screen, otk::Size(geom.width, geom.title_height()), _title,
312            &_title_sur, *(focus ? style->titlebarFocusBackground() :
313                           style->titlebarUnfocusBackground()));
314     
315     renderLabel();
316   }
317
318   if (decorations & Client::Decor_Handle) {
319     render(screen, otk::Size(geom.width, geom.handle_height), _handle,
320            &_handle_sur, *(focus ? style->handleFocusBackground() :
321                            style->handleUnfocusBackground()));
322     render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
323            &_grip_sur, *(focus ? style->gripFocusBackground() :
324                          style->gripUnfocusBackground()));
325     XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
326     XClearWindow(**otk::display, _rgrip);
327   }
328
329   XSetWindowBorder(**otk::display, _plate,
330                    focus ? style->clientBorderFocusColor()->pixel() :
331                    style->clientBorderUnfocusColor()->pixel());
332   
333   adjustShape();
334 }
335
336 void Frame::renderLabel()
337 {
338   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
339   const otk::RenderControl *control =
340     otk::display->renderControl(_client->screen());
341   const otk::Font *font = style->labelFont();
342
343   otk::Surface *s = new otk::Surface(_client->screen(),
344                                      otk::Size(geom.label_width,
345                                                geom.label_height()));
346   control->drawBackground(*s, *(_client->focused() ?
347                                 style->labelFocusBackground() :
348                                 style->labelUnfocusBackground()));
349
350   otk::ustring t = _client->title(); // the actual text to draw
351   int x = geom.bevel;                // x coord for the text
352
353   if ((unsigned)x * 2 > geom.label_width) return; // no room at all
354
355   // find a string that will fit inside the area for text
356   otk::ustring::size_type text_len = t.size();
357   unsigned int length;
358   unsigned int maxsize = geom.label_width - geom.bevel * 2;
359       
360   do {
361     t.resize(text_len);
362     length = font->measureString(t);
363   } while (length > maxsize && text_len-- > 0);
364
365   if (text_len <= 0) return; // won't fit anything
366
367   // justify the text
368   switch (style->labelTextJustify()) {
369   case otk::RenderStyle::RightBottomJustify:
370     x += maxsize - length;
371     break;
372   case otk::RenderStyle::CenterJustify:
373     x += (maxsize - length) / 2;
374     break;
375   case otk::RenderStyle::LeftTopJustify:
376     break;
377   }
378  
379   control->drawString(*s, *font, x, 0,
380                       *(_client->focused() ? style->textFocusColor() :
381                         style->textUnfocusColor()), t);
382
383   XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
384   XClearWindow(**otk::display, _label);
385   if (_label_sur) delete _label_sur;
386   _label_sur = s;
387 }
388
389 void Frame::layoutTitle()
390 {
391   geom.label_width = geom.width - geom.bevel * 2;
392   if (geom.label_width < 1) geom.label_width = 1;
393   XMoveResizeWindow(**otk::display, _label, geom.bevel, geom.bevel,
394                     geom.label_width, geom.font_height);
395 }
396
397 void Frame::adjustPosition()
398 {
399   int x, y;
400   x = _client->area().x();
401   y = _client->area().y();
402   clientGravity(x, y);
403   XMoveWindow(**otk::display, _frame, x, y);
404   _area = otk::Rect(otk::Point(x, y), _area.size());
405 }
406
407
408 void Frame::adjustShape()
409 {
410 #ifdef SHAPE
411   Client::DecorationFlags decorations = _client->decorations();
412   
413   if (!_client->shaped()) {
414     // clear the shape on the frame window
415     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
416                       _innersize.left,
417                       _innersize.top,
418                       None, ShapeSet);
419   } else {
420     // make the frame's shape match the clients
421     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
422                        _innersize.left,
423                        _innersize.top,
424                        _client->window(), ShapeBounding, ShapeSet);
425
426     int num = 0;
427     XRectangle xrect[2];
428
429     if (decorations & Client::Decor_Titlebar) {
430       xrect[0].x = -geom.bevel;
431       xrect[0].y = -geom.bevel;
432       xrect[0].width = geom.width + geom.bwidth * 2;
433       xrect[0].height = geom.title_height() + geom.bwidth * 2;
434       ++num;
435     }
436
437     if (decorations & Client::Decor_Handle) {
438       xrect[1].x = -geom.bevel;
439       xrect[1].y = geom.handle_y;
440       xrect[1].width = geom.width + geom.bwidth * 2;
441       xrect[1].height = geom.handle_height + geom.bwidth * 2;
442       ++num;
443     }
444
445     XShapeCombineRectangles(**otk::display, _frame,
446                             ShapeBounding, 0, 0, xrect, num,
447                             ShapeUnion, Unsorted);
448   }
449 #endif // SHAPE
450 }
451
452
453 void Frame::adjustState()
454 {
455 // XXX  _button_alldesk.update();
456 // XXX  _button_max.update();
457 }
458
459
460 void Frame::grabClient()
461 {
462   // reparent the client to the frame
463   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
464   /*
465     When reparenting the client window, it is usually not mapped yet, since
466     this occurs from a MapRequest. However, in the case where Openbox is
467     starting up, the window is already mapped, so we'll see unmap events for
468     it. There are 2 unmap events generated that we see, one with the 'event'
469     member set the root window, and one set to the client, but both get handled
470     and need to be ignored.
471   */
472   if (openbox->state() == Openbox::State_Starting)
473     _client->ignore_unmaps += 2;
474
475   // select the event mask on the client's parent (to receive config/map req's)
476   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
477
478   // map the client so it maps when the frame does
479   XMapWindow(**otk::display, _client->window());
480
481   adjustSize();
482   adjustPosition();
483 }
484
485
486 void Frame::releaseClient()
487 {
488   XEvent ev;
489
490   // check if the app has already reparented its window away
491   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
492                              ReparentNotify, &ev)) {
493     XPutBackEvent(**otk::display, &ev);
494     // re-map the window since the unmanaging process unmaps it
495     XMapWindow(**otk::display, _client->window());  
496   } else {
497     // according to the ICCCM - if the client doesn't reparent itself, then we
498     // will reparent the window to root for them
499     XReparentWindow(**otk::display, _client->window(),
500                     otk::display->screenInfo(_client->screen())->rootWindow(),
501                     _client->area().x(), _client->area().y());
502   }
503 }
504
505
506 void Frame::clientGravity(int &x, int &y)
507 {
508   // horizontal
509   switch (_client->gravity()) {
510   default:
511   case NorthWestGravity:
512   case SouthWestGravity:
513   case WestGravity:
514     break;
515
516   case NorthGravity:
517   case SouthGravity:
518   case CenterGravity:
519     x -= (_size.left + _size.right) / 2;
520     break;
521
522   case NorthEastGravity:
523   case SouthEastGravity:
524   case EastGravity:
525     x -= _size.left + _size.right;
526     break;
527
528   case ForgetGravity:
529   case StaticGravity:
530     x -= _size.left;
531     break;
532   }
533
534   // vertical
535   switch (_client->gravity()) {
536   default:
537   case NorthWestGravity:
538   case NorthEastGravity:
539   case NorthGravity:
540     break;
541
542   case CenterGravity:
543   case EastGravity:
544   case WestGravity:
545     y -= (_size.top + _size.bottom) / 2;
546     break;
547
548   case SouthWestGravity:
549   case SouthEastGravity:
550   case SouthGravity:
551     y -= _size.top + _size.bottom;
552     break;
553
554   case ForgetGravity:
555   case StaticGravity:
556     y -= _size.top;
557     break;
558   }
559 }
560
561
562 void Frame::frameGravity(int &x, int &y)
563 {
564   // horizontal
565   switch (_client->gravity()) {
566   default:
567   case NorthWestGravity:
568   case WestGravity:
569   case SouthWestGravity:
570     break;
571   case NorthGravity:
572   case CenterGravity:
573   case SouthGravity:
574     x += (_size.left + _size.right) / 2;
575     break;
576   case NorthEastGravity:
577   case EastGravity:
578   case SouthEastGravity:
579     x += _size.left + _size.right;
580     break;
581   case StaticGravity:
582   case ForgetGravity:
583     x += _size.left;
584     break;
585   }
586
587   // vertical
588   switch (_client->gravity()) {
589   default:
590   case NorthWestGravity:
591   case WestGravity:
592   case SouthWestGravity:
593     break;
594   case NorthGravity:
595   case CenterGravity:
596   case SouthGravity:
597     y += (_size.top + _size.bottom) / 2;
598     break;
599   case NorthEastGravity:
600   case EastGravity:
601   case SouthEastGravity:
602     y += _size.top + _size.bottom;
603     break;
604   case StaticGravity:
605   case ForgetGravity:
606     y += _size.top;
607     break;
608   }
609 }
610
611
612 }