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