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
;
233 void Frame::adjustSize()
235 Client::DecorationFlags decorations
= _client
->decorations();
236 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
238 if (decorations
& Client::Decor_Border
) {
239 geom
.bwidth
= style
->frameBorderWidth();
240 geom
.cbwidth
= style
->clientBorderWidth();
242 geom
.bwidth
= geom
.cbwidth
= 0;
244 _innersize
.left
= _innersize
.top
= _innersize
.bottom
= _innersize
.right
=
246 geom
.width
= _client
->area().width() + geom
.cbwidth
* 2;
247 assert(geom
.width
> 0);
250 XSetWindowBorderWidth(**otk::display
, _plate
, geom
.cbwidth
);
251 XSetWindowBorderWidth(**otk::display
, _frame
, geom
.bwidth
);
252 XSetWindowBorderWidth(**otk::display
, _title
, geom
.bwidth
);
253 XSetWindowBorderWidth(**otk::display
, _handle
, geom
.bwidth
);
254 XSetWindowBorderWidth(**otk::display
, _lgrip
, geom
.bwidth
);
255 XSetWindowBorderWidth(**otk::display
, _rgrip
, geom
.bwidth
);
257 // position/size and map/unmap all the windows
259 if (decorations
& Client::Decor_Titlebar
) {
260 XMoveResizeWindow(**otk::display
, _title
, -geom
.bwidth
, -geom
.bwidth
,
261 geom
.width
, geom
.title_height());
262 _innersize
.top
+= geom
.title_height() + geom
.bwidth
;
263 XMapWindow(**otk::display
, _title
);
265 // layout the title bar elements
268 XUnmapWindow(**otk::display
, _title
);
270 if (decorations
& Client::Decor_Handle
) {
271 geom
.handle_y
= _innersize
.top
+ _client
->area().height() + geom
.cbwidth
;
272 XMoveResizeWindow(**otk::display
, _handle
, -geom
.bwidth
, geom
.handle_y
,
273 geom
.width
, geom
.handle_height
);
274 XMoveWindow(**otk::display
, _lgrip
, -geom
.bwidth
, -geom
.bwidth
);
275 XMoveWindow(**otk::display
, _rgrip
,
276 -geom
.bwidth
+ geom
.width
- geom
.grip_width(),
278 _innersize
.bottom
+= geom
.handle_height
+ geom
.bwidth
;
279 XMapWindow(**otk::display
, _handle
);
281 XUnmapWindow(**otk::display
, _handle
);
283 XResizeWindow(**otk::display
, _frame
, geom
.width
,
284 (_client
->shaded() ? geom
.title_height() :
285 _innersize
.top
+ _innersize
.bottom
+
286 _client
->area().height()));
288 // do this in two steps because clients whose gravity is set to
289 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
290 XMoveWindow(**otk::display
, _plate
, _innersize
.left
- geom
.cbwidth
,
291 _innersize
.top
- geom
.cbwidth
);
292 XResizeWindow(**otk::display
, _plate
, _client
->area().width(),
293 _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 (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 int maxsize
= geom
.label_width
- geom
.bevel
* 2;
360 length
= font
->measureString(t
); // this returns an unsigned, so check < 0
361 if (length
< 0) length
= maxsize
; // if the string's that long just adjust
362 } while (length
> maxsize
&& text_len
-- > 0);
364 if (text_len
<= 0) return; // won't fit anything
367 switch (style
->labelTextJustify()) {
368 case otk::RenderStyle::RightBottomJustify
:
369 x
+= maxsize
- length
;
371 case otk::RenderStyle::CenterJustify
:
372 x
+= (maxsize
- length
) / 2;
374 case otk::RenderStyle::LeftTopJustify
:
378 control
->drawString(*s
, *font
, x
, 0,
379 *(_client
->focused() ? style
->textFocusColor() :
380 style
->textUnfocusColor()), t
);
382 XSetWindowBackgroundPixmap(**otk::display
, _label
, s
->pixmap());
383 XClearWindow(**otk::display
, _label
);
384 if (_label_sur
) delete _label_sur
;
388 void Frame::layoutTitle()
390 geom
.label_width
= geom
.width
- geom
.bevel
* 2;
391 if (geom
.label_width
< 1) geom
.label_width
= 1;
392 XMoveResizeWindow(**otk::display
, _label
, geom
.bevel
, geom
.bevel
,
393 geom
.label_width
, geom
.font_height
);
396 void Frame::adjustPosition()
399 x
= _client
->area().x();
400 y
= _client
->area().y();
402 XMoveWindow(**otk::display
, _frame
, x
, y
);
403 _area
= otk::Rect(otk::Point(x
, y
), _area
.size());
407 void Frame::adjustShape()
410 Client::DecorationFlags decorations
= _client
->decorations();
412 if (!_client
->shaped()) {
413 // clear the shape on the frame window
414 XShapeCombineMask(**otk::display
, _frame
, ShapeBounding
,
419 // make the frame's shape match the clients
420 XShapeCombineShape(**otk::display
, _frame
, ShapeBounding
,
423 _client
->window(), ShapeBounding
, ShapeSet
);
428 if (decorations
& Client::Decor_Titlebar
) {
429 xrect
[0].x
= -geom
.bevel
;
430 xrect
[0].y
= -geom
.bevel
;
431 xrect
[0].width
= geom
.width
+ geom
.bwidth
* 2;
432 xrect
[0].height
= geom
.title_height() + geom
.bwidth
* 2;
436 if (decorations
& Client::Decor_Handle
) {
437 xrect
[1].x
= -geom
.bevel
;
438 xrect
[1].y
= geom
.handle_y
;
439 xrect
[1].width
= geom
.width
+ geom
.bwidth
* 2;
440 xrect
[1].height
= geom
.handle_height
+ geom
.bwidth
* 2;
444 XShapeCombineRectangles(**otk::display
, _frame
,
445 ShapeBounding
, 0, 0, xrect
, num
,
446 ShapeUnion
, Unsorted
);
452 void Frame::adjustState()
454 // XXX _button_alldesk.update();
455 // XXX _button_max.update();
459 void Frame::grabClient()
461 // reparent the client to the frame
462 XReparentWindow(**otk::display
, _client
->window(), _plate
, 0, 0);
464 When reparenting the client window, it is usually not mapped yet, since
465 this occurs from a MapRequest. However, in the case where Openbox is
466 starting up, the window is already mapped, so we'll see unmap events for
467 it. There are 2 unmap events generated that we see, one with the 'event'
468 member set the root window, and one set to the client, but both get handled
469 and need to be ignored.
471 if (openbox
->state() == Openbox::State_Starting
)
472 _client
->ignore_unmaps
+= 2;
474 // select the event mask on the client's parent (to receive config/map req's)
475 XSelectInput(**otk::display
, _plate
, SubstructureRedirectMask
);
477 // map the client so it maps when the frame does
478 XMapWindow(**otk::display
, _client
->window());
485 void Frame::releaseClient()
489 // check if the app has already reparented its window away
490 if (XCheckTypedWindowEvent(**otk::display
, _client
->window(),
491 ReparentNotify
, &ev
)) {
492 XPutBackEvent(**otk::display
, &ev
);
493 // re-map the window since the unmanaging process unmaps it
494 XMapWindow(**otk::display
, _client
->window());
496 // according to the ICCCM - if the client doesn't reparent itself, then we
497 // will reparent the window to root for them
498 XReparentWindow(**otk::display
, _client
->window(),
499 otk::display
->screenInfo(_client
->screen())->rootWindow(),
500 _client
->area().x(), _client
->area().y());
505 void Frame::clientGravity(int &x
, int &y
)
508 switch (_client
->gravity()) {
510 case NorthWestGravity
:
511 case SouthWestGravity
:
518 x
-= (_size
.left
+ _size
.right
) / 2;
521 case NorthEastGravity
:
522 case SouthEastGravity
:
524 x
-= _size
.left
+ _size
.right
;
534 switch (_client
->gravity()) {
536 case NorthWestGravity
:
537 case NorthEastGravity
:
544 y
-= (_size
.top
+ _size
.bottom
) / 2;
547 case SouthWestGravity
:
548 case SouthEastGravity
:
550 y
-= _size
.top
+ _size
.bottom
;
561 void Frame::frameGravity(int &x
, int &y
)
564 switch (_client
->gravity()) {
566 case NorthWestGravity
:
568 case SouthWestGravity
:
573 x
+= (_size
.left
+ _size
.right
) / 2;
575 case NorthEastGravity
:
577 case SouthEastGravity
:
578 x
+= _size
.left
+ _size
.right
;
587 switch (_client
->gravity()) {
589 case NorthWestGravity
:
591 case SouthWestGravity
:
596 y
+= (_size
.top
+ _size
.bottom
) / 2;
598 case NorthEastGravity
:
600 case SouthEastGravity
:
601 y
+= _size
.top
+ _size
.bottom
;