]> icculus.org git repositories - mikachu/openbox.git/blob - src/frame.cc
No longer using otk widgets for the frame decorations.
[mikachu/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   XMoveResizeWindow(**otk::display, _plate,
291                     _innersize.left - geom.cbwidth,
292                     _innersize.top - geom.cbwidth,
293                     _client->area().width(), _client->area().height());
294
295   _size.left   = _innersize.left + geom.bwidth;
296   _size.right  = _innersize.right + geom.bwidth;
297   _size.top    = _innersize.top + geom.bwidth;
298   _size.bottom = _innersize.bottom + geom.bwidth;
299
300   _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
301                                                 _size.left + _size.right,
302                                                 _client->area().height() +
303                                                 _size.top + _size.bottom));
304
305   // render all the elements
306   int screen = _client->screen();
307   bool focus = _client->focused();
308   if (decorations & Client::Decor_Titlebar) {
309     render(screen, otk::Size(geom.width, geom.title_height()), _title,
310            &_title_sur, *(focus ? style->titlebarFocusBackground() :
311                           style->titlebarUnfocusBackground()));
312     
313     renderLabel();
314   }
315
316   if (decorations & Client::Decor_Handle) {
317     render(screen, otk::Size(geom.width, geom.handle_height), _handle,
318            &_handle_sur, *(focus ? style->handleFocusBackground() :
319                            style->handleUnfocusBackground()));
320     render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
321            &_grip_sur, *(focus ? style->gripFocusBackground() :
322                          style->gripUnfocusBackground()));
323     XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
324     XClearWindow(**otk::display, _rgrip);
325   }
326
327   XSetWindowBorder(**otk::display, _plate,
328                    focus ? style->clientBorderFocusColor()->pixel() :
329                    style->clientBorderUnfocusColor()->pixel());
330   
331   adjustShape();
332 }
333
334 void Frame::renderLabel()
335 {
336   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
337   const otk::RenderControl *control =
338     otk::display->renderControl(_client->screen());
339   const otk::Font *font = style->labelFont();
340
341   otk::Surface *s = new otk::Surface(_client->screen(),
342                                      otk::Size(geom.label_width,
343                                                geom.label_height()));
344   control->drawBackground(*s, *(_client->focused() ?
345                                 style->labelFocusBackground() :
346                                 style->labelUnfocusBackground()));
347
348   otk::ustring t = _client->title(); // the actual text to draw
349   int x = geom.bevel;                // x coord for the text
350
351   if ((unsigned)x * 2 > geom.label_width) return; // no room at all
352
353   // find a string that will fit inside the area for text
354   otk::ustring::size_type text_len = t.size();
355   unsigned int length;
356   unsigned int maxsize = geom.label_width - geom.bevel * 2;
357       
358   do {
359     t.resize(text_len);
360     length = font->measureString(t);
361   } while (length > maxsize && text_len-- > 0);
362
363   if (text_len <= 0) return; // won't fit anything
364
365   // justify the text
366   switch (style->labelTextJustify()) {
367   case otk::RenderStyle::RightBottomJustify:
368     x += maxsize - length;
369     break;
370   case otk::RenderStyle::CenterJustify:
371     x += (maxsize - length) / 2;
372     break;
373   case otk::RenderStyle::LeftTopJustify:
374     break;
375   }
376  
377   control->drawString(*s, *font, x, 0,
378                       *(_client->focused() ? style->textFocusColor() :
379                         style->textUnfocusColor()), t);
380
381   XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
382   XClearWindow(**otk::display, _label);
383   if (_label_sur) delete _label_sur;
384   _label_sur = s;
385 }
386
387 void Frame::layoutTitle()
388 {
389   geom.label_width = geom.width - geom.bevel * 2;
390   if (geom.label_width < 1) geom.label_width = 1;
391   XMoveResizeWindow(**otk::display, _label, geom.bevel, geom.bevel,
392                     geom.label_width, geom.font_height);
393 }
394
395 void Frame::adjustPosition()
396 {
397   int x, y;
398   x = _client->area().x();
399   y = _client->area().y();
400   clientGravity(x, y);
401   XMoveWindow(**otk::display, _frame, x, y);
402   _area = otk::Rect(otk::Point(x, y), _area.size());
403 }
404
405
406 void Frame::adjustShape()
407 {
408 #ifdef SHAPE
409   Client::DecorationFlags decorations = _client->decorations();
410   
411   if (!_client->shaped()) {
412     // clear the shape on the frame window
413     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
414                       _innersize.left,
415                       _innersize.top,
416                       None, ShapeSet);
417   } else {
418     // make the frame's shape match the clients
419     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
420                        _innersize.left,
421                        _innersize.top,
422                        _client->window(), ShapeBounding, ShapeSet);
423
424     int num = 0;
425     XRectangle xrect[2];
426
427     if (decorations & Client::Decor_Titlebar) {
428       xrect[0].x = -geom.bevel;
429       xrect[0].y = -geom.bevel;
430       xrect[0].width = geom.width + geom.bwidth * 2;
431       xrect[0].height = geom.title_height() + geom.bwidth * 2;
432       ++num;
433     }
434
435     if (decorations & Client::Decor_Handle) {
436       xrect[1].x = -geom.bevel;
437       xrect[1].y = geom.handle_y;
438       xrect[1].width = geom.width + geom.bwidth * 2;
439       xrect[1].height = geom.handle_height + geom.bwidth * 2;
440       ++num;
441     }
442
443     XShapeCombineRectangles(**otk::display, _frame,
444                             ShapeBounding, 0, 0, xrect, num,
445                             ShapeUnion, Unsorted);
446   }
447 #endif // SHAPE
448 }
449
450
451 void Frame::adjustState()
452 {
453 // XXX  _button_alldesk.update();
454 // XXX  _button_max.update();
455 }
456
457
458 void Frame::grabClient()
459 {
460   // reparent the client to the frame
461   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
462   /*
463     When reparenting the client window, it is usually not mapped yet, since
464     this occurs from a MapRequest. However, in the case where Openbox is
465     starting up, the window is already mapped, so we'll see unmap events for
466     it. There are 2 unmap events generated that we see, one with the 'event'
467     member set the root window, and one set to the client, but both get handled
468     and need to be ignored.
469   */
470   if (openbox->state() == Openbox::State_Starting)
471     _client->ignore_unmaps += 2;
472
473   // select the event mask on the client's parent (to receive config/map req's)
474   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
475
476   // map the client so it maps when the frame does
477   XMapWindow(**otk::display, _client->window());
478
479   adjustSize();
480   adjustPosition();
481 }
482
483
484 void Frame::releaseClient()
485 {
486   XEvent ev;
487
488   // check if the app has already reparented its window away
489   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
490                              ReparentNotify, &ev)) {
491     XPutBackEvent(**otk::display, &ev);
492     // re-map the window since the unmanaging process unmaps it
493     XMapWindow(**otk::display, _client->window());  
494   } else {
495     // according to the ICCCM - if the client doesn't reparent itself, then we
496     // will reparent the window to root for them
497     XReparentWindow(**otk::display, _client->window(),
498                     otk::display->screenInfo(_client->screen())->rootWindow(),
499                     _client->area().x(), _client->area().y());
500   }
501 }
502
503
504 void Frame::clientGravity(int &x, int &y)
505 {
506   // horizontal
507   switch (_client->gravity()) {
508   default:
509   case NorthWestGravity:
510   case SouthWestGravity:
511   case WestGravity:
512     break;
513
514   case NorthGravity:
515   case SouthGravity:
516   case CenterGravity:
517     x -= (_size.left + _size.right) / 2;
518     break;
519
520   case NorthEastGravity:
521   case SouthEastGravity:
522   case EastGravity:
523     x -= _size.left + _size.right;
524     break;
525
526   case ForgetGravity:
527   case StaticGravity:
528     x -= _size.left;
529     break;
530   }
531
532   // vertical
533   switch (_client->gravity()) {
534   default:
535   case NorthWestGravity:
536   case NorthEastGravity:
537   case NorthGravity:
538     break;
539
540   case CenterGravity:
541   case EastGravity:
542   case WestGravity:
543     y -= (_size.top + _size.bottom) / 2;
544     break;
545
546   case SouthWestGravity:
547   case SouthEastGravity:
548   case SouthGravity:
549     y -= _size.top + _size.bottom;
550     break;
551
552   case ForgetGravity:
553   case StaticGravity:
554     y -= _size.top;
555     break;
556   }
557 }
558
559
560 void Frame::frameGravity(int &x, int &y)
561 {
562   // horizontal
563   switch (_client->gravity()) {
564   default:
565   case NorthWestGravity:
566   case WestGravity:
567   case SouthWestGravity:
568     break;
569   case NorthGravity:
570   case CenterGravity:
571   case SouthGravity:
572     x += (_size.left + _size.right) / 2;
573     break;
574   case NorthEastGravity:
575   case EastGravity:
576   case SouthEastGravity:
577     x += _size.left + _size.right;
578     break;
579   case StaticGravity:
580   case ForgetGravity:
581     x += _size.left;
582     break;
583   }
584
585   // vertical
586   switch (_client->gravity()) {
587   default:
588   case NorthWestGravity:
589   case WestGravity:
590   case SouthWestGravity:
591     break;
592   case NorthGravity:
593   case CenterGravity:
594   case SouthGravity:
595     y += (_size.top + _size.bottom) / 2;
596     break;
597   case NorthEastGravity:
598   case EastGravity:
599   case SouthEastGravity:
600     y += _size.top + _size.bottom;
601     break;
602   case StaticGravity:
603   case ForgetGravity:
604     y += _size.top;
605     break;
606   }
607 }
608
609
610 }