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