]> icculus.org git repositories - mikachu/openbox.git/blob - src/frame.cc
add \n's to the signal printfs
[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     XSetWindowBorder(otk::OBDisplay::display, _titlebar,
271                      _style->getBorderWidth());
272     XMapWindow(otk::OBDisplay::display, _label);
273     if (_decorations & OBClient::Decor_Iconify)
274       XMapWindow(otk::OBDisplay::display, _button_iconify);
275     else
276       XUnmapWindow(otk::OBDisplay::display, _button_iconify);
277     if (_decorations & OBClient::Decor_Maximize)
278       XMapWindow(otk::OBDisplay::display, _button_max);
279     else
280       XUnmapWindow(otk::OBDisplay::display, _button_max);
281     if (_decorations & OBClient::Decor_Sticky)
282       XMapWindow(otk::OBDisplay::display, _button_stick);
283     else
284       XUnmapWindow(otk::OBDisplay::display, _button_stick);
285     if (_decorations & OBClient::Decor_Close)
286       XMapWindow(otk::OBDisplay::display, _button_close);
287     else
288       XUnmapWindow(otk::OBDisplay::display, _button_close);
289   } else {
290     XUnmapWindow(otk::OBDisplay::display, _titlebar);
291     XUnmapWindow(otk::OBDisplay::display, _label);
292     XUnmapWindow(otk::OBDisplay::display, _button_iconify);
293     XUnmapWindow(otk::OBDisplay::display, _button_max);
294     XUnmapWindow(otk::OBDisplay::display, _button_stick);
295     XUnmapWindow(otk::OBDisplay::display, _button_close);
296   }
297
298   if (_decorations & OBClient::Decor_Handle) {
299     XMapWindow(otk::OBDisplay::display, _handle);
300     XSetWindowBorder(otk::OBDisplay::display, _handle,
301                      _style->getBorderWidth());
302     XMapWindow(otk::OBDisplay::display, _grip_left);
303     XSetWindowBorder(otk::OBDisplay::display, _grip_left,
304                      _style->getBorderWidth());
305     XMapWindow(otk::OBDisplay::display, _grip_right);
306     XSetWindowBorder(otk::OBDisplay::display, _grip_right,
307                      _style->getBorderWidth());
308   } else {
309     XUnmapWindow(otk::OBDisplay::display, _handle);
310     XUnmapWindow(otk::OBDisplay::display, _grip_left);
311     XUnmapWindow(otk::OBDisplay::display, _grip_right);
312   }
313   
314   // XXX: more is gunna have to happen here
315
316   updateShape();
317 }
318
319
320 void OBFrame::updateShape()
321 {
322 #ifdef SHAPE
323   if (!_client->shaped()) {
324     // clear the shape on the frame window
325     XShapeCombineMask(otk::OBDisplay::display, _window, ShapeBounding,
326                       _size.left - 2,//frame.margin.left - frame.border_w,
327                       _size.top - 2,//frame.margin.top - frame.border_w,
328                       None, ShapeSet);
329   } else {
330     // make the frame's shape match the clients
331     XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
332                        _size.left - 2,
333                        _size.top - 2,
334                        _client->window(), ShapeBounding, ShapeSet);
335
336   int num = 0;
337     XRectangle xrect[2];
338
339     /*
340     if (decorations & Decor_Titlebar) {
341     xrect[0].x = xrect[0].y = -frame.border_w;
342     xrect[0].width = frame.rect.width();
343     xrect[0].height = frame.title_h + (frame.border_w * 2);
344     ++num;
345     }
346
347     if (decorations & Decor_Handle) {
348     xrect[1].x = -frame.border_w;
349     xrect[1].y = frame.rect.height() - frame.margin.bottom +
350     frame.mwm_border_w - frame.border_w;
351     xrect[1].width = frame.rect.width();
352     xrect[1].height = frame.handle_h + (frame.border_w * 2);
353     ++num;
354     }*/
355
356     XShapeCombineRectangles(otk::OBDisplay::display, _window,
357                             ShapeBounding, 0, 0, xrect, num,
358                             ShapeUnion, Unsorted);
359   }
360 #endif // SHAPE
361 }
362
363
364 void OBFrame::grabClient()
365 {
366   
367   XGrabServer(otk::OBDisplay::display);
368
369   // select the event mask on the frame
370   XSelectInput(otk::OBDisplay::display, _window, SubstructureRedirectMask);
371
372   // reparent the client to the frame
373   XSelectInput(otk::OBDisplay::display, _client->window(),
374                OBClient::event_mask & ~StructureNotifyMask);
375   XReparentWindow(otk::OBDisplay::display, _client->window(), _window,
376                   _size.left, _size.top);
377   XSelectInput(otk::OBDisplay::display, _client->window(),
378                OBClient::event_mask);
379
380   // raise the client above the frame
381   XRaiseWindow(otk::OBDisplay::display, _client->window());
382   // map the client so it maps when the frame does
383   XMapWindow(otk::OBDisplay::display, _client->window());
384   
385   XUngrabServer(otk::OBDisplay::display);
386
387   update();
388 }
389
390
391 void OBFrame::releaseClient(bool remap)
392 {
393   // check if the app has already reparented its window to the root window
394   XEvent ev;
395   if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
396                              ReparentNotify, &ev)) {
397     remap = true; // XXX: why do we remap the window if they already
398                   // reparented to root?
399   } else {
400     // according to the ICCCM - if the client doesn't reparent to
401     // root, then we have to do it for them
402     XReparentWindow(otk::OBDisplay::display, _client->window(),
403                     _screen->getRootWindow(),
404                     _client->area().x(), _client->area().y());
405   }
406
407   // if we want to remap the window, do so now
408   if (remap)
409     XMapWindow(otk::OBDisplay::display, _client->window());
410 }
411
412
413 Window OBFrame::createChild(Window parent, Cursor cursor)
414 {
415   XSetWindowAttributes attrib_create;
416   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
417
418   attrib_create.background_pixmap = None;
419   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
420                              ButtonMotionMask | ExposureMask;
421
422   if (cursor) {
423     create_mask |= CWCursor;
424     attrib_create.cursor = cursor;
425   }
426
427   return XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0,
428                        _screen->getDepth(), InputOutput, _screen->getVisual(),
429                        create_mask, &attrib_create);
430 }
431
432
433 Window OBFrame::createFrame()
434 {
435   XSetWindowAttributes attrib_create;
436   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
437                               CWOverrideRedirect | CWEventMask;
438
439   attrib_create.background_pixmap = None;
440   attrib_create.colormap = _screen->getColormap();
441   attrib_create.override_redirect = True;
442   attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | ButtonPress;
443   /*
444     We catch button presses because other wise they get passed down to the
445     root window, which will then cause root menus to show when you click the
446     window's frame.
447   */
448
449   return XCreateWindow(otk::OBDisplay::display, _screen->getRootWindow(),
450                        0, 0, 1, 1, _style->getBorderWidth(),
451                        _screen->getDepth(), InputOutput, _screen->getVisual(),
452                        create_mask, &attrib_create);
453 }
454
455 }