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