]> icculus.org git repositories - mikachu/openbox.git/blob - src/frame.cc
frames' subemelents are created and positioned and everything
[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();
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                          width, _style->getHandleWidth());
197     _grip_left_area.setRect(0,
198                             _handle_area.y() + _handle_area.height(),
199                             // XXX: get a Point class in otk and use that for
200                             // the 'buttons size' since theyre all the same
201                             _button_iconify_area.width() * 2,
202                             _handle_area.height());
203     _grip_right_area.setRect(((_handle_area.right() + 1) -
204                               _button_iconify_area.width() * 2),
205                              _handle_area.y() + _handle_area.height(),
206                              // XXX: get a Point class in otk and use that for
207                              // the 'buttons size' since theyre all the same
208                              _button_iconify_area.width() * 2,
209                              _handle_area.height());
210     _size.bottom += _handle_area.height();
211   }
212   
213
214   // position/size all the windows
215
216   XResizeWindow(otk::OBDisplay::display, _window,
217                 _size.left + _size.right + _client->area().width(),
218                 _size.top + _size.bottom + _client->area().height());
219
220   XMoveWindow(otk::OBDisplay::display, _client->window(),
221               _size.left, _size.top);
222
223   if (_decorations & OBClient::Decor_Titlebar) {
224     XMoveResizeWindow(otk::OBDisplay::display, _titlebar,
225                       _titlebar_area.x(), _titlebar_area.y(),
226                       _titlebar_area.width(), _titlebar_area.height());
227     XMoveResizeWindow(otk::OBDisplay::display, _label,
228                       _label_area.x(), _label_area.y(),
229                       _label_area.width(), _label_area.height());
230     if (_decorations & OBClient::Decor_Iconify)
231       XMoveResizeWindow(otk::OBDisplay::display, _button_iconify,
232                         _button_iconify_area.x(), _button_iconify_area.y(),
233                         _button_iconify_area.width(),
234                         _button_iconify_area.height());
235     if (_decorations & OBClient::Decor_Maximize)
236       XMoveResizeWindow(otk::OBDisplay::display, _button_max,
237                         _button_max_area.x(), _button_max_area.y(),
238                         _button_max_area.width(),
239                         _button_max_area.height());
240     if (_decorations & OBClient::Decor_Sticky)
241       XMoveResizeWindow(otk::OBDisplay::display, _button_stick,
242                         _button_stick_area.x(), _button_stick_area.y(),
243                         _button_stick_area.width(),
244                         _button_stick_area.height());
245     if (_decorations & OBClient::Decor_Close)
246       XMoveResizeWindow(otk::OBDisplay::display, _button_close,
247                         _button_close_area.x(), _button_close_area.y(),
248                         _button_close_area.width(),
249                         _button_close_area.height());
250   }
251
252   if (_decorations & OBClient::Decor_Handle) {
253     XMoveResizeWindow(otk::OBDisplay::display, _handle,
254                       _handle_area.x(), _handle_area.y(),
255                       _handle_area.width(), _handle_area.height());
256     XMoveResizeWindow(otk::OBDisplay::display, _grip_left,
257                       _grip_left_area.x(), _grip_left_area.y(),
258                       _grip_left_area.width(), _grip_left_area.height());
259     XMoveResizeWindow(otk::OBDisplay::display, _grip_right,
260                       _grip_right_area.x(), _grip_right_area.y(),
261                       _grip_right_area.width(), _grip_right_area.height());
262   }
263
264   // map/unmap all the windows
265   if (_decorations & OBClient::Decor_Titlebar) {
266     XMapWindow(otk::OBDisplay::display, _titlebar);
267     XMapWindow(otk::OBDisplay::display, _label);
268     if (_decorations & OBClient::Decor_Iconify)
269       XMapWindow(otk::OBDisplay::display, _button_iconify);
270     else
271       XUnmapWindow(otk::OBDisplay::display, _button_iconify);
272     if (_decorations & OBClient::Decor_Maximize)
273       XMapWindow(otk::OBDisplay::display, _button_max);
274     else
275       XUnmapWindow(otk::OBDisplay::display, _button_max);
276     if (_decorations & OBClient::Decor_Sticky)
277       XMapWindow(otk::OBDisplay::display, _button_stick);
278     else
279       XUnmapWindow(otk::OBDisplay::display, _button_stick);
280     if (_decorations & OBClient::Decor_Close)
281       XMapWindow(otk::OBDisplay::display, _button_close);
282     else
283       XUnmapWindow(otk::OBDisplay::display, _button_close);
284   } else {
285     XUnmapWindow(otk::OBDisplay::display, _titlebar);
286     XUnmapWindow(otk::OBDisplay::display, _label);
287     XUnmapWindow(otk::OBDisplay::display, _button_iconify);
288     XUnmapWindow(otk::OBDisplay::display, _button_max);
289     XUnmapWindow(otk::OBDisplay::display, _button_stick);
290     XUnmapWindow(otk::OBDisplay::display, _button_close);
291   }
292
293   if (_decorations & OBClient::Decor_Handle) {
294     XMapWindow(otk::OBDisplay::display, _handle);
295     XMapWindow(otk::OBDisplay::display, _grip_left);
296     XMapWindow(otk::OBDisplay::display, _grip_right);
297   } else {
298     XUnmapWindow(otk::OBDisplay::display, _handle);
299     XUnmapWindow(otk::OBDisplay::display, _grip_left);
300     XUnmapWindow(otk::OBDisplay::display, _grip_right);
301   }
302   
303   // XXX: more is gunna have to happen here
304
305   updateShape();
306 }
307
308
309 void OBFrame::updateShape()
310 {
311 #ifdef SHAPE
312   if (!_client->shaped()) {
313     // clear the shape on the frame window
314     XShapeCombineMask(otk::OBDisplay::display, _window, ShapeBounding,
315                       _size.left - 2,//frame.margin.left - frame.border_w,
316                       _size.top - 2,//frame.margin.top - frame.border_w,
317                       None, ShapeSet);
318   } else {
319     // make the frame's shape match the clients
320     XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
321                        _size.left - 2,
322                        _size.top - 2,
323                        _client->window(), ShapeBounding, ShapeSet);
324
325   int num = 0;
326     XRectangle xrect[2];
327
328     /*
329     if (decorations & Decor_Titlebar) {
330     xrect[0].x = xrect[0].y = -frame.border_w;
331     xrect[0].width = frame.rect.width();
332     xrect[0].height = frame.title_h + (frame.border_w * 2);
333     ++num;
334     }
335
336     if (decorations & Decor_Handle) {
337     xrect[1].x = -frame.border_w;
338     xrect[1].y = frame.rect.height() - frame.margin.bottom +
339     frame.mwm_border_w - frame.border_w;
340     xrect[1].width = frame.rect.width();
341     xrect[1].height = frame.handle_h + (frame.border_w * 2);
342     ++num;
343     }*/
344
345     XShapeCombineRectangles(otk::OBDisplay::display, _window,
346                             ShapeBounding, 0, 0, xrect, num,
347                             ShapeUnion, Unsorted);
348   }
349 #endif // SHAPE
350 }
351
352
353 void OBFrame::grabClient()
354 {
355   
356   XGrabServer(otk::OBDisplay::display);
357
358   // select the event mask on the frame
359   XSelectInput(otk::OBDisplay::display, _window, SubstructureRedirectMask);
360
361   // reparent the client to the frame
362   XSelectInput(otk::OBDisplay::display, _client->window(),
363                OBClient::event_mask & ~StructureNotifyMask);
364   XReparentWindow(otk::OBDisplay::display, _client->window(), _window,
365                   _size.left, _size.top);
366   XSelectInput(otk::OBDisplay::display, _client->window(),
367                OBClient::event_mask);
368
369   // raise the client above the frame
370   XRaiseWindow(otk::OBDisplay::display, _client->window());
371   // map the client so it maps when the frame does
372   XMapWindow(otk::OBDisplay::display, _client->window());
373   
374   XUngrabServer(otk::OBDisplay::display);
375
376   update();
377 }
378
379
380 void OBFrame::releaseClient(bool remap)
381 {
382   // check if the app has already reparented its window to the root window
383   XEvent ev;
384   if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
385                              ReparentNotify, &ev)) {
386     remap = true; // XXX: why do we remap the window if they already
387                   // reparented to root?
388   } else {
389     // according to the ICCCM - if the client doesn't reparent to
390     // root, then we have to do it for them
391     XReparentWindow(otk::OBDisplay::display, _client->window(),
392                     _screen->getRootWindow(),
393                     _client->area().x(), _client->area().y());
394   }
395
396   // if we want to remap the window, do so now
397   if (remap)
398     XMapWindow(otk::OBDisplay::display, _client->window());
399 }
400
401
402 Window OBFrame::createChild(Window parent, Cursor cursor)
403 {
404   XSetWindowAttributes attrib_create;
405   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
406
407   attrib_create.background_pixmap = None;
408   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
409                              ButtonMotionMask | ExposureMask;
410
411   if (cursor) {
412     create_mask |= CWCursor;
413     attrib_create.cursor = cursor;
414   }
415
416   return XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0,
417                        _screen->getDepth(), InputOutput, _screen->getVisual(),
418                        create_mask, &attrib_create);
419 }
420
421
422 Window OBFrame::createFrame()
423 {
424   XSetWindowAttributes attrib_create;
425   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
426                               CWOverrideRedirect | CWEventMask;
427
428   attrib_create.background_pixmap = None;
429   attrib_create.colormap = _screen->getColormap();
430   attrib_create.override_redirect = True;
431   attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | ButtonPress;
432   /*
433     We catch button presses because other wise they get passed down to the
434     root window, which will then cause root menus to show when you click the
435     window's frame.
436   */
437
438   return XCreateWindow(otk::OBDisplay::display, _screen->getRootWindow(),
439                        0, 0, 1, 1, _style->getBorderWidth(),
440                        _screen->getDepth(), InputOutput, _screen->getVisual(),
441                        create_mask, &attrib_create);
442 }
443
444 }