1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
4 # include "../config.h"
9 #include <X11/extensions/shape.h>
16 #include "otk/display.hh"
23 const long Frame::event_mask;
25 Window createWindow(const otk::ScreenInfo *info, Window parent,
26 unsigned long mask, XSetWindowAttributes *attrib)
28 return XCreateWindow(**otk::display, parent, 0, 0, 1, 1, 0,
29 info->depth(), InputOutput, info->visual(),
34 Frame::Frame(Client *client)
55 XSetWindowAttributes attrib;
57 const otk::ScreenInfo *info = otk::display->screenInfo(client->screen());
59 // create all of the decor windows (except title bar buttons)
60 mask = CWOverrideRedirect | CWEventMask;
61 attrib.event_mask = Frame::event_mask;
62 attrib.override_redirect = true;
63 _frame = createWindow(info, info->rootWindow(), mask, &attrib);
66 _plate = createWindow(info, _frame, mask, &attrib);
68 attrib.event_mask = (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
70 _title = createWindow(info, _frame, mask, &attrib);
71 _label = createWindow(info, _title, mask, &attrib);
72 _handle = createWindow(info, _frame, mask, &attrib);
74 attrib.cursor = openbox->cursors().ll_angle;
75 _lgrip = createWindow(info, _handle, mask, &attrib);
76 attrib.cursor = openbox->cursors().lr_angle;
77 _rgrip = createWindow(info, _handle, mask, &attrib);
79 // the other stuff is shown based on decor settings
80 XMapWindow(**otk::display, _plate);
81 XMapWindow(**otk::display, _lgrip);
82 XMapWindow(**otk::display, _rgrip);
83 XMapWindow(**otk::display, _label);
85 applyStyle(*otk::RenderStyle::style(_client->screen()));
89 _buttons = new Window[0];
90 _buttons_sur = new otk::Surface*[0];
91 _titleorder = new unsigned int[1];
92 _titleorder[0] = (unsigned)-1;
94 // register all of the windows with the event dispatcher
95 Window *w = allWindows();
96 for (unsigned int i = 0; w[i]; ++i)
97 openbox->registerHandler(w[i], this);
103 // unregister all of the windows with the event dispatcher
104 Window *w = allWindows();
105 for (unsigned int i = 0; w[i]; ++i)
106 openbox->clearHandler(w[i]);
109 for (unsigned int i = 0; i < _numbuttons; ++i) {
110 XDestroyWindow(**otk::display, _buttons[i]);
111 delete _buttons_sur[i];
113 XDestroyWindow(**otk::display, _rgrip);
114 XDestroyWindow(**otk::display, _lgrip);
115 XDestroyWindow(**otk::display, _handle);
116 XDestroyWindow(**otk::display, _label);
117 XDestroyWindow(**otk::display, _title);
118 XDestroyWindow(**otk::display, _frame);
120 if (_frame_sur) delete _frame_sur;
121 if (_title_sur) delete _title_sur;
122 if (_label_sur) delete _label_sur;
123 if (_handle_sur) delete _handle_sur;
124 if (_grip_sur) delete _grip_sur;
127 delete [] _titleorder;
128 delete [] _buttons_sur;
135 XMapWindow(**otk::display, _frame);
143 XUnmapWindow(**otk::display, _frame);
147 MouseContext::MC Frame::mouseContext(Window win) const
149 if (win == _frame) return MouseContext::Frame;
151 win == _label) return MouseContext::Titlebar;
152 if (win == _handle) return MouseContext::Handle;
153 if (win == _plate) return MouseContext::Window;
155 win == _rgrip) return MouseContext::Grip;
156 return (MouseContext::MC) -1;
159 Window *Frame::allWindows() const
161 Window *w = new Window[7 + _numbuttons + 1];
170 for (unsigned int j = 0; j < _numbuttons; ++j)
171 w[j + i++] = _buttons[j];
176 void Frame::applyStyle(const otk::RenderStyle &style)
178 // set static border colors
179 XSetWindowBorder(**otk::display, _frame, style.frameBorderColor()->pixel());
180 XSetWindowBorder(**otk::display, _title, style.frameBorderColor()->pixel());
181 XSetWindowBorder(**otk::display, _handle, style.frameBorderColor()->pixel());
182 XSetWindowBorder(**otk::display, _lgrip, style.frameBorderColor()->pixel());
183 XSetWindowBorder(**otk::display, _rgrip, style.frameBorderColor()->pixel());
185 // size all the fixed-size elements
186 geom.font_height = style.labelFont()->height();
187 if (geom.font_height < 1) geom.font_height = 1;
188 geom.button_size = geom.font_height - 2;
189 if (geom.button_size < 1) geom.button_size = 1;
190 geom.handle_height = style.handleWidth();
191 if (geom.handle_height < 1) geom.handle_height = 1;
192 geom.bevel = style.bevelWidth();
194 XResizeWindow(**otk::display, _lgrip, geom.grip_width(), geom.handle_height);
195 XResizeWindow(**otk::display, _rgrip, geom.grip_width(), geom.handle_height);
197 for (unsigned int i = 0; i < _numbuttons; ++i)
198 XResizeWindow(**otk::display, _buttons[i],
199 geom.button_size, geom.button_size);
202 void Frame::styleChanged(const otk::RenderStyle &style)
206 // size/position everything
211 void Frame::adjustFocus()
213 // XXX optimizations later...
217 void Frame::adjustTitle()
219 // XXX optimizations later...
223 static void render(int screen, const otk::Size &size, Window win,
224 otk::Surface **surface,
225 const otk::RenderTexture &texture)
227 otk::Surface *s = new otk::Surface(screen, size);
228 otk::display->renderControl(screen)->drawBackground(*s, texture);
229 XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
230 XClearWindow(**otk::display, win);
231 if (*surface) delete *surface;
235 void Frame::adjustSize()
237 Client::DecorationFlags decorations = _client->decorations();
238 const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
240 if (decorations & Client::Decor_Border) {
241 geom.bwidth = style->frameBorderWidth();
242 geom.cbwidth = style->clientBorderWidth();
244 geom.bwidth = geom.cbwidth = 0;
246 _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
248 geom.width = _client->area().width() + geom.cbwidth * 2;
249 assert(geom.width > 0);
252 XSetWindowBorderWidth(**otk::display, _plate, geom.cbwidth);
253 XSetWindowBorderWidth(**otk::display, _frame, geom.bwidth);
254 XSetWindowBorderWidth(**otk::display, _title, geom.bwidth);
255 XSetWindowBorderWidth(**otk::display, _handle, geom.bwidth);
256 XSetWindowBorderWidth(**otk::display, _lgrip, geom.bwidth);
257 XSetWindowBorderWidth(**otk::display, _rgrip, geom.bwidth);
259 // position/size and map/unmap all the windows
261 if (decorations & Client::Decor_Titlebar) {
262 XMoveResizeWindow(**otk::display, _title, -geom.bwidth, -geom.bwidth,
263 geom.width, geom.title_height());
264 _innersize.top += geom.title_height() + geom.bwidth;
265 XMapWindow(**otk::display, _title);
267 // layout the title bar elements
270 XUnmapWindow(**otk::display, _title);
272 if (decorations & Client::Decor_Handle) {
273 geom.handle_y = _innersize.top + _client->area().height() + geom.cbwidth;
274 XMoveResizeWindow(**otk::display, _handle, -geom.bwidth, geom.handle_y,
275 geom.width, geom.handle_height);
276 XMoveWindow(**otk::display, _lgrip, -geom.bwidth, -geom.bwidth);
277 XMoveWindow(**otk::display, _rgrip,
278 -geom.bwidth + geom.width - geom.grip_width(),
280 _innersize.bottom += geom.handle_height + geom.bwidth;
281 XMapWindow(**otk::display, _handle);
283 XUnmapWindow(**otk::display, _handle);
285 XResizeWindow(**otk::display, _frame, geom.width,
286 (_client->shaded() ? geom.title_height() :
287 _innersize.top + _innersize.bottom +
288 _client->area().height()));
290 // do this in two steps because clients whose gravity is set to
291 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
292 XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
293 _innersize.top - geom.cbwidth);
294 XResizeWindow(**otk::display, _plate, _client->area().width(),
295 _client->area().height());
297 _size.left = _innersize.left + geom.bwidth;
298 _size.right = _innersize.right + geom.bwidth;
299 _size.top = _innersize.top + geom.bwidth;
300 _size.bottom = _innersize.bottom + geom.bwidth;
302 _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
303 _size.left + _size.right,
304 _client->area().height() +
305 _size.top + _size.bottom));
307 // render all the elements
308 int screen = _client->screen();
309 bool focus = _client->focused();
310 if (decorations & Client::Decor_Titlebar) {
311 render(screen, otk::Size(geom.width, geom.title_height()), _title,
312 &_title_sur, *(focus ? style->titlebarFocusBackground() :
313 style->titlebarUnfocusBackground()));
318 if (decorations & Client::Decor_Handle) {
319 render(screen, otk::Size(geom.width, geom.handle_height), _handle,
320 &_handle_sur, *(focus ? style->handleFocusBackground() :
321 style->handleUnfocusBackground()));
322 render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
323 &_grip_sur, *(focus ? style->gripFocusBackground() :
324 style->gripUnfocusBackground()));
325 XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
326 XClearWindow(**otk::display, _rgrip);
329 XSetWindowBorder(**otk::display, _plate,
330 focus ? style->clientBorderFocusColor()->pixel() :
331 style->clientBorderUnfocusColor()->pixel());
336 void Frame::renderLabel()
338 const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
339 const otk::RenderControl *control =
340 otk::display->renderControl(_client->screen());
341 const otk::Font *font = style->labelFont();
343 otk::Surface *s = new otk::Surface(_client->screen(),
344 otk::Size(geom.label_width,
345 geom.label_height()));
346 control->drawBackground(*s, *(_client->focused() ?
347 style->labelFocusBackground() :
348 style->labelUnfocusBackground()));
350 otk::ustring t = _client->title(); // the actual text to draw
351 int x = geom.bevel; // x coord for the text
353 if ((unsigned)x * 2 > geom.label_width) return; // no room at all
355 // find a string that will fit inside the area for text
356 otk::ustring::size_type text_len = t.size();
358 unsigned int maxsize = geom.label_width - geom.bevel * 2;
362 length = font->measureString(t);
363 } while (length > maxsize && text_len-- > 0);
365 if (text_len <= 0) return; // won't fit anything
368 switch (style->labelTextJustify()) {
369 case otk::RenderStyle::RightBottomJustify:
370 x += maxsize - length;
372 case otk::RenderStyle::CenterJustify:
373 x += (maxsize - length) / 2;
375 case otk::RenderStyle::LeftTopJustify:
379 control->drawString(*s, *font, x, 0,
380 *(_client->focused() ? style->textFocusColor() :
381 style->textUnfocusColor()), t);
383 XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
384 XClearWindow(**otk::display, _label);
385 if (_label_sur) delete _label_sur;
389 void Frame::layoutTitle()
391 geom.label_width = geom.width - geom.bevel * 2;
392 if (geom.label_width < 1) geom.label_width = 1;
393 XMoveResizeWindow(**otk::display, _label, geom.bevel, geom.bevel,
394 geom.label_width, geom.font_height);
397 void Frame::adjustPosition()
400 x = _client->area().x();
401 y = _client->area().y();
403 XMoveWindow(**otk::display, _frame, x, y);
404 _area = otk::Rect(otk::Point(x, y), _area.size());
408 void Frame::adjustShape()
411 Client::DecorationFlags decorations = _client->decorations();
413 if (!_client->shaped()) {
414 // clear the shape on the frame window
415 XShapeCombineMask(**otk::display, _frame, ShapeBounding,
420 // make the frame's shape match the clients
421 XShapeCombineShape(**otk::display, _frame, ShapeBounding,
424 _client->window(), ShapeBounding, ShapeSet);
429 if (decorations & Client::Decor_Titlebar) {
430 xrect[0].x = -geom.bevel;
431 xrect[0].y = -geom.bevel;
432 xrect[0].width = geom.width + geom.bwidth * 2;
433 xrect[0].height = geom.title_height() + geom.bwidth * 2;
437 if (decorations & Client::Decor_Handle) {
438 xrect[1].x = -geom.bevel;
439 xrect[1].y = geom.handle_y;
440 xrect[1].width = geom.width + geom.bwidth * 2;
441 xrect[1].height = geom.handle_height + geom.bwidth * 2;
445 XShapeCombineRectangles(**otk::display, _frame,
446 ShapeBounding, 0, 0, xrect, num,
447 ShapeUnion, Unsorted);
453 void Frame::adjustState()
455 // XXX _button_alldesk.update();
456 // XXX _button_max.update();
460 void Frame::grabClient()
462 // reparent the client to the frame
463 XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
465 When reparenting the client window, it is usually not mapped yet, since
466 this occurs from a MapRequest. However, in the case where Openbox is
467 starting up, the window is already mapped, so we'll see unmap events for
468 it. There are 2 unmap events generated that we see, one with the 'event'
469 member set the root window, and one set to the client, but both get handled
470 and need to be ignored.
472 if (openbox->state() == Openbox::State_Starting)
473 _client->ignore_unmaps += 2;
475 // select the event mask on the client's parent (to receive config/map req's)
476 XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
478 // map the client so it maps when the frame does
479 XMapWindow(**otk::display, _client->window());
486 void Frame::releaseClient()
490 // check if the app has already reparented its window away
491 if (XCheckTypedWindowEvent(**otk::display, _client->window(),
492 ReparentNotify, &ev)) {
493 XPutBackEvent(**otk::display, &ev);
494 // re-map the window since the unmanaging process unmaps it
495 XMapWindow(**otk::display, _client->window());
497 // according to the ICCCM - if the client doesn't reparent itself, then we
498 // will reparent the window to root for them
499 XReparentWindow(**otk::display, _client->window(),
500 otk::display->screenInfo(_client->screen())->rootWindow(),
501 _client->area().x(), _client->area().y());
506 void Frame::clientGravity(int &x, int &y)
509 switch (_client->gravity()) {
511 case NorthWestGravity:
512 case SouthWestGravity:
519 x -= (_size.left + _size.right) / 2;
522 case NorthEastGravity:
523 case SouthEastGravity:
525 x -= _size.left + _size.right;
535 switch (_client->gravity()) {
537 case NorthWestGravity:
538 case NorthEastGravity:
545 y -= (_size.top + _size.bottom) / 2;
548 case SouthWestGravity:
549 case SouthEastGravity:
551 y -= _size.top + _size.bottom;
562 void Frame::frameGravity(int &x, int &y)
565 switch (_client->gravity()) {
567 case NorthWestGravity:
569 case SouthWestGravity:
574 x += (_size.left + _size.right) / 2;
576 case NorthEastGravity:
578 case SouthEastGravity:
579 x += _size.left + _size.right;
588 switch (_client->gravity()) {
590 case NorthWestGravity:
592 case SouthWestGravity:
597 y += (_size.top + _size.bottom) / 2;
599 case NorthEastGravity:
601 case SouthEastGravity:
602 y += _size.top + _size.bottom;