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