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