]> Dogcows Code - chaz/openbox/blob - src/frame.cc
ignore the right about of unmap notifies
[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 "openbox.hh"
14 #include "frame.hh"
15 #include "client.hh"
16 #include "python.hh"
17 #include "bindings.hh"
18 #include "otk/display.hh"
19
20 #include <string>
21
22 namespace ob {
23
24 const long OBFrame::event_mask;
25
26 OBFrame::OBFrame(OBClient *client, otk::Style *style)
27 : otk::OtkWidget(Openbox::instance, style, Horizontal, 0, 1, true),
28 OBWidget(Type_Frame),
29 _client(client),
30 _screen(otk::OBDisplay::screenInfo(client->screen())),
31 _plate(this, OBWidget::Type_Plate),
32 _titlebar(this, OBWidget::Type_Titlebar),
33 _button_close(&_titlebar, OBWidget::Type_CloseButton),
34 _button_iconify(&_titlebar, OBWidget::Type_IconifyButton),
35 _button_max(&_titlebar, OBWidget::Type_MaximizeButton),
36 _button_stick(&_titlebar, OBWidget::Type_StickyButton),
37 _label(&_titlebar, OBWidget::Type_Label),
38 _handle(this, OBWidget::Type_Handle),
39 _grip_left(&_handle, OBWidget::Type_LeftGrip),
40 _grip_right(&_handle, OBWidget::Type_RightGrip),
41 _decorations(client->decorations())
42 {
43 assert(client);
44 assert(style);
45
46 XSelectInput(otk::OBDisplay::display, _window, OBFrame::event_mask);
47
48 _grip_left.setCursor(Openbox::instance->cursors().ll_angle);
49 _grip_right.setCursor(Openbox::instance->cursors().lr_angle);
50
51 _label.setText(_client->title());
52
53 _style = 0;
54 setStyle(style);
55
56 otk::OtkWidget::unfocus(); // stuff starts out appearing focused in otk
57
58 _plate.show(); // the other stuff is shown based on decor settings
59
60 grabClient();
61 }
62
63
64 OBFrame::~OBFrame()
65 {
66 releaseClient();
67 }
68
69
70 void OBFrame::setTitle(const std::string &text)
71 {
72 _label.setText(text);
73 _label.update();
74 }
75
76
77 void OBFrame::setStyle(otk::Style *style)
78 {
79 assert(style);
80
81 otk::OtkWidget::setStyle(style);
82
83 // if a style was previously set, then 'replace' is true, cause we're
84 // replacing a style
85 bool replace = (_style);
86
87 if (replace) {
88 // XXX: do shit here whatever
89 }
90
91 _style = style;
92
93 setBorderColor(_style->getBorderColor());
94
95 // if !replace, then adjust() will get called after the client is grabbed!
96 if (replace) {
97 // size/position everything
98 adjustSize();
99 adjustPosition();
100 }
101 }
102
103
104 void OBFrame::focus()
105 {
106 otk::OtkWidget::focus();
107 update();
108 }
109
110
111 void OBFrame::unfocus()
112 {
113 otk::OtkWidget::unfocus();
114 update();
115 }
116
117
118 void OBFrame::adjust()
119 {
120 // the party all happens in adjustSize
121 }
122
123
124 void OBFrame::adjustSize()
125 {
126 // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
127 _decorations = _client->decorations();
128
129 // true/false for whether to show each element of the titlebar
130 bool tit_i = false, tit_m = false, tit_s = false, tit_c = false;
131 int width; // the width of the client and its border
132 int bwidth; // width to make borders
133 int cbwidth; // width of the inner client border
134 int butsize=0; // width and height of the titlebar buttons
135 const int bevel = _style->getBevelWidth();
136
137 if (_decorations & OBClient::Decor_Border) {
138 bwidth = _style->getBorderWidth();
139 cbwidth = _style->getFrameWidth();
140 } else
141 bwidth = cbwidth = 0;
142 _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
143 cbwidth;
144 width = _client->area().width() + cbwidth * 2;
145
146 _plate.setBorderWidth(cbwidth);
147
148 setBorderWidth(bwidth);
149 _titlebar.setBorderWidth(bwidth);
150 _grip_left.setBorderWidth(bwidth);
151 _grip_right.setBorderWidth(bwidth);
152 _handle.setBorderWidth(bwidth);
153
154 if (_decorations & OBClient::Decor_Titlebar) {
155 // set the titlebar size
156 _titlebar.setGeometry(-bwidth,
157 -bwidth,
158 width,
159 _style->getFont()->height() + bevel * 2);
160 _innersize.top += _titlebar.height() + bwidth;
161
162 // set the label size
163 _label.setGeometry(0, bevel, width, _style->getFont()->height());
164 // set the buttons sizes
165 butsize = _label.height() - 2;
166 if (_decorations & OBClient::Decor_Iconify)
167 _button_iconify.setGeometry(0, bevel + 1, butsize, butsize);
168 if (_decorations & OBClient::Decor_Maximize)
169 _button_max.setGeometry(0, bevel + 1, butsize, butsize);
170 if (_decorations & OBClient::Decor_Sticky)
171 _button_stick.setGeometry(0, bevel + 1, butsize, butsize);
172 if (_decorations & OBClient::Decor_Close)
173 _button_close.setGeometry(0, bevel + 1, butsize, butsize);
174
175 // separation between titlebar elements
176 const int sep = bevel + 1;
177
178 std::string layout;
179 if (!python_get_string("titlebar_layout", &layout))
180 layout = "ILMC";
181
182 // this code ensures that the string only has one of each possible
183 // letter, all of the letters are valid, and L exists somewhere in the
184 // string!
185 bool tit_l = false;
186
187 for (std::string::size_type i = 0; i < layout.size(); ++i) {
188 switch (layout[i]) {
189 case 'i':
190 case 'I':
191 if (!tit_i && (_decorations & OBClient::Decor_Iconify)) {
192 tit_i = true;
193 continue;
194 }
195 break;
196 case 'l':
197 case 'L':
198 if (!tit_l) {
199 tit_l = true;
200 continue;
201 }
202 break;
203 case 'm':
204 case 'M':
205 if (!tit_m && (_decorations & OBClient::Decor_Maximize)) {
206 tit_m = true;
207 continue;
208 }
209 break;
210 case 's':
211 case 'S':
212 if (!tit_s && (_decorations & OBClient::Decor_Sticky)) {
213 tit_s = true;
214 continue;
215 }
216 break;
217 case 'c':
218 case 'C':
219 if (!tit_c && (_decorations & OBClient::Decor_Close)) {
220 tit_c = true;
221 continue;
222 }
223 break;
224 }
225 // if we get here then we don't want the letter, kill it
226 layout.erase(i--, 1);
227 }
228 if (!tit_l)
229 layout.append(1, 'L');
230
231 // the size of the label. this ASSUMES the layout has only buttons other
232 // that the ONE LABEL!!
233 // adds an extra sep so that there's a space on either side of the
234 // titlebar.. note: x = sep, below.
235 int lwidth = width - sep * 2 -
236 (butsize + sep) * (layout.size() - 1);
237 // quick sanity check for really small windows. if this is needed, its
238 // obviously not going to be displayed right...
239 // XXX: maybe we should make this look better somehow? constraints?
240 if (lwidth <= 0) lwidth = 1;
241 _label.setWidth(lwidth);
242
243 int x = sep;
244 for (std::string::size_type i = 0, len = layout.size(); i < len; ++i) {
245 switch (layout[i]) {
246 case 'i':
247 case 'I':
248 _button_iconify.move(x, _button_iconify.rect().y());
249 x += _button_iconify.width();
250 break;
251 case 'l':
252 case 'L':
253 _label.move(x, _label.rect().y());
254 x += _label.width();
255 break;
256 case 'm':
257 case 'M':
258 _button_max.move(x, _button_max.rect().y());
259 x += _button_max.width();
260 break;
261 case 's':
262 case 'S':
263 _button_stick.move(x, _button_stick.rect().y());
264 x += _button_stick.width();
265 break;
266 case 'c':
267 case 'C':
268 _button_close.move(x, _button_close.rect().y());
269 x += _button_close.width();
270 break;
271 default:
272 assert(false); // the layout string is invalid!
273 }
274 x += sep;
275 }
276 }
277
278 if (_decorations & OBClient::Decor_Handle) {
279 _handle.setGeometry(-bwidth,
280 _innersize.top + _client->area().height() + cbwidth,
281 width, _style->getHandleWidth());
282 _grip_left.setGeometry(-bwidth,
283 -bwidth,
284 // XXX: get a Point class in otk and use that for
285 // the 'buttons size' since theyre all the same
286 butsize * 2,
287 _handle.height());
288 _grip_right.setGeometry(((_handle.rect().right() + 1) -
289 butsize * 2),
290 -bwidth,
291 // XXX: get a Point class in otk and use that for
292 // the 'buttons size' since theyre all the same
293 butsize * 2,
294 _handle.height());
295 _innersize.bottom += _handle.height() + bwidth;
296 }
297
298
299 // position/size all the windows
300
301 if (_client->shaded())
302 resize(_innersize.left + _innersize.right + _client->area().width(),
303 _titlebar.height());
304 else
305 resize(_innersize.left + _innersize.right + _client->area().width(),
306 _innersize.top + _innersize.bottom + _client->area().height());
307
308 _plate.setGeometry(_innersize.left - cbwidth, _innersize.top - cbwidth,
309 _client->area().width(), _client->area().height());
310
311 // map/unmap all the windows
312 if (_decorations & OBClient::Decor_Titlebar) {
313 _label.show();
314 if (tit_i)
315 _button_iconify.show();
316 else
317 _button_iconify.hide();
318 if (tit_m)
319 _button_max.show();
320 else
321 _button_max.hide();
322 if (tit_s)
323 _button_stick.show();
324 else
325 _button_stick.hide();
326 if (tit_c)
327 _button_close.show();
328 else
329 _button_close.hide();
330 _titlebar.show();
331 } else {
332 _titlebar.hide(true);
333 }
334
335 if (_decorations & OBClient::Decor_Handle)
336 _handle.show(true);
337 else
338 _handle.hide(true);
339
340 _size.left = _innersize.left + bwidth;
341 _size.right = _innersize.right + bwidth;
342 _size.top = _innersize.top + bwidth;
343 _size.bottom = _innersize.bottom + bwidth;
344
345 adjustShape();
346
347 update();
348 }
349
350
351 void OBFrame::adjustPosition()
352 {
353 int x, y;
354 clientGravity(x, y);
355 move(x, y);
356 }
357
358
359 void OBFrame::adjustShape()
360 {
361 #ifdef SHAPE
362 int bwidth = (_decorations & OBClient::Decor_Border) ?
363 _style->getBorderWidth() : 0;
364
365 if (!_client->shaped()) {
366 // clear the shape on the frame window
367 XShapeCombineMask(otk::OBDisplay::display, _window, ShapeBounding,
368 _innersize.left,
369 _innersize.top,
370 None, ShapeSet);
371 } else {
372 // make the frame's shape match the clients
373 XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
374 _innersize.left,
375 _innersize.top,
376 _client->window(), ShapeBounding, ShapeSet);
377
378 int num = 0;
379 XRectangle xrect[2];
380
381 if (_decorations & OBClient::Decor_Titlebar) {
382 xrect[0].x = _titlebar.rect().x();
383 xrect[0].y = _titlebar.rect().y();
384 xrect[0].width = _titlebar.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
385 xrect[0].height = _titlebar.height() + bwidth * 2;
386 ++num;
387 }
388
389 if (_decorations & OBClient::Decor_Handle) {
390 xrect[1].x = _handle.rect().x();
391 xrect[1].y = _handle.rect().y();
392 xrect[1].width = _handle.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
393 xrect[1].height = _handle.height() + bwidth * 2;
394 ++num;
395 }
396
397 XShapeCombineRectangles(otk::OBDisplay::display, window(),
398 ShapeBounding, 0, 0, xrect, num,
399 ShapeUnion, Unsorted);
400 }
401 #endif // SHAPE
402 }
403
404
405 void OBFrame::grabClient()
406 {
407 // reparent the client to the frame
408 XReparentWindow(otk::OBDisplay::display, _client->window(),
409 _plate.window(), 0, 0);
410 /*
411 When reparenting the client window, it is usually not mapped yet, since
412 this occurs from a MapRequest. However, in the case where Openbox is
413 starting up, the window is already mapped, so we'll see unmap events for
414 it. There are 2 unmap events generated that we see, one with the 'event'
415 member set the root window, and one set to the client, but both get handled
416 and need to be ignored.
417 */
418 if (Openbox::instance->state() == Openbox::State_Starting)
419 _client->ignore_unmaps += 2;
420
421 // select the event mask on the client's parent (to receive config req's)
422 XSelectInput(otk::OBDisplay::display, _plate.window(),
423 SubstructureRedirectMask);
424
425 // map the client so it maps when the frame does
426 XMapWindow(otk::OBDisplay::display, _client->window());
427
428 adjustSize();
429 adjustPosition();
430 }
431
432
433 void OBFrame::releaseClient()
434 {
435 // check if the app has already reparented its window to the root window
436 XEvent ev;
437 if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
438 ReparentNotify, &ev)) {
439 // XXX: ob2/bb didn't do this.. look up this process in other wm's!
440 //XPutBackEvent(otk::OBDisplay::display, &ev);
441 XMapWindow(otk::OBDisplay::display, _client->window());
442 } else {
443 // according to the ICCCM - if the client doesn't reparent to
444 // root, then we have to do it for them
445 XReparentWindow(otk::OBDisplay::display, _client->window(),
446 _screen->rootWindow(),
447 _client->area().x(), _client->area().y());
448 }
449
450 // do an extra map here .. ? XXX
451 // XMapWindow(otk::OBDisplay::display, _client->window());
452 }
453
454
455 void OBFrame::clientGravity(int &x, int &y)
456 {
457 x = _client->area().x();
458 y = _client->area().y();
459
460 // horizontal
461 switch (_client->gravity()) {
462 default:
463 case NorthWestGravity:
464 case SouthWestGravity:
465 case WestGravity:
466 break;
467
468 case NorthGravity:
469 case SouthGravity:
470 case CenterGravity:
471 x -= (_size.left + _size.right) / 2;
472 break;
473
474 case NorthEastGravity:
475 case SouthEastGravity:
476 case EastGravity:
477 x -= _size.left + _size.right;
478 break;
479
480 case ForgetGravity:
481 case StaticGravity:
482 x -= _size.left;
483 break;
484 }
485
486 // vertical
487 switch (_client->gravity()) {
488 default:
489 case NorthWestGravity:
490 case NorthEastGravity:
491 case NorthGravity:
492 break;
493
494 case CenterGravity:
495 case EastGravity:
496 case WestGravity:
497 y -= (_size.top + _size.bottom) / 2;
498 break;
499
500 case SouthWestGravity:
501 case SouthEastGravity:
502 case SouthGravity:
503 y -= _size.top + _size.bottom;
504 break;
505
506 case ForgetGravity:
507 case StaticGravity:
508 y -= _size.top;
509 break;
510 }
511 }
512
513
514 void OBFrame::frameGravity(int &x, int &y)
515 {
516 x = rect().x();
517 y = rect().y();
518
519 // horizontal
520 switch (_client->gravity()) {
521 default:
522 case NorthWestGravity:
523 case WestGravity:
524 case SouthWestGravity:
525 break;
526 case NorthGravity:
527 case CenterGravity:
528 case SouthGravity:
529 x += (_size.left + _size.right) / 2;
530 break;
531 case NorthEastGravity:
532 case EastGravity:
533 case SouthEastGravity:
534 x += _size.left + _size.right;
535 break;
536 case StaticGravity:
537 case ForgetGravity:
538 x += _size.left;
539 break;
540 }
541
542 // vertical
543 switch (_client->gravity()) {
544 default:
545 case NorthWestGravity:
546 case WestGravity:
547 case SouthWestGravity:
548 break;
549 case NorthGravity:
550 case CenterGravity:
551 case SouthGravity:
552 y += (_size.top + _size.bottom) / 2;
553 break;
554 case NorthEastGravity:
555 case EastGravity:
556 case SouthEastGravity:
557 y += _size.top + _size.bottom;
558 break;
559 case StaticGravity:
560 case ForgetGravity:
561 y += _size.top;
562 break;
563 }
564 }
565
566
567 }
This page took 0.060802 seconds and 5 git commands to generate.