1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000, 2002 - 2005
5 // Bradley T Hughes <bhughes at trolltech.com>
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
24
25 // make sure we get bt::textPropertyToString()
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28
29 #include "Window.hh"
30 #include "Screen.hh"
31 #include "WindowGroup.hh"
32 #include "Windowmenu.hh"
33 #include "Workspace.hh"
34 #include "blackbox.hh"
35
36 #include <cstdlib>
37
38 #include <Pen.hh>
39 #include <PixmapCache.hh>
40 #include <Unicode.hh>
41
42 #include <X11/Xatom.h>
43 #ifdef SHAPE
44 # include <X11/extensions/shape.h>
45 #endif
46
47 #include <assert.h>
48
49 /*
50 sometimes C++ is a pain in the ass... it gives us stuff like the
51 default copy constructor and assignment operator (which does member
52 my member copy/assignment), but what we don't get is a default
53 comparison operator... how fucked up is that?
54
55 the language is designed to handle this:
56
57 struct foo { int a; int b };
58 foo a = { 0 , 0 }, b = a;
59 foo c;
60 c = a;
61
62 BUT, BUT, BUT, forget about doing this:
63
64 a = findFoo();
65 if (c == a)
66 return; // nothing to do
67 c = a;
68
69 ARGH!@#)(!*@#)(@#*$(!@#
70 */
71 bool operator==(const WMNormalHints &x, const WMNormalHints &y);
72
73
74 // Event mask used for managed client windows.
75 const unsigned long client_window_event_mask =
76 (PropertyChangeMask | StructureNotifyMask);
77
78
79 /*
80 * Returns the appropriate WindowType based on the _NET_WM_WINDOW_TYPE
81 */
window_type_from_atom(const bt::EWMH & ewmh,Atom atom)82 static WindowType window_type_from_atom(const bt::EWMH &ewmh, Atom atom) {
83 if (atom == ewmh.wmWindowTypeDialog())
84 return WindowTypeDialog;
85 if (atom == ewmh.wmWindowTypeDesktop())
86 return WindowTypeDesktop;
87 if (atom == ewmh.wmWindowTypeDock())
88 return WindowTypeDock;
89 if (atom == ewmh.wmWindowTypeMenu())
90 return WindowTypeMenu;
91 if (atom == ewmh.wmWindowTypeSplash())
92 return WindowTypeSplash;
93 if (atom == ewmh.wmWindowTypeToolbar())
94 return WindowTypeToolbar;
95 if (atom == ewmh.wmWindowTypeUtility())
96 return WindowTypeUtility;
97 return WindowTypeNormal;
98 }
99
100
101 /*
102 * Determine the appropriate decorations and functions based on the
103 * given properties and hints.
104 */
update_decorations(WindowDecorationFlags & decorations,WindowFunctionFlags & functions,bool transient,const EWMH & ewmh,const MotifHints & motifhints,const WMNormalHints & wmnormal,const WMProtocols & wmprotocols)105 static void update_decorations(WindowDecorationFlags &decorations,
106 WindowFunctionFlags &functions,
107 bool transient,
108 const EWMH &ewmh,
109 const MotifHints &motifhints,
110 const WMNormalHints &wmnormal,
111 const WMProtocols &wmprotocols) {
112 decorations = AllWindowDecorations;
113 functions = AllWindowFunctions;
114
115 // transients should be kept on the same workspace are their parents
116 if (transient)
117 functions &= ~WindowFunctionChangeWorkspace;
118
119 // modify the window decorations/functions based on window type
120 switch (ewmh.window_type) {
121 case WindowTypeDialog:
122 decorations &= ~(WindowDecorationIconify |
123 WindowDecorationMaximize);
124 functions &= ~(WindowFunctionShade |
125 WindowFunctionIconify |
126 WindowFunctionMaximize |
127 WindowFunctionChangeLayer |
128 WindowFunctionFullScreen);
129 break;
130
131 case WindowTypeDesktop:
132 case WindowTypeDock:
133 case WindowTypeSplash:
134 decorations = NoWindowDecorations;
135 functions = NoWindowFunctions;
136 break;
137
138 case WindowTypeToolbar:
139 case WindowTypeMenu:
140 decorations &= ~(WindowDecorationHandle |
141 WindowDecorationGrip |
142 WindowDecorationBorder |
143 WindowDecorationIconify |
144 WindowDecorationMaximize);
145 functions &= ~(WindowFunctionResize |
146 WindowFunctionShade |
147 WindowFunctionIconify |
148 WindowFunctionMaximize |
149 WindowFunctionFullScreen);
150 break;
151
152 case WindowTypeUtility:
153 decorations &= ~(WindowDecorationIconify |
154 WindowDecorationMaximize);
155 functions &= ~(WindowFunctionShade |
156 WindowFunctionIconify |
157 WindowFunctionMaximize);
158 break;
159
160 default:
161 break;
162 }
163
164 // mask away stuff turned off by Motif hints
165 decorations &= motifhints.decorations;
166 functions &= motifhints.functions;
167
168 // disable shade if we do not have a titlebar
169 if (!(decorations & WindowDecorationTitlebar))
170 functions &= ~WindowFunctionShade;
171
172 // disable grips and maximize if we have a fixed size window
173 if ((wmnormal.flags & (PMinSize|PMaxSize)) == (PMinSize|PMaxSize)
174 && wmnormal.max_width <= wmnormal.min_width
175 && wmnormal.max_height <= wmnormal.min_height) {
176 decorations &= ~(WindowDecorationMaximize |
177 WindowDecorationGrip);
178 functions &= ~(WindowFunctionResize |
179 WindowFunctionMaximize);
180 }
181
182 // cannot close if client doesn't understand WM_DELETE_WINDOW
183 if (!wmprotocols.wm_delete_window) {
184 decorations &= ~WindowDecorationClose;
185 functions &= ~WindowFunctionClose;
186 }
187 }
188
189
190 /*
191 * Calculate the frame margin based on the given decorations and
192 * style.
193 */
194 static
update_margin(WindowDecorationFlags decorations,const WindowStyle & style)195 bt::EWMH::Strut update_margin(WindowDecorationFlags decorations,
196 const WindowStyle &style) {
197 bt::EWMH::Strut margin;
198
199 const unsigned int bw = ((decorations & WindowDecorationBorder)
200 ? style.frame_border_width
201 : 0u);
202 margin.top = margin.bottom = margin.left = margin.right = bw;
203
204 if (decorations & WindowDecorationTitlebar)
205 margin.top += style.title_height - bw;
206
207 if (decorations & WindowDecorationHandle)
208 margin.bottom += style.handle_height - bw;
209
210 return margin;
211 }
212
213
214 /*
215 * Add specified window to the appropriate window group, creating a
216 * new group if necessary.
217 */
update_window_group(Window window_group,Blackbox * blackbox,BlackboxWindow * win)218 static BWindowGroup *update_window_group(Window window_group,
219 Blackbox *blackbox,
220 BlackboxWindow *win) {
221 BWindowGroup *group = win->findWindowGroup();
222 if (!group) {
223 new BWindowGroup(blackbox, window_group);
224 group = win->findWindowGroup();
225 assert(group != 0);
226 }
227 group->addWindow(win);
228 return group;
229 }
230
231
232 /*
233 * Calculate the size of the frame window and constrain it to the
234 * size specified by the size hints of the client window.
235 *
236 * 'rect' refers to the geometry of the frame in pixels.
237 */
238 enum Corner {
239 TopLeft,
240 TopRight,
241 BottomLeft,
242 BottomRight
243 };
constrain(const bt::Rect & rect,const bt::EWMH::Strut & margin,const WMNormalHints & wmnormal,Corner corner)244 static bt::Rect constrain(const bt::Rect &rect,
245 const bt::EWMH::Strut &margin,
246 const WMNormalHints &wmnormal,
247 Corner corner) {
248 bt::Rect r;
249
250 // 'rect' represents the requested frame size, we need to strip
251 // 'margin' off and constrain the client size
252 r.setCoords(rect.left() + static_cast<signed>(margin.left),
253 rect.top() + static_cast<signed>(margin.top),
254 rect.right() - static_cast<signed>(margin.right),
255 rect.bottom() - static_cast<signed>(margin.bottom));
256
257 unsigned int dw = r.width(), dh = r.height();
258
259 const unsigned int base_width = (wmnormal.base_width
260 ? wmnormal.base_width
261 : wmnormal.min_width),
262 base_height = (wmnormal.base_height
263 ? wmnormal.base_height
264 : wmnormal.min_height);
265
266 // fit to min/max size
267 if (dw < wmnormal.min_width)
268 dw = wmnormal.min_width;
269 if (dh < wmnormal.min_height)
270 dh = wmnormal.min_height;
271
272 if (dw > wmnormal.max_width)
273 dw = wmnormal.max_width;
274 if (dh > wmnormal.max_height)
275 dh = wmnormal.max_height;
276
277 assert(dw >= base_width && dh >= base_height);
278
279 // fit to size increments
280 if (wmnormal.flags & PResizeInc) {
281 dw = (((dw - base_width) / wmnormal.width_inc)
282 * wmnormal.width_inc) + base_width;
283 dh = (((dh - base_height) / wmnormal.height_inc)
284 * wmnormal.height_inc) + base_height;
285 }
286
287 /*
288 * honor aspect ratios (based on twm which is based on uwm)
289 *
290 * The math looks like this:
291 *
292 * minAspectX dwidth maxAspectX
293 * ---------- <= ------- <= ----------
294 * minAspectY dheight maxAspectY
295 *
296 * If that is multiplied out, then the width and height are
297 * invalid in the following situations:
298 *
299 * minAspectX * dheight > minAspectY * dwidth
300 * maxAspectX * dheight < maxAspectY * dwidth
301 *
302 */
303 if (wmnormal.flags & PAspect) {
304 unsigned int delta;
305 const unsigned int min_asp_x = wmnormal.min_aspect_x,
306 min_asp_y = wmnormal.min_aspect_y,
307 max_asp_x = wmnormal.max_aspect_x,
308 max_asp_y = wmnormal.max_aspect_y,
309 w_inc = wmnormal.width_inc,
310 h_inc = wmnormal.height_inc;
311 if (min_asp_x * dh > min_asp_y * dw) {
312 delta = ((min_asp_x * dh / min_asp_y - dw) * w_inc) / w_inc;
313 if (dw + delta <= wmnormal.max_width) {
314 dw += delta;
315 } else {
316 delta = ((dh - (dw * min_asp_y) / min_asp_x) * h_inc) / h_inc;
317 if (dh - delta >= wmnormal.min_height) dh -= delta;
318 }
319 }
320 if (max_asp_x * dh < max_asp_y * dw) {
321 delta = ((max_asp_y * dw / max_asp_x - dh) * h_inc) / h_inc;
322 if (dh + delta <= wmnormal.max_height) {
323 dh += delta;
324 } else {
325 delta = ((dw - (dh * max_asp_x) / max_asp_y) * w_inc) / w_inc;
326 if (dw - delta >= wmnormal.min_width) dw -= delta;
327 }
328 }
329 }
330
331 r.setSize(dw, dh);
332
333 // add 'margin' back onto 'r'
334 r.setCoords(r.left() - margin.left, r.top() - margin.top,
335 r.right() + margin.right, r.bottom() + margin.bottom);
336
337 // move 'r' to the specified corner
338 int dx = rect.right() - r.right();
339 int dy = rect.bottom() - r.bottom();
340
341 switch (corner) {
342 case TopLeft:
343 // nothing to do
344 break;
345
346 case TopRight:
347 r.setPos(r.x() + dx, r.y());
348 break;
349
350 case BottomLeft:
351 r.setPos(r.x(), r.y() + dy);
352 break;
353
354 case BottomRight:
355 r.setPos(r.x() + dx, r.y() + dy);
356 break;
357 }
358
359 return r;
360 }
361
362
363 /*
364 * Positions 'rect' according to the client window geometry and window
365 * gravity.
366 */
applyGravity(const bt::Rect & rect,const bt::EWMH::Strut & margin,int gravity)367 static bt::Rect applyGravity(const bt::Rect &rect,
368 const bt::EWMH::Strut &margin,
369 int gravity) {
370 bt::Rect r;
371
372 // apply horizontal window gravity
373 switch (gravity) {
374 default:
375 case NorthWestGravity:
376 case SouthWestGravity:
377 case WestGravity:
378 r.setX(rect.x());
379 break;
380
381 case NorthGravity:
382 case SouthGravity:
383 case CenterGravity:
384 r.setX(rect.x() - (margin.left + margin.right) / 2);
385 break;
386
387 case NorthEastGravity:
388 case SouthEastGravity:
389 case EastGravity:
390 r.setX(rect.x() - (margin.left + margin.right) + 2);
391 break;
392
393 case ForgetGravity:
394 case StaticGravity:
395 r.setX(rect.x() - margin.left);
396 break;
397 }
398
399 // apply vertical window gravity
400 switch (gravity) {
401 default:
402 case NorthWestGravity:
403 case NorthEastGravity:
404 case NorthGravity:
405 r.setY(rect.y());
406 break;
407
408 case CenterGravity:
409 case EastGravity:
410 case WestGravity:
411 r.setY(rect.y() - ((margin.top + margin.bottom) / 2));
412 break;
413
414 case SouthWestGravity:
415 case SouthEastGravity:
416 case SouthGravity:
417 r.setY(rect.y() - (margin.bottom + margin.top) + 2);
418 break;
419
420 case ForgetGravity:
421 case StaticGravity:
422 r.setY(rect.y() - margin.top);
423 break;
424 }
425
426 r.setSize(rect.width() + margin.left + margin.right,
427 rect.height() + margin.top + margin.bottom);
428 return r;
429 }
430
431
432 /*
433 * The reverse of the applyGravity function.
434 *
435 * Positions 'rect' according to the frame window geometry and window
436 * gravity.
437 */
restoreGravity(const bt::Rect & rect,const bt::EWMH::Strut & margin,int gravity)438 static bt::Rect restoreGravity(const bt::Rect &rect,
439 const bt::EWMH::Strut &margin,
440 int gravity) {
441 bt::Rect r;
442
443 // restore horizontal window gravity
444 switch (gravity) {
445 default:
446 case NorthWestGravity:
447 case SouthWestGravity:
448 case WestGravity:
449 r.setX(rect.x());
450 break;
451
452 case NorthGravity:
453 case SouthGravity:
454 case CenterGravity:
455 r.setX(rect.x() + (margin.left + margin.right) / 2);
456 break;
457
458 case NorthEastGravity:
459 case SouthEastGravity:
460 case EastGravity:
461 r.setX(rect.x() + (margin.left + margin.right) - 2);
462 break;
463
464 case ForgetGravity:
465 case StaticGravity:
466 r.setX(rect.x() + margin.left);
467 break;
468 }
469
470 // restore vertical window gravity
471 switch (gravity) {
472 default:
473 case NorthWestGravity:
474 case NorthEastGravity:
475 case NorthGravity:
476 r.setY(rect.y());
477 break;
478
479 case CenterGravity:
480 case EastGravity:
481 case WestGravity:
482 r.setY(rect.y() + (margin.top + margin.bottom) / 2);
483 break;
484
485 case SouthWestGravity:
486 case SouthEastGravity:
487 case SouthGravity:
488 r.setY(rect.y() + (margin.top + margin.bottom) - 2);
489 break;
490
491 case ForgetGravity:
492 case StaticGravity:
493 r.setY(rect.y() + margin.top);
494 break;
495 }
496
497 r.setSize(rect.width() - margin.left - margin.right,
498 rect.height() - margin.top - margin.bottom);
499 return r;
500 }
501
502
readWMName(Blackbox * blackbox,Window window)503 static bt::ustring readWMName(Blackbox *blackbox, Window window) {
504 bt::ustring name;
505
506 if (!blackbox->ewmh().readWMName(window, name) || name.empty()) {
507 XTextProperty text_prop;
508
509 if (XGetWMName(blackbox->XDisplay(), window, &text_prop)) {
510 name = bt::toUnicode(bt::textPropertyToString(blackbox->XDisplay(),
511 text_prop));
512 XFree((char *) text_prop.value);
513 }
514 }
515
516 if (name.empty())
517 name = bt::toUnicode("Unnamed");
518
519 return name;
520 }
521
522
readWMIconName(Blackbox * blackbox,Window window)523 static bt::ustring readWMIconName(Blackbox *blackbox, Window window) {
524 bt::ustring name;
525
526 if (!blackbox->ewmh().readWMIconName(window, name) || name.empty()) {
527 XTextProperty text_prop;
528 if (XGetWMIconName(blackbox->XDisplay(), window, &text_prop)) {
529 name = bt::toUnicode(bt::textPropertyToString(blackbox->XDisplay(),
530 text_prop));
531 XFree((char *) text_prop.value);
532 }
533 }
534
535 if (name.empty())
536 return bt::ustring();
537
538 return name;
539 }
540
541
readEWMH(const bt::EWMH & bewmh,Window window,int currentWorkspace)542 static EWMH readEWMH(const bt::EWMH &bewmh,
543 Window window,
544 int currentWorkspace) {
545 EWMH ewmh;
546 ewmh.window_type = WindowTypeNormal;
547 ewmh.workspace = 0; // initialized properly below
548 ewmh.modal = false;
549 ewmh.maxv = false;
550 ewmh.maxh = false;
551 ewmh.shaded = false;
552 ewmh.skip_taskbar = false;
553 ewmh.skip_pager = false;
554 ewmh.hidden = false;
555 ewmh.fullscreen = false;
556 ewmh.above = false;
557 ewmh.below = false;
558
559 // note: wm_name and wm_icon_name are read separately
560
561 bool ret;
562
563 bt::EWMH::AtomList atoms;
564 ret = bewmh.readWMWindowType(window, atoms);
565 if (ret) {
566 bt::EWMH::AtomList::iterator it = atoms.begin(), end = atoms.end();
567 for (; it != end; ++it) {
568 if (bewmh.isSupportedWMWindowType(*it)) {
569 ewmh.window_type = ::window_type_from_atom(bewmh, *it);
570 break;
571 }
572 }
573 }
574
575 atoms.clear();
576 ret = bewmh.readWMState(window, atoms);
577 if (ret) {
578 bt::EWMH::AtomList::iterator it = atoms.begin(), end = atoms.end();
579 for (; it != end; ++it) {
580 Atom state = *it;
581 if (state == bewmh.wmStateModal()) {
582 ewmh.modal = true;
583 } else if (state == bewmh.wmStateMaximizedVert()) {
584 ewmh.maxv = true;
585 } else if (state == bewmh.wmStateMaximizedHorz()) {
586 ewmh.maxh = true;
587 } else if (state == bewmh.wmStateShaded()) {
588 ewmh.shaded = true;
589 } else if (state == bewmh.wmStateSkipTaskbar()) {
590 ewmh.skip_taskbar = true;
591 } else if (state == bewmh.wmStateSkipPager()) {
592 ewmh.skip_pager = true;
593 } else if (state == bewmh.wmStateHidden()) {
594 /*
595 ignore _NET_WM_STATE_HIDDEN if present, the wm sets this
596 state, not the application
597 */
598 } else if (state == bewmh.wmStateFullscreen()) {
599 ewmh.fullscreen = true;
600 } else if (state == bewmh.wmStateAbove()) {
601 ewmh.above = true;
602 } else if (state == bewmh.wmStateBelow()) {
603 ewmh.below = true;
604 }
605 }
606 }
607
608 switch (ewmh.window_type) {
609 case WindowTypeDesktop:
610 case WindowTypeDock:
611 // these types should occupy all workspaces by default
612 ewmh.workspace = bt::BSENTINEL;
613 break;
614
615 default:
616 if (!bewmh.readWMDesktop(window, ewmh.workspace))
617 ewmh.workspace = currentWorkspace;
618 break;
619 } //switch
620
621 return ewmh;
622 }
623
624
625 /*
626 * Returns the MotifWM hints for the specified window.
627 */
readMotifWMHints(Blackbox * blackbox,Window window)628 static MotifHints readMotifWMHints(Blackbox *blackbox, Window window) {
629 MotifHints motif;
630 motif.decorations = AllWindowDecorations;
631 motif.functions = AllWindowFunctions;
632
633 /*
634 this structure only contains 3 elements, even though the Motif 2.0
635 structure contains 5, because we only use the first 3
636 */
637 struct PropMotifhints {
638 unsigned long flags;
639 unsigned long functions;
640 unsigned long decorations;
641 };
642 static const unsigned int PROP_MWM_HINTS_ELEMENTS = 3u;
643 enum { // MWM flags
644 MWM_HINTS_FUNCTIONS = 1<<0,
645 MWM_HINTS_DECORATIONS = 1<<1
646 };
647 enum { // MWM functions
648 MWM_FUNC_ALL = 1<<0,
649 MWM_FUNC_RESIZE = 1<<1,
650 MWM_FUNC_MOVE = 1<<2,
651 MWM_FUNC_MINIMIZE = 1<<3,
652 MWM_FUNC_MAXIMIZE = 1<<4,
653 MWM_FUNC_CLOSE = 1<<5
654 };
655 enum { // MWM decorations
656 MWM_DECOR_ALL = 1<<0,
657 MWM_DECOR_BORDER = 1<<1,
658 MWM_DECOR_RESIZEH = 1<<2,
659 MWM_DECOR_TITLE = 1<<3,
660 MWM_DECOR_MENU = 1<<4,
661 MWM_DECOR_MINIMIZE = 1<<5,
662 MWM_DECOR_MAXIMIZE = 1<<6
663 };
664
665 Atom atom_return;
666 PropMotifhints *prop = 0;
667 int format;
668 unsigned long num, len;
669 int ret = XGetWindowProperty(blackbox->XDisplay(), window,
670 blackbox->motifWmHintsAtom(), 0,
671 PROP_MWM_HINTS_ELEMENTS, False,
672 blackbox->motifWmHintsAtom(), &atom_return,
673 &format, &num, &len,
674 (unsigned char **) &prop);
675
676 if (ret != Success || !prop || num != PROP_MWM_HINTS_ELEMENTS) {
677 if (prop) XFree(prop);
678 return motif;
679 }
680
681 if (prop->flags & MWM_HINTS_FUNCTIONS) {
682 if (prop->functions & MWM_FUNC_ALL) {
683 motif.functions = AllWindowFunctions;
684 } else {
685 // default to the functions that cannot be set through
686 // _MOTIF_WM_HINTS
687 motif.functions = (WindowFunctionShade
688 | WindowFunctionChangeWorkspace
689 | WindowFunctionChangeLayer
690 | WindowFunctionFullScreen);
691
692 if (prop->functions & MWM_FUNC_RESIZE)
693 motif.functions |= WindowFunctionResize;
694 if (prop->functions & MWM_FUNC_MOVE)
695 motif.functions |= WindowFunctionMove;
696 if (prop->functions & MWM_FUNC_MINIMIZE)
697 motif.functions |= WindowFunctionIconify;
698 if (prop->functions & MWM_FUNC_MAXIMIZE)
699 motif.functions |= WindowFunctionMaximize;
700 if (prop->functions & MWM_FUNC_CLOSE)
701 motif.functions |= WindowFunctionClose;
702 }
703 }
704
705 if (prop->flags & MWM_HINTS_DECORATIONS) {
706 if (prop->decorations & MWM_DECOR_ALL) {
707 motif.decorations = AllWindowDecorations;
708 } else {
709 motif.decorations = NoWindowDecorations;
710
711 if (prop->decorations & MWM_DECOR_BORDER)
712 motif.decorations |= WindowDecorationBorder;
713 if (prop->decorations & MWM_DECOR_RESIZEH)
714 motif.decorations |= WindowDecorationHandle;
715 if (prop->decorations & MWM_DECOR_TITLE)
716 motif.decorations |= WindowDecorationTitlebar;
717 if (prop->decorations & MWM_DECOR_MINIMIZE)
718 motif.decorations |= WindowDecorationIconify;
719 if (prop->decorations & MWM_DECOR_MAXIMIZE)
720 motif.decorations |= WindowDecorationMaximize;
721 }
722 }
723
724 if (motif.decorations & WindowDecorationHandle) {
725 if (motif.functions & WindowFunctionResize)
726 motif.decorations |= WindowDecorationGrip;
727 else
728 motif.decorations &= ~WindowDecorationGrip;
729 }
730
731 if (motif.decorations & WindowDecorationTitlebar) {
732 if (motif.functions & WindowFunctionClose)
733 motif.decorations |= WindowDecorationClose;
734 else
735 motif.decorations &= ~WindowDecorationClose;
736 }
737
738 XFree(prop);
739
740 return motif;
741 }
742
743
744 /*
745 * Returns the value of the WM_HINTS property. If the property is not
746 * set, a set of default values is returned instead.
747 */
readWMHints(Blackbox * blackbox,Window window)748 static WMHints readWMHints(Blackbox *blackbox, Window window) {
749 WMHints wmh;
750 wmh.accept_focus = false;
751 wmh.window_group = None;
752 wmh.initial_state = NormalState;
753
754 XWMHints *wmhint = XGetWMHints(blackbox->XDisplay(), window);
755 if (!wmhint) return wmh;
756
757 if (wmhint->flags & InputHint)
758 wmh.accept_focus = (wmhint->input == True);
759 if (wmhint->flags & StateHint)
760 wmh.initial_state = wmhint->initial_state;
761 if (wmhint->flags & WindowGroupHint)
762 wmh.window_group = wmhint->window_group;
763
764 XFree(wmhint);
765
766 return wmh;
767 }
768
769
770 /*
771 * Returns the value of the WM_NORMAL_HINTS property. If the property
772 * is not set, a set of default values is returned instead.
773 */
readWMNormalHints(Blackbox * blackbox,Window window,const bt::ScreenInfo & screenInfo)774 static WMNormalHints readWMNormalHints(Blackbox *blackbox,
775 Window window,
776 const bt::ScreenInfo &screenInfo) {
777 WMNormalHints wmnormal;
778 wmnormal.flags = 0;
779 wmnormal.min_width = wmnormal.min_height = 1u;
780 wmnormal.width_inc = wmnormal.height_inc = 1u;
781 wmnormal.min_aspect_x = wmnormal.min_aspect_y = 1u;
782 wmnormal.max_aspect_x = wmnormal.max_aspect_y = 1u;
783 wmnormal.base_width = wmnormal.base_height = 0u;
784 wmnormal.win_gravity = NorthWestGravity;
785
786 /*
787 use the full screen, not the strut modified size. otherwise when
788 the availableArea changes max_width/height will be incorrect and
789 lead to odd rendering bugs.
790 */
791 const bt::Rect &rect = screenInfo.rect();
792 wmnormal.max_width = rect.width();
793 wmnormal.max_height = rect.height();
794
795 XSizeHints sizehint;
796 long unused;
797 if (! XGetWMNormalHints(blackbox->XDisplay(), window, &sizehint, &unused))
798 return wmnormal;
799
800 wmnormal.flags = sizehint.flags;
801
802 if (sizehint.flags & PMinSize) {
803 if (sizehint.min_width > 0)
804 wmnormal.min_width = sizehint.min_width;
805 if (sizehint.min_height > 0)
806 wmnormal.min_height = sizehint.min_height;
807
808 /*
809 if the minimum size is bigger then the screen, adjust the
810 maximum size
811 */
812 if (wmnormal.min_width > wmnormal.max_width)
813 wmnormal.max_width = wmnormal.min_width;
814 if (wmnormal.min_height > wmnormal.max_height)
815 wmnormal.max_height = wmnormal.min_height;
816 }
817
818 if (sizehint.flags & PMaxSize) {
819 if (sizehint.max_width >= static_cast<signed>(wmnormal.min_width))
820 wmnormal.max_width = sizehint.max_width;
821 else
822 wmnormal.max_width = wmnormal.min_width;
823
824 if (sizehint.max_height >= static_cast<signed>(wmnormal.min_height))
825 wmnormal.max_height = sizehint.max_height;
826 else
827 wmnormal.max_height = wmnormal.min_height;
828 }
829
830 if (sizehint.flags & PResizeInc) {
831 wmnormal.width_inc = sizehint.width_inc;
832 wmnormal.height_inc = sizehint.height_inc;
833 }
834
835 if (sizehint.flags & PAspect) {
836 wmnormal.min_aspect_x = sizehint.min_aspect.x;
837 wmnormal.min_aspect_y = sizehint.min_aspect.y;
838 wmnormal.max_aspect_x = sizehint.max_aspect.x;
839 wmnormal.max_aspect_y = sizehint.max_aspect.y;
840 }
841
842 if (sizehint.flags & PBaseSize) {
843 if (sizehint.base_width <= static_cast<signed>(wmnormal.min_width))
844 wmnormal.base_width = sizehint.base_width;
845 if (sizehint.base_height <= static_cast<signed>(wmnormal.min_height))
846 wmnormal.base_height = sizehint.base_height;
847 }
848
849 if (sizehint.flags & PWinGravity)
850 wmnormal.win_gravity = sizehint.win_gravity;
851
852 return wmnormal;
853 }
854
855
856 /*
857 * Retrieve which Window Manager Protocols are supported by the client
858 * window.
859 */
readWMProtocols(Blackbox * blackbox,Window window)860 static WMProtocols readWMProtocols(Blackbox *blackbox,
861 Window window) {
862 WMProtocols protocols;
863 protocols.wm_delete_window = false;
864 protocols.wm_take_focus = false;
865
866 Atom *proto;
867 int num_return = 0;
868
869 if (XGetWMProtocols(blackbox->XDisplay(), window,
870 &proto, &num_return)) {
871 for (int i = 0; i < num_return; ++i) {
872 if (proto[i] == blackbox->wmDeleteWindowAtom()) {
873 protocols.wm_delete_window = true;
874 } else if (proto[i] == blackbox->wmTakeFocusAtom()) {
875 protocols.wm_take_focus = true;
876 }
877 }
878 XFree(proto);
879 }
880
881 return protocols;
882 }
883
884
885 /*
886 * Reads the value of the WM_TRANSIENT_FOR property and returns a
887 * pointer to the transient parent for this window. If the
888 * WM_TRANSIENT_FOR is missing or invalid, this function returns 0.
889 *
890 * 'client.wmhints' should be properly updated before calling this
891 * function.
892 *
893 * Note: a return value of ~0ul signifies a window that should be
894 * transient but has no discernible parent.
895 */
readTransientInfo(Blackbox * blackbox,Window window,const bt::ScreenInfo & screenInfo,const WMHints & wmhints)896 static Window readTransientInfo(Blackbox *blackbox,
897 Window window,
898 const bt::ScreenInfo &screenInfo,
899 const WMHints &wmhints) {
900 Window trans_for = None;
901
902 if (!XGetTransientForHint(blackbox->XDisplay(), window, &trans_for)) {
903 // WM_TRANSIENT_FOR hint not set
904 return 0;
905 }
906
907 if (trans_for == window) {
908 // wierd client... treat this window as a normal window
909 return 0;
910 }
911
912 if (trans_for == None || trans_for == screenInfo.rootWindow()) {
913 /*
914 this is a violation of the ICCCM, yet the EWMH allows this as a
915 way to signify a group transient.
916 */
917 trans_for = wmhints.window_group;
918 }
919
920 return trans_for;
921 }
922
923
readState(unsigned long & current_state,Blackbox * blackbox,Window window)924 static bool readState(unsigned long ¤t_state,
925 Blackbox *blackbox,
926 Window window) {
927 current_state = NormalState;
928
929 Atom atom_return;
930 bool ret = false;
931 int foo;
932 unsigned long *state, ulfoo, nitems;
933
934 if ((XGetWindowProperty(blackbox->XDisplay(), window,
935 blackbox->wmStateAtom(),
936 0l, 2l, False, blackbox->wmStateAtom(),
937 &atom_return, &foo, &nitems, &ulfoo,
938 (unsigned char **) &state) != Success) ||
939 (! state)) {
940 return false;
941 }
942
943 if (nitems >= 1) {
944 current_state = static_cast<unsigned long>(state[0]);
945 ret = true;
946 }
947
948 XFree((void *) state);
949
950 return ret;
951 }
952
953
clearState(Blackbox * blackbox,Window window)954 static void clearState(Blackbox *blackbox, Window window) {
955 XDeleteProperty(blackbox->XDisplay(), window, blackbox->wmStateAtom());
956
957 const bt::EWMH& ewmh = blackbox->ewmh();
958 ewmh.removeProperty(window, ewmh.wmDesktop());
959 ewmh.removeProperty(window, ewmh.wmState());
960 ewmh.removeProperty(window, ewmh.wmAllowedActions());
961 ewmh.removeProperty(window, ewmh.wmVisibleName());
962 ewmh.removeProperty(window, ewmh.wmVisibleIconName());
963 }
964
965
966 /*
967 * Initializes the class with default values/the window's set initial values.
968 */
BlackboxWindow(Blackbox * b,Window w,BScreen * s)969 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
970 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
971 // sizeof(BlackboxWindow));
972
973 #ifdef DEBUG
974 fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
975 #endif // DEBUG
976
977 /*
978 set timer to zero... it is initialized properly later, so we check
979 if timer is zero in the destructor, and assume that the window is not
980 fully constructed if timer is zero...
981 */
982 timer = (bt::Timer*) 0;
983 blackbox = b;
984 client.window = w;
985 _screen = s;
986 lastButtonPressTime = 0;
987
988 /*
989 the server needs to be grabbed here to prevent client's from sending
990 events while we are in the process of managing their window.
991 We hold the grab until after we are done moving the window around.
992 */
993
994 blackbox->XGrabServer();
995
996 // fetch client size and placement
997 XWindowAttributes wattrib;
998 if (! XGetWindowAttributes(blackbox->XDisplay(),
999 client.window, &wattrib) ||
1000 ! wattrib.screen || wattrib.override_redirect) {
1001 #ifdef DEBUG
1002 fprintf(stderr,
1003 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
1004 #endif // DEBUG
1005
1006 blackbox->XUngrabServer();
1007 delete this;
1008 return;
1009 }
1010
1011 // set the eventmask early in the game so that we make sure we get
1012 // all the events we are interested in
1013 XSetWindowAttributes attrib_set;
1014 attrib_set.event_mask = ::client_window_event_mask;
1015 attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
1016 ButtonMotionMask;
1017 XChangeWindowAttributes(blackbox->XDisplay(), client.window,
1018 CWEventMask|CWDontPropagate, &attrib_set);
1019
1020 client.colormap = wattrib.colormap;
1021 window_number = bt::BSENTINEL;
1022 client.strut = 0;
1023 /*
1024 set the initial size and location of client window (relative to the
1025 _root window_). This position is the reference point used with the
1026 window's gravity to find the window's initial position.
1027 */
1028 client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1029 client.premax = client.rect;
1030 client.old_bw = wattrib.border_width;
1031 client.current_state = NormalState;
1032
1033 frame.window = frame.plate = frame.title = frame.handle = None;
1034 frame.close_button = frame.iconify_button = frame.maximize_button = None;
1035 frame.right_grip = frame.left_grip = None;
1036 frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
1037 frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
1038 frame.pbutton = frame.ugrip = frame.fgrip = None;
1039
1040 timer = new bt::Timer(blackbox, this);
1041 timer->setTimeout(blackbox->resource().autoRaiseDelay());
1042
1043 client.title = ::readWMName(blackbox, client.window);
1044 client.icon_title = ::readWMIconName(blackbox, client.window);
1045
1046 // get size, aspect, minimum/maximum size, ewmh and other hints set
1047 // by the client
1048 client.ewmh = ::readEWMH(blackbox->ewmh(), client.window,
1049 _screen->currentWorkspace());
1050 client.motif = ::readMotifWMHints(blackbox, client.window);
1051 client.wmhints = ::readWMHints(blackbox, client.window);
1052 client.wmnormal = ::readWMNormalHints(blackbox, client.window,
1053 _screen->screenInfo());
1054 client.wmprotocols = ::readWMProtocols(blackbox, client.window);
1055 client.transient_for = ::readTransientInfo(blackbox, client.window,
1056 _screen->screenInfo(),
1057 client.wmhints);
1058
1059 if (client.wmhints.window_group != None)
1060 (void) ::update_window_group(client.wmhints.window_group, blackbox, this);
1061
1062 if (isTransient()) {
1063 // add ourselves to our transient_for
1064 BlackboxWindow *win = findTransientFor();
1065 if (win) {
1066 win->addTransient(this);
1067 client.ewmh.workspace = win->workspace();
1068 setLayer(win->layer());
1069 } else if (isGroupTransient()) {
1070 BWindowGroup *group = findWindowGroup();
1071 if (group)
1072 group->addTransient(this);
1073 } else {
1074 // broken client
1075 client.transient_for = 0;
1076 }
1077 }
1078
1079 client.state.visible = false;
1080 client.state.iconic = false;
1081 client.state.moving = false;
1082 client.state.resizing = false;
1083 client.state.focused = false;
1084
1085 switch (windowType()) {
1086 case WindowTypeDesktop:
1087 setLayer(StackingList::LayerDesktop);
1088 break;
1089
1090 case WindowTypeDock:
1091 setLayer(StackingList::LayerAbove);
1092 // fallthrough intended
1093
1094 default:
1095 if (client.ewmh.above)
1096 setLayer(StackingList::LayerAbove);
1097 else if (client.ewmh.below)
1098 setLayer(StackingList::LayerBelow);
1099 break;
1100 } // switch
1101
1102 ::update_decorations(client.decorations,
1103 client.functions,
1104 isTransient(),
1105 client.ewmh,
1106 client.motif,
1107 client.wmnormal,
1108 client.wmprotocols);
1109
1110 // sanity checks
1111 if (client.wmhints.initial_state == IconicState
1112 && !hasWindowFunction(WindowFunctionIconify))
1113 client.wmhints.initial_state = NormalState;
1114 if (isMaximized() && !hasWindowFunction(WindowFunctionMaximize))
1115 client.ewmh.maxv = client.ewmh.maxh = false;
1116 if (isFullScreen() && !hasWindowFunction(WindowFunctionFullScreen))
1117 client.ewmh.fullscreen = false;
1118
1119 bt::EWMH::Strut strut;
1120 if (blackbox->ewmh().readWMStrut(client.window, &strut)) {
1121 client.strut = new bt::EWMH::Strut;
1122 *client.strut = strut;
1123 _screen->addStrut(client.strut);
1124 }
1125
1126 /*
1127 if we just managed the group leader for an existing group, move
1128 all group transients to this window
1129 */
1130 {
1131 BWindowGroup *group = blackbox->findWindowGroup(client.window);
1132 if (group) {
1133 BlackboxWindowList transientList = group->transients();
1134 BlackboxWindowList::const_iterator it = transientList.begin();
1135 const BlackboxWindowList::const_iterator end = transientList.end();
1136 for (; it != end; ++it) {
1137 BlackboxWindow * const w1 = *it;
1138 if (w1->client.transient_for != client.window)
1139 continue;
1140 group->removeTransient(w1);
1141 addTransient(w1);
1142 w1->changeWorkspace(workspace());
1143 w1->changeLayer(layer());
1144 }
1145 }
1146 }
1147
1148 frame.window = createToplevelWindow();
1149 blackbox->insertEventHandler(frame.window, this);
1150
1151 frame.plate = createChildWindow(frame.window, NoEventMask);
1152 blackbox->insertEventHandler(frame.plate, this);
1153
1154 if (client.decorations & WindowDecorationTitlebar)
1155 createTitlebar();
1156
1157 if (client.decorations & WindowDecorationHandle)
1158 createHandle();
1159
1160 // apply the size and gravity to the frame
1161 const WindowStyle &style = _screen->resource().windowStyle();
1162 frame.margin = ::update_margin(client.decorations, style);
1163 frame.rect = ::applyGravity(client.rect,
1164 frame.margin,
1165 client.wmnormal.win_gravity);
1166
1167 associateClientWindow();
1168
1169 blackbox->insertEventHandler(client.window, this);
1170 blackbox->insertWindow(client.window, this);
1171 blackbox->insertWindow(frame.plate, this);
1172
1173 // preserve the window's initial state on first map, and its current
1174 // state across a restart
1175 if (!readState(client.current_state, blackbox, client.window))
1176 client.current_state = client.wmhints.initial_state;
1177
1178 if (client.state.iconic) {
1179 // prepare the window to be iconified
1180 client.current_state = IconicState;
1181 client.state.iconic = False;
1182 } else if (workspace() != bt::BSENTINEL &&
1183 workspace() != _screen->currentWorkspace()) {
1184 client.current_state = WithdrawnState;
1185 }
1186
1187 blackbox->XUngrabServer();
1188
1189 grabButtons();
1190
1191 XMapSubwindows(blackbox->XDisplay(), frame.window);
1192
1193 if (isFullScreen()) {
1194 client.ewmh.fullscreen = false; // trick setFullScreen into working
1195 setFullScreen(true);
1196 } else {
1197 if (isMaximized()) {
1198 remaximize();
1199 } else {
1200 const unsigned long save_state = client.current_state;
1201
1202 bt::Rect r = frame.rect;
1203 // trick configure into working
1204 frame.rect = bt::Rect();
1205 configure(r);
1206
1207 if (isShaded()) {
1208 client.ewmh.shaded = false;
1209 setShaded(true);
1210 }
1211
1212 if (isShaded() && save_state != IconicState) {
1213 /*
1214 At this point in the life of a window, current_state should
1215 only be set to IconicState if the window was an *icon*, not
1216 if it was shaded.
1217 */
1218 client.current_state = save_state;
1219 }
1220 }
1221 }
1222 }
1223
1224
~BlackboxWindow(void)1225 BlackboxWindow::~BlackboxWindow(void) {
1226 #ifdef DEBUG
1227 fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
1228 client.window);
1229 #endif // DEBUG
1230
1231 if (! timer) // window not managed...
1232 return;
1233
1234 if (client.state.moving || client.state.resizing) {
1235 _screen->hideGeometry();
1236 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
1237 }
1238
1239 delete timer;
1240
1241 if (client.strut) {
1242 _screen->removeStrut(client.strut);
1243 delete client.strut;
1244 }
1245
1246 BWindowGroup *group = findWindowGroup();
1247
1248 if (isTransient()) {
1249 // remove ourselves from our transient_for
1250 BlackboxWindow *win = findTransientFor();
1251 if (win) {
1252 win->removeTransient(this);
1253 } else if (isGroupTransient()) {
1254 if (group)
1255 group->removeTransient(this);
1256 }
1257 client.transient_for = 0;
1258 }
1259
1260 if (group)
1261 group->removeWindow(this);
1262
1263 if (frame.title)
1264 destroyTitlebar();
1265
1266 if (frame.handle)
1267 destroyHandle();
1268
1269 blackbox->removeEventHandler(client.window);
1270 blackbox->removeWindow(client.window);
1271
1272 blackbox->removeEventHandler(frame.plate);
1273 blackbox->removeWindow(frame.plate);
1274 XDestroyWindow(blackbox->XDisplay(), frame.plate);
1275
1276 blackbox->removeEventHandler(frame.window);
1277 XDestroyWindow(blackbox->XDisplay(), frame.window);
1278 }
1279
1280
1281 /*
1282 * Creates a new top level window, with a given location, size, and border
1283 * width.
1284 * Returns: the newly created window
1285 */
createToplevelWindow(void)1286 Window BlackboxWindow::createToplevelWindow(void) {
1287 XSetWindowAttributes attrib_create;
1288 unsigned long create_mask = CWColormap | CWOverrideRedirect | CWEventMask;
1289
1290 attrib_create.colormap = _screen->screenInfo().colormap();
1291 attrib_create.override_redirect = True;
1292 attrib_create.event_mask = EnterWindowMask | LeaveWindowMask;
1293
1294 return XCreateWindow(blackbox->XDisplay(),
1295 _screen->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
1296 _screen->screenInfo().depth(), InputOutput,
1297 _screen->screenInfo().visual(),
1298 create_mask, &attrib_create);
1299 }
1300
1301
1302 /*
1303 * Creates a child window, and optionally associates a given cursor with
1304 * the new window.
1305 */
createChildWindow(Window parent,unsigned long event_mask,Cursor cursor)1306 Window BlackboxWindow::createChildWindow(Window parent,
1307 unsigned long event_mask,
1308 Cursor cursor) {
1309 XSetWindowAttributes attrib_create;
1310 unsigned long create_mask = CWEventMask;
1311
1312 attrib_create.event_mask = event_mask;
1313
1314 if (cursor) {
1315 create_mask |= CWCursor;
1316 attrib_create.cursor = cursor;
1317 }
1318
1319 return XCreateWindow(blackbox->XDisplay(), parent, 0, 0, 1, 1, 0,
1320 _screen->screenInfo().depth(), InputOutput,
1321 _screen->screenInfo().visual(),
1322 create_mask, &attrib_create);
1323 }
1324
1325
1326 /*
1327 * Reparents the client window into the newly created frame.
1328 *
1329 * Note: the server must be grabbed before calling this function.
1330 */
associateClientWindow(void)1331 void BlackboxWindow::associateClientWindow(void) {
1332 XSetWindowBorderWidth(blackbox->XDisplay(), client.window, 0);
1333 XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeInsert);
1334
1335 XSelectInput(blackbox->XDisplay(), frame.plate,
1336 FocusChangeMask | SubstructureRedirectMask);
1337
1338 XSelectInput(blackbox->XDisplay(), client.window,
1339 client_window_event_mask & ~StructureNotifyMask);
1340 XReparentWindow(blackbox->XDisplay(), client.window, frame.plate, 0, 0);
1341 XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
1342
1343 #ifdef SHAPE
1344 if (blackbox->hasShapeExtensions()) {
1345 XShapeSelectInput(blackbox->XDisplay(), client.window,
1346 ShapeNotifyMask);
1347
1348 Bool shaped = False;
1349 int foo;
1350 unsigned int ufoo;
1351
1352 XShapeQueryExtents(blackbox->XDisplay(), client.window, &shaped,
1353 &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
1354 &ufoo, &ufoo);
1355 client.state.shaped = shaped;
1356 }
1357 #endif // SHAPE
1358 }
1359
1360
decorate(void)1361 void BlackboxWindow::decorate(void) {
1362 const WindowStyle &style = _screen->resource().windowStyle();
1363 if (client.decorations & WindowDecorationTitlebar) {
1364 // render focused button texture
1365 frame.fbutton =
1366 bt::PixmapCache::find(_screen->screenNumber(),
1367 style.focus.button,
1368 style.button_width,
1369 style.button_width,
1370 frame.fbutton);
1371
1372 // render unfocused button texture
1373 frame.ubutton =
1374 bt::PixmapCache::find(_screen->screenNumber(),
1375 style.unfocus.button,
1376 style.button_width,
1377 style.button_width,
1378 frame.ubutton);
1379
1380 // render pressed button texture
1381 frame.pbutton =
1382 bt::PixmapCache::find(_screen->screenNumber(),
1383 style.pressed,
1384 style.button_width,
1385 style.button_width,
1386 frame.pbutton);
1387
1388 // render focused titlebar texture
1389 frame.ftitle =
1390 bt::PixmapCache::find(_screen->screenNumber(),
1391 style.focus.title,
1392 frame.rect.width(),
1393 style.title_height,
1394 frame.ftitle);
1395
1396 // render unfocused titlebar texture
1397 frame.utitle =
1398 bt::PixmapCache::find(_screen->screenNumber(),
1399 style.unfocus.title,
1400 frame.rect.width(),
1401 style.title_height,
1402 frame.utitle);
1403
1404 // render focused label texture
1405 frame.flabel =
1406 bt::PixmapCache::find(_screen->screenNumber(),
1407 style.focus.label,
1408 frame.label_w,
1409 style.label_height,
1410 frame.flabel);
1411
1412 // render unfocused label texture
1413 frame.ulabel =
1414 bt::PixmapCache::find(_screen->screenNumber(),
1415 style.unfocus.label,
1416 frame.label_w,
1417 style.label_height,
1418 frame.ulabel);
1419 }
1420
1421 if (client.decorations & WindowDecorationHandle) {
1422 frame.fhandle =
1423 bt::PixmapCache::find(_screen->screenNumber(),
1424 style.focus.handle,
1425 frame.rect.width(),
1426 style.handle_height,
1427 frame.fhandle);
1428
1429 frame.uhandle =
1430 bt::PixmapCache::find(_screen->screenNumber(),
1431 style.unfocus.handle,
1432 frame.rect.width(),
1433 style.handle_height,
1434 frame.uhandle);
1435 }
1436
1437 if (client.decorations & WindowDecorationGrip) {
1438 frame.fgrip =
1439 bt::PixmapCache::find(_screen->screenNumber(),
1440 style.focus.grip,
1441 style.grip_width,
1442 style.handle_height,
1443 frame.fgrip);
1444
1445 frame.ugrip =
1446 bt::PixmapCache::find(_screen->screenNumber(),
1447 style.unfocus.grip,
1448 style.grip_width,
1449 style.handle_height,
1450 frame.ugrip);
1451 }
1452 }
1453
1454
createHandle(void)1455 void BlackboxWindow::createHandle(void) {
1456 frame.handle = createChildWindow(frame.window,
1457 ButtonPressMask | ButtonReleaseMask |
1458 ButtonMotionMask | ExposureMask);
1459 blackbox->insertEventHandler(frame.handle, this);
1460
1461 if (client.decorations & WindowDecorationGrip)
1462 createGrips();
1463 }
1464
1465
destroyHandle(void)1466 void BlackboxWindow::destroyHandle(void) {
1467 if (frame.left_grip || frame.right_grip)
1468 destroyGrips();
1469
1470 if (frame.fhandle) bt::PixmapCache::release(frame.fhandle);
1471 if (frame.uhandle) bt::PixmapCache::release(frame.uhandle);
1472
1473 frame.fhandle = frame.uhandle = None;
1474
1475 blackbox->removeEventHandler(frame.handle);
1476 XDestroyWindow(blackbox->XDisplay(), frame.handle);
1477 frame.handle = None;
1478 }
1479
1480
createGrips(void)1481 void BlackboxWindow::createGrips(void) {
1482 frame.left_grip =
1483 createChildWindow(frame.handle,
1484 ButtonPressMask | ButtonReleaseMask |
1485 ButtonMotionMask | ExposureMask,
1486 blackbox->resource().cursors().resize_bottom_left);
1487 blackbox->insertEventHandler(frame.left_grip, this);
1488
1489 frame.right_grip =
1490 createChildWindow(frame.handle,
1491 ButtonPressMask | ButtonReleaseMask |
1492 ButtonMotionMask | ExposureMask,
1493 blackbox->resource().cursors().resize_bottom_right);
1494 blackbox->insertEventHandler(frame.right_grip, this);
1495 }
1496
1497
destroyGrips(void)1498 void BlackboxWindow::destroyGrips(void) {
1499 if (frame.fgrip) bt::PixmapCache::release(frame.fgrip);
1500 if (frame.ugrip) bt::PixmapCache::release(frame.ugrip);
1501
1502 frame.fgrip = frame.ugrip = None;
1503
1504 blackbox->removeEventHandler(frame.left_grip);
1505 blackbox->removeEventHandler(frame.right_grip);
1506
1507 XDestroyWindow(blackbox->XDisplay(), frame.left_grip);
1508 XDestroyWindow(blackbox->XDisplay(), frame.right_grip);
1509 frame.left_grip = frame.right_grip = None;
1510 }
1511
1512
createTitlebar(void)1513 void BlackboxWindow::createTitlebar(void) {
1514 frame.title = createChildWindow(frame.window,
1515 ButtonPressMask | ButtonReleaseMask |
1516 ButtonMotionMask | ExposureMask);
1517 frame.label = createChildWindow(frame.title,
1518 ButtonPressMask | ButtonReleaseMask |
1519 ButtonMotionMask | ExposureMask);
1520 blackbox->insertEventHandler(frame.title, this);
1521 blackbox->insertEventHandler(frame.label, this);
1522
1523 if (client.decorations & WindowDecorationIconify) createIconifyButton();
1524 if (client.decorations & WindowDecorationMaximize) createMaximizeButton();
1525 if (client.decorations & WindowDecorationClose) createCloseButton();
1526 }
1527
1528
destroyTitlebar(void)1529 void BlackboxWindow::destroyTitlebar(void) {
1530 if (frame.close_button)
1531 destroyCloseButton();
1532
1533 if (frame.iconify_button)
1534 destroyIconifyButton();
1535
1536 if (frame.maximize_button)
1537 destroyMaximizeButton();
1538
1539 if (frame.fbutton) bt::PixmapCache::release(frame.fbutton);
1540 if (frame.ubutton) bt::PixmapCache::release(frame.ubutton);
1541 if (frame.pbutton) bt::PixmapCache::release(frame.pbutton);
1542 if (frame.ftitle) bt::PixmapCache::release(frame.ftitle);
1543 if (frame.utitle) bt::PixmapCache::release(frame.utitle);
1544 if (frame.flabel) bt::PixmapCache::release(frame.flabel);
1545 if (frame.ulabel) bt::PixmapCache::release(frame.ulabel);
1546
1547 frame.fbutton = frame.ubutton = frame.pbutton =
1548 frame.ftitle = frame.utitle =
1549 frame.flabel = frame.ulabel = None;
1550
1551 blackbox->removeEventHandler(frame.title);
1552 blackbox->removeEventHandler(frame.label);
1553
1554 XDestroyWindow(blackbox->XDisplay(), frame.label);
1555 XDestroyWindow(blackbox->XDisplay(), frame.title);
1556 frame.title = frame.label = None;
1557 }
1558
1559
createCloseButton(void)1560 void BlackboxWindow::createCloseButton(void) {
1561 if (frame.title != None) {
1562 frame.close_button = createChildWindow(frame.title,
1563 ButtonPressMask |
1564 ButtonReleaseMask |
1565 ButtonMotionMask | ExposureMask);
1566 blackbox->insertEventHandler(frame.close_button, this);
1567 }
1568 }
1569
1570
destroyCloseButton(void)1571 void BlackboxWindow::destroyCloseButton(void) {
1572 blackbox->removeEventHandler(frame.close_button);
1573 XDestroyWindow(blackbox->XDisplay(), frame.close_button);
1574 frame.close_button = None;
1575 }
1576
1577
createIconifyButton(void)1578 void BlackboxWindow::createIconifyButton(void) {
1579 if (frame.title != None) {
1580 frame.iconify_button = createChildWindow(frame.title,
1581 ButtonPressMask |
1582 ButtonReleaseMask |
1583 ButtonMotionMask | ExposureMask);
1584 blackbox->insertEventHandler(frame.iconify_button, this);
1585 }
1586 }
1587
1588
destroyIconifyButton(void)1589 void BlackboxWindow::destroyIconifyButton(void) {
1590 blackbox->removeEventHandler(frame.iconify_button);
1591 XDestroyWindow(blackbox->XDisplay(), frame.iconify_button);
1592 frame.iconify_button = None;
1593 }
1594
1595
createMaximizeButton(void)1596 void BlackboxWindow::createMaximizeButton(void) {
1597 if (frame.title != None) {
1598 frame.maximize_button = createChildWindow(frame.title,
1599 ButtonPressMask |
1600 ButtonReleaseMask |
1601 ButtonMotionMask | ExposureMask);
1602 blackbox->insertEventHandler(frame.maximize_button, this);
1603 }
1604 }
1605
1606
destroyMaximizeButton(void)1607 void BlackboxWindow::destroyMaximizeButton(void) {
1608 blackbox->removeEventHandler(frame.maximize_button);
1609 XDestroyWindow(blackbox->XDisplay(), frame.maximize_button);
1610 frame.maximize_button = None;
1611 }
1612
1613
positionButtons(bool redecorate_label)1614 void BlackboxWindow::positionButtons(bool redecorate_label) {
1615 // we need to use signed ints here to detect windows that are too small
1616 const WindowStyle &style = _screen->resource().windowStyle();
1617 const int extra = style.title_margin == 0 ?
1618 style.focus.button.borderWidth() : 0,
1619 bw = style.button_width + style.title_margin
1620 - extra,
1621 by = style.title_margin +
1622 style.focus.title.borderWidth();
1623 int lx = by, lw = frame.rect.width() - by;
1624
1625 if (client.decorations & WindowDecorationIconify) {
1626 if (frame.iconify_button == None) createIconifyButton();
1627
1628 XMoveResizeWindow(blackbox->XDisplay(), frame.iconify_button, by, by,
1629 style.button_width, style.button_width);
1630 XMapWindow(blackbox->XDisplay(), frame.iconify_button);
1631
1632 lx += bw;
1633 lw -= bw;
1634 } else if (frame.iconify_button) {
1635 destroyIconifyButton();
1636 }
1637
1638 int bx = frame.rect.width() - bw
1639 - style.focus.title.borderWidth() - extra;
1640
1641 if (client.decorations & WindowDecorationClose) {
1642 if (frame.close_button == None) createCloseButton();
1643
1644 XMoveResizeWindow(blackbox->XDisplay(), frame.close_button, bx, by,
1645 style.button_width, style.button_width);
1646 XMapWindow(blackbox->XDisplay(), frame.close_button);
1647
1648 bx -= bw;
1649 lw -= bw;
1650 } else if (frame.close_button) {
1651 destroyCloseButton();
1652 }
1653
1654 if (client.decorations & WindowDecorationMaximize) {
1655 if (frame.maximize_button == None) createMaximizeButton();
1656
1657 XMoveResizeWindow(blackbox->XDisplay(), frame.maximize_button, bx, by,
1658 style.button_width, style.button_width);
1659 XMapWindow(blackbox->XDisplay(), frame.maximize_button);
1660
1661 bx -= bw;
1662 lw -= bw;
1663 } else if (frame.maximize_button) {
1664 destroyMaximizeButton();
1665 }
1666
1667 if (lw > by) {
1668 frame.label_w = lw - by;
1669 XMoveResizeWindow(blackbox->XDisplay(), frame.label, lx, by,
1670 frame.label_w, style.label_height);
1671 XMapWindow(blackbox->XDisplay(), frame.label);
1672
1673 if (redecorate_label) {
1674 frame.flabel =
1675 bt::PixmapCache::find(_screen->screenNumber(),
1676 style.focus.label,
1677 frame.label_w, style.label_height,
1678 frame.flabel);
1679 frame.ulabel =
1680 bt::PixmapCache::find(_screen->screenNumber(),
1681 style.unfocus.label,
1682 frame.label_w, style.label_height,
1683 frame.ulabel);
1684 }
1685
1686 const bt::ustring ellided =
1687 bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
1688 _screen->screenNumber(), style.font);
1689
1690 if (ellided != client.visible_title) {
1691 client.visible_title = ellided;
1692 blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
1693 }
1694 } else {
1695 frame.label_w = 1;
1696 XUnmapWindow(blackbox->XDisplay(), frame.label);
1697 }
1698
1699 redrawLabel();
1700 redrawAllButtons();
1701 }
1702
1703
reconfigure(void)1704 void BlackboxWindow::reconfigure(void) {
1705 const WindowStyle &style = _screen->resource().windowStyle();
1706 if (isMaximized()) {
1707 // update the frame margin in case the style has changed
1708 frame.margin = ::update_margin(client.decorations, style);
1709
1710 // make sure maximized windows have the correct size after a style
1711 // change
1712 remaximize();
1713 } else {
1714 // get the client window geometry as if it was unmanaged
1715 bt::Rect r = frame.rect;
1716 if (client.ewmh.shaded) {
1717 r.setHeight(client.rect.height() + frame.margin.top
1718 + frame.margin.bottom);
1719 }
1720 r = ::restoreGravity(r, frame.margin, client.wmnormal.win_gravity);
1721
1722 // update the frame margin in case the style has changed
1723 frame.margin = ::update_margin(client.decorations, style);
1724
1725 // get the frame window geometry from the client window geometry
1726 // calculated above
1727 r = ::applyGravity(r, frame.margin, client.wmnormal.win_gravity);
1728 if (client.ewmh.shaded) {
1729 frame.rect = r;
1730
1731 positionWindows();
1732 decorate();
1733
1734 // keep the window shaded
1735 frame.rect.setHeight(style.title_height);
1736 XResizeWindow(blackbox->XDisplay(), frame.window,
1737 frame.rect.width(), frame.rect.height());
1738 } else {
1739 // trick configure into working
1740 frame.rect = bt::Rect();
1741 configure(r);
1742 }
1743 }
1744
1745 ungrabButtons();
1746 grabButtons();
1747 }
1748
1749
grabButtons(void)1750 void BlackboxWindow::grabButtons(void) {
1751 if (blackbox->resource().focusModel() == ClickToFocusModel
1752 || blackbox->resource().clickRaise())
1753 // grab button 1 for changing focus/raising
1754 blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
1755 GrabModeSync, GrabModeSync, frame.plate, None,
1756 blackbox->resource().allowScrollLock());
1757
1758 if (hasWindowFunction(WindowFunctionMove))
1759 blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
1760 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1761 GrabModeAsync, frame.window,
1762 blackbox->resource().cursors().move,
1763 blackbox->resource().allowScrollLock());
1764 if (hasWindowFunction(WindowFunctionResize))
1765 blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
1766 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1767 GrabModeAsync, frame.window,
1768 None, blackbox->resource().allowScrollLock());
1769 // alt+middle lowers the window
1770 blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
1771 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
1772 frame.window, None,
1773 blackbox->resource().allowScrollLock());
1774
1775 blackbox->grabButton(Button3, Mod4Mask, frame.window, True,
1776 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
1777 frame.window, None,
1778 blackbox->resource().allowScrollLock());
1779 }
1780
1781
ungrabButtons(void)1782 void BlackboxWindow::ungrabButtons(void) {
1783 blackbox->ungrabButton(Button1, 0, frame.plate);
1784 blackbox->ungrabButton(Button1, Mod1Mask, frame.window);
1785 blackbox->ungrabButton(Button2, Mod1Mask, frame.window);
1786 blackbox->ungrabButton(Button3, Mod1Mask, frame.window);
1787 blackbox->ungrabButton(Button3, Mod4Mask, frame.window);
1788 }
1789
1790
positionWindows(void)1791 void BlackboxWindow::positionWindows(void) {
1792 const WindowStyle &style = _screen->resource().windowStyle();
1793 const unsigned int bw = (hasWindowDecoration(WindowDecorationBorder)
1794 ? style.frame_border_width
1795 : 0);
1796
1797 XMoveResizeWindow(blackbox->XDisplay(), frame.plate,
1798 frame.margin.left - bw,
1799 frame.margin.top - bw,
1800 client.rect.width(), client.rect.height());
1801 XSetWindowBorderWidth(blackbox->XDisplay(), frame.plate, bw);
1802 XMoveResizeWindow(blackbox->XDisplay(), client.window,
1803 0, 0, client.rect.width(), client.rect.height());
1804 // ensure client.rect contains the real location
1805 client.rect.setPos(frame.rect.left() + frame.margin.left,
1806 frame.rect.top() + frame.margin.top);
1807
1808 if (client.decorations & WindowDecorationTitlebar) {
1809 if (frame.title == None) createTitlebar();
1810
1811 XMoveResizeWindow(blackbox->XDisplay(), frame.title,
1812 0, 0, frame.rect.width(), style.title_height);
1813
1814 positionButtons();
1815 XMapSubwindows(blackbox->XDisplay(), frame.title);
1816 XMapWindow(blackbox->XDisplay(), frame.title);
1817 } else if (frame.title) {
1818 destroyTitlebar();
1819 }
1820
1821 if (client.decorations & WindowDecorationHandle) {
1822 if (frame.handle == None) createHandle();
1823
1824 // use client.rect here so the value is correct even if shaded
1825 XMoveResizeWindow(blackbox->XDisplay(), frame.handle,
1826 0, client.rect.height() + frame.margin.top,
1827 frame.rect.width(), style.handle_height);
1828
1829 if (client.decorations & WindowDecorationGrip) {
1830 if (frame.left_grip == None || frame.right_grip == None) createGrips();
1831
1832 XMoveResizeWindow(blackbox->XDisplay(), frame.left_grip, 0, 0,
1833 style.grip_width, style.handle_height);
1834
1835 const int nx = frame.rect.width() - style.grip_width;
1836 XMoveResizeWindow(blackbox->XDisplay(), frame.right_grip, nx, 0,
1837 style.grip_width, style.handle_height);
1838
1839 XMapSubwindows(blackbox->XDisplay(), frame.handle);
1840 } else {
1841 destroyGrips();
1842 }
1843
1844 XMapWindow(blackbox->XDisplay(), frame.handle);
1845 } else if (frame.handle) {
1846 destroyHandle();
1847 }
1848 }
1849
1850
1851 /*
1852 * This function is responsible for updating both the client and the
1853 * frame rectangles. According to the ICCCM a client message is not
1854 * sent for a resize, only a move.
1855 */
configure(int dx,int dy,unsigned int dw,unsigned int dh)1856 void BlackboxWindow::configure(int dx, int dy,
1857 unsigned int dw, unsigned int dh) {
1858 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1859 ! client.state.moving);
1860
1861 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1862 frame.rect.setRect(dx, dy, dw, dh);
1863
1864 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1865 frame.rect.setPos(0, 0);
1866
1867 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1868 frame.rect.top() + frame.margin.top,
1869 frame.rect.right() - frame.margin.right,
1870 frame.rect.bottom() - frame.margin.bottom);
1871
1872 #ifdef SHAPE
1873 if (client.state.shaped)
1874 configureShape();
1875 #endif // SHAPE
1876
1877 XMoveResizeWindow(blackbox->XDisplay(), frame.window,
1878 frame.rect.x(), frame.rect.y(),
1879 frame.rect.width(), frame.rect.height());
1880
1881 positionWindows();
1882 decorate();
1883 redrawWindowFrame();
1884 } else {
1885 frame.rect.setPos(dx, dy);
1886
1887 XMoveWindow(blackbox->XDisplay(), frame.window,
1888 frame.rect.x(), frame.rect.y());
1889 /*
1890 we may have been called just after an opaque window move, so
1891 even though the old coords match the new ones no ConfigureNotify
1892 has been sent yet. There are likely other times when this will
1893 be relevant as well.
1894 */
1895 if (! client.state.moving) send_event = True;
1896 }
1897
1898 if (send_event) {
1899 // if moving, the update and event will occur when the move finishes
1900 client.rect.setPos(frame.rect.left() + frame.margin.left,
1901 frame.rect.top() + frame.margin.top);
1902
1903 XEvent event;
1904 event.type = ConfigureNotify;
1905
1906 event.xconfigure.display = blackbox->XDisplay();
1907 event.xconfigure.event = client.window;
1908 event.xconfigure.window = client.window;
1909 event.xconfigure.x = client.rect.x();
1910 event.xconfigure.y = client.rect.y();
1911 event.xconfigure.width = client.rect.width();
1912 event.xconfigure.height = client.rect.height();
1913 event.xconfigure.border_width = client.old_bw;
1914 event.xconfigure.above = frame.window;
1915 event.xconfigure.override_redirect = False;
1916
1917 XSendEvent(blackbox->XDisplay(), client.window, False,
1918 StructureNotifyMask, &event);
1919 }
1920 }
1921
1922
1923 #ifdef SHAPE
configureShape(void)1924 void BlackboxWindow::configureShape(void) {
1925 XShapeCombineShape(blackbox->XDisplay(), frame.window, ShapeBounding,
1926 frame.margin.left, frame.margin.top,
1927 client.window, ShapeBounding, ShapeSet);
1928
1929 int num = 0;
1930 XRectangle xrect[2];
1931
1932 const WindowStyle &style = _screen->resource().windowStyle();
1933 if (client.decorations & WindowDecorationTitlebar) {
1934 xrect[0].x = xrect[0].y = 0;
1935 xrect[0].width = frame.rect.width();
1936 xrect[0].height = style.title_height;
1937 ++num;
1938 }
1939
1940 if (client.decorations & WindowDecorationHandle) {
1941 xrect[1].x = 0;
1942 xrect[1].y = client.rect.height() + frame.margin.top;
1943 xrect[1].width = frame.rect.width();
1944 xrect[1].height = style.handle_height;
1945 ++num;
1946 }
1947
1948 XShapeCombineRectangles(blackbox->XDisplay(), frame.window,
1949 ShapeBounding, 0, 0, xrect, num,
1950 ShapeUnion, Unsorted);
1951 }
1952 #endif // SHAPE
1953
1954
addTransient(BlackboxWindow * win)1955 void BlackboxWindow::addTransient(BlackboxWindow *win)
1956 { client.transientList.push_front(win); }
1957
1958
removeTransient(BlackboxWindow * win)1959 void BlackboxWindow::removeTransient(BlackboxWindow *win)
1960 { client.transientList.remove(win); }
1961
1962
findTransientFor(void) const1963 BlackboxWindow *BlackboxWindow::findTransientFor(void) const {
1964 BlackboxWindow *win = 0;
1965 if (isTransient()) {
1966 win = blackbox->findWindow(client.transient_for);
1967 if (win && win->_screen != _screen)
1968 win = 0;
1969 }
1970 return win;
1971 }
1972
1973
1974 /*
1975 walk up to either 1) a non-transient window 2) a group transient,
1976 watching out for a circular chain
1977
1978 this function returns zero for non-transient windows
1979 */
findNonTransientParent(void) const1980 BlackboxWindow *BlackboxWindow::findNonTransientParent(void) const {
1981 BlackboxWindowList seen;
1982 seen.push_back(const_cast<BlackboxWindow *>(this));
1983
1984 BlackboxWindow *w = findTransientFor();
1985 if (!w)
1986 return 0;
1987
1988 while (w->isTransient() && !w->isGroupTransient()) {
1989 seen.push_back(w);
1990 BlackboxWindow * const tmp = w->findTransientFor();
1991 if (!tmp)
1992 break;
1993 if (std::find(seen.begin(), seen.end(), tmp) != seen.end()) {
1994 // circular transient chain
1995 break;
1996 }
1997 w = tmp;
1998 }
1999 return w;
2000 }
2001
2002
2003 /*
2004 Returns a list of all transients. This is recursive, so it returns
2005 all transients of transients as well.
2006 */
buildFullTransientList(void) const2007 BlackboxWindowList BlackboxWindow::buildFullTransientList(void) const {
2008 BlackboxWindowList all = client.transientList;
2009 BlackboxWindowList::const_iterator it = client.transientList.begin(),
2010 end = client.transientList.end();
2011 for (; it != end; ++it) {
2012 BlackboxWindowList x = (*it)->buildFullTransientList();
2013 all.splice(all.end(), x);
2014 }
2015 return all;
2016 }
2017
2018
findWindowGroup(void) const2019 BWindowGroup *BlackboxWindow::findWindowGroup(void) const {
2020 BWindowGroup *group = 0;
2021 if (client.wmhints.window_group)
2022 group = blackbox->findWindowGroup(client.wmhints.window_group);
2023 return group;
2024 }
2025
2026
setWorkspace(unsigned int new_workspace)2027 void BlackboxWindow::setWorkspace(unsigned int new_workspace) {
2028 client.ewmh.workspace = new_workspace;
2029 blackbox->ewmh().setWMDesktop(client.window, client.ewmh.workspace);
2030 }
2031
2032
changeWorkspace(unsigned int new_workspace,ChangeWorkspaceOption how)2033 void BlackboxWindow::changeWorkspace(unsigned int new_workspace,
2034 ChangeWorkspaceOption how) {
2035 if (client.ewmh.workspace == new_workspace)
2036 return;
2037
2038 if (isTransient()) {
2039 BlackboxWindow *win = findTransientFor();
2040 if (win) {
2041 if (win->workspace() != new_workspace) {
2042 win->changeWorkspace(new_workspace, how);
2043 return;
2044 }
2045 }
2046 } else {
2047 assert(hasWindowFunction(WindowFunctionChangeWorkspace));
2048 }
2049
2050 Workspace *ws;
2051 if (workspace() != bt::BSENTINEL) {
2052 ws = _screen->findWorkspace(workspace());
2053 assert(ws != 0);
2054 ws->removeWindow(this);
2055 }
2056
2057 if (new_workspace != bt::BSENTINEL) {
2058 ws = _screen->findWorkspace(new_workspace);
2059 assert(ws != 0);
2060 ws->addWindow(this);
2061 }
2062
2063 switch (how) {
2064 case StayOnCurrentWorkspace:
2065 if (isVisible() && workspace() != bt::BSENTINEL
2066 && workspace() != _screen->currentWorkspace()) {
2067 hide();
2068 } else if (!isVisible()
2069 && (workspace() == bt::BSENTINEL
2070 || workspace() == _screen->currentWorkspace())) {
2071 show();
2072 }
2073 break;
2074
2075 case SwitchToNewWorkspace:
2076 /*
2077 we will change to the new workspace soon, so force this window
2078 to be visible
2079 */
2080 show();
2081 break;
2082 }
2083
2084 // change workspace on all transients
2085 if (!client.transientList.empty()) {
2086 BlackboxWindowList::iterator it = client.transientList.begin(),
2087 end = client.transientList.end();
2088 for (; it != end; ++it)
2089 (*it)->changeWorkspace(new_workspace, how);
2090 }
2091 }
2092
2093
changeLayer(StackingList::Layer new_layer)2094 void BlackboxWindow::changeLayer(StackingList::Layer new_layer) {
2095 if (layer() == new_layer)
2096 return;
2097
2098 bool restack = false;
2099 if (isTransient()) {
2100 BlackboxWindow *win = findTransientFor();
2101 if (win) {
2102 if (win->layer() != new_layer) {
2103 win->changeLayer(new_layer);
2104 return;
2105 } else {
2106 restack = true;
2107 }
2108 }
2109 } else {
2110 assert(hasWindowFunction(WindowFunctionChangeLayer));
2111 restack = true;
2112 }
2113
2114 _screen->stackingList().changeLayer(this, new_layer);
2115
2116 if (!client.transientList.empty()) {
2117 BlackboxWindowList::iterator it = client.transientList.begin();
2118 const BlackboxWindowList::iterator end = client.transientList.end();
2119 for (; it != end; ++it)
2120 (*it)->changeLayer(new_layer);
2121 }
2122
2123 if (restack)
2124 _screen->restackWindows();
2125 }
2126
2127
setInputFocus(void)2128 bool BlackboxWindow::setInputFocus(void) {
2129 if (!isVisible())
2130 return false;
2131 if (client.state.focused)
2132 return true;
2133
2134 switch (windowType()) {
2135 case WindowTypeDock:
2136 /*
2137 Many docks have auto-hide features similar to the toolbar and
2138 slit... we don't want these things to be moved to the center of
2139 the screen when switching to an empty workspace.
2140 */
2141 break;
2142
2143 default:
2144 { const bt::Rect &scr = _screen->screenInfo().rect();
2145 if (!frame.rect.intersects(scr)) {
2146 // client is outside the screen, move it to the center
2147 configure(scr.x() + (scr.width() - frame.rect.width()) / 2,
2148 scr.y() + (scr.height() - frame.rect.height()) / 2,
2149 frame.rect.width(), frame.rect.height());
2150 }
2151 break;
2152 }
2153 }
2154
2155 /*
2156 pass focus to any modal transients, giving modal group transients
2157 higher priority
2158 */
2159 BWindowGroup *group = findWindowGroup();
2160 if (group && !group->transients().empty()) {
2161 BlackboxWindowList::const_iterator it = group->transients().begin(),
2162 end = group->transients().end();
2163 for (; it != end; ++it) {
2164 BlackboxWindow * const tmp = *it;
2165 if (!tmp->isVisible() || !tmp->isModal())
2166 continue;
2167 if (tmp == this) {
2168 // we are the newest modal group transient
2169 break;
2170 }
2171 if (isTransient()) {
2172 if (tmp == findNonTransientParent()) {
2173 // we are a transient of the modal group transient
2174 break;
2175 }
2176 }
2177 return tmp->setInputFocus();
2178 }
2179 }
2180
2181 if (!client.transientList.empty()) {
2182 BlackboxWindowList::const_iterator it = client.transientList.begin(),
2183 end = client.transientList.end();
2184 for (; it != end; ++it) {
2185 BlackboxWindow * const tmp = *it;
2186 if (tmp->isVisible() && tmp->isModal())
2187 return tmp->setInputFocus();
2188 }
2189 }
2190
2191 switch (windowType()) {
2192 case WindowTypeDock:
2193 return false;
2194 default:
2195 break;
2196 }
2197
2198 XSetInputFocus(blackbox->XDisplay(), client.window,
2199 RevertToPointerRoot, blackbox->XTime());
2200
2201 if (client.wmprotocols.wm_take_focus) {
2202 XEvent ce;
2203 ce.xclient.type = ClientMessage;
2204 ce.xclient.message_type = blackbox->wmProtocolsAtom();
2205 ce.xclient.display = blackbox->XDisplay();
2206 ce.xclient.window = client.window;
2207 ce.xclient.format = 32;
2208 ce.xclient.data.l[0] = blackbox->wmTakeFocusAtom();
2209 ce.xclient.data.l[1] = blackbox->XTime();
2210 ce.xclient.data.l[2] = 0l;
2211 ce.xclient.data.l[3] = 0l;
2212 ce.xclient.data.l[4] = 0l;
2213 XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
2214 }
2215
2216 return true;
2217 }
2218
2219
show(void)2220 void BlackboxWindow::show(void) {
2221 if (client.state.visible)
2222 return;
2223
2224 if (client.state.iconic)
2225 _screen->removeIcon(this);
2226
2227 client.state.iconic = false;
2228 client.state.visible = true;
2229 setState(isShaded() ? IconicState : NormalState);
2230
2231 XMapWindow(blackbox->XDisplay(), client.window);
2232 XMapSubwindows(blackbox->XDisplay(), frame.window);
2233 XMapWindow(blackbox->XDisplay(), frame.window);
2234
2235 if (!client.transientList.empty()) {
2236 BlackboxWindowList::iterator it = client.transientList.begin(),
2237 end = client.transientList.end();
2238 for (; it != end; ++it)
2239 (*it)->show();
2240 }
2241
2242 #ifdef DEBUG
2243 int real_x, real_y;
2244 Window child;
2245 XTranslateCoordinates(blackbox->XDisplay(), client.window,
2246 _screen->screenInfo().rootWindow(),
2247 0, 0, &real_x, &real_y, &child);
2248 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", title().c_str(),
2249 client.rect.left(), client.rect.top(), real_x, real_y);
2250 assert(client.rect.left() == real_x && client.rect.top() == real_y);
2251 #endif
2252 }
2253
2254
hide(void)2255 void BlackboxWindow::hide(void) {
2256 if (!client.state.visible)
2257 return;
2258
2259 client.state.visible = false;
2260 setState(client.state.iconic ? IconicState : client.current_state);
2261
2262 XUnmapWindow(blackbox->XDisplay(), frame.window);
2263
2264 /*
2265 * we don't want this XUnmapWindow call to generate an UnmapNotify
2266 * event, so we need to clear the event mask on client.window for a
2267 * split second. HOWEVER, since X11 is asynchronous, the window
2268 * could be destroyed in that split second, leaving us with a ghost
2269 * window... so, we need to do this while the X server is grabbed
2270 */
2271 blackbox->XGrabServer();
2272 XSelectInput(blackbox->XDisplay(), client.window,
2273 client_window_event_mask & ~StructureNotifyMask);
2274 XUnmapWindow(blackbox->XDisplay(), client.window);
2275 XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
2276 blackbox->XUngrabServer();
2277 }
2278
2279
close(void)2280 void BlackboxWindow::close(void) {
2281 assert(hasWindowFunction(WindowFunctionClose));
2282
2283 XEvent ce;
2284 ce.xclient.type = ClientMessage;
2285 ce.xclient.message_type = blackbox->wmProtocolsAtom();
2286 ce.xclient.display = blackbox->XDisplay();
2287 ce.xclient.window = client.window;
2288 ce.xclient.format = 32;
2289 ce.xclient.data.l[0] = blackbox->wmDeleteWindowAtom();
2290 ce.xclient.data.l[1] = blackbox->XTime();
2291 ce.xclient.data.l[2] = 0l;
2292 ce.xclient.data.l[3] = 0l;
2293 ce.xclient.data.l[4] = 0l;
2294 XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
2295 }
2296
2297
activate(void)2298 void BlackboxWindow::activate(void) {
2299 if (workspace() != bt::BSENTINEL
2300 && workspace() != _screen->currentWorkspace())
2301 _screen->setCurrentWorkspace(workspace());
2302 if (client.state.iconic)
2303 show();
2304 if (client.ewmh.shaded)
2305 setShaded(false);
2306 if (setInputFocus())
2307 _screen->raiseWindow(this);
2308 }
2309
2310
iconify(void)2311 void BlackboxWindow::iconify(void) {
2312 if (client.state.iconic)
2313 return;
2314
2315 if (isTransient()) {
2316 BlackboxWindow *win = findTransientFor();
2317 if (win) {
2318 if (!win->isIconic()) {
2319 win->iconify();
2320 return;
2321 }
2322 }
2323 } else {
2324 assert(hasWindowFunction(WindowFunctionIconify));
2325 }
2326
2327 _screen->addIcon(this);
2328
2329 client.state.iconic = true;
2330 hide();
2331
2332 // iconify all transients
2333 if (!client.transientList.empty()) {
2334 BlackboxWindowList::iterator it = client.transientList.begin(),
2335 end = client.transientList.end();
2336 for (; it != end; ++it)
2337 (*it)->iconify();
2338 }
2339 }
2340
2341
maximize(unsigned int button)2342 void BlackboxWindow::maximize(unsigned int button) {
2343 assert(hasWindowFunction(WindowFunctionMaximize));
2344
2345 // any maximize operation always unshades
2346 client.ewmh.shaded = false;
2347 frame.rect.setHeight(client.rect.height() + frame.margin.top
2348 + frame.margin.bottom);
2349
2350 if (isMaximized()) {
2351 // restore from maximized
2352 client.ewmh.maxh = client.ewmh.maxv = false;
2353
2354 if (!isFullScreen()) {
2355 /*
2356 when a resize is begun, maximize(0) is called to clear any
2357 maximization flags currently set. Otherwise it still thinks
2358 it is maximized. so we do not need to call configure()
2359 because resizing will handle it
2360 */
2361 if (! client.state.resizing) {
2362 bt::Rect r = ::applyGravity(client.premax,
2363 frame.margin,
2364 client.wmnormal.win_gravity);
2365 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2366 // trick configure into working
2367 frame.rect = bt::Rect();
2368 configure(r);
2369 }
2370
2371 redrawAllButtons(); // in case it is not called in configure()
2372 }
2373
2374 updateEWMHState();
2375 updateEWMHAllowedActions();
2376 return;
2377 }
2378
2379 switch (button) {
2380 case 1:
2381 client.ewmh.maxh = true;
2382 client.ewmh.maxv = true;
2383 break;
2384
2385 case 2:
2386 client.ewmh.maxh = false;
2387 client.ewmh.maxv = true;
2388 break;
2389
2390 case 3:
2391 client.ewmh.maxh = true;
2392 client.ewmh.maxv = false;
2393 break;
2394
2395 default:
2396 assert(0);
2397 break;
2398 }
2399
2400 if (!isFullScreen()) {
2401 // go go gadget-maximize!
2402 bt::Rect r = _screen->availableArea();
2403
2404 if (!client.ewmh.maxh) {
2405 r.setX(frame.rect.x());
2406 r.setWidth(frame.rect.width());
2407 }
2408 if (!client.ewmh.maxv) {
2409 r.setY(frame.rect.y());
2410 r.setHeight(frame.rect.height());
2411 }
2412
2413 // store the current frame geometry, so that we can restore it later
2414 client.premax = ::restoreGravity(frame.rect,
2415 frame.margin,
2416 client.wmnormal.win_gravity);
2417
2418 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2419 // trick configure into working
2420 frame.rect = bt::Rect();
2421 configure(r);
2422 }
2423
2424 updateEWMHState();
2425 updateEWMHAllowedActions();
2426 }
2427
2428
2429 /*
2430 re-maximizes the window to take into account availableArea changes.
2431
2432 note that unlike maximize(), the shaded state is preserved.
2433 */
remaximize(void)2434 void BlackboxWindow::remaximize(void) {
2435 if (isShaded()) {
2436 bt::Rect r = _screen->availableArea();
2437
2438 if (!client.ewmh.maxh) {
2439 r.setX(frame.rect.x());
2440 r.setWidth(frame.rect.width());
2441 }
2442 if (!client.ewmh.maxv) {
2443 r.setY(frame.rect.y());
2444 r.setHeight(frame.rect.height());
2445 }
2446
2447 frame.rect = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2448
2449 positionWindows();
2450 decorate();
2451
2452 // set the frame rect to the shaded size
2453 const WindowStyle &style = _screen->resource().windowStyle();
2454 frame.rect.setHeight(style.title_height);
2455 XResizeWindow(blackbox->XDisplay(), frame.window,
2456 frame.rect.width(), frame.rect.height());
2457 return;
2458 }
2459
2460 unsigned int button = 0u;
2461 if (client.ewmh.maxv) {
2462 button = (client.ewmh.maxh) ? 1u : 2u;
2463 } else if (client.ewmh.maxh) {
2464 button = (client.ewmh.maxv) ? 1u : 3u;
2465 }
2466
2467 // trick maximize() into working
2468 client.ewmh.maxh = client.ewmh.maxv = false;
2469 const bt::Rect tmp = client.premax;
2470 maximize(button);
2471 client.premax = tmp;
2472 }
2473
2474
setShaded(bool shaded)2475 void BlackboxWindow::setShaded(bool shaded) {
2476 assert(hasWindowFunction(WindowFunctionShade));
2477
2478 if (client.ewmh.shaded == shaded)
2479 return;
2480
2481 client.ewmh.shaded = shaded;
2482 if (!isShaded()) {
2483 if (isMaximized()) {
2484 remaximize();
2485 } else {
2486 // set the frame rect to the normal size
2487 frame.rect.setHeight(client.rect.height() + frame.margin.top +
2488 frame.margin.bottom);
2489
2490 XResizeWindow(blackbox->XDisplay(), frame.window,
2491 frame.rect.width(), frame.rect.height());
2492 }
2493
2494 setState(NormalState);
2495 } else {
2496 // set the frame rect to the shaded size
2497 const WindowStyle &style = _screen->resource().windowStyle();
2498 frame.rect.setHeight(style.title_height);
2499
2500 XResizeWindow(blackbox->XDisplay(), frame.window,
2501 frame.rect.width(), frame.rect.height());
2502
2503 setState(IconicState);
2504 }
2505 }
2506
2507
setFullScreen(bool b)2508 void BlackboxWindow::setFullScreen(bool b) {
2509 assert(hasWindowFunction(WindowFunctionFullScreen));
2510
2511 if (client.ewmh.fullscreen == b)
2512 return;
2513
2514 // any fullscreen operation always unshades
2515 client.ewmh.shaded = false;
2516 frame.rect.setHeight(client.rect.height() + frame.margin.top
2517 + frame.margin.bottom);
2518
2519 bool refocus = isFocused();
2520 client.ewmh.fullscreen = b;
2521 if (isFullScreen()) {
2522 // go go gadget-fullscreen!
2523 if (!isMaximized())
2524 client.premax = ::restoreGravity(frame.rect,
2525 frame.margin,
2526 client.wmnormal.win_gravity);
2527
2528 // modify decorations, functions and frame margin
2529 client.decorations = NoWindowDecorations;
2530 client.functions &= ~(WindowFunctionMove |
2531 WindowFunctionResize |
2532 WindowFunctionShade);
2533 const WindowStyle &style = _screen->resource().windowStyle();
2534 frame.margin = ::update_margin(client.decorations, style);
2535
2536 /*
2537 * Note: we don't call ::constrain() here, because many
2538 * applications are broken in this respect. Some specify a
2539 * max-size or aspect-ratio that simply doesn't cover the entire
2540 * screen. Let's try to be smarter than such applications and
2541 * simply cover the entire screen.
2542 */
2543
2544 // trick configure() into working
2545 frame.rect = bt::Rect();
2546 configure(_screen->screenInfo().rect());
2547
2548 if (isVisible())
2549 changeLayer(StackingList::LayerFullScreen);
2550
2551 updateEWMHState();
2552 updateEWMHAllowedActions();
2553 } else {
2554 // restore from fullscreen
2555 ::update_decorations(client.decorations,
2556 client.functions,
2557 isTransient(),
2558 client.ewmh,
2559 client.motif,
2560 client.wmnormal,
2561 client.wmprotocols);
2562 const WindowStyle &style = _screen->resource().windowStyle();
2563 frame.margin = ::update_margin(client.decorations, style);
2564
2565 if (isVisible())
2566 changeLayer(StackingList::LayerNormal);
2567
2568 if (isMaximized()) {
2569 remaximize();
2570 } else {
2571 bt::Rect r = ::applyGravity(client.premax,
2572 frame.margin,
2573 client.wmnormal.win_gravity);
2574 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2575
2576 // trick configure into working
2577 frame.rect = bt::Rect();
2578 configure(r);
2579
2580 updateEWMHState();
2581 updateEWMHAllowedActions();
2582 }
2583 }
2584
2585 ungrabButtons();
2586 grabButtons();
2587
2588 if (refocus)
2589 (void) setInputFocus();
2590 }
2591
2592
redrawWindowFrame(void) const2593 void BlackboxWindow::redrawWindowFrame(void) const {
2594 if (client.decorations & WindowDecorationTitlebar) {
2595 redrawTitle();
2596 redrawLabel();
2597 redrawAllButtons();
2598 }
2599
2600 if (client.decorations & WindowDecorationBorder) {
2601 const WindowStyle &style = _screen->resource().windowStyle();
2602 const bt::Color &c = (isFocused()
2603 ? style.focus.frame_border
2604 : style.unfocus.frame_border);
2605 XSetWindowBorder(blackbox->XDisplay(), frame.plate,
2606 c.pixel(_screen->screenNumber()));
2607 }
2608
2609 if (client.decorations & WindowDecorationHandle) {
2610 redrawHandle();
2611
2612 if (client.decorations & WindowDecorationGrip)
2613 redrawGrips();
2614 }
2615 }
2616
2617
setFocused(bool focused)2618 void BlackboxWindow::setFocused(bool focused) {
2619 if (focused == client.state.focused)
2620 return;
2621
2622 client.state.focused = isVisible() ? focused : false;
2623
2624 if (isVisible()) {
2625 redrawWindowFrame();
2626
2627 if (client.state.focused) {
2628 XInstallColormap(blackbox->XDisplay(), client.colormap);
2629 } else {
2630 if (client.ewmh.fullscreen && layer() != StackingList::LayerBelow)
2631 changeLayer(StackingList::LayerBelow);
2632 }
2633 }
2634 }
2635
2636
setState(unsigned long new_state)2637 void BlackboxWindow::setState(unsigned long new_state) {
2638 client.current_state = new_state;
2639
2640 unsigned long state[2];
2641 state[0] = client.current_state;
2642 state[1] = None;
2643 XChangeProperty(blackbox->XDisplay(), client.window,
2644 blackbox->wmStateAtom(), blackbox->wmStateAtom(), 32,
2645 PropModeReplace, (unsigned char *) state, 2);
2646
2647 updateEWMHState();
2648 updateEWMHAllowedActions();
2649 }
2650
2651
updateEWMHState()2652 void BlackboxWindow::updateEWMHState() {
2653 const bt::EWMH& ewmh = blackbox->ewmh();
2654
2655 // set _NET_WM_STATE
2656 bt::EWMH::AtomList atoms;
2657 if (isModal())
2658 atoms.push_back(ewmh.wmStateModal());
2659 if (isShaded())
2660 atoms.push_back(ewmh.wmStateShaded());
2661 if (isIconic())
2662 atoms.push_back(ewmh.wmStateHidden());
2663 if (isFullScreen())
2664 atoms.push_back(ewmh.wmStateFullscreen());
2665 if (client.ewmh.maxh)
2666 atoms.push_back(ewmh.wmStateMaximizedHorz());
2667 if (client.ewmh.maxv)
2668 atoms.push_back(ewmh.wmStateMaximizedVert());
2669 if (client.ewmh.skip_taskbar)
2670 atoms.push_back(ewmh.wmStateSkipTaskbar());
2671 if (client.ewmh.skip_pager)
2672 atoms.push_back(ewmh.wmStateSkipPager());
2673
2674 switch (layer()) {
2675 case StackingList::LayerAbove:
2676 atoms.push_back(ewmh.wmStateAbove());
2677 break;
2678 case StackingList::LayerBelow:
2679 atoms.push_back(ewmh.wmStateBelow());
2680 break;
2681 default:
2682 break;
2683 }
2684
2685 if (atoms.empty())
2686 ewmh.removeProperty(client.window, ewmh.wmState());
2687 else
2688 ewmh.setWMState(client.window, atoms);
2689 }
2690
2691
updateEWMHAllowedActions()2692 void BlackboxWindow::updateEWMHAllowedActions() {
2693 const bt::EWMH& ewmh = blackbox->ewmh();
2694
2695 // set _NET_WM_ALLOWED_ACTIONS
2696 bt::EWMH::AtomList atoms;
2697 if (! client.state.iconic) {
2698 if (hasWindowFunction(WindowFunctionChangeWorkspace))
2699 atoms.push_back(ewmh.wmActionChangeDesktop());
2700
2701 if (hasWindowFunction(WindowFunctionIconify))
2702 atoms.push_back(ewmh.wmActionMinimize());
2703
2704 if (hasWindowFunction(WindowFunctionShade))
2705 atoms.push_back(ewmh.wmActionShade());
2706
2707 if (hasWindowFunction(WindowFunctionMove))
2708 atoms.push_back(ewmh.wmActionMove());
2709
2710 if (hasWindowFunction(WindowFunctionResize))
2711 atoms.push_back(ewmh.wmActionResize());
2712
2713 if (hasWindowFunction(WindowFunctionMaximize)) {
2714 atoms.push_back(ewmh.wmActionMaximizeHorz());
2715 atoms.push_back(ewmh.wmActionMaximizeVert());
2716 }
2717
2718 atoms.push_back(ewmh.wmActionFullscreen());
2719 }
2720
2721 if (hasWindowFunction(WindowFunctionClose))
2722 atoms.push_back(ewmh.wmActionClose());
2723
2724 if (atoms.empty())
2725 ewmh.removeProperty(client.window, ewmh.wmAllowedActions());
2726 else
2727 ewmh.setWMAllowedActions(client.window, atoms);
2728 }
2729
2730
redrawTitle(void) const2731 void BlackboxWindow::redrawTitle(void) const {
2732 const WindowStyle &style = _screen->resource().windowStyle();
2733 const bt::Rect u(0, 0, frame.rect.width(), style.title_height);
2734 bt::drawTexture(_screen->screenNumber(),
2735 (client.state.focused
2736 ? style.focus.title
2737 : style.unfocus.title),
2738 frame.title, u, u,
2739 (client.state.focused
2740 ? frame.ftitle
2741 : frame.utitle));
2742 }
2743
2744
redrawLabel(void) const2745 void BlackboxWindow::redrawLabel(void) const {
2746 const WindowStyle &style = _screen->resource().windowStyle();
2747 bt::Rect u(0, 0, frame.label_w, style.label_height);
2748 Pixmap p = (client.state.focused ? frame.flabel : frame.ulabel);
2749 if (p == ParentRelative) {
2750 const bt::Texture &texture =
2751 (isFocused() ? style.focus.title : style.unfocus.title);
2752 int offset = texture.borderWidth();
2753 if (client.decorations & WindowDecorationIconify)
2754 offset += style.button_width + style.title_margin;
2755
2756 const bt::Rect t(-(style.title_margin + offset),
2757 -(style.title_margin + texture.borderWidth()),
2758 frame.rect.width(), style.title_height);
2759 bt::drawTexture(_screen->screenNumber(), texture, frame.label, t, u,
2760 (client.state.focused ? frame.ftitle : frame.utitle));
2761 } else {
2762 bt::drawTexture(_screen->screenNumber(),
2763 (client.state.focused
2764 ? style.focus.label
2765 : style.unfocus.label),
2766 frame.label, u, u, p);
2767 }
2768
2769 const bt::Pen pen(_screen->screenNumber(),
2770 ((client.state.focused)
2771 ? style.focus.text
2772 : style.unfocus.text));
2773 u.setCoords(u.left() + style.label_margin,
2774 u.top() + style.label_margin,
2775 u.right() - style.label_margin,
2776 u.bottom() - style.label_margin);
2777 bt::drawText(style.font, pen, frame.label, u,
2778 style.alignment, client.visible_title);
2779 }
2780
2781
redrawAllButtons(void) const2782 void BlackboxWindow::redrawAllButtons(void) const {
2783 if (frame.iconify_button) redrawIconifyButton();
2784 if (frame.maximize_button) redrawMaximizeButton();
2785 if (frame.close_button) redrawCloseButton();
2786 }
2787
2788
redrawIconifyButton(bool pressed) const2789 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2790 const WindowStyle &style = _screen->resource().windowStyle();
2791 const bt::Rect u(0, 0, style.button_width, style.button_width);
2792 Pixmap p = (pressed ? frame.pbutton :
2793 (client.state.focused ? frame.fbutton : frame.ubutton));
2794 if (p == ParentRelative) {
2795 const bt::Texture &texture =
2796 (isFocused() ? style.focus.title : style.unfocus.title);
2797 const bt::Rect t(-(style.title_margin + texture.borderWidth()),
2798 -(style.title_margin + texture.borderWidth()),
2799 frame.rect.width(), style.title_height);
2800 bt::drawTexture(_screen->screenNumber(), texture, frame.iconify_button,
2801 t, u, (client.state.focused
2802 ? frame.ftitle
2803 : frame.utitle));
2804 } else {
2805 bt::drawTexture(_screen->screenNumber(),
2806 (pressed ? style.pressed :
2807 (client.state.focused ? style.focus.button :
2808 style.unfocus.button)),
2809 frame.iconify_button, u, u, p);
2810 }
2811
2812 const bt::Pen pen(_screen->screenNumber(),
2813 (client.state.focused
2814 ? style.focus.foreground
2815 : style.unfocus.foreground));
2816 bt::drawBitmap(style.iconify, pen, frame.iconify_button, u);
2817 }
2818
2819
redrawMaximizeButton(bool pressed) const2820 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2821 const WindowStyle &style = _screen->resource().windowStyle();
2822 const bt::Rect u(0, 0, style.button_width, style.button_width);
2823 Pixmap p = (pressed ? frame.pbutton :
2824 (client.state.focused ? frame.fbutton : frame.ubutton));
2825 if (p == ParentRelative) {
2826 const bt::Texture &texture =
2827 (isFocused() ? style.focus.title : style.unfocus.title);
2828 int button_w = style.button_width
2829 + style.title_margin + texture.borderWidth();
2830 if (client.decorations & WindowDecorationClose)
2831 button_w *= 2;
2832 const bt::Rect t(-(frame.rect.width() - button_w),
2833 -(style.title_margin + texture.borderWidth()),
2834 frame.rect.width(), style.title_height);
2835 bt::drawTexture(_screen->screenNumber(), texture, frame.maximize_button,
2836 t, u, (client.state.focused
2837 ? frame.ftitle
2838 : frame.utitle));
2839 } else {
2840 bt::drawTexture(_screen->screenNumber(),
2841 (pressed ? style.pressed :
2842 (client.state.focused ? style.focus.button :
2843 style.unfocus.button)),
2844 frame.maximize_button, u, u, p);
2845 }
2846
2847 const bt::Pen pen(_screen->screenNumber(),
2848 (client.state.focused
2849 ? style.focus.foreground
2850 : style.unfocus.foreground));
2851 bt::drawBitmap(isMaximized() ? style.restore : style.maximize,
2852 pen, frame.maximize_button, u);
2853 }
2854
2855
redrawCloseButton(bool pressed) const2856 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2857 const WindowStyle &style = _screen->resource().windowStyle();
2858 const bt::Rect u(0, 0, style.button_width, style.button_width);
2859 Pixmap p = (pressed ? frame.pbutton :
2860 (client.state.focused ? frame.fbutton : frame.ubutton));
2861 if (p == ParentRelative) {
2862 const bt::Texture &texture =
2863 (isFocused() ? style.focus.title : style.unfocus.title);
2864 const int button_w = style.button_width +
2865 style.title_margin +
2866 texture.borderWidth();
2867 const bt::Rect t(-(frame.rect.width() - button_w),
2868 -(style.title_margin + texture.borderWidth()),
2869 frame.rect.width(), style.title_height);
2870 bt::drawTexture(_screen->screenNumber(),texture, frame.close_button, t, u,
2871 (client.state.focused ? frame.ftitle : frame.utitle));
2872 } else {
2873 bt::drawTexture(_screen->screenNumber(),
2874 (pressed ? style.pressed :
2875 (client.state.focused ? style.focus.button :
2876 style.unfocus.button)),
2877 frame.close_button, u, u, p);
2878 }
2879
2880 const bt::Pen pen(_screen->screenNumber(),
2881 (client.state.focused
2882 ? style.focus.foreground
2883 : style.unfocus.foreground));
2884 bt::drawBitmap(style.close, pen, frame.close_button, u);
2885 }
2886
2887
redrawHandle(void) const2888 void BlackboxWindow::redrawHandle(void) const {
2889 const WindowStyle &style = _screen->resource().windowStyle();
2890 const bt::Rect u(0, 0, frame.rect.width(), style.handle_height);
2891 bt::drawTexture(_screen->screenNumber(),
2892 (client.state.focused ? style.focus.handle :
2893 style.unfocus.handle),
2894 frame.handle, u, u,
2895 (client.state.focused ? frame.fhandle : frame.uhandle));
2896 }
2897
2898
redrawGrips(void) const2899 void BlackboxWindow::redrawGrips(void) const {
2900 const WindowStyle &style = _screen->resource().windowStyle();
2901 const bt::Rect u(0, 0, style.grip_width, style.handle_height);
2902 Pixmap p = (client.state.focused ? frame.fgrip : frame.ugrip);
2903 if (p == ParentRelative) {
2904 bt::Rect t(0, 0, frame.rect.width(), style.handle_height);
2905 bt::drawTexture(_screen->screenNumber(),
2906 (client.state.focused ? style.focus.handle :
2907 style.unfocus.handle),
2908 frame.right_grip, t, u, p);
2909
2910 t.setPos(-(frame.rect.width() - style.grip_width), 0);
2911 bt::drawTexture(_screen->screenNumber(),
2912 (client.state.focused ? style.focus.handle :
2913 style.unfocus.handle),
2914 frame.right_grip, t, u, p);
2915 } else {
2916 bt::drawTexture(_screen->screenNumber(),
2917 (client.state.focused ? style.focus.grip :
2918 style.unfocus.grip),
2919 frame.left_grip, u, u, p);
2920
2921 bt::drawTexture(_screen->screenNumber(),
2922 (client.state.focused ? style.focus.grip :
2923 style.unfocus.grip),
2924 frame.right_grip, u, u, p);
2925 }
2926 }
2927
2928
2929 void
clientMessageEvent(const XClientMessageEvent * const event)2930 BlackboxWindow::clientMessageEvent(const XClientMessageEvent * const event) {
2931 if (event->format != 32)
2932 return;
2933
2934 const bt::EWMH& ewmh = blackbox->ewmh();
2935
2936 if (event->message_type == blackbox->wmChangeStateAtom()) {
2937 if (event->data.l[0] == IconicState) {
2938 if (hasWindowFunction(WindowFunctionIconify))
2939 iconify();
2940 } else if (event->data.l[0] == NormalState) {
2941 activate();
2942 }
2943 } else if (event->message_type == ewmh.activeWindow()) {
2944 activate();
2945 } else if (event->message_type == ewmh.closeWindow()) {
2946 if (hasWindowFunction(WindowFunctionClose))
2947 close();
2948 } else if (event->message_type == ewmh.moveresizeWindow()) {
2949 XConfigureRequestEvent request;
2950 request.window = event->window;
2951 request.value_mask =
2952 (event->data.l[0] >> 8) & (CWX | CWY | CWWidth | CWHeight);
2953 request.x = event->data.l[1];
2954 request.y = event->data.l[2];
2955 request.width = event->data.l[3];
2956 request.height = event->data.l[4];
2957
2958 const int gravity = (event->data.l[0] & 0xff);
2959 const int old_gravity = client.wmnormal.win_gravity;
2960 if (event->data.l[0] != 0)
2961 client.wmnormal.win_gravity = gravity;
2962
2963 configureRequestEvent(&request);
2964
2965 client.wmnormal.win_gravity = old_gravity;
2966 } else if (event->message_type == ewmh.wmDesktop()) {
2967 if (hasWindowFunction(WindowFunctionChangeWorkspace)) {
2968 const unsigned int new_workspace = event->data.l[0];
2969 changeWorkspace(new_workspace);
2970 }
2971 } else if (event->message_type == ewmh.wmState()) {
2972 Atom action = event->data.l[0],
2973 first = event->data.l[1],
2974 second = event->data.l[2];
2975
2976 if (first == ewmh.wmStateModal() || second == ewmh.wmStateModal()) {
2977 if ((action == ewmh.wmStateAdd() ||
2978 (action == ewmh.wmStateToggle() && ! client.ewmh.modal)) &&
2979 isTransient())
2980 client.ewmh.modal = true;
2981 else
2982 client.ewmh.modal = false;
2983 }
2984
2985 if (hasWindowFunction(WindowFunctionMaximize)) {
2986 int max_horz = 0, max_vert = 0;
2987
2988 if (first == ewmh.wmStateMaximizedHorz() ||
2989 second == ewmh.wmStateMaximizedHorz()) {
2990 max_horz = ((action == ewmh.wmStateAdd()
2991 || (action == ewmh.wmStateToggle()
2992 && !client.ewmh.maxh))
2993 ? 1 : -1);
2994 }
2995
2996 if (first == ewmh.wmStateMaximizedVert() ||
2997 second == ewmh.wmStateMaximizedVert()) {
2998 max_vert = ((action == ewmh.wmStateAdd()
2999 || (action == ewmh.wmStateToggle()
3000 && !client.ewmh.maxv))
3001 ? 1 : -1);
3002 }
3003
3004 if (max_horz != 0 || max_vert != 0) {
3005 if (isMaximized())
3006 maximize(0);
3007 unsigned int button = 0u;
3008 if (max_horz == 1 && max_vert != 1)
3009 button = 3u;
3010 else if (max_vert == 1 && max_horz != 1)
3011 button = 2u;
3012 else if (max_vert == 1 && max_horz == 1)
3013 button = 1u;
3014 if (button)
3015 maximize(button);
3016 }
3017 }
3018
3019 if (hasWindowFunction(WindowFunctionShade)) {
3020 if (first == ewmh.wmStateShaded() ||
3021 second == ewmh.wmStateShaded()) {
3022 if (action == ewmh.wmStateRemove())
3023 setShaded(false);
3024 else if (action == ewmh.wmStateAdd())
3025 setShaded(true);
3026 else if (action == ewmh.wmStateToggle())
3027 setShaded(!isShaded());
3028 }
3029 }
3030
3031 if (first == ewmh.wmStateSkipTaskbar()
3032 || second == ewmh.wmStateSkipTaskbar()
3033 || first == ewmh.wmStateSkipPager()
3034 || second == ewmh.wmStateSkipPager()) {
3035 if (first == ewmh.wmStateSkipTaskbar()
3036 || second == ewmh.wmStateSkipTaskbar()) {
3037 client.ewmh.skip_taskbar = (action == ewmh.wmStateAdd()
3038 || (action == ewmh.wmStateToggle()
3039 && !client.ewmh.skip_taskbar));
3040 }
3041 if (first == ewmh.wmStateSkipPager()
3042 || second == ewmh.wmStateSkipPager()) {
3043 client.ewmh.skip_pager = (action == ewmh.wmStateAdd()
3044 || (action == ewmh.wmStateToggle()
3045 && !client.ewmh.skip_pager));
3046 }
3047 // we do nothing with skip_*, but others might... we should at
3048 // least make sure these are present in _NET_WM_STATE
3049 updateEWMHState();
3050 }
3051
3052 if (first == ewmh.wmStateHidden() ||
3053 second == ewmh.wmStateHidden()) {
3054 /*
3055 ignore _NET_WM_STATE_HIDDEN, the wm sets this state, not the
3056 application
3057 */
3058 }
3059
3060 if (hasWindowFunction(WindowFunctionFullScreen)) {
3061 if (first == ewmh.wmStateFullscreen() ||
3062 second == ewmh.wmStateFullscreen()) {
3063 if (action == ewmh.wmStateAdd() ||
3064 (action == ewmh.wmStateToggle() &&
3065 ! client.ewmh.fullscreen)) {
3066 setFullScreen(true);
3067 } else if (action == ewmh.wmStateToggle() ||
3068 action == ewmh.wmStateRemove()) {
3069 setFullScreen(false);
3070 }
3071 }
3072 }
3073
3074 if (hasWindowFunction(WindowFunctionChangeLayer)) {
3075 if (first == ewmh.wmStateAbove() ||
3076 second == ewmh.wmStateAbove()) {
3077 if (action == ewmh.wmStateAdd() ||
3078 (action == ewmh.wmStateToggle() &&
3079 layer() != StackingList::LayerAbove)) {
3080 changeLayer(StackingList::LayerAbove);
3081 } else if (action == ewmh.wmStateToggle() ||
3082 action == ewmh.wmStateRemove()) {
3083 changeLayer(StackingList::LayerNormal);
3084 }
3085 }
3086
3087 if (first == ewmh.wmStateBelow() ||
3088 second == ewmh.wmStateBelow()) {
3089 if (action == ewmh.wmStateAdd() ||
3090 (action == ewmh.wmStateToggle() &&
3091 layer() != StackingList::LayerBelow)) {
3092 changeLayer(StackingList::LayerBelow);
3093 } else if (action == ewmh.wmStateToggle() ||
3094 action == ewmh.wmStateRemove()) {
3095 changeLayer(StackingList::LayerNormal);
3096 }
3097 }
3098 }
3099 }
3100 }
3101
3102
unmapNotifyEvent(const XUnmapEvent * const event)3103 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent * const event) {
3104 if (event->window != client.window)
3105 return;
3106
3107 #ifdef DEBUG
3108 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
3109 client.window);
3110 #endif // DEBUG
3111
3112 _screen->releaseWindow(this);
3113 }
3114
3115
3116 void
destroyNotifyEvent(const XDestroyWindowEvent * const event)3117 BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent * const event) {
3118 if (event->window != client.window)
3119 return;
3120
3121 #ifdef DEBUG
3122 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
3123 client.window);
3124 #endif // DEBUG
3125
3126 _screen->releaseWindow(this);
3127 }
3128
3129
reparentNotifyEvent(const XReparentEvent * const event)3130 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent * const event) {
3131 if (event->window != client.window || event->parent == frame.plate)
3132 return;
3133
3134 #ifdef DEBUG
3135 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
3136 "0x%lx.\n", client.window, event->parent);
3137 #endif // DEBUG
3138
3139 /*
3140 put the ReparentNotify event back into the queue so that
3141 BlackboxWindow::restore(void) can do the right thing
3142 */
3143 XEvent replay;
3144 replay.xreparent = *event;
3145 XPutBackEvent(blackbox->XDisplay(), &replay);
3146
3147 _screen->releaseWindow(this);
3148 }
3149
3150
propertyNotifyEvent(const XPropertyEvent * const event)3151 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent * const event) {
3152 #ifdef DEBUG
3153 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
3154 client.window);
3155 #endif
3156
3157 switch(event->atom) {
3158 case XA_WM_TRANSIENT_FOR: {
3159 if (isTransient()) {
3160 // remove ourselves from our transient_for
3161 BlackboxWindow *win = findTransientFor();
3162 if (win) {
3163 win->removeTransient(this);
3164 } else if (isGroupTransient()) {
3165 BWindowGroup *group = findWindowGroup();
3166 if (group)
3167 group->removeTransient(this);
3168 }
3169 }
3170
3171 // determine if this is a transient window
3172 client.transient_for = ::readTransientInfo(blackbox,
3173 client.window,
3174 _screen->screenInfo(),
3175 client.wmhints);
3176
3177 if (isTransient()) {
3178 BlackboxWindow *win = findTransientFor();
3179 if (win) {
3180 // add ourselves to our new transient_for
3181 win->addTransient(this);
3182 changeWorkspace(win->workspace());
3183 changeLayer(win->layer());
3184 } else if (isGroupTransient()) {
3185 BWindowGroup *group = findWindowGroup();
3186 if (group)
3187 group->addTransient(this);
3188 } else {
3189 // broken client
3190 client.transient_for = 0;
3191 }
3192 }
3193
3194 ::update_decorations(client.decorations,
3195 client.functions,
3196 isTransient(),
3197 client.ewmh,
3198 client.motif,
3199 client.wmnormal,
3200 client.wmprotocols);
3201
3202 reconfigure();
3203 break;
3204 }
3205
3206 case XA_WM_HINTS: {
3207 // remove from current window group
3208 BWindowGroup *group = findWindowGroup();
3209 if (group) {
3210 if (isTransient() && !findTransientFor() && isGroupTransient())
3211 group->removeTransient(this);
3212 group->removeWindow(this);
3213 group = 0;
3214 }
3215
3216 client.wmhints = ::readWMHints(blackbox, client.window);
3217
3218 if (client.wmhints.window_group != None) {
3219 // add to new window group
3220 group = ::update_window_group(client.wmhints.window_group,
3221 blackbox,
3222 this);
3223 if (isTransient() && !findTransientFor() && isGroupTransient()) {
3224 if (group)
3225 group->addTransient(this);
3226 }
3227 }
3228 break;
3229 }
3230
3231 case XA_WM_ICON_NAME: {
3232 client.icon_title = ::readWMIconName(blackbox, client.window);
3233 if (client.state.iconic)
3234 _screen->propagateWindowName(this);
3235 break;
3236 }
3237
3238 case XA_WM_NAME: {
3239 client.title = ::readWMName(blackbox, client.window);
3240
3241 client.visible_title =
3242 bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
3243 _screen->screenNumber(),
3244 _screen->resource().windowStyle().font);
3245 blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
3246
3247 if (client.decorations & WindowDecorationTitlebar)
3248 redrawLabel();
3249
3250 _screen->propagateWindowName(this);
3251 break;
3252 }
3253
3254 case XA_WM_NORMAL_HINTS: {
3255 WMNormalHints wmnormal = ::readWMNormalHints(blackbox, client.window,
3256 _screen->screenInfo());
3257 if (wmnormal == client.wmnormal) {
3258 // apps like xv and GNU emacs seem to like to repeatedly set
3259 // this property over and over
3260 break;
3261 }
3262
3263 client.wmnormal = wmnormal;
3264
3265 ::update_decorations(client.decorations,
3266 client.functions,
3267 isTransient(),
3268 client.ewmh,
3269 client.motif,
3270 client.wmnormal,
3271 client.wmprotocols);
3272
3273 reconfigure();
3274 break;
3275 }
3276
3277 default: {
3278 if (event->atom == blackbox->wmProtocolsAtom()) {
3279 client.wmprotocols = ::readWMProtocols(blackbox, client.window);
3280
3281 ::update_decorations(client.decorations,
3282 client.functions,
3283 isTransient(),
3284 client.ewmh,
3285 client.motif,
3286 client.wmnormal,
3287 client.wmprotocols);
3288
3289 reconfigure();
3290 } else if (event->atom == blackbox->motifWmHintsAtom()) {
3291 client.motif = ::readMotifWMHints(blackbox, client.window);
3292
3293 ::update_decorations(client.decorations,
3294 client.functions,
3295 isTransient(),
3296 client.ewmh,
3297 client.motif,
3298 client.wmnormal,
3299 client.wmprotocols);
3300
3301 reconfigure();
3302 } else if (event->atom == blackbox->ewmh().wmStrut()) {
3303 if (! client.strut) {
3304 client.strut = new bt::EWMH::Strut;
3305 _screen->addStrut(client.strut);
3306 }
3307
3308 blackbox->ewmh().readWMStrut(client.window, client.strut);
3309 if (client.strut->left || client.strut->right ||
3310 client.strut->top || client.strut->bottom) {
3311 _screen->updateStrut();
3312 } else {
3313 _screen->removeStrut(client.strut);
3314 delete client.strut;
3315 }
3316 }
3317
3318 break;
3319 }
3320 } // switch
3321 }
3322
3323
exposeEvent(const XExposeEvent * const event)3324 void BlackboxWindow::exposeEvent(const XExposeEvent * const event) {
3325 #ifdef DEBUG
3326 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
3327 #endif
3328
3329 if (frame.title == event->window)
3330 redrawTitle();
3331 else if (frame.label == event->window)
3332 redrawLabel();
3333 else if (frame.close_button == event->window)
3334 redrawCloseButton();
3335 else if (frame.maximize_button == event->window)
3336 redrawMaximizeButton();
3337 else if (frame.iconify_button == event->window)
3338 redrawIconifyButton();
3339 else if (frame.handle == event->window)
3340 redrawHandle();
3341 else if (frame.left_grip == event->window ||
3342 frame.right_grip == event->window)
3343 redrawGrips();
3344 }
3345
3346
configureRequestEvent(const XConfigureRequestEvent * const event)3347 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *
3348 const event) {
3349 if (event->window != client.window || client.state.iconic)
3350 return;
3351
3352 if (event->value_mask & CWBorderWidth)
3353 client.old_bw = event->border_width;
3354
3355 if (event->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
3356 bt::Rect req = frame.rect;
3357
3358 if (event->value_mask & (CWX | CWY)) {
3359 req = ::restoreGravity(req, frame.margin, client.wmnormal.win_gravity);
3360
3361 if (event->value_mask & CWX)
3362 req.setX(event->x);
3363 if (event->value_mask & CWY)
3364 req.setY(event->y);
3365
3366 req = ::applyGravity(req, frame.margin, client.wmnormal.win_gravity);
3367 }
3368
3369 if (event->value_mask & (CWWidth | CWHeight)) {
3370 if (event->value_mask & CWWidth)
3371 req.setWidth(event->width + frame.margin.left + frame.margin.right);
3372 if (event->value_mask & CWHeight)
3373 req.setHeight(event->height + frame.margin.top + frame.margin.bottom);
3374 }
3375
3376 configure(req);
3377 }
3378
3379 if (event->value_mask & CWStackMode) {
3380 switch (event->detail) {
3381 case Below:
3382 case BottomIf:
3383 _screen->lowerWindow(this);
3384 break;
3385
3386 case Above:
3387 case TopIf:
3388 default:
3389 _screen->raiseWindow(this);
3390 break;
3391 }
3392 }
3393 }
3394
3395
buttonPressEvent(const XButtonEvent * const event)3396 void BlackboxWindow::buttonPressEvent(const XButtonEvent * const event) {
3397 #ifdef DEBUG
3398 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3399 client.window);
3400 #endif
3401
3402 if (frame.maximize_button == event->window) {
3403 if (event->button < 4)
3404 redrawMaximizeButton(true);
3405 } else if (frame.iconify_button == event->window) {
3406 if (event->button == 1)
3407 redrawIconifyButton(true);
3408 } else if (frame.close_button == event->window) {
3409 if (event->button == 1)
3410 redrawCloseButton(true);
3411 } else {
3412 if (event->button == 1
3413 || (event->button == 3 && event->state == Mod1Mask)) {
3414 frame.grab_x = event->x_root - frame.rect.x();
3415 frame.grab_y = event->y_root - frame.rect.y();
3416
3417 _screen->raiseWindow(this);
3418
3419 if (! client.state.focused)
3420 (void) setInputFocus();
3421 else
3422 XInstallColormap(blackbox->XDisplay(), client.colormap);
3423
3424 if (frame.plate == event->window) {
3425 XAllowEvents(blackbox->XDisplay(), ReplayPointer, event->time);
3426 } else if ((frame.title == event->window
3427 || frame.label == event->window)
3428 && hasWindowFunction(WindowFunctionShade)) {
3429 if ((event->time - lastButtonPressTime <=
3430 blackbox->resource().doubleClickInterval()) ||
3431 event->state == ControlMask) {
3432 lastButtonPressTime = 0;
3433 setShaded(!isShaded());
3434 } else {
3435 lastButtonPressTime = event->time;
3436 }
3437 }
3438 } else if (event->button == 2) {
3439 _screen->lowerWindow(this);
3440 } else if (event->button == 3
3441 || (event->button == 3 && event->state == Mod4Mask)) {
3442 const int extra = _screen->resource().windowStyle().frame_border_width;
3443 const bt::Rect rect(client.rect.x() - extra,
3444 client.rect.y() - extra,
3445 client.rect.width() + (extra * 2),
3446 client.rect.height() + (extra * 2));
3447
3448 Windowmenu *windowmenu = _screen->windowmenu(this);
3449 windowmenu->popup(event->x_root, event->y_root, rect);
3450 } else if (blackbox->resource().shadeWindowWithMouseWheel()) {
3451 if (event->button == 4
3452 && hasWindowFunction(WindowFunctionShade)
3453 && !isShaded()) {
3454 setShaded(true);
3455 } else if (event->button == 5
3456 && hasWindowFunction(WindowFunctionShade)
3457 && isShaded()) {
3458 setShaded(false);
3459 }
3460 }
3461 }
3462 }
3463
3464
buttonReleaseEvent(const XButtonEvent * const event)3465 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent * const event) {
3466 #ifdef DEBUG
3467 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3468 client.window);
3469 #endif
3470
3471 const WindowStyle &style = _screen->resource().windowStyle();
3472 if (event->window == frame.maximize_button) {
3473 if (event->button < 4) {
3474 if (bt::within(event->x, event->y,
3475 style.button_width, style.button_width)) {
3476 maximize(event->button);
3477 _screen->raiseWindow(this);
3478 } else {
3479 redrawMaximizeButton();
3480 }
3481 }
3482 } else if (event->window == frame.iconify_button) {
3483 if (event->button == 1) {
3484 if (bt::within(event->x, event->y,
3485 style.button_width, style.button_width))
3486 iconify();
3487 else
3488 redrawIconifyButton();
3489 }
3490 } else if (event->window == frame.close_button) {
3491 if (event->button == 1) {
3492 if (bt::within(event->x, event->y,
3493 style.button_width, style.button_width))
3494 close();
3495 redrawCloseButton();
3496 }
3497 } else if (client.state.moving) {
3498 finishMove();
3499 } else if (client.state.resizing) {
3500 finishResize();
3501 } else if (event->window == frame.window) {
3502 if (event->button == 2 && event->state == Mod1Mask)
3503 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
3504 }
3505 }
3506
3507
motionNotifyEvent(const XMotionEvent * const event)3508 void BlackboxWindow::motionNotifyEvent(const XMotionEvent * const event) {
3509 #ifdef DEBUG
3510 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3511 client.window);
3512 #endif
3513
3514 if (hasWindowFunction(WindowFunctionMove)
3515 && !client.state.resizing
3516 && event->state & Button1Mask
3517 && (frame.title == event->window || frame.label == event->window
3518 || frame.handle == event->window || frame.window == event->window)) {
3519 if (! client.state.moving)
3520 startMove();
3521 else
3522 continueMove(event->x_root, event->y_root);
3523 } else if (hasWindowFunction(WindowFunctionResize)
3524 && (event->state & Button1Mask
3525 && (event->window == frame.right_grip
3526 || event->window == frame.left_grip))
3527 || (event->state & Button3Mask
3528 && event->state & Mod1Mask
3529 && event->window == frame.window)) {
3530 if (!client.state.resizing)
3531 startResize(event->window);
3532 else
3533 continueResize(event->x_root, event->y_root);
3534 }
3535 }
3536
3537
enterNotifyEvent(const XCrossingEvent * const event)3538 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent * const event) {
3539 if (event->window != frame.window || event->mode != NotifyNormal)
3540 return;
3541
3542 if (blackbox->resource().focusModel() == ClickToFocusModel || !isVisible())
3543 return;
3544
3545 switch (windowType()) {
3546 case WindowTypeDesktop:
3547 case WindowTypeDock:
3548 // these types cannot be focused w/ sloppy focus
3549 return;
3550
3551 default:
3552 break;
3553 }
3554
3555 XEvent next;
3556 bool leave = False, inferior = False;
3557
3558 while (XCheckTypedWindowEvent(blackbox->XDisplay(), event->window,
3559 LeaveNotify, &next)) {
3560 if (next.type == LeaveNotify && next.xcrossing.mode == NotifyNormal) {
3561 leave = True;
3562 inferior = (next.xcrossing.detail == NotifyInferior);
3563 }
3564 }
3565
3566 if ((! leave || inferior) && ! isFocused())
3567 (void) setInputFocus();
3568
3569 if (blackbox->resource().autoRaise())
3570 timer->start();
3571 }
3572
3573
3574 void
leaveNotifyEvent(const XCrossingEvent * const)3575 BlackboxWindow::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
3576 if (!(blackbox->resource().focusModel() == SloppyFocusModel
3577 && blackbox->resource().autoRaise()))
3578 return;
3579
3580 if (timer->isTiming())
3581 timer->stop();
3582 }
3583
3584
3585 #ifdef SHAPE
shapeEvent(const XEvent * const)3586 void BlackboxWindow::shapeEvent(const XEvent * const /*unused*/)
3587 { if (client.state.shaped) configureShape(); }
3588 #endif // SHAPE
3589
3590
3591 /*
3592 *
3593 */
restore(void)3594 void BlackboxWindow::restore(void) {
3595 XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeDelete);
3596 XSelectInput(blackbox->XDisplay(), client.window, NoEventMask);
3597 XSelectInput(blackbox->XDisplay(), frame.plate, NoEventMask);
3598
3599 client.state.visible = false;
3600
3601 /*
3602 remove WM_STATE unless the we are shutting down (in which case we
3603 want to make sure we preserve the state across restarts).
3604 */
3605 if (!blackbox->shuttingDown()) {
3606 clearState(blackbox, client.window);
3607 } else if (isShaded() && !isIconic()) {
3608 // do not leave a shaded window as an icon unless it was an icon
3609 setState(NormalState);
3610 }
3611
3612 client.rect = ::restoreGravity(frame.rect, frame.margin,
3613 client.wmnormal.win_gravity);
3614
3615 blackbox->XGrabServer();
3616
3617 XUnmapWindow(blackbox->XDisplay(), frame.window);
3618 XUnmapWindow(blackbox->XDisplay(), client.window);
3619
3620 XSetWindowBorderWidth(blackbox->XDisplay(), client.window, client.old_bw);
3621 if (isMaximized()) {
3622 // preserve the original size
3623 client.rect = client.premax;
3624 XMoveResizeWindow(blackbox->XDisplay(), client.window,
3625 client.premax.x(),
3626 client.premax.y(),
3627 client.premax.width(),
3628 client.premax.height());
3629 } else {
3630 XMoveWindow(blackbox->XDisplay(), client.window,
3631 client.rect.x() - frame.rect.x(),
3632 client.rect.y() - frame.rect.y());
3633 }
3634
3635 blackbox->XUngrabServer();
3636
3637 XEvent unused;
3638 if (!XCheckTypedWindowEvent(blackbox->XDisplay(), client.window,
3639 ReparentNotify, &unused)) {
3640 /*
3641 according to the ICCCM, the window manager is responsible for
3642 reparenting the window back to root... however, we don't want to
3643 do this if the window has been reparented by someone else
3644 (i.e. not us).
3645 */
3646 XReparentWindow(blackbox->XDisplay(), client.window,
3647 _screen->screenInfo().rootWindow(),
3648 client.rect.x(), client.rect.y());
3649 }
3650
3651 if (blackbox->shuttingDown())
3652 XMapWindow(blackbox->XDisplay(), client.window);
3653 }
3654
3655
3656 // timer for autoraise
timeout(bt::Timer *)3657 void BlackboxWindow::timeout(bt::Timer *)
3658 { _screen->raiseWindow(this); }
3659
3660
startMove()3661 void BlackboxWindow::startMove() {
3662 // begin a move
3663 XGrabPointer(blackbox->XDisplay(), frame.window, false,
3664 Button1MotionMask | ButtonReleaseMask,
3665 GrabModeAsync, GrabModeAsync, None,
3666 blackbox->resource().cursors().move, blackbox->XTime());
3667
3668 client.state.moving = true;
3669
3670 if (! blackbox->resource().opaqueMove()) {
3671 blackbox->XGrabServer();
3672
3673 frame.changing = frame.rect;
3674 _screen->showGeometry(BScreen::Position, frame.changing);
3675
3676 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3677 const int bw = _screen->resource().windowStyle().frame_border_width,
3678 hw = bw / 2;
3679 pen.setGCFunction(GXxor);
3680 pen.setLineWidth(bw);
3681 pen.setSubWindowMode(IncludeInferiors);
3682 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3683 pen.gc(),
3684 frame.changing.x() + hw,
3685 frame.changing.y() + hw,
3686 frame.changing.width() - bw,
3687 frame.changing.height() - bw);
3688 }
3689 }
3690
3691
3692 static
collisionAdjust(int * dx,int * dy,int x,int y,unsigned int width,unsigned int height,const bt::Rect & rect,int snap_distance,bool snapCenter=false)3693 void collisionAdjust(int *dx, int *dy, int x, int y,
3694 unsigned int width, unsigned int height,
3695 const bt::Rect& rect, int snap_distance,
3696 bool snapCenter = false)
3697 {
3698 // window corners
3699 const int wleft = x,
3700 wright = x + width - 1,
3701 wtop = y,
3702 wbottom = y + height - 1,
3703 // left, right, top + bottom are for rect, douterleft = left border of rect
3704 dinnerleft = std::abs(wleft - rect.left()),
3705 dinnerright = std::abs(wright - rect.right()),
3706 dinnertop = std::abs(wtop - rect.top()),
3707 dinnerbottom = std::abs(wbottom - rect.bottom()),
3708 douterleft = std::abs(wright - rect.left()),
3709 douterright = std::abs(wleft - rect.right()),
3710 doutertop = std::abs(wbottom - rect.top()),
3711 douterbottom = std::abs(wtop - rect.bottom());
3712
3713 if ((wtop <= rect.bottom() && wbottom >= rect.top())
3714 || doutertop <= snap_distance
3715 || douterbottom <= snap_distance) {
3716 // snap left or right
3717 if (douterleft <= dinnerleft && douterleft <= snap_distance)
3718 // snap outer left
3719 *dx = (x - (rect.left() - width));
3720 else if (douterright <= dinnerright && douterright <= snap_distance)
3721 // snap outer right
3722 *dx = (x - rect.right() - 1);
3723 else if (dinnerleft <= dinnerright && dinnerleft < snap_distance)
3724 // snap inner left
3725 *dx = (x - rect.left());
3726 else if (dinnerright < snap_distance)
3727 // snap inner right
3728 *dx = (x - (rect.right() - width + 1));
3729 }
3730
3731 if ((wleft <= rect.right() && wright >= rect.left())
3732 || douterleft <= snap_distance
3733 || douterright <= snap_distance) {
3734 // snap top or bottom
3735 if (doutertop <= dinnertop && doutertop <= snap_distance)
3736 // snap outer top
3737 *dy = (y - (rect.top() - height));
3738 else if (douterbottom <= dinnerbottom && douterbottom <= snap_distance)
3739 // snap outer bottom
3740 *dy = (y - rect.bottom() - 1);
3741 else if (dinnertop <= dinnerbottom && dinnertop < snap_distance)
3742 // snap inner top
3743 *dy = (y - rect.top());
3744 else if (dinnerbottom < snap_distance)
3745 // snap inner bottom
3746 *dy = (y - (rect.bottom() - height + 1));
3747 }
3748
3749 if (snapCenter) {
3750 const int cwx = x + width / 2;
3751 const int cwy = y + height / 2;
3752 const int crx = rect.x() + rect.width() / 2;
3753 const int cry = rect.y() + rect.height() / 2;
3754 const int cdx = std::abs(cwx - crx);
3755 const int cdy = std::abs(cwy - cry);
3756 if (cdx <= snap_distance)
3757 // snap to horizontal center
3758 *dx = x - (rect.x() + ((rect.width() - width) / 2));
3759 if (cdy <= snap_distance)
3760 // snap to vertical center
3761 *dy = y - (rect.y() + ((rect.height() - height) / 2));
3762 }
3763 }
3764
3765
snapAdjust(int * x,int * y)3766 void BlackboxWindow::snapAdjust(int *x, int *y) {
3767 int nx, ny, dx, dy, init_dx, init_dy;
3768 const int edge_distance = blackbox->resource().edgeSnapThreshold();
3769 const int win_distance = blackbox->resource().windowSnapThreshold();
3770
3771 nx = (win_distance > edge_distance) ? win_distance : edge_distance;
3772 ny = (win_distance > edge_distance) ? win_distance : edge_distance;
3773 dx = init_dx = ++nx; dy = init_dy = ++ny;
3774
3775 if (edge_distance) {
3776 collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(), frame.rect.height(),
3777 _screen->availableArea(), edge_distance, true);
3778 nx = (dx != init_dx && std::abs(dx) < std::abs(nx)) ? dx : nx; dx = init_dx;
3779 ny = (dy != init_dy && std::abs(dy) < std::abs(ny)) ? dy : ny; dy = init_dy;
3780 if (!blackbox->resource().fullMaximization()) {
3781 collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(), frame.rect.height(),
3782 _screen->screenInfo().rect(), edge_distance);
3783 nx = (dx != init_dx && std::abs(dx) < std::abs(nx)) ? dx : nx; dx = init_dx;
3784 ny = (dy != init_dy && std::abs(dy) < std::abs(ny)) ? dy : ny; dy = init_dy;
3785 }
3786 }
3787 if (win_distance) {
3788 StackingList::const_iterator it = _screen->stackingList().begin(),
3789 end = _screen->stackingList().end();
3790 for (; it != end; ++it) {
3791 BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
3792 if (win && win != this &&
3793 win->workspace() == _screen->currentWorkspace()) {
3794 collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(),
3795 frame.rect.height(), win->frame.rect, win_distance);
3796 nx = (dx != init_dx && std::abs(dx) < std::abs(nx)) ? dx : nx; dx = init_dx;
3797 ny = (dy != init_dy && std::abs(dy) < std::abs(ny)) ? dy : ny; dy = init_dy;
3798 }
3799 }
3800 }
3801
3802 *x = (nx != init_dx) ? (*x - nx) : *x;
3803 *y = (ny != init_dy) ? (*y - ny) : *y;
3804 }
3805
3806
continueMove(int x_root,int y_root)3807 void BlackboxWindow::continueMove(int x_root, int y_root) {
3808 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3809
3810 snapAdjust(&dx, &dy);
3811
3812 if (blackbox->resource().opaqueMove()) {
3813 configure(dx, dy, frame.rect.width(), frame.rect.height());
3814 } else {
3815 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3816 const int bw = _screen->resource().windowStyle().frame_border_width,
3817 hw = bw / 2;
3818 pen.setGCFunction(GXxor);
3819 pen.setLineWidth(bw);
3820 pen.setSubWindowMode(IncludeInferiors);
3821 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3822 pen.gc(),
3823 frame.changing.x() + hw,
3824 frame.changing.y() + hw,
3825 frame.changing.width() - bw,
3826 frame.changing.height() - bw);
3827
3828 frame.changing.setPos(dx, dy);
3829
3830 XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
3831 pen.gc(),
3832 frame.changing.x() + hw,
3833 frame.changing.y() + hw,
3834 frame.changing.width() - bw,
3835 frame.changing.height() - bw);
3836 }
3837
3838 _screen->showGeometry(BScreen::Position, bt::Rect(dx, dy, 0, 0));
3839 }
3840
3841
finishMove()3842 void BlackboxWindow::finishMove() {
3843 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
3844
3845 client.state.moving = false;
3846
3847 if (!blackbox->resource().opaqueMove()) {
3848 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3849 const int bw = _screen->resource().windowStyle().frame_border_width,
3850 hw = bw / 2;
3851 pen.setGCFunction(GXxor);
3852 pen.setLineWidth(bw);
3853 pen.setSubWindowMode(IncludeInferiors);
3854 XDrawRectangle(blackbox->XDisplay(),
3855 _screen->screenInfo().rootWindow(),
3856 pen.gc(),
3857 frame.changing.x() + hw,
3858 frame.changing.y() + hw,
3859 frame.changing.width() - bw,
3860 frame.changing.height() - bw);
3861 blackbox->XUngrabServer();
3862
3863 configure(frame.changing);
3864 } else {
3865 configure(frame.rect);
3866 }
3867
3868 _screen->hideGeometry();
3869 }
3870
3871
startResize(Window window)3872 void BlackboxWindow::startResize(Window window) {
3873 if (frame.grab_x < (signed) frame.rect.width() / 2) {
3874 if (frame.grab_y < (signed) frame.rect.height() / 2)
3875 frame.corner = BottomRight;
3876 else
3877 frame.corner = TopRight;
3878 } else {
3879 if (frame.grab_y < (signed) frame.rect.height() / 2)
3880 frame.corner = BottomLeft;
3881 else
3882 frame.corner = TopLeft;
3883 }
3884
3885 Cursor cursor = None;
3886 switch (frame.corner) {
3887 case TopLeft:
3888 cursor = blackbox->resource().cursors().resize_bottom_right;
3889 frame.grab_x = frame.rect.width() - frame.grab_x;
3890 frame.grab_y = frame.rect.height() - frame.grab_y;
3891 break;
3892 case BottomLeft:
3893 cursor = blackbox->resource().cursors().resize_top_right;
3894 frame.grab_x = frame.rect.width() - frame.grab_x;
3895 break;
3896 case TopRight:
3897 cursor = blackbox->resource().cursors().resize_bottom_left;
3898 frame.grab_y = frame.rect.height() - frame.grab_y;
3899 break;
3900 case BottomRight:
3901 cursor = blackbox->resource().cursors().resize_top_left;
3902 break;
3903 }
3904
3905 // begin a resize
3906 XGrabPointer(blackbox->XDisplay(), window, False,
3907 ButtonMotionMask | ButtonReleaseMask,
3908 GrabModeAsync, GrabModeAsync, None, cursor, blackbox->XTime());
3909
3910 client.state.resizing = true;
3911
3912 frame.changing = constrain(frame.rect, frame.margin, client.wmnormal,
3913 Corner(frame.corner));
3914
3915 if (!blackbox->resource().opaqueResize()) {
3916 blackbox->XGrabServer();
3917
3918 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3919 const int bw = _screen->resource().windowStyle().frame_border_width,
3920 hw = bw / 2;
3921 pen.setGCFunction(GXxor);
3922 pen.setLineWidth(bw);
3923 pen.setSubWindowMode(IncludeInferiors);
3924 XDrawRectangle(blackbox->XDisplay(),
3925 _screen->screenInfo().rootWindow(),
3926 pen.gc(),
3927 frame.changing.x() + hw,
3928 frame.changing.y() + hw,
3929 frame.changing.width() - bw,
3930 frame.changing.height() - bw);
3931 } else {
3932 // unset maximized state when resized
3933 if (isMaximized())
3934 maximize(0);
3935 }
3936
3937 showGeometry(frame.changing);
3938 }
3939
3940
continueResize(int x_root,int y_root)3941 void BlackboxWindow::continueResize(int x_root, int y_root) {
3942 // continue a resize
3943 const bt::Rect curr = frame.changing;
3944
3945 switch (frame.corner) {
3946 case TopLeft:
3947 case BottomLeft:
3948 frame.changing.setCoords(frame.changing.left(),
3949 frame.changing.top(),
3950 std::max<signed>(x_root + frame.grab_x,
3951 frame.changing.left()
3952 + (frame.margin.left
3953 + frame.margin.right + 1)),
3954 frame.changing.bottom());
3955 break;
3956 case TopRight:
3957 case BottomRight:
3958 frame.changing.setCoords(std::min<signed>(x_root - frame.grab_x,
3959 frame.changing.right()
3960 - (frame.margin.left
3961 + frame.margin.right + 1)),
3962 frame.changing.top(),
3963 frame.changing.right(),
3964 frame.changing.bottom());
3965 break;
3966 }
3967
3968 switch (frame.corner) {
3969 case TopLeft:
3970 case TopRight:
3971 frame.changing.setCoords(frame.changing.left(),
3972 frame.changing.top(),
3973 frame.changing.right(),
3974 std::max<signed>(y_root + frame.grab_y,
3975 frame.changing.top()
3976 + (frame.margin.top
3977 + frame.margin.bottom + 1)));
3978 break;
3979 case BottomLeft:
3980 case BottomRight:
3981 frame.changing.setCoords(frame.changing.left(),
3982 std::min<signed>(y_root - frame.grab_y,
3983 frame.rect.bottom()
3984 - (frame.margin.top
3985 + frame.margin.bottom + 1)),
3986 frame.changing.right(),
3987 frame.changing.bottom());
3988 break;
3989 }
3990
3991 frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
3992 Corner(frame.corner));
3993
3994 if (curr != frame.changing) {
3995 if (blackbox->resource().opaqueResize()) {
3996 configure(frame.changing);
3997 } else {
3998 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3999 const int bw = _screen->resource().windowStyle().frame_border_width,
4000 hw = bw / 2;
4001 pen.setGCFunction(GXxor);
4002 pen.setLineWidth(bw);
4003 pen.setSubWindowMode(IncludeInferiors);
4004 XDrawRectangle(blackbox->XDisplay(),
4005 _screen->screenInfo().rootWindow(),
4006 pen.gc(),
4007 curr.x() + hw,
4008 curr.y() + hw,
4009 curr.width() - bw,
4010 curr.height() - bw);
4011
4012 XDrawRectangle(blackbox->XDisplay(),
4013 _screen->screenInfo().rootWindow(),
4014 pen.gc(),
4015 frame.changing.x() + hw,
4016 frame.changing.y() + hw,
4017 frame.changing.width() - bw,
4018 frame.changing.height() - bw);
4019 }
4020
4021 showGeometry(frame.changing);
4022 }
4023 }
4024
4025
finishResize()4026 void BlackboxWindow::finishResize() {
4027
4028 if (!blackbox->resource().opaqueResize()) {
4029 bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
4030 const int bw = _screen->resource().windowStyle().frame_border_width,
4031 hw = bw / 2;
4032 pen.setGCFunction(GXxor);
4033 pen.setLineWidth(bw);
4034 pen.setSubWindowMode(IncludeInferiors);
4035 XDrawRectangle(blackbox->XDisplay(),
4036 _screen->screenInfo().rootWindow(),
4037 pen.gc(),
4038 frame.changing.x() + hw,
4039 frame.changing.y() + hw,
4040 frame.changing.width() - bw,
4041 frame.changing.height() - bw);
4042
4043 blackbox->XUngrabServer();
4044
4045 // unset maximized state when resized
4046 if (isMaximized())
4047 maximize(0);
4048 }
4049
4050 client.state.resizing = false;
4051
4052 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
4053
4054 _screen->hideGeometry();
4055
4056 frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
4057 Corner(frame.corner));
4058 configure(frame.changing);
4059 }
4060
4061
4062 /*
4063 * show the geometry of the window based on rectangle r.
4064 * The logical width and height are used here. This refers to the user's
4065 * perception of the window size (for example an xterm resizes in cells,
4066 * not in pixels). No extra work is needed if there is no difference between
4067 * the logical and actual dimensions.
4068 */
showGeometry(const bt::Rect & r) const4069 void BlackboxWindow::showGeometry(const bt::Rect &r) const {
4070 unsigned int w = r.width(), h = r.height();
4071
4072 // remove the window frame
4073 w -= frame.margin.left + frame.margin.right;
4074 h -= frame.margin.top + frame.margin.bottom;
4075
4076 if (client.wmnormal.flags & PResizeInc) {
4077 if (client.wmnormal.flags & (PMinSize|PBaseSize)) {
4078 w -= ((client.wmnormal.base_width)
4079 ? client.wmnormal.base_width
4080 : client.wmnormal.min_width);
4081 h -= ((client.wmnormal.base_height)
4082 ? client.wmnormal.base_height
4083 : client.wmnormal.min_height);
4084 }
4085
4086 w /= client.wmnormal.width_inc;
4087 h /= client.wmnormal.height_inc;
4088 }
4089
4090 _screen->showGeometry(BScreen::Size, bt::Rect(0, 0, w, h));
4091 }
4092
4093
4094 // see my rant above for an explanation of this operator
operator ==(const WMNormalHints & x,const WMNormalHints & y)4095 bool operator==(const WMNormalHints &x, const WMNormalHints &y) {
4096 return (x.flags == y.flags
4097 && x.min_width == y.min_width
4098 && x.min_height == y.min_height
4099 && x.max_width == y.max_width
4100 && x.max_height == y.max_height
4101 && x.width_inc == y.width_inc
4102 && x.height_inc == y.height_inc
4103 && x.min_aspect_x == y.min_aspect_x
4104 && x.min_aspect_y == y.min_aspect_y
4105 && x.max_aspect_x == y.max_aspect_x
4106 && x.max_aspect_y == y.max_aspect_y
4107 && x.base_width == y.base_width
4108 && x.base_height == y.base_height
4109 && x.win_gravity == y.win_gravity);
4110 }
4111