]> Dogcows Code - chaz/openbox/blob - src/frame.cc
proper order for headers
[chaz/openbox] / src / frame.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #include "config.h"
4
5 extern "C" {
6 #ifdef SHAPE
7 #include <X11/extensions/shape.h>
8 #endif // SHAPE
9 }
10
11 #include "frame.hh"
12 #include "client.hh"
13 #include "openbox.hh"
14 #include "otk/display.hh"
15
16 #include <string>
17 #include <cassert>
18
19 namespace ob {
20
21 const long Frame::event_mask;
22
23 Window createWindow(const otk::ScreenInfo *info, Window parent,
24 unsigned long mask, XSetWindowAttributes *attrib)
25 {
26 return XCreateWindow(**otk::display, parent, 0, 0, 1, 1, 0,
27 info->depth(), InputOutput, info->visual(),
28 mask, attrib);
29
30 }
31
32 Frame::Frame(Client *client)
33 : _client(client),
34 _visible(false),
35 _plate(0),
36 _title(0),
37 _label(0),
38 _handle(0),
39 _lgrip(0),
40 _rgrip(0),
41 _buttons(0),
42 _numbuttons(0),
43 _titleorder(0),
44 _frame_sur(0),
45 _title_sur(0),
46 _label_sur(0),
47 _handle_sur(0),
48 _grip_sur(0),
49 _buttons_sur(0)
50 {
51 assert(client);
52
53 XSetWindowAttributes attrib;
54 unsigned long mask;
55 const otk::ScreenInfo *info = otk::display->screenInfo(client->screen());
56
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);
62
63 mask = 0;
64 _plate = createWindow(info, _frame, mask, &attrib);
65 mask = CWEventMask;
66 attrib.event_mask = (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
67 ExposureMask);
68 _title = createWindow(info, _frame, mask, &attrib);
69 _label = createWindow(info, _title, mask, &attrib);
70 _handle = createWindow(info, _frame, mask, &attrib);
71 mask |= CWCursor;
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);
76
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);
82
83 applyStyle(*otk::RenderStyle::style(_client->screen()));
84
85 // XXX load buttons
86 _numbuttons = 0;
87 _buttons = new Window[0];
88 _buttons_sur = new otk::Surface*[0];
89 _titleorder = new int[1];
90 _titleorder[0] = -1;
91
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);
96 delete [] w;
97 }
98
99 Frame::~Frame()
100 {
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]);
105 delete [] w;
106
107 for (int i = 0; i < _numbuttons; ++i) {
108 XDestroyWindow(**otk::display, _buttons[i]);
109 delete _buttons_sur[i];
110 }
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);
117
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;
123
124 delete [] _buttons;
125 delete [] _titleorder;
126 delete [] _buttons_sur;
127 }
128
129 void Frame::show()
130 {
131 if (!_visible) {
132 _visible = true;
133 XMapWindow(**otk::display, _frame);
134 }
135 }
136
137 void Frame::hide()
138 {
139 if (_visible) {
140 _visible = false;
141 XUnmapWindow(**otk::display, _frame);
142 }
143 }
144
145 MouseContext::MC Frame::mouseContext(Window win) const
146 {
147 if (win == _frame) return MouseContext::Frame;
148 if (win == _title ||
149 win == _label) return MouseContext::Titlebar;
150 if (win == _handle) return MouseContext::Handle;
151 if (win == _plate) return MouseContext::Window;
152 if (win == _lgrip ||
153 win == _rgrip) return MouseContext::Grip;
154 return (MouseContext::MC) -1;
155 }
156
157 Window *Frame::allWindows() const
158 {
159 Window *w = new Window[7 + _numbuttons + 1];
160 unsigned int i = 0;
161 w[i++] = _frame;
162 w[i++] = _plate;
163 w[i++] = _title;
164 w[i++] = _label;
165 w[i++] = _handle;
166 w[i++] = _lgrip;
167 w[i++] = _rgrip;
168 for (int j = 0; j < _numbuttons; ++j)
169 w[j + i++] = _buttons[j];
170 w[i] = 0;
171 return w;
172 }
173
174 void Frame::applyStyle(const otk::RenderStyle &style)
175 {
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());
182
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();
191
192 XResizeWindow(**otk::display, _lgrip, geom.grip_width(), geom.handle_height);
193 XResizeWindow(**otk::display, _rgrip, geom.grip_width(), geom.handle_height);
194
195 for (int i = 0; i < _numbuttons; ++i)
196 XResizeWindow(**otk::display, _buttons[i],
197 geom.button_size, geom.button_size);
198 }
199
200 void Frame::styleChanged(const otk::RenderStyle &style)
201 {
202 applyStyle(style);
203
204 // size/position everything
205 adjustSize();
206 adjustPosition();
207 }
208
209 void Frame::adjustFocus()
210 {
211 // XXX optimizations later...
212 adjustSize();
213 }
214
215 void Frame::adjustTitle()
216 {
217 // XXX optimizations later...
218 adjustSize();
219 }
220
221 static void render(int screen, const otk::Size &size, Window win,
222 otk::Surface **surface,
223 const otk::RenderTexture &texture)
224 {
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;
230 s->freePixelData();
231 *surface = s;
232 }
233
234 void Frame::adjustSize()
235 {
236 Client::DecorationFlags decorations = _client->decorations();
237 const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
238
239 if (decorations & Client::Decor_Border) {
240 geom.bwidth = style->frameBorderWidth();
241 geom.cbwidth = style->clientBorderWidth();
242 } else {
243 geom.bwidth = geom.cbwidth = 0;
244 }
245 _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
246 geom.cbwidth;
247 geom.width = _client->area().width() + geom.cbwidth * 2;
248 assert(geom.width > 0);
249
250 // set border widths
251 XSetWindowBorderWidth(**otk::display, _plate, geom.cbwidth);
252 XSetWindowBorderWidth(**otk::display, _frame, geom.bwidth);
253 XSetWindowBorderWidth(**otk::display, _title, geom.bwidth);
254 XSetWindowBorderWidth(**otk::display, _handle, geom.bwidth);
255 XSetWindowBorderWidth(**otk::display, _lgrip, geom.bwidth);
256 XSetWindowBorderWidth(**otk::display, _rgrip, geom.bwidth);
257
258 // position/size and map/unmap all the windows
259
260 if (decorations & Client::Decor_Titlebar) {
261 XMoveResizeWindow(**otk::display, _title, -geom.bwidth, -geom.bwidth,
262 geom.width, geom.title_height());
263 _innersize.top += geom.title_height() + geom.bwidth;
264 XMapWindow(**otk::display, _title);
265
266 // layout the title bar elements
267 layoutTitle();
268 } else
269 XUnmapWindow(**otk::display, _title);
270
271 if (decorations & Client::Decor_Handle) {
272 geom.handle_y = _innersize.top + _client->area().height() + geom.cbwidth;
273 XMoveResizeWindow(**otk::display, _handle, -geom.bwidth, geom.handle_y,
274 geom.width, geom.handle_height);
275 XMoveWindow(**otk::display, _lgrip, -geom.bwidth, -geom.bwidth);
276 XMoveWindow(**otk::display, _rgrip,
277 -geom.bwidth + geom.width - geom.grip_width(),
278 -geom.bwidth);
279 _innersize.bottom += geom.handle_height + geom.bwidth;
280 XMapWindow(**otk::display, _handle);
281 } else
282 XUnmapWindow(**otk::display, _handle);
283
284 XResizeWindow(**otk::display, _frame, geom.width,
285 (_client->shaded() ? geom.title_height() :
286 _innersize.top + _innersize.bottom +
287 _client->area().height()));
288
289 // do this in two steps because clients whose gravity is set to
290 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
291 XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
292 _innersize.top - geom.cbwidth);
293 XResizeWindow(**otk::display, _plate, _client->area().width(),
294 _client->area().height());
295
296 _size.left = _innersize.left + geom.bwidth;
297 _size.right = _innersize.right + geom.bwidth;
298 _size.top = _innersize.top + geom.bwidth;
299 _size.bottom = _innersize.bottom + geom.bwidth;
300
301 _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
302 _size.left + _size.right,
303 _client->area().height() +
304 _size.top + _size.bottom));
305
306 // render all the elements
307 int screen = _client->screen();
308 bool focus = _client->focused();
309 if (decorations & Client::Decor_Titlebar) {
310 render(screen, otk::Size(geom.width, geom.title_height()), _title,
311 &_title_sur, *(focus ? style->titlebarFocusBackground() :
312 style->titlebarUnfocusBackground()));
313
314 renderLabel();
315 }
316
317 if (decorations & Client::Decor_Handle) {
318 render(screen, otk::Size(geom.width, geom.handle_height), _handle,
319 &_handle_sur, *(focus ? style->handleFocusBackground() :
320 style->handleUnfocusBackground()));
321 render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
322 &_grip_sur, *(focus ? style->gripFocusBackground() :
323 style->gripUnfocusBackground()));
324 XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
325 XClearWindow(**otk::display, _rgrip);
326 }
327
328 XSetWindowBorder(**otk::display, _plate,
329 focus ? style->clientBorderFocusColor()->pixel() :
330 style->clientBorderUnfocusColor()->pixel());
331
332 adjustShape();
333 }
334
335 void Frame::renderLabel()
336 {
337 const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
338 const otk::RenderControl *control =
339 otk::display->renderControl(_client->screen());
340 const otk::Font *font = style->labelFont();
341
342 otk::Surface *s = new otk::Surface(_client->screen(),
343 otk::Size(geom.label_width,
344 geom.label_height()));
345 control->drawBackground(*s, *(_client->focused() ?
346 style->labelFocusBackground() :
347 style->labelUnfocusBackground()));
348
349 otk::ustring t = _client->title(); // the actual text to draw
350 int x = geom.bevel; // x coord for the text
351
352 if (x * 2 > geom.label_width) return; // no room at all
353
354 // find a string that will fit inside the area for text
355 otk::ustring::size_type text_len = t.size();
356 int length;
357 int maxsize = geom.label_width - geom.bevel * 2;
358
359 do {
360 t.resize(text_len);
361 length = font->measureString(t); // this returns an unsigned, so check < 0
362 if (length < 0) length = maxsize; // if the string's that long just adjust
363 } while (length > maxsize && text_len-- > 0);
364
365 if (text_len <= 0) return; // won't fit anything
366
367 // justify the text
368 switch (style->labelTextJustify()) {
369 case otk::RenderStyle::RightBottomJustify:
370 x += maxsize - length;
371 break;
372 case otk::RenderStyle::CenterJustify:
373 x += (maxsize - length) / 2;
374 break;
375 case otk::RenderStyle::LeftTopJustify:
376 break;
377 }
378
379 control->drawString(*s, *font, x, 0,
380 *(_client->focused() ? style->textFocusColor() :
381 style->textUnfocusColor()), t);
382
383 XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
384 XClearWindow(**otk::display, _label);
385 if (_label_sur) delete _label_sur;
386 s->freePixelData();
387 _label_sur = s;
388 }
389
390 void Frame::layoutTitle()
391 {
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);
396 }
397
398 void Frame::adjustPosition()
399 {
400 int x, y;
401 x = _client->area().x();
402 y = _client->area().y();
403 clientGravity(x, y);
404 XMoveWindow(**otk::display, _frame, x, y);
405 _area = otk::Rect(otk::Point(x, y), _area.size());
406 }
407
408
409 void Frame::adjustShape()
410 {
411 #ifdef SHAPE
412 Client::DecorationFlags decorations = _client->decorations();
413
414 if (!_client->shaped()) {
415 // clear the shape on the frame window
416 XShapeCombineMask(**otk::display, _frame, ShapeBounding,
417 _innersize.left,
418 _innersize.top,
419 None, ShapeSet);
420 } else {
421 // make the frame's shape match the clients
422 XShapeCombineShape(**otk::display, _frame, ShapeBounding,
423 _innersize.left,
424 _innersize.top,
425 _client->window(), ShapeBounding, ShapeSet);
426
427 int num = 0;
428 XRectangle xrect[2];
429
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;
435 ++num;
436 }
437
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;
443 ++num;
444 }
445
446 XShapeCombineRectangles(**otk::display, _frame,
447 ShapeBounding, 0, 0, xrect, num,
448 ShapeUnion, Unsorted);
449 }
450 #endif // SHAPE
451 }
452
453
454 void Frame::adjustState()
455 {
456 // XXX _button_alldesk.update();
457 // XXX _button_max.update();
458 }
459
460
461 void Frame::grabClient()
462 {
463 // reparent the client to the frame
464 XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
465 /*
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.
472 */
473 if (openbox->state() == Openbox::State_Starting)
474 _client->ignore_unmaps += 2;
475
476 // select the event mask on the client's parent (to receive config/map req's)
477 XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
478
479 // map the client so it maps when the frame does
480 XMapWindow(**otk::display, _client->window());
481
482 adjustSize();
483 adjustPosition();
484 }
485
486
487 void Frame::releaseClient()
488 {
489 XEvent ev;
490
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());
497 } else {
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());
503 }
504 }
505
506
507 void Frame::clientGravity(int &x, int &y)
508 {
509 // horizontal
510 switch (_client->gravity()) {
511 default:
512 case NorthWestGravity:
513 case SouthWestGravity:
514 case WestGravity:
515 break;
516
517 case NorthGravity:
518 case SouthGravity:
519 case CenterGravity:
520 x -= (_size.left + _size.right) / 2;
521 break;
522
523 case NorthEastGravity:
524 case SouthEastGravity:
525 case EastGravity:
526 x -= _size.left + _size.right;
527 break;
528
529 case ForgetGravity:
530 case StaticGravity:
531 x -= _size.left;
532 break;
533 }
534
535 // vertical
536 switch (_client->gravity()) {
537 default:
538 case NorthWestGravity:
539 case NorthEastGravity:
540 case NorthGravity:
541 break;
542
543 case CenterGravity:
544 case EastGravity:
545 case WestGravity:
546 y -= (_size.top + _size.bottom) / 2;
547 break;
548
549 case SouthWestGravity:
550 case SouthEastGravity:
551 case SouthGravity:
552 y -= _size.top + _size.bottom;
553 break;
554
555 case ForgetGravity:
556 case StaticGravity:
557 y -= _size.top;
558 break;
559 }
560 }
561
562
563 void Frame::frameGravity(int &x, int &y)
564 {
565 // horizontal
566 switch (_client->gravity()) {
567 default:
568 case NorthWestGravity:
569 case WestGravity:
570 case SouthWestGravity:
571 break;
572 case NorthGravity:
573 case CenterGravity:
574 case SouthGravity:
575 x += (_size.left + _size.right) / 2;
576 break;
577 case NorthEastGravity:
578 case EastGravity:
579 case SouthEastGravity:
580 x += _size.left + _size.right;
581 break;
582 case StaticGravity:
583 case ForgetGravity:
584 x += _size.left;
585 break;
586 }
587
588 // vertical
589 switch (_client->gravity()) {
590 default:
591 case NorthWestGravity:
592 case WestGravity:
593 case SouthWestGravity:
594 break;
595 case NorthGravity:
596 case CenterGravity:
597 case SouthGravity:
598 y += (_size.top + _size.bottom) / 2;
599 break;
600 case NorthEastGravity:
601 case EastGravity:
602 case SouthEastGravity:
603 y += _size.top + _size.bottom;
604 break;
605 case StaticGravity:
606 case ForgetGravity:
607 y += _size.top;
608 break;
609 }
610 }
611
612
613 }
This page took 0.063025 seconds and 4 git commands to generate.