]> Dogcows Code - chaz/openbox/blob - src/frame.cc
provide access to the desktop names
[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 *surface = s;
231 }
232
233 void Frame::adjustSize()
234 {
235 Client::DecorationFlags decorations = _client->decorations();
236 const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
237
238 if (decorations & Client::Decor_Border) {
239 geom.bwidth = style->frameBorderWidth();
240 geom.cbwidth = style->clientBorderWidth();
241 } else {
242 geom.bwidth = geom.cbwidth = 0;
243 }
244 _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
245 geom.cbwidth;
246 geom.width = _client->area().width() + geom.cbwidth * 2;
247 assert(geom.width > 0);
248
249 // set border widths
250 XSetWindowBorderWidth(**otk::display, _plate, geom.cbwidth);
251 XSetWindowBorderWidth(**otk::display, _frame, geom.bwidth);
252 XSetWindowBorderWidth(**otk::display, _title, geom.bwidth);
253 XSetWindowBorderWidth(**otk::display, _handle, geom.bwidth);
254 XSetWindowBorderWidth(**otk::display, _lgrip, geom.bwidth);
255 XSetWindowBorderWidth(**otk::display, _rgrip, geom.bwidth);
256
257 // position/size and map/unmap all the windows
258
259 if (decorations & Client::Decor_Titlebar) {
260 XMoveResizeWindow(**otk::display, _title, -geom.bwidth, -geom.bwidth,
261 geom.width, geom.title_height());
262 _innersize.top += geom.title_height() + geom.bwidth;
263 XMapWindow(**otk::display, _title);
264
265 // layout the title bar elements
266 layoutTitle();
267 } else
268 XUnmapWindow(**otk::display, _title);
269
270 if (decorations & Client::Decor_Handle) {
271 geom.handle_y = _innersize.top + _client->area().height() + geom.cbwidth;
272 XMoveResizeWindow(**otk::display, _handle, -geom.bwidth, geom.handle_y,
273 geom.width, geom.handle_height);
274 XMoveWindow(**otk::display, _lgrip, -geom.bwidth, -geom.bwidth);
275 XMoveWindow(**otk::display, _rgrip,
276 -geom.bwidth + geom.width - geom.grip_width(),
277 -geom.bwidth);
278 _innersize.bottom += geom.handle_height + geom.bwidth;
279 XMapWindow(**otk::display, _handle);
280 } else
281 XUnmapWindow(**otk::display, _handle);
282
283 XResizeWindow(**otk::display, _frame, geom.width,
284 (_client->shaded() ? geom.title_height() :
285 _innersize.top + _innersize.bottom +
286 _client->area().height()));
287
288 // do this in two steps because clients whose gravity is set to
289 // 'Static' don't end up getting moved at all with an XMoveResizeWindow
290 XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
291 _innersize.top - geom.cbwidth);
292 XResizeWindow(**otk::display, _plate, _client->area().width(),
293 _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 (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 int length;
356 int maxsize = geom.label_width - geom.bevel * 2;
357
358 do {
359 t.resize(text_len);
360 length = font->measureString(t); // this returns an unsigned, so check < 0
361 if (length < 0) length = maxsize; // if the string's that long just adjust
362 } while (length > maxsize && text_len-- > 0);
363
364 if (text_len <= 0) return; // won't fit anything
365
366 // justify the text
367 switch (style->labelTextJustify()) {
368 case otk::RenderStyle::RightBottomJustify:
369 x += maxsize - length;
370 break;
371 case otk::RenderStyle::CenterJustify:
372 x += (maxsize - length) / 2;
373 break;
374 case otk::RenderStyle::LeftTopJustify:
375 break;
376 }
377
378 control->drawString(*s, *font, x, 0,
379 *(_client->focused() ? style->textFocusColor() :
380 style->textUnfocusColor()), t);
381
382 XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
383 XClearWindow(**otk::display, _label);
384 if (_label_sur) delete _label_sur;
385 _label_sur = s;
386 }
387
388 void Frame::layoutTitle()
389 {
390 geom.label_width = geom.width - geom.bevel * 2;
391 if (geom.label_width < 1) geom.label_width = 1;
392 XMoveResizeWindow(**otk::display, _label, geom.bevel, geom.bevel,
393 geom.label_width, geom.font_height);
394 }
395
396 void Frame::adjustPosition()
397 {
398 int x, y;
399 x = _client->area().x();
400 y = _client->area().y();
401 clientGravity(x, y);
402 XMoveWindow(**otk::display, _frame, x, y);
403 _area = otk::Rect(otk::Point(x, y), _area.size());
404 }
405
406
407 void Frame::adjustShape()
408 {
409 #ifdef SHAPE
410 Client::DecorationFlags decorations = _client->decorations();
411
412 if (!_client->shaped()) {
413 // clear the shape on the frame window
414 XShapeCombineMask(**otk::display, _frame, ShapeBounding,
415 _innersize.left,
416 _innersize.top,
417 None, ShapeSet);
418 } else {
419 // make the frame's shape match the clients
420 XShapeCombineShape(**otk::display, _frame, ShapeBounding,
421 _innersize.left,
422 _innersize.top,
423 _client->window(), ShapeBounding, ShapeSet);
424
425 int num = 0;
426 XRectangle xrect[2];
427
428 if (decorations & Client::Decor_Titlebar) {
429 xrect[0].x = -geom.bevel;
430 xrect[0].y = -geom.bevel;
431 xrect[0].width = geom.width + geom.bwidth * 2;
432 xrect[0].height = geom.title_height() + geom.bwidth * 2;
433 ++num;
434 }
435
436 if (decorations & Client::Decor_Handle) {
437 xrect[1].x = -geom.bevel;
438 xrect[1].y = geom.handle_y;
439 xrect[1].width = geom.width + geom.bwidth * 2;
440 xrect[1].height = geom.handle_height + geom.bwidth * 2;
441 ++num;
442 }
443
444 XShapeCombineRectangles(**otk::display, _frame,
445 ShapeBounding, 0, 0, xrect, num,
446 ShapeUnion, Unsorted);
447 }
448 #endif // SHAPE
449 }
450
451
452 void Frame::adjustState()
453 {
454 // XXX _button_alldesk.update();
455 // XXX _button_max.update();
456 }
457
458
459 void Frame::grabClient()
460 {
461 // reparent the client to the frame
462 XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
463 /*
464 When reparenting the client window, it is usually not mapped yet, since
465 this occurs from a MapRequest. However, in the case where Openbox is
466 starting up, the window is already mapped, so we'll see unmap events for
467 it. There are 2 unmap events generated that we see, one with the 'event'
468 member set the root window, and one set to the client, but both get handled
469 and need to be ignored.
470 */
471 if (openbox->state() == Openbox::State_Starting)
472 _client->ignore_unmaps += 2;
473
474 // select the event mask on the client's parent (to receive config/map req's)
475 XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
476
477 // map the client so it maps when the frame does
478 XMapWindow(**otk::display, _client->window());
479
480 adjustSize();
481 adjustPosition();
482 }
483
484
485 void Frame::releaseClient()
486 {
487 XEvent ev;
488
489 // check if the app has already reparented its window away
490 if (XCheckTypedWindowEvent(**otk::display, _client->window(),
491 ReparentNotify, &ev)) {
492 XPutBackEvent(**otk::display, &ev);
493 // re-map the window since the unmanaging process unmaps it
494 XMapWindow(**otk::display, _client->window());
495 } else {
496 // according to the ICCCM - if the client doesn't reparent itself, then we
497 // will reparent the window to root for them
498 XReparentWindow(**otk::display, _client->window(),
499 otk::display->screenInfo(_client->screen())->rootWindow(),
500 _client->area().x(), _client->area().y());
501 }
502 }
503
504
505 void Frame::clientGravity(int &x, int &y)
506 {
507 // horizontal
508 switch (_client->gravity()) {
509 default:
510 case NorthWestGravity:
511 case SouthWestGravity:
512 case WestGravity:
513 break;
514
515 case NorthGravity:
516 case SouthGravity:
517 case CenterGravity:
518 x -= (_size.left + _size.right) / 2;
519 break;
520
521 case NorthEastGravity:
522 case SouthEastGravity:
523 case EastGravity:
524 x -= _size.left + _size.right;
525 break;
526
527 case ForgetGravity:
528 case StaticGravity:
529 x -= _size.left;
530 break;
531 }
532
533 // vertical
534 switch (_client->gravity()) {
535 default:
536 case NorthWestGravity:
537 case NorthEastGravity:
538 case NorthGravity:
539 break;
540
541 case CenterGravity:
542 case EastGravity:
543 case WestGravity:
544 y -= (_size.top + _size.bottom) / 2;
545 break;
546
547 case SouthWestGravity:
548 case SouthEastGravity:
549 case SouthGravity:
550 y -= _size.top + _size.bottom;
551 break;
552
553 case ForgetGravity:
554 case StaticGravity:
555 y -= _size.top;
556 break;
557 }
558 }
559
560
561 void Frame::frameGravity(int &x, int &y)
562 {
563 // horizontal
564 switch (_client->gravity()) {
565 default:
566 case NorthWestGravity:
567 case WestGravity:
568 case SouthWestGravity:
569 break;
570 case NorthGravity:
571 case CenterGravity:
572 case SouthGravity:
573 x += (_size.left + _size.right) / 2;
574 break;
575 case NorthEastGravity:
576 case EastGravity:
577 case SouthEastGravity:
578 x += _size.left + _size.right;
579 break;
580 case StaticGravity:
581 case ForgetGravity:
582 x += _size.left;
583 break;
584 }
585
586 // vertical
587 switch (_client->gravity()) {
588 default:
589 case NorthWestGravity:
590 case WestGravity:
591 case SouthWestGravity:
592 break;
593 case NorthGravity:
594 case CenterGravity:
595 case SouthGravity:
596 y += (_size.top + _size.bottom) / 2;
597 break;
598 case NorthEastGravity:
599 case EastGravity:
600 case SouthEastGravity:
601 y += _size.top + _size.bottom;
602 break;
603 case StaticGravity:
604 case ForgetGravity:
605 y += _size.top;
606 break;
607 }
608 }
609
610
611 }
This page took 0.072984 seconds and 4 git commands to generate.