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 XMoveResizeWindow(**otk::display
, _plate
,
291 _innersize
.left
- geom
.cbwidth
,
292 _innersize
.top
- geom
.cbwidth
,
293 _client
->area().width(), _client
->area().height());
295 _size
.left
= _innersize
.left
+ geom
.bwidth
;
296 _size
.right
= _innersize
.right
+ geom
.bwidth
;
297 _size
.top
= _innersize
.top
+ geom
.bwidth
;
298 _size
.bottom
= _innersize
.bottom
+ geom
.bwidth
;
300 _area
= otk::Rect(_area
.position(), otk::Size(_client
->area().width() +
301 _size
.left
+ _size
.right
,
302 _client
->area().height() +
303 _size
.top
+ _size
.bottom
));
305 // render all the elements
306 int screen
= _client
->screen();
307 bool focus
= _client
->focused();
308 if (decorations
& Client::Decor_Titlebar
) {
309 render(screen
, otk::Size(geom
.width
, geom
.title_height()), _title
,
310 &_title_sur
, *(focus
? style
->titlebarFocusBackground() :
311 style
->titlebarUnfocusBackground()));
316 if (decorations
& Client::Decor_Handle
) {
317 render(screen
, otk::Size(geom
.width
, geom
.handle_height
), _handle
,
318 &_handle_sur
, *(focus
? style
->handleFocusBackground() :
319 style
->handleUnfocusBackground()));
320 render(screen
, otk::Size(geom
.grip_width(), geom
.handle_height
), _lgrip
,
321 &_grip_sur
, *(focus
? style
->gripFocusBackground() :
322 style
->gripUnfocusBackground()));
323 XSetWindowBackgroundPixmap(**otk::display
, _rgrip
, _grip_sur
->pixmap());
324 XClearWindow(**otk::display
, _rgrip
);
327 XSetWindowBorder(**otk::display
, _plate
,
328 focus
? style
->clientBorderFocusColor()->pixel() :
329 style
->clientBorderUnfocusColor()->pixel());
334 void Frame::renderLabel()
336 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
337 const otk::RenderControl
*control
=
338 otk::display
->renderControl(_client
->screen());
339 const otk::Font
*font
= style
->labelFont();
341 otk::Surface
*s
= new otk::Surface(_client
->screen(),
342 otk::Size(geom
.label_width
,
343 geom
.label_height()));
344 control
->drawBackground(*s
, *(_client
->focused() ?
345 style
->labelFocusBackground() :
346 style
->labelUnfocusBackground()));
348 otk::ustring t
= _client
->title(); // the actual text to draw
349 int x
= geom
.bevel
; // x coord for the text
351 if ((unsigned)x
* 2 > geom
.label_width
) return; // no room at all
353 // find a string that will fit inside the area for text
354 otk::ustring::size_type text_len
= t
.size();
356 unsigned int maxsize
= geom
.label_width
- geom
.bevel
* 2;
360 length
= font
->measureString(t
);
361 } while (length
> maxsize
&& text_len
-- > 0);
363 if (text_len
<= 0) return; // won't fit anything
366 switch (style
->labelTextJustify()) {
367 case otk::RenderStyle::RightBottomJustify
:
368 x
+= maxsize
- length
;
370 case otk::RenderStyle::CenterJustify
:
371 x
+= (maxsize
- length
) / 2;
373 case otk::RenderStyle::LeftTopJustify
:
377 control
->drawString(*s
, *font
, x
, 0,
378 *(_client
->focused() ? style
->textFocusColor() :
379 style
->textUnfocusColor()), t
);
381 XSetWindowBackgroundPixmap(**otk::display
, _label
, s
->pixmap());
382 XClearWindow(**otk::display
, _label
);
383 if (_label_sur
) delete _label_sur
;
387 void Frame::layoutTitle()
389 geom
.label_width
= geom
.width
- geom
.bevel
* 2;
390 if (geom
.label_width
< 1) geom
.label_width
= 1;
391 XMoveResizeWindow(**otk::display
, _label
, geom
.bevel
, geom
.bevel
,
392 geom
.label_width
, geom
.font_height
);
395 void Frame::adjustPosition()
398 x
= _client
->area().x();
399 y
= _client
->area().y();
401 XMoveWindow(**otk::display
, _frame
, x
, y
);
402 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
406 void Frame::adjustShape()
409 Client::DecorationFlags decorations
= _client
->decorations();
411 if (!_client
->shaped()) {
412 // clear the shape on the frame window
413 XShapeCombineMask(**otk::display
, _frame
, ShapeBounding
,
418 // make the frame's shape match the clients
419 XShapeCombineShape(**otk::display
, _frame
, ShapeBounding
,
422 _client
->window(), ShapeBounding
, ShapeSet
);
427 if (decorations
& Client::Decor_Titlebar
) {
428 xrect
[0].x
= -geom
.bevel
;
429 xrect
[0].y
= -geom
.bevel
;
430 xrect
[0].width
= geom
.width
+ geom
.bwidth
* 2;
431 xrect
[0].height
= geom
.title_height() + geom
.bwidth
* 2;
435 if (decorations
& Client::Decor_Handle
) {
436 xrect
[1].x
= -geom
.bevel
;
437 xrect
[1].y
= geom
.handle_y
;
438 xrect
[1].width
= geom
.width
+ geom
.bwidth
* 2;
439 xrect
[1].height
= geom
.handle_height
+ geom
.bwidth
* 2;
443 XShapeCombineRectangles(**otk::display
, _frame
,
444 ShapeBounding
, 0, 0, xrect
, num
,
445 ShapeUnion
, Unsorted
);
451 void Frame::adjustState()
453 // XXX _button_alldesk.update();
454 // XXX _button_max.update();
458 void Frame::grabClient()
460 // reparent the client to the frame
461 XReparentWindow(**otk::display
, _client
->window(), _plate
, 0, 0);
463 When reparenting the client window, it is usually not mapped yet, since
464 this occurs from a MapRequest. However, in the case where Openbox is
465 starting up, the window is already mapped, so we'll see unmap events for
466 it. There are 2 unmap events generated that we see, one with the 'event'
467 member set the root window, and one set to the client, but both get handled
468 and need to be ignored.
470 if (openbox
->state() == Openbox::State_Starting
)
471 _client
->ignore_unmaps
+= 2;
473 // select the event mask on the client's parent (to receive config/map req's)
474 XSelectInput(**otk::display
, _plate
, SubstructureRedirectMask
);
476 // map the client so it maps when the frame does
477 XMapWindow(**otk::display
, _client
->window());
484 void Frame::releaseClient()
488 // check if the app has already reparented its window away
489 if (XCheckTypedWindowEvent(**otk::display
, _client
->window(),
490 ReparentNotify
, &ev
)) {
491 XPutBackEvent(**otk::display
, &ev
);
492 // re-map the window since the unmanaging process unmaps it
493 XMapWindow(**otk::display
, _client
->window());
495 // according to the ICCCM - if the client doesn't reparent itself, then we
496 // will reparent the window to root for them
497 XReparentWindow(**otk::display
, _client
->window(),
498 otk::display
->screenInfo(_client
->screen())->rootWindow(),
499 _client
->area().x(), _client
->area().y());
504 void Frame::clientGravity(int &x
, int &y
)
507 switch (_client
->gravity()) {
509 case NorthWestGravity
:
510 case SouthWestGravity
:
517 x
-= (_size
.left
+ _size
.right
) / 2;
520 case NorthEastGravity
:
521 case SouthEastGravity
:
523 x
-= _size
.left
+ _size
.right
;
533 switch (_client
->gravity()) {
535 case NorthWestGravity
:
536 case NorthEastGravity
:
543 y
-= (_size
.top
+ _size
.bottom
) / 2;
546 case SouthWestGravity
:
547 case SouthEastGravity
:
549 y
-= _size
.top
+ _size
.bottom
;
560 void Frame::frameGravity(int &x
, int &y
)
563 switch (_client
->gravity()) {
565 case NorthWestGravity
:
567 case SouthWestGravity
:
572 x
+= (_size
.left
+ _size
.right
) / 2;
574 case NorthEastGravity
:
576 case SouthEastGravity
:
577 x
+= _size
.left
+ _size
.right
;
586 switch (_client
->gravity()) {
588 case NorthWestGravity
:
590 case SouthWestGravity
:
595 y
+= (_size
.top
+ _size
.bottom
) / 2;
597 case NorthEastGravity
:
599 case SouthEastGravity
:
600 y
+= _size
.top
+ _size
.bottom
;