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 int[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 (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 (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 (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 // do this in two steps because clients whose gravity is set to
291 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
292 XMoveWindow(**otk::display
, _plate
, _innersize
.left
- geom
.cbwidth
,
293 _innersize
.top
- geom
.cbwidth
);
294 XResizeWindow(**otk::display
, _plate
, _client
->area().width(),
295 _client
->area().height());
297 _size
.left
= _innersize
.left
+ geom
.bwidth
;
298 _size
.right
= _innersize
.right
+ geom
.bwidth
;
299 _size
.top
= _innersize
.top
+ geom
.bwidth
;
300 _size
.bottom
= _innersize
.bottom
+ geom
.bwidth
;
302 _area
= otk::Rect(_area
.position(), otk::Size(_client
->area().width() +
303 _size
.left
+ _size
.right
,
304 _client
->area().height() +
305 _size
.top
+ _size
.bottom
));
307 // render all the elements
308 int screen
= _client
->screen();
309 bool focus
= _client
->focused();
310 if (decorations
& Client::Decor_Titlebar
) {
311 render(screen
, otk::Size(geom
.width
, geom
.title_height()), _title
,
312 &_title_sur
, *(focus
? style
->titlebarFocusBackground() :
313 style
->titlebarUnfocusBackground()));
318 if (decorations
& Client::Decor_Handle
) {
319 render(screen
, otk::Size(geom
.width
, geom
.handle_height
), _handle
,
320 &_handle_sur
, *(focus
? style
->handleFocusBackground() :
321 style
->handleUnfocusBackground()));
322 render(screen
, otk::Size(geom
.grip_width(), geom
.handle_height
), _lgrip
,
323 &_grip_sur
, *(focus
? style
->gripFocusBackground() :
324 style
->gripUnfocusBackground()));
325 XSetWindowBackgroundPixmap(**otk::display
, _rgrip
, _grip_sur
->pixmap());
326 XClearWindow(**otk::display
, _rgrip
);
329 XSetWindowBorder(**otk::display
, _plate
,
330 focus
? style
->clientBorderFocusColor()->pixel() :
331 style
->clientBorderUnfocusColor()->pixel());
336 void Frame::renderLabel()
338 const otk::RenderStyle
*style
= otk::RenderStyle::style(_client
->screen());
339 const otk::RenderControl
*control
=
340 otk::display
->renderControl(_client
->screen());
341 const otk::Font
*font
= style
->labelFont();
343 otk::Surface
*s
= new otk::Surface(_client
->screen(),
344 otk::Size(geom
.label_width
,
345 geom
.label_height()));
346 control
->drawBackground(*s
, *(_client
->focused() ?
347 style
->labelFocusBackground() :
348 style
->labelUnfocusBackground()));
350 otk::ustring t
= _client
->title(); // the actual text to draw
351 int x
= geom
.bevel
; // x coord for the text
353 if (x
* 2 > geom
.label_width
) return; // no room at all
355 // find a string that will fit inside the area for text
356 otk::ustring::size_type text_len
= t
.size();
358 int maxsize
= geom
.label_width
- geom
.bevel
* 2;
362 length
= font
->measureString(t
); // this returns an unsigned, so check < 0
363 if (length
< 0) length
= maxsize
; // if the string's that long just adjust
364 } while (length
> maxsize
&& text_len
-- > 0);
366 if (text_len
<= 0) return; // won't fit anything
369 switch (style
->labelTextJustify()) {
370 case otk::RenderStyle::RightBottomJustify
:
371 x
+= maxsize
- length
;
373 case otk::RenderStyle::CenterJustify
:
374 x
+= (maxsize
- length
) / 2;
376 case otk::RenderStyle::LeftTopJustify
:
380 control
->drawString(*s
, *font
, x
, 0,
381 *(_client
->focused() ? style
->textFocusColor() :
382 style
->textUnfocusColor()), t
);
384 XSetWindowBackgroundPixmap(**otk::display
, _label
, s
->pixmap());
385 XClearWindow(**otk::display
, _label
);
386 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
;