1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
7 #include <X11/extensions/shape.h>
14 #include "otk/display.hh"
21 const long Frame::event_mask
;
23 Window
createWindow(const otk::ScreenInfo
*info
, Window parent
,
24 unsigned long mask
, XSetWindowAttributes
*attrib
)
26 return XCreateWindow(**otk::display
, parent
, 0, 0, 1, 1, 0,
27 info
->depth(), InputOutput
, info
->visual(),
32 Frame::Frame(Client
*client
)
53 XSetWindowAttributes attrib
;
55 const otk::ScreenInfo
*info
= otk::display
->screenInfo(client
->screen());
57 // create all of the decor windows (except title bar buttons)
58 mask
= CWOverrideRedirect
| CWEventMask
;
59 attrib
.event_mask
= Frame::event_mask
;
60 attrib
.override_redirect
= true;
61 _frame
= createWindow(info
, info
->rootWindow(), mask
, &attrib
);
64 _plate
= createWindow(info
, _frame
, mask
, &attrib
);
66 attrib
.event_mask
= (ButtonPressMask
| ButtonReleaseMask
| ButtonMotionMask
|
68 _title
= createWindow(info
, _frame
, mask
, &attrib
);
69 _label
= createWindow(info
, _title
, mask
, &attrib
);
70 _handle
= createWindow(info
, _frame
, mask
, &attrib
);
72 attrib
.cursor
= openbox
->cursors().ll_angle
;
73 _lgrip
= createWindow(info
, _handle
, mask
, &attrib
);
74 attrib
.cursor
= openbox
->cursors().lr_angle
;
75 _rgrip
= createWindow(info
, _handle
, mask
, &attrib
);
77 // the other stuff is shown based on decor settings
78 XMapWindow(**otk::display
, _plate
);
79 XMapWindow(**otk::display
, _lgrip
);
80 XMapWindow(**otk::display
, _rgrip
);
81 XMapWindow(**otk::display
, _label
);
83 applyStyle(*otk::RenderStyle::style(_client
->screen()));
87 _buttons
= new Window
[0];
88 _buttons_sur
= new otk::Surface
*[0];
89 _titleorder
= new int[1];
92 // register all of the windows with the event dispatcher
93 Window
*w
= allWindows();
94 for (unsigned int i
= 0; w
[i
]; ++i
)
95 openbox
->registerHandler(w
[i
], this);
101 // unregister all of the windows with the event dispatcher
102 Window
*w
= allWindows();
103 for (unsigned int i
= 0; w
[i
]; ++i
)
104 openbox
->clearHandler(w
[i
]);
107 for (int i
= 0; i
< _numbuttons
; ++i
) {
108 XDestroyWindow(**otk::display
, _buttons
[i
]);
109 delete _buttons_sur
[i
];
111 XDestroyWindow(**otk::display
, _rgrip
);
112 XDestroyWindow(**otk::display
, _lgrip
);
113 XDestroyWindow(**otk::display
, _handle
);
114 XDestroyWindow(**otk::display
, _label
);
115 XDestroyWindow(**otk::display
, _title
);
116 XDestroyWindow(**otk::display
, _frame
);
118 if (_frame_sur
) delete _frame_sur
;
119 if (_title_sur
) delete _title_sur
;
120 if (_label_sur
) delete _label_sur
;
121 if (_handle_sur
) delete _handle_sur
;
122 if (_grip_sur
) delete _grip_sur
;
125 delete [] _titleorder
;
126 delete [] _buttons_sur
;
133 XMapWindow(**otk::display
, _frame
);
141 XUnmapWindow(**otk::display
, _frame
);
145 MouseContext::MC
Frame::mouseContext(Window win
) const
147 if (win
== _frame
) return MouseContext::Frame
;
149 win
== _label
) return MouseContext::Titlebar
;
150 if (win
== _handle
) return MouseContext::Handle
;
151 if (win
== _plate
) return MouseContext::Window
;
153 win
== _rgrip
) return MouseContext::Grip
;
154 return (MouseContext::MC
) -1;
157 Window
*Frame::allWindows() const
159 Window
*w
= new Window
[7 + _numbuttons
+ 1];
168 for (int j
= 0; j
< _numbuttons
; ++j
)
169 w
[j
+ i
++] = _buttons
[j
];
174 void Frame::applyStyle(const otk::RenderStyle
&style
)
176 // set static border colors
177 XSetWindowBorder(**otk::display
, _frame
, style
.frameBorderColor()->pixel());
178 XSetWindowBorder(**otk::display
, _title
, style
.frameBorderColor()->pixel());
179 XSetWindowBorder(**otk::display
, _handle
, style
.frameBorderColor()->pixel());
180 XSetWindowBorder(**otk::display
, _lgrip
, style
.frameBorderColor()->pixel());
181 XSetWindowBorder(**otk::display
, _rgrip
, style
.frameBorderColor()->pixel());
183 // size all the fixed-size elements
184 geom
.font_height
= style
.labelFont()->height();
185 if (geom
.font_height
< 1) geom
.font_height
= 1;
186 geom
.button_size
= geom
.font_height
- 2;
187 if (geom
.button_size
< 1) geom
.button_size
= 1;
188 geom
.handle_height
= style
.handleWidth();
189 if (geom
.handle_height
< 1) geom
.handle_height
= 1;
190 geom
.bevel
= style
.bevelWidth();
192 XResizeWindow(**otk::display
, _lgrip
, geom
.grip_width(), geom
.handle_height
);
193 XResizeWindow(**otk::display
, _rgrip
, geom
.grip_width(), geom
.handle_height
);
195 for (int i
= 0; i
< _numbuttons
; ++i
)
196 XResizeWindow(**otk::display
, _buttons
[i
],
197 geom
.button_size
, geom
.button_size
);
200 void Frame::styleChanged(const otk::RenderStyle
&style
)
204 // size/position everything
209 void Frame::adjustFocus()
211 // XXX optimizations later...
215 void Frame::adjustTitle()
217 // XXX optimizations later...
221 static void render(int screen
, const otk::Size
&size
, Window win
,
222 otk::Surface
**surface
,
223 const otk::RenderTexture
&texture
)
225 otk::Surface
*s
= new otk::Surface(screen
, size
);
226 otk::display
->renderControl(screen
)->drawBackground(*s
, texture
);
227 XSetWindowBackgroundPixmap(**otk::display
, win
, s
->pixmap());
228 XClearWindow(**otk::display
, win
);
229 if (*surface
) delete *surface
;
234 void Frame::adjustSize()
236 Client::DecorationFlags decorations
= _client
->decorations();
237 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
239 if (decorations
& Client::Decor_Border
) {
240 geom
.bwidth
= style
->frameBorderWidth();
241 geom
.cbwidth
= style
->clientBorderWidth();
243 geom
.bwidth
= geom
.cbwidth
= 0;
245 _innersize
.left
= _innersize
.top
= _innersize
.bottom
= _innersize
.right
=
247 geom
.width
= _client
->area().width() + geom
.cbwidth
* 2;
248 assert(geom
.width
> 0);
251 XSetWindowBorderWidth(**otk::display
, _plate
, geom
.cbwidth
);
252 XSetWindowBorderWidth(**otk::display
, _frame
, geom
.bwidth
);
253 XSetWindowBorderWidth(**otk::display
, _title
, geom
.bwidth
);
254 XSetWindowBorderWidth(**otk::display
, _handle
, geom
.bwidth
);
255 XSetWindowBorderWidth(**otk::display
, _lgrip
, geom
.bwidth
);
256 XSetWindowBorderWidth(**otk::display
, _rgrip
, geom
.bwidth
);
258 // position/size and map/unmap all the windows
260 if (decorations
& Client::Decor_Titlebar
) {
261 XMoveResizeWindow(**otk::display
, _title
, -geom
.bwidth
, -geom
.bwidth
,
262 geom
.width
, geom
.title_height());
263 _innersize
.top
+= geom
.title_height() + geom
.bwidth
;
264 XMapWindow(**otk::display
, _title
);
266 // layout the title bar elements
269 XUnmapWindow(**otk::display
, _title
);
271 if (decorations
& Client::Decor_Handle
) {
272 geom
.handle_y
= _innersize
.top
+ _client
->area().height() + geom
.cbwidth
;
273 XMoveResizeWindow(**otk::display
, _handle
, -geom
.bwidth
, geom
.handle_y
,
274 geom
.width
, geom
.handle_height
);
275 XMoveWindow(**otk::display
, _lgrip
, -geom
.bwidth
, -geom
.bwidth
);
276 XMoveWindow(**otk::display
, _rgrip
,
277 -geom
.bwidth
+ geom
.width
- geom
.grip_width(),
279 _innersize
.bottom
+= geom
.handle_height
+ geom
.bwidth
;
280 XMapWindow(**otk::display
, _handle
);
282 XUnmapWindow(**otk::display
, _handle
);
284 XResizeWindow(**otk::display
, _frame
, geom
.width
,
285 (_client
->shaded() ? geom
.title_height() :
286 _innersize
.top
+ _innersize
.bottom
+
287 _client
->area().height()));
289 // do this in two steps because clients whose gravity is set to
290 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
291 XMoveWindow(**otk::display
, _plate
, _innersize
.left
- geom
.cbwidth
,
292 _innersize
.top
- geom
.cbwidth
);
293 XResizeWindow(**otk::display
, _plate
, _client
->area().width(),
294 _client
->area().height());
296 _size
.left
= _innersize
.left
+ geom
.bwidth
;
297 _size
.right
= _innersize
.right
+ geom
.bwidth
;
298 _size
.top
= _innersize
.top
+ geom
.bwidth
;
299 _size
.bottom
= _innersize
.bottom
+ geom
.bwidth
;
301 _area
= otk::Rect(_area
.position(), otk::Size(_client
->area().width() +
302 _size
.left
+ _size
.right
,
303 _client
->area().height() +
304 _size
.top
+ _size
.bottom
));
306 // render all the elements
307 int screen
= _client
->screen();
308 bool focus
= _client
->focused();
309 if (decorations
& Client::Decor_Titlebar
) {
310 render(screen
, otk::Size(geom
.width
, geom
.title_height()), _title
,
311 &_title_sur
, *(focus
? style
->titlebarFocusBackground() :
312 style
->titlebarUnfocusBackground()));
317 if (decorations
& Client::Decor_Handle
) {
318 render(screen
, otk::Size(geom
.width
, geom
.handle_height
), _handle
,
319 &_handle_sur
, *(focus
? style
->handleFocusBackground() :
320 style
->handleUnfocusBackground()));
321 render(screen
, otk::Size(geom
.grip_width(), geom
.handle_height
), _lgrip
,
322 &_grip_sur
, *(focus
? style
->gripFocusBackground() :
323 style
->gripUnfocusBackground()));
324 XSetWindowBackgroundPixmap(**otk::display
, _rgrip
, _grip_sur
->pixmap());
325 XClearWindow(**otk::display
, _rgrip
);
328 XSetWindowBorder(**otk::display
, _plate
,
329 focus
? style
->clientBorderFocusColor()->pixel() :
330 style
->clientBorderUnfocusColor()->pixel());
335 void Frame::renderLabel()
337 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
338 const otk::RenderControl
*control
=
339 otk::display
->renderControl(_client
->screen());
340 const otk::Font
*font
= style
->labelFont();
342 otk::Surface
*s
= new otk::Surface(_client
->screen(),
343 otk::Size(geom
.label_width
,
344 geom
.label_height()));
345 control
->drawBackground(*s
, *(_client
->focused() ?
346 style
->labelFocusBackground() :
347 style
->labelUnfocusBackground()));
349 otk::ustring t
= _client
->title(); // the actual text to draw
350 int x
= geom
.bevel
; // x coord for the text
352 if (x
* 2 > geom
.label_width
) return; // no room at all
354 // find a string that will fit inside the area for text
355 otk::ustring::size_type text_len
= t
.size();
357 int maxsize
= geom
.label_width
- geom
.bevel
* 2;
361 length
= font
->measureString(t
); // this returns an unsigned, so check < 0
362 if (length
< 0) length
= maxsize
; // if the string's that long just adjust
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
;
390 void Frame::layoutTitle()
392 geom
.label_width
= geom
.width
- geom
.bevel
* 2;
393 if (geom
.label_width
< 1) geom
.label_width
= 1;
394 XMoveResizeWindow(**otk::display
, _label
, geom
.bevel
, geom
.bevel
,
395 geom
.label_width
, geom
.font_height
);
398 void Frame::adjustPosition()
401 x
= _client
->area().x();
402 y
= _client
->area().y();
404 XMoveWindow(**otk::display
, _frame
, x
, y
);
405 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
409 void Frame::adjustShape()
412 Client::DecorationFlags decorations
= _client
->decorations();
414 if (!_client
->shaped()) {
415 // clear the shape on the frame window
416 XShapeCombineMask(**otk::display
, _frame
, ShapeBounding
,
421 // make the frame's shape match the clients
422 XShapeCombineShape(**otk::display
, _frame
, ShapeBounding
,
425 _client
->window(), ShapeBounding
, ShapeSet
);
430 if (decorations
& Client::Decor_Titlebar
) {
431 xrect
[0].x
= -geom
.bevel
;
432 xrect
[0].y
= -geom
.bevel
;
433 xrect
[0].width
= geom
.width
+ geom
.bwidth
* 2;
434 xrect
[0].height
= geom
.title_height() + geom
.bwidth
* 2;
438 if (decorations
& Client::Decor_Handle
) {
439 xrect
[1].x
= -geom
.bevel
;
440 xrect
[1].y
= geom
.handle_y
;
441 xrect
[1].width
= geom
.width
+ geom
.bwidth
* 2;
442 xrect
[1].height
= geom
.handle_height
+ geom
.bwidth
* 2;
446 XShapeCombineRectangles(**otk::display
, _frame
,
447 ShapeBounding
, 0, 0, xrect
, num
,
448 ShapeUnion
, Unsorted
);
454 void Frame::adjustState()
456 // XXX _button_alldesk.update();
457 // XXX _button_max.update();
461 void Frame::grabClient()
463 // reparent the client to the frame
464 XReparentWindow(**otk::display
, _client
->window(), _plate
, 0, 0);
466 When reparenting the client window, it is usually not mapped yet, since
467 this occurs from a MapRequest. However, in the case where Openbox is
468 starting up, the window is already mapped, so we'll see unmap events for
469 it. There are 2 unmap events generated that we see, one with the 'event'
470 member set the root window, and one set to the client, but both get handled
471 and need to be ignored.
473 if (openbox
->state() == Openbox::State_Starting
)
474 _client
->ignore_unmaps
+= 2;
476 // select the event mask on the client's parent (to receive config/map req's)
477 XSelectInput(**otk::display
, _plate
, SubstructureRedirectMask
);
479 // map the client so it maps when the frame does
480 XMapWindow(**otk::display
, _client
->window());
487 void Frame::releaseClient()
491 // check if the app has already reparented its window away
492 if (XCheckTypedWindowEvent(**otk::display
, _client
->window(),
493 ReparentNotify
, &ev
)) {
494 XPutBackEvent(**otk::display
, &ev
);
495 // re-map the window since the unmanaging process unmaps it
496 XMapWindow(**otk::display
, _client
->window());
498 // according to the ICCCM - if the client doesn't reparent itself, then we
499 // will reparent the window to root for them
500 XReparentWindow(**otk::display
, _client
->window(),
501 otk::display
->screenInfo(_client
->screen())->rootWindow(),
502 _client
->area().x(), _client
->area().y());
507 void Frame::clientGravity(int &x
, int &y
)
510 switch (_client
->gravity()) {
512 case NorthWestGravity
:
513 case SouthWestGravity
:
520 x
-= (_size
.left
+ _size
.right
) / 2;
523 case NorthEastGravity
:
524 case SouthEastGravity
:
526 x
-= _size
.left
+ _size
.right
;
536 switch (_client
->gravity()) {
538 case NorthWestGravity
:
539 case NorthEastGravity
:
546 y
-= (_size
.top
+ _size
.bottom
) / 2;
549 case SouthWestGravity
:
550 case SouthEastGravity
:
552 y
-= _size
.top
+ _size
.bottom
;
563 void Frame::frameGravity(int &x
, int &y
)
566 switch (_client
->gravity()) {
568 case NorthWestGravity
:
570 case SouthWestGravity
:
575 x
+= (_size
.left
+ _size
.right
) / 2;
577 case NorthEastGravity
:
579 case SouthEastGravity
:
580 x
+= _size
.left
+ _size
.right
;
589 switch (_client
->gravity()) {
591 case NorthWestGravity
:
593 case SouthWestGravity
:
598 y
+= (_size
.top
+ _size
.bottom
) / 2;
600 case NorthEastGravity
:
602 case SouthEastGravity
:
603 y
+= _size
.top
+ _size
.bottom
;