]> Dogcows Code - chaz/openbox/blob - src/frame.cc
pretty sure all frame elements are placed correctly now
[chaz/openbox] / src / frame.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 extern "C" {
8 #ifdef SHAPE
9 #include <X11/extensions/shape.h>
10 #endif // SHAPE
11 }
12
13 #include "frame.hh"
14 #include "client.hh"
15 #include "otk/display.hh"
16
17 #include <string>
18
19 namespace ob {
20
21 OBFrame::OBFrame(const OBClient *client, const otk::Style *style)
22 : _client(client),
23 _screen(otk::OBDisplay::screenInfo(client->screen()))
24 {
25 assert(client);
26 assert(style);
27
28 _decorations = client->decorations();
29
30 // create the base frame parent window
31 _window = createFrame();
32 assert(_window);
33
34 // create all of the style element child windows
35 _titlebar = createChild(_window, 0);
36 assert(_titlebar);
37 _button_iconify = createChild(_titlebar, 0);
38 assert(_button_iconify);
39 _button_max = createChild(_titlebar, 0);
40 assert(_button_max);
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);
46 assert(_label);
47 XMapSubwindows(otk::OBDisplay::display, _titlebar);
48
49 _handle = createChild(_window, 0);
50 assert(_handle);
51 _grip_left = createChild(_handle, 0);
52 assert(_grip_left);
53 _grip_right = createChild(_handle, 0);
54 assert(_grip_right);
55 XMapSubwindows(otk::OBDisplay::display, _handle);
56
57 _style = 0;
58 loadStyle(style);
59
60 grabClient();
61 }
62
63
64 OBFrame::~OBFrame()
65 {
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);
75
76 releaseClient(false);
77
78 XDestroyWindow(otk::OBDisplay::display, _window);
79 }
80
81
82 void OBFrame::loadStyle(const otk::Style *style)
83 {
84 assert(style);
85
86 // if a style was previously set, then 'replace' is true, cause we're
87 // replacing a style
88 bool replace = (_style);
89
90 if (replace) {
91 // XXX: do shit here whatever
92 }
93
94 _style = style;
95
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());
116
117 // if !replace, then update() will get called after the client is grabbed!
118 if (replace) {
119 update();
120
121 // XXX: make everything redraw
122 }
123 }
124
125
126 void OBFrame::update()
127 {
128 // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
129 _decorations = _client->decorations();
130
131 int width; // the width of the client window and the border around it
132
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;
137 } else {
138 _size.left = _size.top = _size.bottom = _size.right = 0;
139 width = _client->area().width();
140 }
141
142 if (_decorations & OBClient::Decor_Titlebar) {
143 // set the titlebar size
144 _titlebar_area.setRect(-_style->getBorderWidth(),
145 -_style->getBorderWidth(),
146 width,
147 (_style->getFont()->height() +
148 _style->getBevelWidth() * 2));
149 _size.top += _titlebar_area.height() + _style->getBorderWidth();
150
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);
171
172 // separation between titlebar elements
173 const int sep = _style->getBevelWidth() + 1;
174
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
178 // the string!
179
180 int x = sep;
181 for (int i = 0, len = layout.size(); i < len; ++i) {
182 otk::Rect *area;
183 switch (layout[i]) {
184 case 'I':
185 if (!(_decorations & OBClient::Decor_Iconify))
186 continue; // skip it
187 area = &_button_iconify_area;
188 break;
189 case 'L':
190 area = &_label_area;
191 break;
192 case 'M':
193 if (!(_decorations & OBClient::Decor_Maximize))
194 continue; // skip it
195 area = &_button_max_area;
196 break;
197 case 'S':
198 if (!(_decorations & OBClient::Decor_Sticky))
199 continue; // skip it
200 area = &_button_stick_area;
201 break;
202 case 'C':
203 if (!(_decorations & OBClient::Decor_Close))
204 continue; // skip it
205 area = &_button_close_area;
206 break;
207 default:
208 assert(false); // the layout string is invalid!
209 continue; // just to fuck with g++
210 }
211 area->setX(x);
212 if (layout[i] != 'L')
213 _label_area.setWidth(_label_area.width() - area->width());
214 x += sep + area->width();
215 }
216 }
217
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();
236 }
237
238
239 // position/size all the windows
240
241 XResizeWindow(otk::OBDisplay::display, _window,
242 _size.left + _size.right + _client->area().width(),
243 _size.top + _size.bottom + _client->area().height());
244
245 XMoveWindow(otk::OBDisplay::display, _client->window(),
246 _size.left, _size.top);
247
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());
275 }
276
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());
287 }
288
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);
294 else
295 XUnmapWindow(otk::OBDisplay::display, _button_iconify);
296 if (_decorations & OBClient::Decor_Maximize)
297 XMapWindow(otk::OBDisplay::display, _button_max);
298 else
299 XUnmapWindow(otk::OBDisplay::display, _button_max);
300 if (_decorations & OBClient::Decor_Sticky)
301 XMapWindow(otk::OBDisplay::display, _button_stick);
302 else
303 XUnmapWindow(otk::OBDisplay::display, _button_stick);
304 if (_decorations & OBClient::Decor_Close)
305 XMapWindow(otk::OBDisplay::display, _button_close);
306 else
307 XUnmapWindow(otk::OBDisplay::display, _button_close);
308 XMapWindow(otk::OBDisplay::display, _titlebar);
309 } else {
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);
316 }
317
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);
322 } else {
323 XUnmapWindow(otk::OBDisplay::display, _handle);
324 XUnmapWindow(otk::OBDisplay::display, _grip_left);
325 XUnmapWindow(otk::OBDisplay::display, _grip_right);
326 }
327
328 // XXX: more is gunna have to happen here
329
330 updateShape();
331 }
332
333
334 void OBFrame::updateShape()
335 {
336 #ifdef SHAPE
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,
342 None, ShapeSet);
343 } else {
344 // make the frame's shape match the clients
345 XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
346 _size.left - 2,
347 _size.top - 2,
348 _client->window(), ShapeBounding, ShapeSet);
349
350 int num = 0;
351 XRectangle xrect[2];
352
353 /*
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);
358 ++num;
359 }
360
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);
367 ++num;
368 }*/
369
370 XShapeCombineRectangles(otk::OBDisplay::display, _window,
371 ShapeBounding, 0, 0, xrect, num,
372 ShapeUnion, Unsorted);
373 }
374 #endif // SHAPE
375 }
376
377
378 void OBFrame::grabClient()
379 {
380
381 XGrabServer(otk::OBDisplay::display);
382
383 // select the event mask on the frame
384 XSelectInput(otk::OBDisplay::display, _window, SubstructureRedirectMask);
385
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);
393
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());
398
399 XUngrabServer(otk::OBDisplay::display);
400
401 update();
402 }
403
404
405 void OBFrame::releaseClient(bool remap)
406 {
407 // check if the app has already reparented its window to the root window
408 XEvent ev;
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?
413 } else {
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());
419 }
420
421 // if we want to remap the window, do so now
422 if (remap)
423 XMapWindow(otk::OBDisplay::display, _client->window());
424 }
425
426
427 Window OBFrame::createChild(Window parent, Cursor cursor)
428 {
429 XSetWindowAttributes attrib_create;
430 unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
431
432 attrib_create.background_pixmap = None;
433 attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
434 ButtonMotionMask | ExposureMask;
435
436 if (cursor) {
437 create_mask |= CWCursor;
438 attrib_create.cursor = cursor;
439 }
440
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
445 return w;
446 }
447
448
449 Window OBFrame::createFrame()
450 {
451 XSetWindowAttributes attrib_create;
452 unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
453 CWOverrideRedirect | CWEventMask;
454
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;
459 /*
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
462 window's frame.
463 */
464
465 return XCreateWindow(otk::OBDisplay::display, _screen->getRootWindow(),
466 0, 0, 1, 1, 0,
467 _screen->getDepth(), InputOutput, _screen->getVisual(),
468 create_mask, &attrib_create);
469 }
470
471 }
This page took 0.052189 seconds and 4 git commands to generate.