]> icculus.org git repositories - dana/openbox.git/blob - src/frame.cc
more layout fixes for the decoration elements
[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 "otk/display.hh"
16
17 #include <string>
18
19 namespace ob {
20
21 OBFrame::OBFrame(const OBClient *client, const otk::Style *style)
22   : _client(client),
23     _screen(otk::OBDisplay::screenInfo(client->screen()))
24 {
25   assert(client);
26   assert(style);
27  
28   _decorations = client->decorations();
29  
30   // create the base frame parent window
31   _window = createFrame();
32   assert(_window);
33
34   // create all of the style element child windows
35   _titlebar = createChild(_window, 0);
36   assert(_titlebar);
37   _button_iconify = createChild(_titlebar, 0);
38   assert(_button_iconify);
39   _button_max = createChild(_titlebar, 0);
40   assert(_button_max);
41   _button_stick = createChild(_titlebar, 0);
42   assert(_button_stick);
43   _button_close = createChild(_titlebar, 0);
44   assert(_button_close);
45   _label = createChild(_titlebar, 0);
46   assert(_label);
47   XMapSubwindows(otk::OBDisplay::display, _titlebar);
48
49   _handle = createChild(_window, 0);
50   assert(_handle);
51   _grip_left = createChild(_handle, 0);
52   assert(_grip_left);
53   _grip_right = createChild(_handle, 0);
54   assert(_grip_right);
55   XMapSubwindows(otk::OBDisplay::display, _handle);
56   
57   _style = 0;
58   loadStyle(style);
59
60   grabClient();
61 }
62
63
64 OBFrame::~OBFrame()
65 {
66   XDestroyWindow(otk::OBDisplay::display, _button_iconify);
67   XDestroyWindow(otk::OBDisplay::display, _button_max);
68   XDestroyWindow(otk::OBDisplay::display, _button_stick);
69   XDestroyWindow(otk::OBDisplay::display, _button_close);
70   XDestroyWindow(otk::OBDisplay::display, _label);
71   XDestroyWindow(otk::OBDisplay::display, _titlebar);
72   XDestroyWindow(otk::OBDisplay::display, _grip_left);
73   XDestroyWindow(otk::OBDisplay::display, _grip_right);
74   XDestroyWindow(otk::OBDisplay::display, _handle);
75
76   releaseClient(false);
77
78   XDestroyWindow(otk::OBDisplay::display, _window);
79 }
80
81
82 void OBFrame::loadStyle(const otk::Style *style)
83 {
84   assert(style);
85
86   // if a style was previously set, then 'replace' is true, cause we're
87   // replacing a style
88   bool replace = (_style);
89
90   if (replace) {
91     // XXX: do shit here whatever
92   }
93   
94   _style = style;
95
96   XSetWindowBorderWidth(otk::OBDisplay::display, _window,
97                         _style->getBorderWidth());
98   XSetWindowBorder(otk::OBDisplay::display, _window,
99                    _style->getBorderColor().pixel());
100   XSetWindowBorderWidth(otk::OBDisplay::display, _titlebar,
101                         _style->getBorderWidth());
102   XSetWindowBorder(otk::OBDisplay::display, _titlebar,
103                    _style->getBorderColor().pixel());
104   XSetWindowBorderWidth(otk::OBDisplay::display, _grip_left,
105                         _style->getBorderWidth());
106   XSetWindowBorder(otk::OBDisplay::display, _grip_left,
107                    _style->getBorderColor().pixel());
108   XSetWindowBorderWidth(otk::OBDisplay::display, _grip_right,
109                         _style->getBorderWidth());
110   XSetWindowBorder(otk::OBDisplay::display, _grip_right,
111                    _style->getBorderColor().pixel());
112   XSetWindowBorderWidth(otk::OBDisplay::display, _handle,
113                         _style->getBorderWidth());
114   XSetWindowBorder(otk::OBDisplay::display, _handle,
115                    _style->getBorderColor().pixel());
116   
117   // XXX: if (focused)
118     XSetWindowBackground(otk::OBDisplay::display, _window,
119                          _style->getFrameFocus().color().pixel());
120   // XXX: else  
121   // XXX:  XSetWindowBackground(otk::OBDisplay::display, _window,
122   // XXX:                       _style->getFrameUnfocus().color().pixel());
123
124   // if !replace, then update() will get called after the client is grabbed!
125   if (replace) {
126     update();
127     
128     // XXX: make everything redraw
129   }
130 }
131
132
133 void OBFrame::update()
134 {
135   // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
136   _decorations = _client->decorations();
137
138   int width;   // the width of the client window and the border around it
139   
140   if (_decorations & OBClient::Decor_Border) {
141     _size.left = _size.top = _size.bottom = _size.right =
142       _style->getFrameWidth();
143     width = _client->area().width() + _style->getFrameWidth() * 2;
144   } else {
145     _size.left = _size.top = _size.bottom = _size.right = 0;
146     width = _client->area().width();
147   }
148
149   if (_decorations & OBClient::Decor_Titlebar) {
150     // set the titlebar size
151     _titlebar_area.setRect(-_style->getBorderWidth(),
152                            -_style->getBorderWidth(),
153                            width,
154                            (_style->getFont().height() +
155                             _style->getBevelWidth() * 2));
156     _size.top += _titlebar_area.height() + _style->getBorderWidth();
157
158     // set the label size
159     _label_area.setRect(0, _style->getBevelWidth(),
160                         width, _style->getFont().height());
161     // set the buttons sizes
162     if (_decorations & OBClient::Decor_Iconify)
163       _button_iconify_area.setRect(0, _style->getBevelWidth() + 1,
164                                    _label_area.height() - 2,
165                                    _label_area.height() - 2);
166     if (_decorations & OBClient::Decor_Maximize)
167       _button_max_area.setRect(0, _style->getBevelWidth() + 1,
168                                _label_area.height() - 2,
169                                _label_area.height() - 2);
170     if (_decorations & OBClient::Decor_Sticky)
171       _button_stick_area.setRect(0, _style->getBevelWidth() + 1,
172                                  _label_area.height() - 2,
173                                  _label_area.height() - 2);
174     if (_decorations & OBClient::Decor_Close)
175       _button_close_area.setRect(0, _style->getBevelWidth() + 1,
176                                  _label_area.height() - 2,
177                                  _label_area.height() - 2);
178
179     // separation between titlebar elements
180     const int sep = _style->getBevelWidth() + 1;
181
182     std::string layout = "ILMC"; // XXX: get this from somewhere
183     // XXX: it is REQUIRED that by this point, the string only has one of each
184     // possible letter, all of the letters are valid, and L exists somewhere in
185     // the string!
186
187     int x = sep;
188     for (int i = 0, len = layout.size(); i < len; ++i) {
189       otk::Rect *area;
190       switch (layout[i]) {
191       case 'I':
192         if (!(_decorations & OBClient::Decor_Iconify))
193           continue; // skip it
194         area = &_button_iconify_area;
195         break;
196       case 'L':
197         area = &_label_area;
198         break;
199       case 'M':
200         if (!(_decorations & OBClient::Decor_Maximize))
201           continue; // skip it
202         area = &_button_max_area;
203         break;
204       case 'S':
205         if (!(_decorations & OBClient::Decor_Sticky))
206           continue; // skip it
207         area = &_button_stick_area;
208         break;
209       case 'C':
210         if (!(_decorations & OBClient::Decor_Close))
211           continue; // skip it
212         area = &_button_close_area;
213         break;
214       default:
215         assert(false); // the layout string is invalid!
216         continue; // just to fuck with g++
217       }
218       area->setX(x);
219       if (layout[i] != 'L')
220         _label_area.setWidth(_label_area.width() - area->width());
221       x += sep + area->width();
222     }
223   }
224
225   if (_decorations & OBClient::Decor_Handle) {
226     _handle_area.setRect(-_style->getBorderWidth(),
227                          _size.top + _client->area().height() +
228                          _style->getFrameWidth(),
229                          width, _style->getHandleWidth());
230     _grip_left_area.setRect(-_style->getBorderWidth(),
231                             -_style->getBorderWidth(),
232                             // XXX: get a Point class in otk and use that for
233                             // the 'buttons size' since theyre all the same
234                             _button_iconify_area.width() * 2,
235                             _handle_area.height());
236     _grip_right_area.setRect(((_handle_area.right() + 1) -
237                               _button_iconify_area.width() * 2),
238                              -_style->getBorderWidth(),
239                              // XXX: get a Point class in otk and use that for
240                              // the 'buttons size' since theyre all the same
241                              _button_iconify_area.width() * 2,
242                              _handle_area.height());
243     _size.bottom += _handle_area.height() + _style->getBorderWidth();
244   }
245   
246
247   // position/size all the windows
248
249   XResizeWindow(otk::OBDisplay::display, _window,
250                 _size.left + _size.right + _client->area().width(),
251                 _size.top + _size.bottom + _client->area().height());
252
253   XMoveWindow(otk::OBDisplay::display, _client->window(),
254               _size.left, _size.top);
255
256   if (_decorations & OBClient::Decor_Titlebar) {
257     XMoveResizeWindow(otk::OBDisplay::display, _titlebar,
258                       _titlebar_area.x(), _titlebar_area.y(),
259                       _titlebar_area.width(), _titlebar_area.height());
260     XMoveResizeWindow(otk::OBDisplay::display, _label,
261                       _label_area.x(), _label_area.y(),
262                       _label_area.width(), _label_area.height());
263     if (_decorations & OBClient::Decor_Iconify)
264       XMoveResizeWindow(otk::OBDisplay::display, _button_iconify,
265                         _button_iconify_area.x(), _button_iconify_area.y(),
266                         _button_iconify_area.width(),
267                         _button_iconify_area.height());
268     if (_decorations & OBClient::Decor_Maximize)
269       XMoveResizeWindow(otk::OBDisplay::display, _button_max,
270                         _button_max_area.x(), _button_max_area.y(),
271                         _button_max_area.width(),
272                         _button_max_area.height());
273     if (_decorations & OBClient::Decor_Sticky)
274       XMoveResizeWindow(otk::OBDisplay::display, _button_stick,
275                         _button_stick_area.x(), _button_stick_area.y(),
276                         _button_stick_area.width(),
277                         _button_stick_area.height());
278     if (_decorations & OBClient::Decor_Close)
279       XMoveResizeWindow(otk::OBDisplay::display, _button_close,
280                         _button_close_area.x(), _button_close_area.y(),
281                         _button_close_area.width(),
282                         _button_close_area.height());
283   }
284
285   if (_decorations & OBClient::Decor_Handle) {
286     XMoveResizeWindow(otk::OBDisplay::display, _handle,
287                       _handle_area.x(), _handle_area.y(),
288                       _handle_area.width(), _handle_area.height());
289     XMoveResizeWindow(otk::OBDisplay::display, _grip_left,
290                       _grip_left_area.x(), _grip_left_area.y(),
291                       _grip_left_area.width(), _grip_left_area.height());
292     XMoveResizeWindow(otk::OBDisplay::display, _grip_right,
293                       _grip_right_area.x(), _grip_right_area.y(),
294                       _grip_right_area.width(), _grip_right_area.height());
295   }
296
297   // map/unmap all the windows
298   if (_decorations & OBClient::Decor_Titlebar) {
299     XMapWindow(otk::OBDisplay::display, _label);
300     if (_decorations & OBClient::Decor_Iconify)
301       XMapWindow(otk::OBDisplay::display, _button_iconify);
302     else
303       XUnmapWindow(otk::OBDisplay::display, _button_iconify);
304     if (_decorations & OBClient::Decor_Maximize)
305       XMapWindow(otk::OBDisplay::display, _button_max);
306     else
307       XUnmapWindow(otk::OBDisplay::display, _button_max);
308     if (_decorations & OBClient::Decor_Sticky)
309       XMapWindow(otk::OBDisplay::display, _button_stick);
310     else
311       XUnmapWindow(otk::OBDisplay::display, _button_stick);
312     if (_decorations & OBClient::Decor_Close)
313       XMapWindow(otk::OBDisplay::display, _button_close);
314     else
315       XUnmapWindow(otk::OBDisplay::display, _button_close);
316     XMapWindow(otk::OBDisplay::display, _titlebar);
317   } else {
318     XUnmapWindow(otk::OBDisplay::display, _titlebar);
319     XUnmapWindow(otk::OBDisplay::display, _label);
320     XUnmapWindow(otk::OBDisplay::display, _button_iconify);
321     XUnmapWindow(otk::OBDisplay::display, _button_max);
322     XUnmapWindow(otk::OBDisplay::display, _button_stick);
323     XUnmapWindow(otk::OBDisplay::display, _button_close);
324   }
325
326   if (_decorations & OBClient::Decor_Handle) {
327     XMapWindow(otk::OBDisplay::display, _grip_left);
328     XMapWindow(otk::OBDisplay::display, _grip_right);
329     XMapWindow(otk::OBDisplay::display, _handle);
330   } else {
331     XUnmapWindow(otk::OBDisplay::display, _handle);
332     XUnmapWindow(otk::OBDisplay::display, _grip_left);
333     XUnmapWindow(otk::OBDisplay::display, _grip_right);
334   }
335   
336   // XXX: more is gunna have to happen here
337
338   updateShape();
339 }
340
341
342 void OBFrame::updateShape()
343 {
344 #ifdef SHAPE
345   if (!_client->shaped()) {
346     // clear the shape on the frame window
347     XShapeCombineMask(otk::OBDisplay::display, _window, ShapeBounding,
348                       _size.left,
349                       _size.top,
350                       None, ShapeSet);
351   } else {
352     // make the frame's shape match the clients
353     XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
354                        _size.left,
355                        _size.top,
356                        _client->window(), ShapeBounding, ShapeSet);
357
358   int num = 0;
359     XRectangle xrect[2];
360
361     /*
362     if (decorations & Decor_Titlebar) {
363     xrect[0].x = xrect[0].y = -frame.border_w;
364     xrect[0].width = frame.rect.width();
365     xrect[0].height = frame.title_h + (frame.border_w * 2);
366     ++num;
367     }
368
369     if (decorations & Decor_Handle) {
370     xrect[1].x = -frame.border_w;
371     xrect[1].y = frame.rect.height() - frame.margin.bottom +
372     frame.mwm_border_w - frame.border_w;
373     xrect[1].width = frame.rect.width();
374     xrect[1].height = frame.handle_h + (frame.border_w * 2);
375     ++num;
376     }*/
377
378     XShapeCombineRectangles(otk::OBDisplay::display, _window,
379                             ShapeBounding, 0, 0, xrect, num,
380                             ShapeUnion, Unsorted);
381   }
382 #endif // SHAPE
383 }
384
385
386 void OBFrame::grabClient()
387 {
388   
389   XGrabServer(otk::OBDisplay::display);
390
391   // select the event mask on the frame
392   XSelectInput(otk::OBDisplay::display, _window, SubstructureRedirectMask);
393
394   // reparent the client to the frame
395   XSelectInput(otk::OBDisplay::display, _client->window(),
396                OBClient::event_mask & ~StructureNotifyMask);
397   XReparentWindow(otk::OBDisplay::display, _client->window(), _window, 0, 0);
398   XSelectInput(otk::OBDisplay::display, _client->window(),
399                OBClient::event_mask);
400
401   // raise the client above the frame
402   XRaiseWindow(otk::OBDisplay::display, _client->window());
403   // map the client so it maps when the frame does
404   XMapWindow(otk::OBDisplay::display, _client->window());
405   
406   XUngrabServer(otk::OBDisplay::display);
407
408   update();
409 }
410
411
412 void OBFrame::releaseClient(bool remap)
413 {
414   // check if the app has already reparented its window to the root window
415   XEvent ev;
416   if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
417                              ReparentNotify, &ev)) {
418     remap = true; // XXX: why do we remap the window if they already
419                   // reparented to root?
420   } else {
421     // according to the ICCCM - if the client doesn't reparent to
422     // root, then we have to do it for them
423     XReparentWindow(otk::OBDisplay::display, _client->window(),
424                     _screen->getRootWindow(),
425                     _client->area().x(), _client->area().y());
426   }
427
428   // if we want to remap the window, do so now
429   if (remap)
430     XMapWindow(otk::OBDisplay::display, _client->window());
431 }
432
433
434 Window OBFrame::createChild(Window parent, Cursor cursor)
435 {
436   XSetWindowAttributes attrib_create;
437   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
438
439   attrib_create.background_pixmap = None;
440   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
441                              ButtonMotionMask | ExposureMask;
442
443   if (cursor) {
444     create_mask |= CWCursor;
445     attrib_create.cursor = cursor;
446   }
447
448   Window w = XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0,
449                            _screen->getDepth(), InputOutput,
450                            _screen->getVisual(), create_mask, &attrib_create);
451   XRaiseWindow(otk::OBDisplay::display, w); // raise above the parent
452   return w;
453 }
454
455
456 Window OBFrame::createFrame()
457 {
458   XSetWindowAttributes attrib_create;
459   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
460                               CWOverrideRedirect | CWEventMask;
461
462   attrib_create.background_pixmap = None;
463   attrib_create.colormap = _screen->getColormap();
464   attrib_create.override_redirect = True;
465   attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | ButtonPress;
466   /*
467     We catch button presses because other wise they get passed down to the
468     root window, which will then cause root menus to show when you click the
469     window's frame.
470   */
471
472   return XCreateWindow(otk::OBDisplay::display, _screen->getRootWindow(),
473                        0, 0, 1, 1, 0,
474                        _screen->getDepth(), InputOutput, _screen->getVisual(),
475                        create_mask, &attrib_create);
476 }
477
478 }