1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
4 # include "../config.h"
9 #include <X11/extensions/shape.h>
15 #include "otk/display.hh"
21 OBFrame::OBFrame(const OBClient
*client
, const otk::Style
*style
)
23 _screen(otk::OBDisplay::screenInfo(client
->screen()))
28 _decorations
= client
->decorations();
30 // create the base frame parent window
31 _window
= createFrame();
34 // create all of the style element child windows
35 _titlebar
= createChild(_window
, 0);
37 _button_iconify
= createChild(_titlebar
, 0);
38 assert(_button_iconify
);
39 _button_max
= createChild(_titlebar
, 0);
41 _button_stick
= createChild(_titlebar
, 0);
42 assert(_button_stick
);
43 _button_close
= createChild(_titlebar
, 0);
44 assert(_button_close
);
45 _label
= createChild(_titlebar
, 0);
47 XMapSubwindows(otk::OBDisplay::display
, _titlebar
);
49 _handle
= createChild(_window
, 0);
51 _grip_left
= createChild(_handle
, 0);
53 _grip_right
= createChild(_handle
, 0);
55 XMapSubwindows(otk::OBDisplay::display
, _handle
);
66 XDestroyWindow(otk::OBDisplay::display
, _button_iconify
);
67 XDestroyWindow(otk::OBDisplay::display
, _button_max
);
68 XDestroyWindow(otk::OBDisplay::display
, _button_stick
);
69 XDestroyWindow(otk::OBDisplay::display
, _button_close
);
70 XDestroyWindow(otk::OBDisplay::display
, _label
);
71 XDestroyWindow(otk::OBDisplay::display
, _titlebar
);
72 XDestroyWindow(otk::OBDisplay::display
, _grip_left
);
73 XDestroyWindow(otk::OBDisplay::display
, _grip_right
);
74 XDestroyWindow(otk::OBDisplay::display
, _handle
);
78 XDestroyWindow(otk::OBDisplay::display
, _window
);
82 void OBFrame::loadStyle(const otk::Style
*style
)
86 // if a style was previously set, then 'replace' is true, cause we're
88 bool replace
= (_style
);
91 // XXX: do shit here whatever
96 XSetWindowBorderWidth(otk::OBDisplay::display
, _window
,
97 _style
->getBorderWidth());
98 XSetWindowBorder(otk::OBDisplay::display
, _window
,
99 _style
->getBorderColor().pixel());
100 XSetWindowBorderWidth(otk::OBDisplay::display
, _titlebar
,
101 _style
->getBorderWidth());
102 XSetWindowBorder(otk::OBDisplay::display
, _titlebar
,
103 _style
->getBorderColor().pixel());
104 XSetWindowBorderWidth(otk::OBDisplay::display
, _grip_left
,
105 _style
->getBorderWidth());
106 XSetWindowBorder(otk::OBDisplay::display
, _grip_left
,
107 _style
->getBorderColor().pixel());
108 XSetWindowBorderWidth(otk::OBDisplay::display
, _grip_right
,
109 _style
->getBorderWidth());
110 XSetWindowBorder(otk::OBDisplay::display
, _grip_right
,
111 _style
->getBorderColor().pixel());
112 XSetWindowBorderWidth(otk::OBDisplay::display
, _handle
,
113 _style
->getBorderWidth());
114 XSetWindowBorder(otk::OBDisplay::display
, _handle
,
115 _style
->getBorderColor().pixel());
117 // if !replace, then update() will get called after the client is grabbed!
121 // XXX: make everything redraw
126 void OBFrame::update()
128 // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
129 _decorations
= _client
->decorations();
131 int width
; // the width of the client window and the border around it
133 if (_decorations
& OBClient::Decor_Border
) {
134 _size
.left
= _size
.top
= _size
.bottom
= _size
.right
=
135 _style
->getFrameWidth();
136 width
= _client
->area().width() + _style
->getFrameWidth() * 2;
138 _size
.left
= _size
.top
= _size
.bottom
= _size
.right
= 0;
139 width
= _client
->area().width();
142 if (_decorations
& OBClient::Decor_Titlebar
) {
143 // set the titlebar size
144 _titlebar_area
.setRect(-_style
->getBorderWidth(),
145 -_style
->getBorderWidth(),
147 (_style
->getFont()->height() +
148 _style
->getBevelWidth() * 2));
149 _size
.top
+= _titlebar_area
.height() + _style
->getBorderWidth();
151 // set the label size
152 _label_area
.setRect(0, _style
->getBevelWidth(),
153 width
, _style
->getFont()->height());
154 // set the buttons sizes
155 if (_decorations
& OBClient::Decor_Iconify
)
156 _button_iconify_area
.setRect(0, _style
->getBevelWidth() + 1,
157 _label_area
.height() - 2,
158 _label_area
.height() - 2);
159 if (_decorations
& OBClient::Decor_Maximize
)
160 _button_max_area
.setRect(0, _style
->getBevelWidth() + 1,
161 _label_area
.height() - 2,
162 _label_area
.height() - 2);
163 if (_decorations
& OBClient::Decor_Sticky
)
164 _button_stick_area
.setRect(0, _style
->getBevelWidth() + 1,
165 _label_area
.height() - 2,
166 _label_area
.height() - 2);
167 if (_decorations
& OBClient::Decor_Close
)
168 _button_close_area
.setRect(0, _style
->getBevelWidth() + 1,
169 _label_area
.height() - 2,
170 _label_area
.height() - 2);
172 // separation between titlebar elements
173 const int sep
= _style
->getBevelWidth() + 1;
175 std::string layout
= "ILMC"; // XXX: get this from somewhere
176 // XXX: it is REQUIRED that by this point, the string only has one of each
177 // possible letter, all of the letters are valid, and L exists somewhere in
181 for (int i
= 0, len
= layout
.size(); i
< len
; ++i
) {
185 if (!(_decorations
& OBClient::Decor_Iconify
))
187 area
= &_button_iconify_area
;
193 if (!(_decorations
& OBClient::Decor_Maximize
))
195 area
= &_button_max_area
;
198 if (!(_decorations
& OBClient::Decor_Sticky
))
200 area
= &_button_stick_area
;
203 if (!(_decorations
& OBClient::Decor_Close
))
205 area
= &_button_close_area
;
208 assert(false); // the layout string is invalid!
209 continue; // just to fuck with g++
212 if (layout
[i
] != 'L')
213 _label_area
.setWidth(_label_area
.width() - area
->width());
214 x
+= sep
+ area
->width();
218 if (_decorations
& OBClient::Decor_Handle
) {
219 _handle_area
.setRect(-_style
->getBorderWidth(),
220 _size
.top
+ _client
->area().height(),
221 width
, _style
->getHandleWidth());
222 _grip_left_area
.setRect(-_style
->getBorderWidth(),
223 -_style
->getBorderWidth(),
224 // XXX: get a Point class in otk and use that for
225 // the 'buttons size' since theyre all the same
226 _button_iconify_area
.width() * 2,
227 _handle_area
.height());
228 _grip_right_area
.setRect(((_handle_area
.right() + 1) -
229 _button_iconify_area
.width() * 2),
230 -_style
->getBorderWidth(),
231 // XXX: get a Point class in otk and use that for
232 // the 'buttons size' since theyre all the same
233 _button_iconify_area
.width() * 2,
234 _handle_area
.height());
235 _size
.bottom
+= _handle_area
.height() + _style
->getBorderWidth();
239 // position/size all the windows
241 XResizeWindow(otk::OBDisplay::display
, _window
,
242 _size
.left
+ _size
.right
+ _client
->area().width(),
243 _size
.top
+ _size
.bottom
+ _client
->area().height());
245 XMoveWindow(otk::OBDisplay::display
, _client
->window(),
246 _size
.left
, _size
.top
);
248 if (_decorations
& OBClient::Decor_Titlebar
) {
249 XMoveResizeWindow(otk::OBDisplay::display
, _titlebar
,
250 _titlebar_area
.x(), _titlebar_area
.y(),
251 _titlebar_area
.width(), _titlebar_area
.height());
252 XMoveResizeWindow(otk::OBDisplay::display
, _label
,
253 _label_area
.x(), _label_area
.y(),
254 _label_area
.width(), _label_area
.height());
255 if (_decorations
& OBClient::Decor_Iconify
)
256 XMoveResizeWindow(otk::OBDisplay::display
, _button_iconify
,
257 _button_iconify_area
.x(), _button_iconify_area
.y(),
258 _button_iconify_area
.width(),
259 _button_iconify_area
.height());
260 if (_decorations
& OBClient::Decor_Maximize
)
261 XMoveResizeWindow(otk::OBDisplay::display
, _button_max
,
262 _button_max_area
.x(), _button_max_area
.y(),
263 _button_max_area
.width(),
264 _button_max_area
.height());
265 if (_decorations
& OBClient::Decor_Sticky
)
266 XMoveResizeWindow(otk::OBDisplay::display
, _button_stick
,
267 _button_stick_area
.x(), _button_stick_area
.y(),
268 _button_stick_area
.width(),
269 _button_stick_area
.height());
270 if (_decorations
& OBClient::Decor_Close
)
271 XMoveResizeWindow(otk::OBDisplay::display
, _button_close
,
272 _button_close_area
.x(), _button_close_area
.y(),
273 _button_close_area
.width(),
274 _button_close_area
.height());
277 if (_decorations
& OBClient::Decor_Handle
) {
278 XMoveResizeWindow(otk::OBDisplay::display
, _handle
,
279 _handle_area
.x(), _handle_area
.y(),
280 _handle_area
.width(), _handle_area
.height());
281 XMoveResizeWindow(otk::OBDisplay::display
, _grip_left
,
282 _grip_left_area
.x(), _grip_left_area
.y(),
283 _grip_left_area
.width(), _grip_left_area
.height());
284 XMoveResizeWindow(otk::OBDisplay::display
, _grip_right
,
285 _grip_right_area
.x(), _grip_right_area
.y(),
286 _grip_right_area
.width(), _grip_right_area
.height());
289 // map/unmap all the windows
290 if (_decorations
& OBClient::Decor_Titlebar
) {
291 XMapWindow(otk::OBDisplay::display
, _label
);
292 if (_decorations
& OBClient::Decor_Iconify
)
293 XMapWindow(otk::OBDisplay::display
, _button_iconify
);
295 XUnmapWindow(otk::OBDisplay::display
, _button_iconify
);
296 if (_decorations
& OBClient::Decor_Maximize
)
297 XMapWindow(otk::OBDisplay::display
, _button_max
);
299 XUnmapWindow(otk::OBDisplay::display
, _button_max
);
300 if (_decorations
& OBClient::Decor_Sticky
)
301 XMapWindow(otk::OBDisplay::display
, _button_stick
);
303 XUnmapWindow(otk::OBDisplay::display
, _button_stick
);
304 if (_decorations
& OBClient::Decor_Close
)
305 XMapWindow(otk::OBDisplay::display
, _button_close
);
307 XUnmapWindow(otk::OBDisplay::display
, _button_close
);
308 XMapWindow(otk::OBDisplay::display
, _titlebar
);
310 XUnmapWindow(otk::OBDisplay::display
, _titlebar
);
311 XUnmapWindow(otk::OBDisplay::display
, _label
);
312 XUnmapWindow(otk::OBDisplay::display
, _button_iconify
);
313 XUnmapWindow(otk::OBDisplay::display
, _button_max
);
314 XUnmapWindow(otk::OBDisplay::display
, _button_stick
);
315 XUnmapWindow(otk::OBDisplay::display
, _button_close
);
318 if (_decorations
& OBClient::Decor_Handle
) {
319 XMapWindow(otk::OBDisplay::display
, _grip_left
);
320 XMapWindow(otk::OBDisplay::display
, _grip_right
);
321 XMapWindow(otk::OBDisplay::display
, _handle
);
323 XUnmapWindow(otk::OBDisplay::display
, _handle
);
324 XUnmapWindow(otk::OBDisplay::display
, _grip_left
);
325 XUnmapWindow(otk::OBDisplay::display
, _grip_right
);
328 // XXX: more is gunna have to happen here
334 void OBFrame::updateShape()
337 if (!_client
->shaped()) {
338 // clear the shape on the frame window
339 XShapeCombineMask(otk::OBDisplay::display
, _window
, ShapeBounding
,
340 _size
.left
- 2,//frame.margin.left - frame.border_w,
341 _size
.top
- 2,//frame.margin.top - frame.border_w,
344 // make the frame's shape match the clients
345 XShapeCombineShape(otk::OBDisplay::display
, _window
, ShapeBounding
,
348 _client
->window(), ShapeBounding
, ShapeSet
);
354 if (decorations & Decor_Titlebar) {
355 xrect[0].x = xrect[0].y = -frame.border_w;
356 xrect[0].width = frame.rect.width();
357 xrect[0].height = frame.title_h + (frame.border_w * 2);
361 if (decorations & Decor_Handle) {
362 xrect[1].x = -frame.border_w;
363 xrect[1].y = frame.rect.height() - frame.margin.bottom +
364 frame.mwm_border_w - frame.border_w;
365 xrect[1].width = frame.rect.width();
366 xrect[1].height = frame.handle_h + (frame.border_w * 2);
370 XShapeCombineRectangles(otk::OBDisplay::display
, _window
,
371 ShapeBounding
, 0, 0, xrect
, num
,
372 ShapeUnion
, Unsorted
);
378 void OBFrame::grabClient()
381 XGrabServer(otk::OBDisplay::display
);
383 // select the event mask on the frame
384 XSelectInput(otk::OBDisplay::display
, _window
, SubstructureRedirectMask
);
386 // reparent the client to the frame
387 XSelectInput(otk::OBDisplay::display
, _client
->window(),
388 OBClient::event_mask
& ~StructureNotifyMask
);
389 XReparentWindow(otk::OBDisplay::display
, _client
->window(), _window
,
390 _size
.left
, _size
.top
);
391 XSelectInput(otk::OBDisplay::display
, _client
->window(),
392 OBClient::event_mask
);
394 // raise the client above the frame
395 XRaiseWindow(otk::OBDisplay::display
, _client
->window());
396 // map the client so it maps when the frame does
397 XMapWindow(otk::OBDisplay::display
, _client
->window());
399 XUngrabServer(otk::OBDisplay::display
);
405 void OBFrame::releaseClient(bool remap
)
407 // check if the app has already reparented its window to the root window
409 if (XCheckTypedWindowEvent(otk::OBDisplay::display
, _client
->window(),
410 ReparentNotify
, &ev
)) {
411 remap
= true; // XXX: why do we remap the window if they already
412 // reparented to root?
414 // according to the ICCCM - if the client doesn't reparent to
415 // root, then we have to do it for them
416 XReparentWindow(otk::OBDisplay::display
, _client
->window(),
417 _screen
->getRootWindow(),
418 _client
->area().x(), _client
->area().y());
421 // if we want to remap the window, do so now
423 XMapWindow(otk::OBDisplay::display
, _client
->window());
427 Window
OBFrame::createChild(Window parent
, Cursor cursor
)
429 XSetWindowAttributes attrib_create
;
430 unsigned long create_mask
= CWBackPixmap
| CWBorderPixel
| CWEventMask
;
432 attrib_create
.background_pixmap
= None
;
433 attrib_create
.event_mask
= ButtonPressMask
| ButtonReleaseMask
|
434 ButtonMotionMask
| ExposureMask
;
437 create_mask
|= CWCursor
;
438 attrib_create
.cursor
= cursor
;
441 Window w
= XCreateWindow(otk::OBDisplay::display
, parent
, 0, 0, 1, 1, 0,
442 _screen
->getDepth(), InputOutput
,
443 _screen
->getVisual(), create_mask
, &attrib_create
);
444 XRaiseWindow(otk::OBDisplay::display
, w
); // raise above the parent
449 Window
OBFrame::createFrame()
451 XSetWindowAttributes attrib_create
;
452 unsigned long create_mask
= CWBackPixmap
| CWBorderPixel
| CWColormap
|
453 CWOverrideRedirect
| CWEventMask
;
455 attrib_create
.background_pixmap
= None
;
456 attrib_create
.colormap
= _screen
->getColormap();
457 attrib_create
.override_redirect
= True
;
458 attrib_create
.event_mask
= EnterWindowMask
| LeaveWindowMask
| ButtonPress
;
460 We catch button presses because other wise they get passed down to the
461 root window, which will then cause root menus to show when you click the
465 return XCreateWindow(otk::OBDisplay::display
, _screen
->getRootWindow(),
467 _screen
->getDepth(), InputOutput
, _screen
->getVisual(),
468 create_mask
, &attrib_create
);