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),
66 XSetWindowAttributes attrib
;
68 const otk::ScreenInfo
*info
= otk::display
->screenInfo(client
->screen());
70 // create all of the decor windows (except title bar buttons)
71 mask
= CWOverrideRedirect
| CWEventMask
;
72 attrib
.event_mask
= Frame::event_mask
;
73 attrib
.override_redirect
= true;
74 _frame
= createWindow(info
, info
->rootWindow(), mask
, &attrib
);
77 _plate
= createWindow(info
, _frame
, mask
, &attrib
);
79 attrib
.event_mask
= (ButtonPressMask
| ButtonReleaseMask
| ButtonMotionMask
|
81 _title
= createWindow(info
, _frame
, mask
, &attrib
);
82 _label
= createWindow(info
, _title
, mask
, &attrib
);
83 _max
= createWindow(info
, _title
, mask
, &attrib
);
84 _close
= createWindow(info
, _title
, mask
, &attrib
);
85 _desk
= createWindow(info
, _title
, mask
, &attrib
);
86 _icon
= createWindow(info
, _title
, mask
, &attrib
);
87 _iconify
= createWindow(info
, _title
, mask
, &attrib
);
88 _handle
= createWindow(info
, _frame
, mask
, &attrib
);
90 attrib
.cursor
= openbox
->cursors().ll_angle
;
91 _lgrip
= createWindow(info
, _handle
, mask
, &attrib
);
92 attrib
.cursor
= openbox
->cursors().lr_angle
;
93 _rgrip
= createWindow(info
, _handle
, mask
, &attrib
);
95 // the other stuff is shown based on decor settings
96 XMapWindow(**otk::display
, _plate
);
97 XMapWindow(**otk::display
, _lgrip
);
98 XMapWindow(**otk::display
, _rgrip
);
99 XMapWindow(**otk::display
, _label
);
101 applyStyle(*otk::RenderStyle::style(_client
->screen()));
105 // register all of the windows with the event dispatcher
106 Window
*w
= allWindows();
107 for (unsigned int i
= 0; w
[i
]; ++i
)
108 openbox
->registerHandler(w
[i
], this);
114 // unregister all of the windows with the event dispatcher
115 Window
*w
= allWindows();
116 for (unsigned int i
= 0; w
[i
]; ++i
)
117 openbox
->clearHandler(w
[i
]);
120 XDestroyWindow(**otk::display
, _rgrip
);
121 XDestroyWindow(**otk::display
, _lgrip
);
122 XDestroyWindow(**otk::display
, _handle
);
123 XDestroyWindow(**otk::display
, _max
);
124 XDestroyWindow(**otk::display
, _icon
);
125 XDestroyWindow(**otk::display
, _iconify
);
126 XDestroyWindow(**otk::display
, _desk
);
127 XDestroyWindow(**otk::display
, _close
);
128 XDestroyWindow(**otk::display
, _label
);
129 XDestroyWindow(**otk::display
, _title
);
130 XDestroyWindow(**otk::display
, _frame
);
132 if (_frame_sur
) delete _frame_sur
;
133 if (_title_sur
) delete _title_sur
;
134 if (_label_sur
) delete _label_sur
;
135 if (_handle_sur
) delete _handle_sur
;
136 if (_grip_sur
) delete _grip_sur
;
137 if (_max_sur
) delete _max_sur
;
138 if (_desk_sur
) delete _desk_sur
;
139 if (_iconify_sur
) delete _iconify_sur
;
140 if (_icon_sur
) delete _icon_sur
;
141 if (_close_sur
) delete _close_sur
;
148 XMapWindow(**otk::display
, _frame
);
156 XUnmapWindow(**otk::display
, _frame
);
160 void Frame::buttonPressHandler(const XButtonEvent
&e
)
162 if (_press_button
) return;
163 _press_button
= e
.button
;
165 if (e
.window
== _max
) {
169 if (e
.window
== _close
) {
173 if (e
.window
== _desk
) {
177 if (e
.window
== _iconify
) {
178 _iconify_press
= true;
181 if (e
.window
== _icon
) {
187 void Frame::buttonReleaseHandler(const XButtonEvent
&e
)
189 if (e
.button
!= _press_button
) return;
192 if (e
.window
== _max
) {
196 if (e
.window
== _close
) {
197 _close_press
= false;
200 if (e
.window
== _desk
) {
204 if (e
.window
== _iconify
) {
205 _iconify_press
= false;
208 if (e
.window
== _icon
) {
214 MouseContext::MC
Frame::mouseContext(Window win
) const
216 if (win
== _frame
) return MouseContext::Frame
;
218 win
== _label
) return MouseContext::Titlebar
;
219 if (win
== _handle
) return MouseContext::Handle
;
220 if (win
== _plate
) return MouseContext::Window
;
222 win
== _rgrip
) return MouseContext::Grip
;
223 if (win
== _max
) return MouseContext::MaximizeButton
;
224 if (win
== _close
) return MouseContext::CloseButton
;
225 if (win
== _desk
) return MouseContext::AllDesktopsButton
;
226 if (win
== _iconify
)return MouseContext::IconifyButton
;
227 if (win
== _icon
) return MouseContext::IconButton
;
228 return (MouseContext::MC
) -1;
231 Window
*Frame::allWindows() const
233 Window
*w
= new Window
[12 + 1];
251 void Frame::applyStyle(const otk::RenderStyle
&style
)
253 // set static border colors
254 XSetWindowBorder(**otk::display
, _frame
, style
.frameBorderColor()->pixel());
255 XSetWindowBorder(**otk::display
, _title
, style
.frameBorderColor()->pixel());
256 XSetWindowBorder(**otk::display
, _handle
, style
.frameBorderColor()->pixel());
257 XSetWindowBorder(**otk::display
, _lgrip
, style
.frameBorderColor()->pixel());
258 XSetWindowBorder(**otk::display
, _rgrip
, style
.frameBorderColor()->pixel());
260 // size all the fixed-size elements
261 geom
.font_height
= style
.labelFont()->height();
262 if (geom
.font_height
< 1) geom
.font_height
= 1;
263 geom
.button_size
= geom
.font_height
- 2;
264 if (geom
.button_size
< 1) geom
.button_size
= 1;
265 geom
.handle_height
= style
.handleWidth();
266 if (geom
.handle_height
< 1) geom
.handle_height
= 1;
267 geom
.bevel
= style
.bevelWidth();
269 XResizeWindow(**otk::display
, _lgrip
, geom
.grip_width(), geom
.handle_height
);
270 XResizeWindow(**otk::display
, _rgrip
, geom
.grip_width(), geom
.handle_height
);
272 XResizeWindow(**otk::display
, _max
, geom
.button_size
, geom
.button_size
);
273 XResizeWindow(**otk::display
, _close
, geom
.button_size
, geom
.button_size
);
274 XResizeWindow(**otk::display
, _desk
, geom
.button_size
, geom
.button_size
);
275 XResizeWindow(**otk::display
, _iconify
, geom
.button_size
, geom
.button_size
);
276 XResizeWindow(**otk::display
, _icon
, geom
.button_size
, geom
.button_size
);
279 void Frame::styleChanged(const otk::RenderStyle
&style
)
283 // size/position everything
288 void Frame::adjustFocus()
290 // XXX optimizations later...
294 void Frame::adjustTitle()
296 // XXX optimizations later...
300 static void render(int screen
, const otk::Size
&size
, Window win
,
301 otk::Surface
**surface
,
302 const otk::RenderTexture
&texture
, bool freedata
=true)
304 otk::Surface
*s
= new otk::Surface(screen
, size
);
305 otk::display
->renderControl(screen
)->drawBackground(*s
, texture
);
306 XSetWindowBackgroundPixmap(**otk::display
, win
, s
->pixmap());
307 XClearWindow(**otk::display
, win
);
308 if (*surface
) delete *surface
;
309 if (freedata
) s
->freePixelData();
313 void Frame::adjustSize()
315 Client::DecorationFlags decorations
= _client
->decorations();
316 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
318 if (decorations
& Client::Decor_Border
) {
319 geom
.bwidth
= style
->frameBorderWidth();
320 geom
.cbwidth
= style
->clientBorderWidth();
322 geom
.bwidth
= geom
.cbwidth
= 0;
324 _innersize
.left
= _innersize
.top
= _innersize
.bottom
= _innersize
.right
=
326 geom
.width
= _client
->area().width() + geom
.cbwidth
* 2;
327 assert(geom
.width
> 0);
330 XSetWindowBorderWidth(**otk::display
, _plate
, geom
.cbwidth
);
331 XSetWindowBorderWidth(**otk::display
, _frame
, geom
.bwidth
);
332 XSetWindowBorderWidth(**otk::display
, _title
, geom
.bwidth
);
333 XSetWindowBorderWidth(**otk::display
, _handle
, geom
.bwidth
);
334 XSetWindowBorderWidth(**otk::display
, _lgrip
, geom
.bwidth
);
335 XSetWindowBorderWidth(**otk::display
, _rgrip
, geom
.bwidth
);
337 // position/size and map/unmap all the windows
339 if (decorations
& Client::Decor_Titlebar
) {
340 XMoveResizeWindow(**otk::display
, _title
, -geom
.bwidth
, -geom
.bwidth
,
341 geom
.width
, geom
.title_height());
342 _innersize
.top
+= geom
.title_height() + geom
.bwidth
;
343 XMapWindow(**otk::display
, _title
);
345 // layout the title bar elements
348 XUnmapWindow(**otk::display
, _title
);
350 if (decorations
& Client::Decor_Handle
) {
351 geom
.handle_y
= _innersize
.top
+ _client
->area().height() + geom
.cbwidth
;
352 XMoveResizeWindow(**otk::display
, _handle
, -geom
.bwidth
, geom
.handle_y
,
353 geom
.width
, geom
.handle_height
);
354 XMoveWindow(**otk::display
, _lgrip
, -geom
.bwidth
, -geom
.bwidth
);
355 XMoveWindow(**otk::display
, _rgrip
,
356 -geom
.bwidth
+ geom
.width
- geom
.grip_width(),
358 _innersize
.bottom
+= geom
.handle_height
+ geom
.bwidth
;
359 XMapWindow(**otk::display
, _handle
);
361 XUnmapWindow(**otk::display
, _handle
);
363 XResizeWindow(**otk::display
, _frame
, geom
.width
,
364 (_client
->shaded() ? geom
.title_height() :
365 _innersize
.top
+ _innersize
.bottom
+
366 _client
->area().height()));
368 // do this in two steps because clients whose gravity is set to
369 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
370 XMoveWindow(**otk::display
, _plate
, _innersize
.left
- geom
.cbwidth
,
371 _innersize
.top
- geom
.cbwidth
);
372 XResizeWindow(**otk::display
, _plate
, _client
->area().width(),
373 _client
->area().height());
375 _size
.left
= _innersize
.left
+ geom
.bwidth
;
376 _size
.right
= _innersize
.right
+ geom
.bwidth
;
377 _size
.top
= _innersize
.top
+ geom
.bwidth
;
378 _size
.bottom
= _innersize
.bottom
+ geom
.bwidth
;
380 _area
= otk::Rect(_area
.position(), otk::Size(_client
->area().width() +
381 _size
.left
+ _size
.right
,
382 _client
->area().height() +
383 _size
.top
+ _size
.bottom
));
385 // render all the elements
386 int screen
= _client
->screen();
387 bool focus
= _client
->focused();
388 if (decorations
& Client::Decor_Titlebar
) {
389 render(screen
, otk::Size(geom
.width
, geom
.title_height()), _title
,
390 &_title_sur
, *(focus
? style
->titlebarFocusBackground() :
391 style
->titlebarUnfocusBackground()), false);
401 if (decorations
& Client::Decor_Handle
) {
402 render(screen
, otk::Size(geom
.width
, geom
.handle_height
), _handle
,
403 &_handle_sur
, *(focus
? style
->handleFocusBackground() :
404 style
->handleUnfocusBackground()));
405 render(screen
, otk::Size(geom
.grip_width(), geom
.handle_height
), _lgrip
,
406 &_grip_sur
, *(focus
? style
->gripFocusBackground() :
407 style
->gripUnfocusBackground()));
408 XSetWindowBackgroundPixmap(**otk::display
, _rgrip
, _grip_sur
->pixmap());
409 XClearWindow(**otk::display
, _rgrip
);
412 XSetWindowBorder(**otk::display
, _plate
,
413 focus
? style
->clientBorderFocusColor()->pixel() :
414 style
->clientBorderUnfocusColor()->pixel());
419 void Frame::renderLabel()
421 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
422 const otk::RenderControl
*control
=
423 otk::display
->renderControl(_client
->screen());
424 const otk::Font
*font
= style
->labelFont();
426 otk::Surface
*s
= new otk::Surface(_client
->screen(),
427 otk::Size(geom
.label_width
,
428 geom
.label_height()));
429 control
->drawBackground(*s
, *(_client
->focused() ?
430 style
->labelFocusBackground() :
431 style
->labelUnfocusBackground()));
433 otk::ustring t
= _client
->title(); // the actual text to draw
434 int x
= geom
.bevel
; // x coord for the text
436 if (x
* 2 > geom
.label_width
) return; // no room at all
438 // find a string that will fit inside the area for text
439 otk::ustring::size_type text_len
= t
.size();
441 int maxsize
= geom
.label_width
- geom
.bevel
* 2;
445 length
= font
->measureString(t
); // this returns an unsigned, so check < 0
446 if (length
< 0) length
= maxsize
; // if the string's that long just adjust
447 } while (length
> maxsize
&& text_len
-- > 0);
449 if (text_len
<= 0) return; // won't fit anything
452 switch (style
->labelTextJustify()) {
453 case otk::RenderStyle::RightBottomJustify
:
454 x
+= maxsize
- length
;
456 case otk::RenderStyle::CenterJustify
:
457 x
+= (maxsize
- length
) / 2;
459 case otk::RenderStyle::LeftTopJustify
:
463 control
->drawString(*s
, *font
, x
, 0,
464 *(_client
->focused() ? style
->textFocusColor() :
465 style
->textUnfocusColor()), t
);
467 XSetWindowBackgroundPixmap(**otk::display
, _label
, s
->pixmap());
468 XClearWindow(**otk::display
, _label
);
469 if (_label_sur
) delete _label_sur
;
474 static void renderButton(int screen
, bool focus
, bool press
, Window win
,
475 otk::Surface
**sur
, int butsize
,
476 const otk::PixmapMask
*mask
)
478 const otk::RenderStyle
*style
= otk::RenderStyle::style(screen
);
479 const otk::RenderControl
*control
= otk::display
->renderControl(screen
);
480 otk::Surface
*s
= new otk::Surface(screen
, otk::Size(butsize
, butsize
));
482 const otk::RenderTexture
*tx
= (focus
?
484 style
->buttonPressFocusBackground() :
485 style
->buttonUnpressFocusBackground()) :
487 style
->buttonPressUnfocusBackground() :
488 style
->buttonUnpressUnfocusBackground()));
489 const otk::RenderColor
*maskcolor
= (focus
?
490 style
->buttonFocusColor() :
491 style
->buttonUnfocusColor());
492 control
->drawBackground(*s
, *tx
);
493 control
->drawMask(*s
, *maskcolor
, *mask
);
495 XSetWindowBackgroundPixmap(**otk::display
, win
, s
->pixmap());
496 XClearWindow(**otk::display
, win
);
497 if (*sur
) delete *sur
;
501 void Frame::renderMax()
503 renderButton(_client
->screen(), _client
->focused(), _max_press
, _max
,
504 &_max_sur
, geom
.button_size
,
505 otk::RenderStyle::style(_client
->screen())->maximizeMask());
508 void Frame::renderDesk()
510 renderButton(_client
->screen(), _client
->focused(), _desk_press
, _desk
,
511 &_desk_sur
, geom
.button_size
,
512 otk::RenderStyle::style(_client
->screen())->alldesktopsMask());
515 void Frame::renderIconify()
517 renderButton(_client
->screen(), _client
->focused(), _iconify_press
, _iconify
,
518 &_iconify_sur
, geom
.button_size
,
519 otk::RenderStyle::style(_client
->screen())->iconifyMask());
522 void Frame::renderClose()
524 renderButton(_client
->screen(), _client
->focused(), _close_press
, _close
,
525 &_close_sur
, geom
.button_size
,
526 otk::RenderStyle::style(_client
->screen())->closeMask());
529 void Frame::renderIcon()
531 const int screen
= _client
->screen();
532 const otk::RenderControl
*control
= otk::display
->renderControl(screen
);
534 otk::Surface
*s
= new otk::Surface(screen
, otk::Size(geom
.button_size
,
536 otk::pixel32
*dest
= s
->pixelData(), *src
;
537 int w
= _title_sur
->size().width();
539 src
= _title_sur
->pixelData() + w
* (geom
.bevel
+ 1) + geom
.icon_x
;
541 // get the background under the icon button
542 for (int y
= 0; y
< geom
.button_size
; ++y
, src
+= w
- geom
.button_size
)
543 for (int x
= 0; x
< geom
.button_size
; ++x
, ++dest
, ++src
)
545 control
->drawImage(*s
, 0, 0, 0);
547 XSetWindowBackgroundPixmap(**otk::display
, _icon
, s
->pixmap());
548 XClearWindow(**otk::display
, _icon
);
549 if (_icon_sur
) delete _icon_sur
;
553 void Frame::layoutTitle()
555 geom
.label_width
= geom
.width
- geom
.bevel
* 2 -
556 (geom
.button_size
+ geom
.bevel
) * (_layout
.size() - 1);
557 if (geom
.label_width
< 1) geom
.label_width
= 1;
559 XResizeWindow(**otk::display
, _label
, geom
.label_width
, geom
.font_height
);
562 bool n
, d
, i
, l
, m
,c
;
563 n
= d
= i
= l
= m
= c
= false;
564 for (const char *lc
= _layout
.c_str(); *lc
; ++lc
) {
569 XMapWindow(**otk::display
, _icon
);
570 XMoveWindow(**otk::display
, _icon
, x
, geom
.bevel
+ 1);
572 x
+= geom
.button_size
;
576 XMapWindow(**otk::display
, _desk
);
577 XMoveWindow(**otk::display
, _desk
, x
, geom
.bevel
+ 1);
579 x
+= geom
.button_size
;
583 XMapWindow(**otk::display
, _iconify
);
584 XMoveWindow(**otk::display
, _iconify
, x
, geom
.bevel
+ 1);
586 x
+= geom
.button_size
;
590 XMapWindow(**otk::display
, _label
);
591 XMoveWindow(**otk::display
, _label
, x
, geom
.bevel
);
593 x
+= geom
.label_width
;
597 XMapWindow(**otk::display
, _max
);
598 XMoveWindow(**otk::display
, _max
, x
, geom
.bevel
+ 1);
600 x
+= geom
.button_size
;
604 XMapWindow(**otk::display
, _close
);
605 XMoveWindow(**otk::display
, _close
, x
, geom
.bevel
+ 1);
607 x
+= geom
.button_size
;
612 if (!n
) XUnmapWindow(**otk::display
, _icon
);
613 if (!d
) XUnmapWindow(**otk::display
, _desk
);
614 if (!i
) XUnmapWindow(**otk::display
, _iconify
);
615 if (!l
) XUnmapWindow(**otk::display
, _label
);
616 if (!m
) XUnmapWindow(**otk::display
, _max
);
617 if (!c
) XUnmapWindow(**otk::display
, _close
);
620 void Frame::adjustPosition()
623 x
= _client
->area().x();
624 y
= _client
->area().y();
626 XMoveWindow(**otk::display
, _frame
, x
, y
);
627 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
631 void Frame::adjustShape()
634 Client::DecorationFlags decorations
= _client
->decorations();
636 if (!_client
->shaped()) {
637 // clear the shape on the frame window
638 XShapeCombineMask(**otk::display
, _frame
, ShapeBounding
,
643 // make the frame's shape match the clients
644 XShapeCombineShape(**otk::display
, _frame
, ShapeBounding
,
647 _client
->window(), ShapeBounding
, ShapeSet
);
652 if (decorations
& Client::Decor_Titlebar
) {
653 xrect
[0].x
= -geom
.bevel
;
654 xrect
[0].y
= -geom
.bevel
;
655 xrect
[0].width
= geom
.width
+ geom
.bwidth
* 2;
656 xrect
[0].height
= geom
.title_height() + geom
.bwidth
* 2;
660 if (decorations
& Client::Decor_Handle
) {
661 xrect
[1].x
= -geom
.bevel
;
662 xrect
[1].y
= geom
.handle_y
;
663 xrect
[1].width
= geom
.width
+ geom
.bwidth
* 2;
664 xrect
[1].height
= geom
.handle_height
+ geom
.bwidth
* 2;
668 XShapeCombineRectangles(**otk::display
, _frame
,
669 ShapeBounding
, 0, 0, xrect
, num
,
670 ShapeUnion
, Unsorted
);
676 void Frame::adjustState()
678 // XXX _button_alldesk.update();
679 // XXX _button_max.update();
683 void Frame::grabClient()
685 // reparent the client to the frame
686 XReparentWindow(**otk::display
, _client
->window(), _plate
, 0, 0);
688 When reparenting the client window, it is usually not mapped yet, since
689 this occurs from a MapRequest. However, in the case where Openbox is
690 starting up, the window is already mapped, so we'll see unmap events for
691 it. There are 2 unmap events generated that we see, one with the 'event'
692 member set the root window, and one set to the client, but both get handled
693 and need to be ignored.
695 if (openbox
->state() == Openbox::State_Starting
)
696 _client
->ignore_unmaps
+= 2;
698 // select the event mask on the client's parent (to receive config/map req's)
699 XSelectInput(**otk::display
, _plate
, SubstructureRedirectMask
);
701 // map the client so it maps when the frame does
702 XMapWindow(**otk::display
, _client
->window());
709 void Frame::releaseClient()
713 // check if the app has already reparented its window away
714 if (XCheckTypedWindowEvent(**otk::display
, _client
->window(),
715 ReparentNotify
, &ev
)) {
716 XPutBackEvent(**otk::display
, &ev
);
717 // re-map the window since the unmanaging process unmaps it
718 XMapWindow(**otk::display
, _client
->window());
720 // according to the ICCCM - if the client doesn't reparent itself, then we
721 // will reparent the window to root for them
722 XReparentWindow(**otk::display
, _client
->window(),
723 otk::display
->screenInfo(_client
->screen())->rootWindow(),
724 _client
->area().x(), _client
->area().y());
729 void Frame::clientGravity(int &x
, int &y
)
732 switch (_client
->gravity()) {
734 case NorthWestGravity
:
735 case SouthWestGravity
:
742 x
-= (_size
.left
+ _size
.right
) / 2;
745 case NorthEastGravity
:
746 case SouthEastGravity
:
748 x
-= _size
.left
+ _size
.right
;
758 switch (_client
->gravity()) {
760 case NorthWestGravity
:
761 case NorthEastGravity
:
768 y
-= (_size
.top
+ _size
.bottom
) / 2;
771 case SouthWestGravity
:
772 case SouthEastGravity
:
774 y
-= _size
.top
+ _size
.bottom
;
785 void Frame::frameGravity(int &x
, int &y
)
788 switch (_client
->gravity()) {
790 case NorthWestGravity
:
792 case SouthWestGravity
:
797 x
+= (_size
.left
+ _size
.right
) / 2;
799 case NorthEastGravity
:
801 case SouthEastGravity
:
802 x
+= _size
.left
+ _size
.right
;
811 switch (_client
->gravity()) {
813 case NorthWestGravity
:
815 case SouthWestGravity
:
820 y
+= (_size
.top
+ _size
.bottom
) / 2;
822 case NorthEastGravity
:
824 case SouthEastGravity
:
825 y
+= _size
.top
+ _size
.bottom
;