1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
7 #include <X11/extensions/shape.h>
14 #include "otk/display.hh"
15 #include "otk/surface.hh"
22 const long Frame::event_mask
;
24 Window
createWindow(const otk::ScreenInfo
*info
, Window parent
,
25 unsigned long mask
, XSetWindowAttributes
*attrib
)
27 return XCreateWindow(**otk::display
, parent
, 0, 0, 1, 1, 0,
28 info
->depth(), InputOutput
, info
->visual(),
33 Frame::Frame(Client
*client
)
59 _iconify_press(false),
65 XSetWindowAttributes attrib
;
67 const otk::ScreenInfo
*info
= otk::display
->screenInfo(client
->screen());
69 // create all of the decor windows (except title bar buttons)
70 mask
= CWOverrideRedirect
| CWEventMask
;
71 attrib
.event_mask
= Frame::event_mask
;
72 attrib
.override_redirect
= true;
73 _frame
= createWindow(info
, info
->rootWindow(), mask
, &attrib
);
76 _plate
= createWindow(info
, _frame
, mask
, &attrib
);
78 attrib
.event_mask
= (ButtonPressMask
| ButtonReleaseMask
| ButtonMotionMask
|
80 _title
= createWindow(info
, _frame
, mask
, &attrib
);
81 _label
= createWindow(info
, _title
, mask
, &attrib
);
82 _max
= createWindow(info
, _title
, mask
, &attrib
);
83 _close
= createWindow(info
, _title
, mask
, &attrib
);
84 _desk
= createWindow(info
, _title
, mask
, &attrib
);
85 _icon
= createWindow(info
, _title
, mask
, &attrib
);
86 _iconify
= createWindow(info
, _title
, mask
, &attrib
);
87 _handle
= createWindow(info
, _frame
, mask
, &attrib
);
89 attrib
.cursor
= openbox
->cursors().ll_angle
;
90 _lgrip
= createWindow(info
, _handle
, mask
, &attrib
);
91 attrib
.cursor
= openbox
->cursors().lr_angle
;
92 _rgrip
= createWindow(info
, _handle
, mask
, &attrib
);
94 // the other stuff is shown based on decor settings
95 XMapWindow(**otk::display
, _plate
);
96 XMapWindow(**otk::display
, _lgrip
);
97 XMapWindow(**otk::display
, _rgrip
);
98 XMapWindow(**otk::display
, _label
);
100 applyStyle(*otk::RenderStyle::style(_client
->screen()));
104 // register all of the windows with the event dispatcher
105 Window
*w
= allWindows();
106 for (unsigned int i
= 0; w
[i
]; ++i
)
107 openbox
->registerHandler(w
[i
], this);
113 // unregister all of the windows with the event dispatcher
114 Window
*w
= allWindows();
115 for (unsigned int i
= 0; w
[i
]; ++i
)
116 openbox
->clearHandler(w
[i
]);
119 XDestroyWindow(**otk::display
, _rgrip
);
120 XDestroyWindow(**otk::display
, _lgrip
);
121 XDestroyWindow(**otk::display
, _handle
);
122 XDestroyWindow(**otk::display
, _max
);
123 XDestroyWindow(**otk::display
, _icon
);
124 XDestroyWindow(**otk::display
, _iconify
);
125 XDestroyWindow(**otk::display
, _desk
);
126 XDestroyWindow(**otk::display
, _close
);
127 XDestroyWindow(**otk::display
, _label
);
128 XDestroyWindow(**otk::display
, _title
);
129 XDestroyWindow(**otk::display
, _frame
);
131 if (_frame_sur
) delete _frame_sur
;
132 if (_title_sur
) delete _title_sur
;
133 if (_label_sur
) delete _label_sur
;
134 if (_handle_sur
) delete _handle_sur
;
135 if (_grip_sur
) delete _grip_sur
;
136 if (_max_sur
) delete _max_sur
;
137 if (_desk_sur
) delete _desk_sur
;
138 if (_iconify_sur
) delete _iconify_sur
;
139 if (_icon_sur
) delete _icon_sur
;
140 if (_close_sur
) delete _close_sur
;
147 XMapWindow(**otk::display
, _frame
);
155 XUnmapWindow(**otk::display
, _frame
);
159 MouseContext::MC
Frame::mouseContext(Window win
) const
161 if (win
== _frame
) return MouseContext::Frame
;
163 win
== _label
) return MouseContext::Titlebar
;
164 if (win
== _handle
) return MouseContext::Handle
;
165 if (win
== _plate
) return MouseContext::Window
;
167 win
== _rgrip
) return MouseContext::Grip
;
168 if (win
== _max
) return MouseContext::MaximizeButton
;
169 if (win
== _close
) return MouseContext::CloseButton
;
170 if (win
== _desk
) return MouseContext::AllDesktopsButton
;
171 if (win
== _iconify
)return MouseContext::IconifyButton
;
172 if (win
== _icon
) return MouseContext::IconButton
;
173 return (MouseContext::MC
) -1;
176 Window
*Frame::allWindows() const
178 Window
*w
= new Window
[12 + 1];
196 void Frame::applyStyle(const otk::RenderStyle
&style
)
198 // set static border colors
199 XSetWindowBorder(**otk::display
, _frame
, style
.frameBorderColor()->pixel());
200 XSetWindowBorder(**otk::display
, _title
, style
.frameBorderColor()->pixel());
201 XSetWindowBorder(**otk::display
, _handle
, style
.frameBorderColor()->pixel());
202 XSetWindowBorder(**otk::display
, _lgrip
, style
.frameBorderColor()->pixel());
203 XSetWindowBorder(**otk::display
, _rgrip
, style
.frameBorderColor()->pixel());
205 // size all the fixed-size elements
206 geom
.font_height
= style
.labelFont()->height();
207 if (geom
.font_height
< 1) geom
.font_height
= 1;
208 geom
.button_size
= geom
.font_height
- 2;
209 if (geom
.button_size
< 1) geom
.button_size
= 1;
210 geom
.handle_height
= style
.handleWidth();
211 if (geom
.handle_height
< 1) geom
.handle_height
= 1;
212 geom
.bevel
= style
.bevelWidth();
214 XResizeWindow(**otk::display
, _lgrip
, geom
.grip_width(), geom
.handle_height
);
215 XResizeWindow(**otk::display
, _rgrip
, geom
.grip_width(), geom
.handle_height
);
217 XResizeWindow(**otk::display
, _max
, geom
.button_size
, geom
.button_size
);
218 XResizeWindow(**otk::display
, _close
, geom
.button_size
, geom
.button_size
);
219 XResizeWindow(**otk::display
, _desk
, geom
.button_size
, geom
.button_size
);
220 XResizeWindow(**otk::display
, _iconify
, geom
.button_size
, geom
.button_size
);
221 XResizeWindow(**otk::display
, _icon
, geom
.button_size
, geom
.button_size
);
224 void Frame::styleChanged(const otk::RenderStyle
&style
)
228 // size/position everything
233 void Frame::adjustFocus()
235 // XXX optimizations later...
239 void Frame::adjustTitle()
241 // XXX optimizations later...
245 static void render(int screen
, const otk::Size
&size
, Window win
,
246 otk::Surface
**surface
,
247 const otk::RenderTexture
&texture
, bool freedata
=true)
249 otk::Surface
*s
= new otk::Surface(screen
, size
);
250 otk::display
->renderControl(screen
)->drawBackground(*s
, texture
);
251 XSetWindowBackgroundPixmap(**otk::display
, win
, s
->pixmap());
252 XClearWindow(**otk::display
, win
);
253 if (*surface
) delete *surface
;
254 if (freedata
) s
->freePixelData();
258 void Frame::adjustSize()
260 Client::DecorationFlags decorations
= _client
->decorations();
261 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
263 if (decorations
& Client::Decor_Border
) {
264 geom
.bwidth
= style
->frameBorderWidth();
265 geom
.cbwidth
= style
->clientBorderWidth();
267 geom
.bwidth
= geom
.cbwidth
= 0;
269 _innersize
.left
= _innersize
.top
= _innersize
.bottom
= _innersize
.right
=
271 geom
.width
= _client
->area().width() + geom
.cbwidth
* 2;
272 assert(geom
.width
> 0);
275 XSetWindowBorderWidth(**otk::display
, _plate
, geom
.cbwidth
);
276 XSetWindowBorderWidth(**otk::display
, _frame
, geom
.bwidth
);
277 XSetWindowBorderWidth(**otk::display
, _title
, geom
.bwidth
);
278 XSetWindowBorderWidth(**otk::display
, _handle
, geom
.bwidth
);
279 XSetWindowBorderWidth(**otk::display
, _lgrip
, geom
.bwidth
);
280 XSetWindowBorderWidth(**otk::display
, _rgrip
, geom
.bwidth
);
282 // position/size and map/unmap all the windows
284 if (decorations
& Client::Decor_Titlebar
) {
285 XMoveResizeWindow(**otk::display
, _title
, -geom
.bwidth
, -geom
.bwidth
,
286 geom
.width
, geom
.title_height());
287 _innersize
.top
+= geom
.title_height() + geom
.bwidth
;
288 XMapWindow(**otk::display
, _title
);
290 // layout the title bar elements
293 XUnmapWindow(**otk::display
, _title
);
295 if (decorations
& Client::Decor_Handle
) {
296 geom
.handle_y
= _innersize
.top
+ _client
->area().height() + geom
.cbwidth
;
297 XMoveResizeWindow(**otk::display
, _handle
, -geom
.bwidth
, geom
.handle_y
,
298 geom
.width
, geom
.handle_height
);
299 XMoveWindow(**otk::display
, _lgrip
, -geom
.bwidth
, -geom
.bwidth
);
300 XMoveWindow(**otk::display
, _rgrip
,
301 -geom
.bwidth
+ geom
.width
- geom
.grip_width(),
303 _innersize
.bottom
+= geom
.handle_height
+ geom
.bwidth
;
304 XMapWindow(**otk::display
, _handle
);
306 XUnmapWindow(**otk::display
, _handle
);
308 XResizeWindow(**otk::display
, _frame
, geom
.width
,
309 (_client
->shaded() ? geom
.title_height() :
310 _innersize
.top
+ _innersize
.bottom
+
311 _client
->area().height()));
313 // do this in two steps because clients whose gravity is set to
314 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
315 XMoveWindow(**otk::display
, _plate
, _innersize
.left
- geom
.cbwidth
,
316 _innersize
.top
- geom
.cbwidth
);
317 XResizeWindow(**otk::display
, _plate
, _client
->area().width(),
318 _client
->area().height());
320 _size
.left
= _innersize
.left
+ geom
.bwidth
;
321 _size
.right
= _innersize
.right
+ geom
.bwidth
;
322 _size
.top
= _innersize
.top
+ geom
.bwidth
;
323 _size
.bottom
= _innersize
.bottom
+ geom
.bwidth
;
325 _area
= otk::Rect(_area
.position(), otk::Size(_client
->area().width() +
326 _size
.left
+ _size
.right
,
327 _client
->area().height() +
328 _size
.top
+ _size
.bottom
));
330 // render all the elements
331 int screen
= _client
->screen();
332 bool focus
= _client
->focused();
333 if (decorations
& Client::Decor_Titlebar
) {
334 render(screen
, otk::Size(geom
.width
, geom
.title_height()), _title
,
335 &_title_sur
, *(focus
? style
->titlebarFocusBackground() :
336 style
->titlebarUnfocusBackground()), false);
346 if (decorations
& Client::Decor_Handle
) {
347 render(screen
, otk::Size(geom
.width
, geom
.handle_height
), _handle
,
348 &_handle_sur
, *(focus
? style
->handleFocusBackground() :
349 style
->handleUnfocusBackground()));
350 render(screen
, otk::Size(geom
.grip_width(), geom
.handle_height
), _lgrip
,
351 &_grip_sur
, *(focus
? style
->gripFocusBackground() :
352 style
->gripUnfocusBackground()));
353 XSetWindowBackgroundPixmap(**otk::display
, _rgrip
, _grip_sur
->pixmap());
354 XClearWindow(**otk::display
, _rgrip
);
357 XSetWindowBorder(**otk::display
, _plate
,
358 focus
? style
->clientBorderFocusColor()->pixel() :
359 style
->clientBorderUnfocusColor()->pixel());
364 void Frame::renderLabel()
366 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
367 const otk::RenderControl
*control
=
368 otk::display
->renderControl(_client
->screen());
369 const otk::Font
*font
= style
->labelFont();
371 otk::Surface
*s
= new otk::Surface(_client
->screen(),
372 otk::Size(geom
.label_width
,
373 geom
.label_height()));
374 control
->drawBackground(*s
, *(_client
->focused() ?
375 style
->labelFocusBackground() :
376 style
->labelUnfocusBackground()));
378 otk::ustring t
= _client
->title(); // the actual text to draw
379 int x
= geom
.bevel
; // x coord for the text
381 if (x
* 2 > geom
.label_width
) return; // no room at all
383 // find a string that will fit inside the area for text
384 otk::ustring::size_type text_len
= t
.size();
386 int maxsize
= geom
.label_width
- geom
.bevel
* 2;
390 length
= font
->measureString(t
); // this returns an unsigned, so check < 0
391 if (length
< 0) length
= maxsize
; // if the string's that long just adjust
392 } while (length
> maxsize
&& text_len
-- > 0);
394 if (text_len
<= 0) return; // won't fit anything
397 switch (style
->labelTextJustify()) {
398 case otk::RenderStyle::RightBottomJustify
:
399 x
+= maxsize
- length
;
401 case otk::RenderStyle::CenterJustify
:
402 x
+= (maxsize
- length
) / 2;
404 case otk::RenderStyle::LeftTopJustify
:
408 control
->drawString(*s
, *font
, x
, 0,
409 *(_client
->focused() ? style
->textFocusColor() :
410 style
->textUnfocusColor()), t
);
412 XSetWindowBackgroundPixmap(**otk::display
, _label
, s
->pixmap());
413 XClearWindow(**otk::display
, _label
);
414 if (_label_sur
) delete _label_sur
;
419 static void renderButton(int screen
, bool focus
, bool press
, Window win
,
420 otk::Surface
**sur
, int butsize
,
421 const otk::PixmapMask
*mask
)
423 const otk::RenderStyle
*style
= otk::RenderStyle::style(screen
);
424 const otk::RenderControl
*control
= otk::display
->renderControl(screen
);
425 otk::Surface
*s
= new otk::Surface(screen
, otk::Size(butsize
, butsize
));
427 const otk::RenderTexture
*tx
= (focus
?
429 style
->buttonPressFocusBackground() :
430 style
->buttonUnpressFocusBackground()) :
432 style
->buttonPressUnfocusBackground() :
433 style
->buttonUnpressUnfocusBackground()));
434 const otk::RenderColor
*maskcolor
= (focus
?
435 style
->buttonFocusColor() :
436 style
->buttonUnfocusColor());
437 control
->drawBackground(*s
, *tx
);
438 control
->drawMask(*s
, *maskcolor
, *mask
);
440 XSetWindowBackgroundPixmap(**otk::display
, win
, s
->pixmap());
441 XClearWindow(**otk::display
, win
);
442 if (*sur
) delete *sur
;
446 void Frame::renderMax()
448 renderButton(_client
->screen(), _client
->focused(), _max_press
, _max
,
449 &_max_sur
, geom
.button_size
,
450 otk::RenderStyle::style(_client
->screen())->maximizeMask());
453 void Frame::renderDesk()
455 renderButton(_client
->screen(), _client
->focused(), _desk_press
, _desk
,
456 &_desk_sur
, geom
.button_size
,
457 otk::RenderStyle::style(_client
->screen())->alldesktopsMask());
460 void Frame::renderIconify()
462 renderButton(_client
->screen(), _client
->focused(), _iconify_press
, _iconify
,
463 &_iconify_sur
, geom
.button_size
,
464 otk::RenderStyle::style(_client
->screen())->iconifyMask());
467 void Frame::renderClose()
469 renderButton(_client
->screen(), _client
->focused(), _close_press
, _close
,
470 &_close_sur
, geom
.button_size
,
471 otk::RenderStyle::style(_client
->screen())->closeMask());
474 void Frame::renderIcon()
476 const int screen
= _client
->screen();
477 const otk::RenderControl
*control
= otk::display
->renderControl(screen
);
479 otk::Surface
*s
= new otk::Surface(screen
, otk::Size(geom
.button_size
,
481 otk::pixel32
*dest
= s
->pixelData(), *src
;
482 int w
= _title_sur
->size().width();
484 src
= _title_sur
->pixelData() + w
* (geom
.bevel
+ 1) + geom
.icon_x
;
486 // get the background under the icon button
487 for (int y
= 0; y
< geom
.button_size
; ++y
, src
+= w
- geom
.button_size
)
488 for (int x
= 0; x
< geom
.button_size
; ++x
, ++dest
, ++src
)
490 control
->drawImage(*s
, 0, 0, 0);
492 XSetWindowBackgroundPixmap(**otk::display
, _icon
, s
->pixmap());
493 XClearWindow(**otk::display
, _icon
);
494 if (_icon_sur
) delete _icon_sur
;
498 void Frame::layoutTitle()
500 geom
.label_width
= geom
.width
- geom
.bevel
* 2 -
501 (geom
.button_size
+ geom
.bevel
) * (_layout
.size() - 1);
502 if (geom
.label_width
< 1) geom
.label_width
= 1;
504 XResizeWindow(**otk::display
, _label
, geom
.label_width
, geom
.font_height
);
507 bool n
, d
, i
, l
, m
,c
;
508 n
= d
= i
= l
= m
= c
= false;
509 for (const char *lc
= _layout
.c_str(); *lc
; ++lc
) {
514 XMapWindow(**otk::display
, _icon
);
515 XMoveWindow(**otk::display
, _icon
, x
, geom
.bevel
+ 1);
517 x
+= geom
.button_size
;
521 XMapWindow(**otk::display
, _desk
);
522 XMoveWindow(**otk::display
, _desk
, x
, geom
.bevel
+ 1);
524 x
+= geom
.button_size
;
528 XMapWindow(**otk::display
, _iconify
);
529 XMoveWindow(**otk::display
, _iconify
, x
, geom
.bevel
+ 1);
531 x
+= geom
.button_size
;
535 XMapWindow(**otk::display
, _label
);
536 XMoveWindow(**otk::display
, _label
, x
, geom
.bevel
);
538 x
+= geom
.label_width
;
542 XMapWindow(**otk::display
, _max
);
543 XMoveWindow(**otk::display
, _max
, x
, geom
.bevel
+ 1);
545 x
+= geom
.button_size
;
549 XMapWindow(**otk::display
, _close
);
550 XMoveWindow(**otk::display
, _close
, x
, geom
.bevel
+ 1);
552 x
+= geom
.button_size
;
557 if (!n
) XUnmapWindow(**otk::display
, _icon
);
558 if (!d
) XUnmapWindow(**otk::display
, _desk
);
559 if (!i
) XUnmapWindow(**otk::display
, _iconify
);
560 if (!l
) XUnmapWindow(**otk::display
, _label
);
561 if (!m
) XUnmapWindow(**otk::display
, _max
);
562 if (!c
) XUnmapWindow(**otk::display
, _close
);
565 void Frame::adjustPosition()
568 x
= _client
->area().x();
569 y
= _client
->area().y();
571 XMoveWindow(**otk::display
, _frame
, x
, y
);
572 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
576 void Frame::adjustShape()
579 Client::DecorationFlags decorations
= _client
->decorations();
581 if (!_client
->shaped()) {
582 // clear the shape on the frame window
583 XShapeCombineMask(**otk::display
, _frame
, ShapeBounding
,
588 // make the frame's shape match the clients
589 XShapeCombineShape(**otk::display
, _frame
, ShapeBounding
,
592 _client
->window(), ShapeBounding
, ShapeSet
);
597 if (decorations
& Client::Decor_Titlebar
) {
598 xrect
[0].x
= -geom
.bevel
;
599 xrect
[0].y
= -geom
.bevel
;
600 xrect
[0].width
= geom
.width
+ geom
.bwidth
* 2;
601 xrect
[0].height
= geom
.title_height() + geom
.bwidth
* 2;
605 if (decorations
& Client::Decor_Handle
) {
606 xrect
[1].x
= -geom
.bevel
;
607 xrect
[1].y
= geom
.handle_y
;
608 xrect
[1].width
= geom
.width
+ geom
.bwidth
* 2;
609 xrect
[1].height
= geom
.handle_height
+ geom
.bwidth
* 2;
613 XShapeCombineRectangles(**otk::display
, _frame
,
614 ShapeBounding
, 0, 0, xrect
, num
,
615 ShapeUnion
, Unsorted
);
621 void Frame::adjustState()
623 // XXX _button_alldesk.update();
624 // XXX _button_max.update();
628 void Frame::grabClient()
630 // reparent the client to the frame
631 XReparentWindow(**otk::display
, _client
->window(), _plate
, 0, 0);
633 When reparenting the client window, it is usually not mapped yet, since
634 this occurs from a MapRequest. However, in the case where Openbox is
635 starting up, the window is already mapped, so we'll see unmap events for
636 it. There are 2 unmap events generated that we see, one with the 'event'
637 member set the root window, and one set to the client, but both get handled
638 and need to be ignored.
640 if (openbox
->state() == Openbox::State_Starting
)
641 _client
->ignore_unmaps
+= 2;
643 // select the event mask on the client's parent (to receive config/map req's)
644 XSelectInput(**otk::display
, _plate
, SubstructureRedirectMask
);
646 // map the client so it maps when the frame does
647 XMapWindow(**otk::display
, _client
->window());
654 void Frame::releaseClient()
658 // check if the app has already reparented its window away
659 if (XCheckTypedWindowEvent(**otk::display
, _client
->window(),
660 ReparentNotify
, &ev
)) {
661 XPutBackEvent(**otk::display
, &ev
);
662 // re-map the window since the unmanaging process unmaps it
663 XMapWindow(**otk::display
, _client
->window());
665 // according to the ICCCM - if the client doesn't reparent itself, then we
666 // will reparent the window to root for them
667 XReparentWindow(**otk::display
, _client
->window(),
668 otk::display
->screenInfo(_client
->screen())->rootWindow(),
669 _client
->area().x(), _client
->area().y());
674 void Frame::clientGravity(int &x
, int &y
)
677 switch (_client
->gravity()) {
679 case NorthWestGravity
:
680 case SouthWestGravity
:
687 x
-= (_size
.left
+ _size
.right
) / 2;
690 case NorthEastGravity
:
691 case SouthEastGravity
:
693 x
-= _size
.left
+ _size
.right
;
703 switch (_client
->gravity()) {
705 case NorthWestGravity
:
706 case NorthEastGravity
:
713 y
-= (_size
.top
+ _size
.bottom
) / 2;
716 case SouthWestGravity
:
717 case SouthEastGravity
:
719 y
-= _size
.top
+ _size
.bottom
;
730 void Frame::frameGravity(int &x
, int &y
)
733 switch (_client
->gravity()) {
735 case NorthWestGravity
:
737 case SouthWestGravity
:
742 x
+= (_size
.left
+ _size
.right
) / 2;
744 case NorthEastGravity
:
746 case SouthEastGravity
:
747 x
+= _size
.left
+ _size
.right
;
756 switch (_client
->gravity()) {
758 case NorthWestGravity
:
760 case SouthWestGravity
:
765 y
+= (_size
.top
+ _size
.bottom
) / 2;
767 case NorthEastGravity
:
769 case SouthEastGravity
:
770 y
+= _size
.top
+ _size
.bottom
;