]> Dogcows Code - chaz/openbox/blob - src/frame.cc
signed ints instead of unsigned ints again. less pain. pain bad.
[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 int[1];
92 _titleorder[0] = -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 (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 (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 (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 // do this in two steps because clients whose gravity is set to
291 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
292 XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
293 _innersize.top - geom.cbwidth);
294 XResizeWindow(**otk::display, _plate, _client->area().width(),
295 _client->area().height());
296
297 _size.left = _innersize.left + geom.bwidth;
298 _size.right = _innersize.right + geom.bwidth;
299 _size.top = _innersize.top + geom.bwidth;
300 _size.bottom = _innersize.bottom + geom.bwidth;
301
302 _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
303 _size.left + _size.right,
304 _client->area().height() +
305 _size.top + _size.bottom));
306
307 // render all the elements
308 int screen = _client->screen();
309 bool focus = _client->focused();
310 if (decorations & Client::Decor_Titlebar) {
311 render(screen, otk::Size(geom.width, geom.title_height()), _title,
312 &_title_sur, *(focus ? style->titlebarFocusBackground() :
313 style->titlebarUnfocusBackground()));
314
315 renderLabel();
316 }
317
318 if (decorations & Client::Decor_Handle) {
319 render(screen, otk::Size(geom.width, geom.handle_height), _handle,
320 &_handle_sur, *(focus ? style->handleFocusBackground() :
321 style->handleUnfocusBackground()));
322 render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
323 &_grip_sur, *(focus ? style->gripFocusBackground() :
324 style->gripUnfocusBackground()));
325 XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
326 XClearWindow(**otk::display, _rgrip);
327 }
328
329 XSetWindowBorder(**otk::display, _plate,
330 focus ? style->clientBorderFocusColor()->pixel() :
331 style->clientBorderUnfocusColor()->pixel());
332
333 adjustShape();
334 }
335
336 void Frame::renderLabel()
337 {
338 const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
339 const otk::RenderControl *control =
340 otk::display->renderControl(_client->screen());
341 const otk::Font *font = style->labelFont();
342
343 otk::Surface *s = new otk::Surface(_client->screen(),
344 otk::Size(geom.label_width,
345 geom.label_height()));
346 control->drawBackground(*s, *(_client->focused() ?
347 style->labelFocusBackground() :
348 style->labelUnfocusBackground()));
349
350 otk::ustring t = _client->title(); // the actual text to draw
351 int x = geom.bevel; // x coord for the text
352
353 if (x * 2 > geom.label_width) return; // no room at all
354
355 // find a string that will fit inside the area for text
356 otk::ustring::size_type text_len = t.size();
357 int length;
358 int maxsize = geom.label_width - geom.bevel * 2;
359
360 do {
361 t.resize(text_len);
362 length = font->measureString(t); // this returns an unsigned, so check < 0
363 if (length < 0) length = maxsize; // if the string's that long just adjust
364 } while (length > maxsize && text_len-- > 0);
365
366 if (text_len <= 0) return; // won't fit anything
367
368 // justify the text
369 switch (style->labelTextJustify()) {
370 case otk::RenderStyle::RightBottomJustify:
371 x += maxsize - length;
372 break;
373 case otk::RenderStyle::CenterJustify:
374 x += (maxsize - length) / 2;
375 break;
376 case otk::RenderStyle::LeftTopJustify:
377 break;
378 }
379
380 control->drawString(*s, *font, x, 0,
381 *(_client->focused() ? style->textFocusColor() :
382 style->textUnfocusColor()), t);
383
384 XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
385 XClearWindow(**otk::display, _label);
386 if (_label_sur) delete _label_sur;
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.065491 seconds and 4 git commands to generate.