]> Dogcows Code - chaz/openbox/blob - src/frame.cc
split the move and resize on the client window
[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 // 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 ((unsigned)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 unsigned int length;
358 unsigned int maxsize = geom.label_width - geom.bevel * 2;
359
360 do {
361 t.resize(text_len);
362 length = font->measureString(t);
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 _label_sur = s;
387 }
388
389 void Frame::layoutTitle()
390 {
391 geom.label_width = geom.width - geom.bevel * 2;
392 if (geom.label_width < 1) geom.label_width = 1;
393 XMoveResizeWindow(**otk::display, _label, geom.bevel, geom.bevel,
394 geom.label_width, geom.font_height);
395 }
396
397 void Frame::adjustPosition()
398 {
399 int x, y;
400 x = _client->area().x();
401 y = _client->area().y();
402 clientGravity(x, y);
403 XMoveWindow(**otk::display, _frame, x, y);
404 _area = otk::Rect(otk::Point(x, y), _area.size());
405 }
406
407
408 void Frame::adjustShape()
409 {
410 #ifdef SHAPE
411 Client::DecorationFlags decorations = _client->decorations();
412
413 if (!_client->shaped()) {
414 // clear the shape on the frame window
415 XShapeCombineMask(**otk::display, _frame, ShapeBounding,
416 _innersize.left,
417 _innersize.top,
418 None, ShapeSet);
419 } else {
420 // make the frame's shape match the clients
421 XShapeCombineShape(**otk::display, _frame, ShapeBounding,
422 _innersize.left,
423 _innersize.top,
424 _client->window(), ShapeBounding, ShapeSet);
425
426 int num = 0;
427 XRectangle xrect[2];
428
429 if (decorations & Client::Decor_Titlebar) {
430 xrect[0].x = -geom.bevel;
431 xrect[0].y = -geom.bevel;
432 xrect[0].width = geom.width + geom.bwidth * 2;
433 xrect[0].height = geom.title_height() + geom.bwidth * 2;
434 ++num;
435 }
436
437 if (decorations & Client::Decor_Handle) {
438 xrect[1].x = -geom.bevel;
439 xrect[1].y = geom.handle_y;
440 xrect[1].width = geom.width + geom.bwidth * 2;
441 xrect[1].height = geom.handle_height + geom.bwidth * 2;
442 ++num;
443 }
444
445 XShapeCombineRectangles(**otk::display, _frame,
446 ShapeBounding, 0, 0, xrect, num,
447 ShapeUnion, Unsorted);
448 }
449 #endif // SHAPE
450 }
451
452
453 void Frame::adjustState()
454 {
455 // XXX _button_alldesk.update();
456 // XXX _button_max.update();
457 }
458
459
460 void Frame::grabClient()
461 {
462 // reparent the client to the frame
463 XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
464 /*
465 When reparenting the client window, it is usually not mapped yet, since
466 this occurs from a MapRequest. However, in the case where Openbox is
467 starting up, the window is already mapped, so we'll see unmap events for
468 it. There are 2 unmap events generated that we see, one with the 'event'
469 member set the root window, and one set to the client, but both get handled
470 and need to be ignored.
471 */
472 if (openbox->state() == Openbox::State_Starting)
473 _client->ignore_unmaps += 2;
474
475 // select the event mask on the client's parent (to receive config/map req's)
476 XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
477
478 // map the client so it maps when the frame does
479 XMapWindow(**otk::display, _client->window());
480
481 adjustSize();
482 adjustPosition();
483 }
484
485
486 void Frame::releaseClient()
487 {
488 XEvent ev;
489
490 // check if the app has already reparented its window away
491 if (XCheckTypedWindowEvent(**otk::display, _client->window(),
492 ReparentNotify, &ev)) {
493 XPutBackEvent(**otk::display, &ev);
494 // re-map the window since the unmanaging process unmaps it
495 XMapWindow(**otk::display, _client->window());
496 } else {
497 // according to the ICCCM - if the client doesn't reparent itself, then we
498 // will reparent the window to root for them
499 XReparentWindow(**otk::display, _client->window(),
500 otk::display->screenInfo(_client->screen())->rootWindow(),
501 _client->area().x(), _client->area().y());
502 }
503 }
504
505
506 void Frame::clientGravity(int &x, int &y)
507 {
508 // horizontal
509 switch (_client->gravity()) {
510 default:
511 case NorthWestGravity:
512 case SouthWestGravity:
513 case WestGravity:
514 break;
515
516 case NorthGravity:
517 case SouthGravity:
518 case CenterGravity:
519 x -= (_size.left + _size.right) / 2;
520 break;
521
522 case NorthEastGravity:
523 case SouthEastGravity:
524 case EastGravity:
525 x -= _size.left + _size.right;
526 break;
527
528 case ForgetGravity:
529 case StaticGravity:
530 x -= _size.left;
531 break;
532 }
533
534 // vertical
535 switch (_client->gravity()) {
536 default:
537 case NorthWestGravity:
538 case NorthEastGravity:
539 case NorthGravity:
540 break;
541
542 case CenterGravity:
543 case EastGravity:
544 case WestGravity:
545 y -= (_size.top + _size.bottom) / 2;
546 break;
547
548 case SouthWestGravity:
549 case SouthEastGravity:
550 case SouthGravity:
551 y -= _size.top + _size.bottom;
552 break;
553
554 case ForgetGravity:
555 case StaticGravity:
556 y -= _size.top;
557 break;
558 }
559 }
560
561
562 void Frame::frameGravity(int &x, int &y)
563 {
564 // horizontal
565 switch (_client->gravity()) {
566 default:
567 case NorthWestGravity:
568 case WestGravity:
569 case SouthWestGravity:
570 break;
571 case NorthGravity:
572 case CenterGravity:
573 case SouthGravity:
574 x += (_size.left + _size.right) / 2;
575 break;
576 case NorthEastGravity:
577 case EastGravity:
578 case SouthEastGravity:
579 x += _size.left + _size.right;
580 break;
581 case StaticGravity:
582 case ForgetGravity:
583 x += _size.left;
584 break;
585 }
586
587 // vertical
588 switch (_client->gravity()) {
589 default:
590 case NorthWestGravity:
591 case WestGravity:
592 case SouthWestGravity:
593 break;
594 case NorthGravity:
595 case CenterGravity:
596 case SouthGravity:
597 y += (_size.top + _size.bottom) / 2;
598 break;
599 case NorthEastGravity:
600 case EastGravity:
601 case SouthEastGravity:
602 y += _size.top + _size.bottom;
603 break;
604 case StaticGravity:
605 case ForgetGravity:
606 y += _size.top;
607 break;
608 }
609 }
610
611
612 }
This page took 0.059605 seconds and 4 git commands to generate.