avoid dangling pointers by setting them to zero after deleting
[blackbox.git] / src / Window.cc
blobc61a30eb71427ef96a38e9a9683e2e9f2e219e25
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:
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
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.
25 // make sure we get bt::textPropertyToString()
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
29 #include "Window.hh"
30 #include "Screen.hh"
31 #include "WindowGroup.hh"
32 #include "Windowmenu.hh"
33 #include "Workspace.hh"
34 #include "blackbox.hh"
36 #include <Pen.hh>
37 #include <PixmapCache.hh>
38 #include <Unicode.hh>
40 #include <X11/Xatom.h>
41 #ifdef SHAPE
42 # include <X11/extensions/shape.h>
43 #endif
45 #include <assert.h>
48 sometimes C++ is a pain in the ass... it gives us stuff like the
49 default copy constructor and assignment operator (which does member
50 my member copy/assignment), but what we don't get is a default
51 comparison operator... how fucked up is that?
53 the language is designed to handle this:
55 struct foo { int a; int b };
56 foo a = { 0 , 0 }, b = a;
57 foo c;
58 c = a;
60 BUT, BUT, BUT, forget about doing this:
62 a = findFoo();
63 if (c == a)
64 return; // nothing to do
65 c = a;
67 ARGH!@#)(!*@#)(@#*$(!@#
69 bool operator==(const WMNormalHints &x, const WMNormalHints &y);
72 // Event mask used for managed client windows.
73 const unsigned long client_window_event_mask =
74 (PropertyChangeMask | StructureNotifyMask);
78 * Returns the appropriate WindowType based on the _NET_WM_WINDOW_TYPE
80 static WindowType window_type_from_atom(const bt::EWMH &ewmh, Atom atom) {
81 if (atom == ewmh.wmWindowTypeDialog())
82 return WindowTypeDialog;
83 if (atom == ewmh.wmWindowTypeDesktop())
84 return WindowTypeDesktop;
85 if (atom == ewmh.wmWindowTypeDock())
86 return WindowTypeDock;
87 if (atom == ewmh.wmWindowTypeMenu())
88 return WindowTypeMenu;
89 if (atom == ewmh.wmWindowTypeSplash())
90 return WindowTypeSplash;
91 if (atom == ewmh.wmWindowTypeToolbar())
92 return WindowTypeToolbar;
93 if (atom == ewmh.wmWindowTypeUtility())
94 return WindowTypeUtility;
95 return WindowTypeNormal;
100 * Determine the appropriate decorations and functions based on the
101 * given properties and hints.
103 static void update_decorations(WindowDecorationFlags &decorations,
104 WindowFunctionFlags &functions,
105 bool transient,
106 const EWMH &ewmh,
107 const MotifHints &motifhints,
108 const WMNormalHints &wmnormal,
109 const WMProtocols &wmprotocols) {
110 decorations = AllWindowDecorations;
111 functions = AllWindowFunctions;
113 // transients should be kept on the same workspace are their parents
114 if (transient)
115 functions &= ~WindowFunctionChangeWorkspace;
117 // modify the window decorations/functions based on window type
118 switch (ewmh.window_type) {
119 case WindowTypeDialog:
120 decorations &= ~(WindowDecorationIconify |
121 WindowDecorationMaximize);
122 functions &= ~(WindowFunctionShade |
123 WindowFunctionIconify |
124 WindowFunctionMaximize |
125 WindowFunctionChangeLayer |
126 WindowFunctionFullScreen);
127 break;
129 case WindowTypeDesktop:
130 case WindowTypeDock:
131 case WindowTypeSplash:
132 decorations = NoWindowDecorations;
133 functions = NoWindowFunctions;
134 break;
136 case WindowTypeToolbar:
137 case WindowTypeMenu:
138 decorations &= ~(WindowDecorationHandle |
139 WindowDecorationGrip |
140 WindowDecorationBorder |
141 WindowDecorationIconify |
142 WindowDecorationMaximize);
143 functions &= ~(WindowFunctionResize |
144 WindowFunctionShade |
145 WindowFunctionIconify |
146 WindowFunctionMaximize |
147 WindowFunctionFullScreen);
148 break;
150 case WindowTypeUtility:
151 decorations &= ~(WindowDecorationIconify |
152 WindowDecorationMaximize);
153 functions &= ~(WindowFunctionShade |
154 WindowFunctionIconify |
155 WindowFunctionMaximize);
156 break;
158 default:
159 break;
162 // mask away stuff turned off by Motif hints
163 decorations &= motifhints.decorations;
164 functions &= motifhints.functions;
166 // disable shade if we do not have a titlebar
167 if (!(decorations & WindowDecorationTitlebar))
168 functions &= ~WindowFunctionShade;
170 // disable grips and maximize if we have a fixed size window
171 if ((wmnormal.flags & (PMinSize|PMaxSize)) == (PMinSize|PMaxSize)
172 && wmnormal.max_width <= wmnormal.min_width
173 && wmnormal.max_height <= wmnormal.min_height) {
174 decorations &= ~(WindowDecorationMaximize |
175 WindowDecorationGrip);
176 functions &= ~(WindowFunctionResize |
177 WindowFunctionMaximize);
180 // cannot close if client doesn't understand WM_DELETE_WINDOW
181 if (!wmprotocols.wm_delete_window) {
182 decorations &= ~WindowDecorationClose;
183 functions &= ~WindowFunctionClose;
189 * Calculate the frame margin based on the given decorations and
190 * style.
192 static
193 bt::EWMH::Strut update_margin(WindowDecorationFlags decorations,
194 const WindowStyle &style) {
195 bt::EWMH::Strut margin;
197 const unsigned int bw = ((decorations & WindowDecorationBorder)
198 ? style.frame_border_width
199 : 0u);
200 margin.top = margin.bottom = margin.left = margin.right = bw;
202 if (decorations & WindowDecorationTitlebar)
203 margin.top += style.title_height - bw;
205 if (decorations & WindowDecorationHandle)
206 margin.bottom += style.handle_height - bw;
208 return margin;
213 * Add specified window to the appropriate window group, creating a
214 * new group if necessary.
216 static BWindowGroup *update_window_group(Window window_group,
217 Blackbox *blackbox,
218 BlackboxWindow *win) {
219 BWindowGroup *group = win->findWindowGroup();
220 if (!group) {
221 new BWindowGroup(blackbox, window_group);
222 group = win->findWindowGroup();
223 assert(group != 0);
225 group->addWindow(win);
226 return group;
231 * Calculate the size of the frame window and constrain it to the
232 * size specified by the size hints of the client window.
234 * 'rect' refers to the geometry of the frame in pixels.
236 enum Corner {
237 TopLeft,
238 TopRight,
239 BottomLeft,
240 BottomRight
242 static bt::Rect constrain(const bt::Rect &rect,
243 const bt::EWMH::Strut &margin,
244 const WMNormalHints &wmnormal,
245 Corner corner) {
246 bt::Rect r;
248 // 'rect' represents the requested frame size, we need to strip
249 // 'margin' off and constrain the client size
250 r.setCoords(rect.left() + static_cast<signed>(margin.left),
251 rect.top() + static_cast<signed>(margin.top),
252 rect.right() - static_cast<signed>(margin.right),
253 rect.bottom() - static_cast<signed>(margin.bottom));
255 unsigned int dw = r.width(), dh = r.height();
257 const unsigned int base_width = (wmnormal.base_width
258 ? wmnormal.base_width
259 : wmnormal.min_width),
260 base_height = (wmnormal.base_height
261 ? wmnormal.base_height
262 : wmnormal.min_height);
264 // fit to min/max size
265 if (dw < wmnormal.min_width)
266 dw = wmnormal.min_width;
267 if (dh < wmnormal.min_height)
268 dh = wmnormal.min_height;
270 if (dw > wmnormal.max_width)
271 dw = wmnormal.max_width;
272 if (dh > wmnormal.max_height)
273 dh = wmnormal.max_height;
275 assert(dw >= base_width && dh >= base_height);
277 // fit to size increments
278 if (wmnormal.flags & PResizeInc) {
279 dw = (((dw - base_width) / wmnormal.width_inc)
280 * wmnormal.width_inc) + base_width;
281 dh = (((dh - base_height) / wmnormal.height_inc)
282 * wmnormal.height_inc) + base_height;
286 * honor aspect ratios (based on twm which is based on uwm)
288 * The math looks like this:
290 * minAspectX dwidth maxAspectX
291 * ---------- <= ------- <= ----------
292 * minAspectY dheight maxAspectY
294 * If that is multiplied out, then the width and height are
295 * invalid in the following situations:
297 * minAspectX * dheight > minAspectY * dwidth
298 * maxAspectX * dheight < maxAspectY * dwidth
301 if (wmnormal.flags & PAspect) {
302 unsigned int delta;
303 const unsigned int min_asp_x = wmnormal.min_aspect_x,
304 min_asp_y = wmnormal.min_aspect_y,
305 max_asp_x = wmnormal.max_aspect_x,
306 max_asp_y = wmnormal.max_aspect_y,
307 w_inc = wmnormal.width_inc,
308 h_inc = wmnormal.height_inc;
309 if (min_asp_x * dh > min_asp_y * dw) {
310 delta = ((min_asp_x * dh / min_asp_y - dw) * w_inc) / w_inc;
311 if (dw + delta <= wmnormal.max_width) {
312 dw += delta;
313 } else {
314 delta = ((dh - (dw * min_asp_y) / min_asp_x) * h_inc) / h_inc;
315 if (dh - delta >= wmnormal.min_height) dh -= delta;
318 if (max_asp_x * dh < max_asp_y * dw) {
319 delta = ((max_asp_y * dw / max_asp_x - dh) * h_inc) / h_inc;
320 if (dh + delta <= wmnormal.max_height) {
321 dh += delta;
322 } else {
323 delta = ((dw - (dh * max_asp_x) / max_asp_y) * w_inc) / w_inc;
324 if (dw - delta >= wmnormal.min_width) dw -= delta;
329 r.setSize(dw, dh);
331 // add 'margin' back onto 'r'
332 r.setCoords(r.left() - margin.left, r.top() - margin.top,
333 r.right() + margin.right, r.bottom() + margin.bottom);
335 // move 'r' to the specified corner
336 int dx = rect.right() - r.right();
337 int dy = rect.bottom() - r.bottom();
339 switch (corner) {
340 case TopLeft:
341 // nothing to do
342 break;
344 case TopRight:
345 r.setPos(r.x() + dx, r.y());
346 break;
348 case BottomLeft:
349 r.setPos(r.x(), r.y() + dy);
350 break;
352 case BottomRight:
353 r.setPos(r.x() + dx, r.y() + dy);
354 break;
357 return r;
362 * Positions 'rect' according to the client window geometry and window
363 * gravity.
365 static bt::Rect applyGravity(const bt::Rect &rect,
366 const bt::EWMH::Strut &margin,
367 int gravity) {
368 bt::Rect r;
370 // apply horizontal window gravity
371 switch (gravity) {
372 default:
373 case NorthWestGravity:
374 case SouthWestGravity:
375 case WestGravity:
376 r.setX(rect.x());
377 break;
379 case NorthGravity:
380 case SouthGravity:
381 case CenterGravity:
382 r.setX(rect.x() - (margin.left + margin.right) / 2);
383 break;
385 case NorthEastGravity:
386 case SouthEastGravity:
387 case EastGravity:
388 r.setX(rect.x() - (margin.left + margin.right) + 2);
389 break;
391 case ForgetGravity:
392 case StaticGravity:
393 r.setX(rect.x() - margin.left);
394 break;
397 // apply vertical window gravity
398 switch (gravity) {
399 default:
400 case NorthWestGravity:
401 case NorthEastGravity:
402 case NorthGravity:
403 r.setY(rect.y());
404 break;
406 case CenterGravity:
407 case EastGravity:
408 case WestGravity:
409 r.setY(rect.y() - ((margin.top + margin.bottom) / 2));
410 break;
412 case SouthWestGravity:
413 case SouthEastGravity:
414 case SouthGravity:
415 r.setY(rect.y() - (margin.bottom + margin.top) + 2);
416 break;
418 case ForgetGravity:
419 case StaticGravity:
420 r.setY(rect.y() - margin.top);
421 break;
424 r.setSize(rect.width() + margin.left + margin.right,
425 rect.height() + margin.top + margin.bottom);
426 return r;
431 * The reverse of the applyGravity function.
433 * Positions 'rect' according to the frame window geometry and window
434 * gravity.
436 static bt::Rect restoreGravity(const bt::Rect &rect,
437 const bt::EWMH::Strut &margin,
438 int gravity) {
439 bt::Rect r;
441 // restore horizontal window gravity
442 switch (gravity) {
443 default:
444 case NorthWestGravity:
445 case SouthWestGravity:
446 case WestGravity:
447 r.setX(rect.x());
448 break;
450 case NorthGravity:
451 case SouthGravity:
452 case CenterGravity:
453 r.setX(rect.x() + (margin.left + margin.right) / 2);
454 break;
456 case NorthEastGravity:
457 case SouthEastGravity:
458 case EastGravity:
459 r.setX(rect.x() + (margin.left + margin.right) - 2);
460 break;
462 case ForgetGravity:
463 case StaticGravity:
464 r.setX(rect.x() + margin.left);
465 break;
468 // restore vertical window gravity
469 switch (gravity) {
470 default:
471 case NorthWestGravity:
472 case NorthEastGravity:
473 case NorthGravity:
474 r.setY(rect.y());
475 break;
477 case CenterGravity:
478 case EastGravity:
479 case WestGravity:
480 r.setY(rect.y() + (margin.top + margin.bottom) / 2);
481 break;
483 case SouthWestGravity:
484 case SouthEastGravity:
485 case SouthGravity:
486 r.setY(rect.y() + (margin.top + margin.bottom) - 2);
487 break;
489 case ForgetGravity:
490 case StaticGravity:
491 r.setY(rect.y() + margin.top);
492 break;
495 r.setSize(rect.width() - margin.left - margin.right,
496 rect.height() - margin.top - margin.bottom);
497 return r;
501 static bt::ustring readWMName(Blackbox *blackbox, Window window) {
502 bt::ustring name;
504 if (!blackbox->ewmh().readWMName(window, name) || name.empty()) {
505 XTextProperty text_prop;
507 if (XGetWMName(blackbox->XDisplay(), window, &text_prop)) {
508 name = bt::toUnicode(bt::textPropertyToString(blackbox->XDisplay(),
509 text_prop));
510 XFree((char *) text_prop.value);
514 if (name.empty())
515 name = bt::toUnicode("Unnamed");
517 return name;
521 static bt::ustring readWMIconName(Blackbox *blackbox, Window window) {
522 bt::ustring name;
524 if (!blackbox->ewmh().readWMIconName(window, name) || name.empty()) {
525 XTextProperty text_prop;
526 if (XGetWMIconName(blackbox->XDisplay(), window, &text_prop)) {
527 name = bt::toUnicode(bt::textPropertyToString(blackbox->XDisplay(),
528 text_prop));
529 XFree((char *) text_prop.value);
533 if (name.empty())
534 return bt::ustring();
536 return name;
540 static EWMH readEWMH(const bt::EWMH &bewmh,
541 Window window,
542 int currentWorkspace) {
543 EWMH ewmh;
544 ewmh.window_type = WindowTypeNormal;
545 ewmh.workspace = 0; // initialized properly below
546 ewmh.modal = false;
547 ewmh.maxv = false;
548 ewmh.maxh = false;
549 ewmh.shaded = false;
550 ewmh.skip_taskbar = false;
551 ewmh.skip_pager = false;
552 ewmh.hidden = false;
553 ewmh.fullscreen = false;
554 ewmh.above = false;
555 ewmh.below = false;
557 // note: wm_name and wm_icon_name are read separately
559 bool ret;
561 bt::EWMH::AtomList atoms;
562 ret = bewmh.readWMWindowType(window, atoms);
563 if (ret) {
564 bt::EWMH::AtomList::iterator it = atoms.begin(), end = atoms.end();
565 for (; it != end; ++it) {
566 if (bewmh.isSupportedWMWindowType(*it)) {
567 ewmh.window_type = ::window_type_from_atom(bewmh, *it);
568 break;
573 atoms.clear();
574 ret = bewmh.readWMState(window, atoms);
575 if (ret) {
576 bt::EWMH::AtomList::iterator it = atoms.begin(), end = atoms.end();
577 for (; it != end; ++it) {
578 Atom state = *it;
579 if (state == bewmh.wmStateModal()) {
580 ewmh.modal = true;
581 } else if (state == bewmh.wmStateMaximizedVert()) {
582 ewmh.maxv = true;
583 } else if (state == bewmh.wmStateMaximizedHorz()) {
584 ewmh.maxh = true;
585 } else if (state == bewmh.wmStateShaded()) {
586 ewmh.shaded = true;
587 } else if (state == bewmh.wmStateSkipTaskbar()) {
588 ewmh.skip_taskbar = true;
589 } else if (state == bewmh.wmStateSkipPager()) {
590 ewmh.skip_pager = true;
591 } else if (state == bewmh.wmStateHidden()) {
593 ignore _NET_WM_STATE_HIDDEN if present, the wm sets this
594 state, not the application
596 } else if (state == bewmh.wmStateFullscreen()) {
597 ewmh.fullscreen = true;
598 } else if (state == bewmh.wmStateAbove()) {
599 ewmh.above = true;
600 } else if (state == bewmh.wmStateBelow()) {
601 ewmh.below = true;
606 switch (ewmh.window_type) {
607 case WindowTypeDesktop:
608 case WindowTypeDock:
609 // these types should occupy all workspaces by default
610 ewmh.workspace = bt::BSENTINEL;
611 break;
613 default:
614 if (!bewmh.readWMDesktop(window, ewmh.workspace))
615 ewmh.workspace = currentWorkspace;
616 break;
617 } //switch
619 return ewmh;
624 * Returns the MotifWM hints for the specified window.
626 static MotifHints readMotifWMHints(Blackbox *blackbox, Window window) {
627 MotifHints motif;
628 motif.decorations = AllWindowDecorations;
629 motif.functions = AllWindowFunctions;
632 this structure only contains 3 elements, even though the Motif 2.0
633 structure contains 5, because we only use the first 3
635 struct PropMotifhints {
636 unsigned long flags;
637 unsigned long functions;
638 unsigned long decorations;
640 static const unsigned int PROP_MWM_HINTS_ELEMENTS = 3u;
641 enum { // MWM flags
642 MWM_HINTS_FUNCTIONS = 1<<0,
643 MWM_HINTS_DECORATIONS = 1<<1
645 enum { // MWM functions
646 MWM_FUNC_ALL = 1<<0,
647 MWM_FUNC_RESIZE = 1<<1,
648 MWM_FUNC_MOVE = 1<<2,
649 MWM_FUNC_MINIMIZE = 1<<3,
650 MWM_FUNC_MAXIMIZE = 1<<4,
651 MWM_FUNC_CLOSE = 1<<5
653 enum { // MWM decorations
654 MWM_DECOR_ALL = 1<<0,
655 MWM_DECOR_BORDER = 1<<1,
656 MWM_DECOR_RESIZEH = 1<<2,
657 MWM_DECOR_TITLE = 1<<3,
658 MWM_DECOR_MENU = 1<<4,
659 MWM_DECOR_MINIMIZE = 1<<5,
660 MWM_DECOR_MAXIMIZE = 1<<6
663 Atom atom_return;
664 PropMotifhints *prop = 0;
665 int format;
666 unsigned long num, len;
667 int ret = XGetWindowProperty(blackbox->XDisplay(), window,
668 blackbox->motifWmHintsAtom(), 0,
669 PROP_MWM_HINTS_ELEMENTS, False,
670 blackbox->motifWmHintsAtom(), &atom_return,
671 &format, &num, &len,
672 (unsigned char **) &prop);
674 if (ret != Success || !prop || num != PROP_MWM_HINTS_ELEMENTS) {
675 if (prop) XFree(prop);
676 return motif;
679 if (prop->flags & MWM_HINTS_FUNCTIONS) {
680 if (prop->functions & MWM_FUNC_ALL) {
681 motif.functions = AllWindowFunctions;
682 } else {
683 // default to the functions that cannot be set through
684 // _MOTIF_WM_HINTS
685 motif.functions = (WindowFunctionShade
686 | WindowFunctionChangeWorkspace
687 | WindowFunctionChangeLayer
688 | WindowFunctionFullScreen);
690 if (prop->functions & MWM_FUNC_RESIZE)
691 motif.functions |= WindowFunctionResize;
692 if (prop->functions & MWM_FUNC_MOVE)
693 motif.functions |= WindowFunctionMove;
694 if (prop->functions & MWM_FUNC_MINIMIZE)
695 motif.functions |= WindowFunctionIconify;
696 if (prop->functions & MWM_FUNC_MAXIMIZE)
697 motif.functions |= WindowFunctionMaximize;
698 if (prop->functions & MWM_FUNC_CLOSE)
699 motif.functions |= WindowFunctionClose;
703 if (prop->flags & MWM_HINTS_DECORATIONS) {
704 if (prop->decorations & MWM_DECOR_ALL) {
705 motif.decorations = AllWindowDecorations;
706 } else {
707 motif.decorations = NoWindowDecorations;
709 if (prop->decorations & MWM_DECOR_BORDER)
710 motif.decorations |= WindowDecorationBorder;
711 if (prop->decorations & MWM_DECOR_RESIZEH)
712 motif.decorations |= WindowDecorationHandle;
713 if (prop->decorations & MWM_DECOR_TITLE)
714 motif.decorations |= WindowDecorationTitlebar;
715 if (prop->decorations & MWM_DECOR_MINIMIZE)
716 motif.decorations |= WindowDecorationIconify;
717 if (prop->decorations & MWM_DECOR_MAXIMIZE)
718 motif.decorations |= WindowDecorationMaximize;
722 if (motif.decorations & WindowDecorationHandle) {
723 if (motif.functions & WindowFunctionResize)
724 motif.decorations |= WindowDecorationGrip;
725 else
726 motif.decorations &= ~WindowDecorationGrip;
729 if (motif.decorations & WindowDecorationTitlebar) {
730 if (motif.functions & WindowFunctionClose)
731 motif.decorations |= WindowDecorationClose;
732 else
733 motif.decorations &= ~WindowDecorationClose;
736 XFree(prop);
738 return motif;
743 * Returns the value of the WM_HINTS property. If the property is not
744 * set, a set of default values is returned instead.
746 static WMHints readWMHints(Blackbox *blackbox, Window window) {
747 WMHints wmh;
748 wmh.accept_focus = false;
749 wmh.window_group = None;
750 wmh.initial_state = NormalState;
752 XWMHints *wmhint = XGetWMHints(blackbox->XDisplay(), window);
753 if (!wmhint) return wmh;
755 if (wmhint->flags & InputHint)
756 wmh.accept_focus = (wmhint->input == True);
757 if (wmhint->flags & StateHint)
758 wmh.initial_state = wmhint->initial_state;
759 if (wmhint->flags & WindowGroupHint)
760 wmh.window_group = wmhint->window_group;
762 XFree(wmhint);
764 return wmh;
769 * Returns the value of the WM_NORMAL_HINTS property. If the property
770 * is not set, a set of default values is returned instead.
772 static WMNormalHints readWMNormalHints(Blackbox *blackbox,
773 Window window,
774 const bt::ScreenInfo &screenInfo) {
775 WMNormalHints wmnormal;
776 wmnormal.flags = 0;
777 wmnormal.min_width = wmnormal.min_height = 1u;
778 wmnormal.width_inc = wmnormal.height_inc = 1u;
779 wmnormal.min_aspect_x = wmnormal.min_aspect_y = 1u;
780 wmnormal.max_aspect_x = wmnormal.max_aspect_y = 1u;
781 wmnormal.base_width = wmnormal.base_height = 0u;
782 wmnormal.win_gravity = NorthWestGravity;
785 use the full screen, not the strut modified size. otherwise when
786 the availableArea changes max_width/height will be incorrect and
787 lead to odd rendering bugs.
789 const bt::Rect &rect = screenInfo.rect();
790 wmnormal.max_width = rect.width();
791 wmnormal.max_height = rect.height();
793 XSizeHints sizehint;
794 long unused;
795 if (! XGetWMNormalHints(blackbox->XDisplay(), window, &sizehint, &unused))
796 return wmnormal;
798 wmnormal.flags = sizehint.flags;
800 if (sizehint.flags & PMinSize) {
801 if (sizehint.min_width > 0)
802 wmnormal.min_width = sizehint.min_width;
803 if (sizehint.min_height > 0)
804 wmnormal.min_height = sizehint.min_height;
807 if the minimum size is bigger then the screen, adjust the
808 maximum size
810 if (wmnormal.min_width > wmnormal.max_width)
811 wmnormal.max_width = wmnormal.min_width;
812 if (wmnormal.min_height > wmnormal.max_height)
813 wmnormal.max_height = wmnormal.min_height;
816 if (sizehint.flags & PMaxSize) {
817 if (sizehint.max_width >= static_cast<signed>(wmnormal.min_width))
818 wmnormal.max_width = sizehint.max_width;
819 else
820 wmnormal.max_width = wmnormal.min_width;
822 if (sizehint.max_height >= static_cast<signed>(wmnormal.min_height))
823 wmnormal.max_height = sizehint.max_height;
824 else
825 wmnormal.max_height = wmnormal.min_height;
828 if (sizehint.flags & PResizeInc) {
829 wmnormal.width_inc = sizehint.width_inc;
830 wmnormal.height_inc = sizehint.height_inc;
833 if (sizehint.flags & PAspect) {
834 wmnormal.min_aspect_x = sizehint.min_aspect.x;
835 wmnormal.min_aspect_y = sizehint.min_aspect.y;
836 wmnormal.max_aspect_x = sizehint.max_aspect.x;
837 wmnormal.max_aspect_y = sizehint.max_aspect.y;
840 if (sizehint.flags & PBaseSize) {
841 if (sizehint.base_width <= static_cast<signed>(wmnormal.min_width))
842 wmnormal.base_width = sizehint.base_width;
843 if (sizehint.base_height <= static_cast<signed>(wmnormal.min_height))
844 wmnormal.base_height = sizehint.base_height;
847 if (sizehint.flags & PWinGravity)
848 wmnormal.win_gravity = sizehint.win_gravity;
850 return wmnormal;
855 * Retrieve which Window Manager Protocols are supported by the client
856 * window.
858 static WMProtocols readWMProtocols(Blackbox *blackbox,
859 Window window) {
860 WMProtocols protocols;
861 protocols.wm_delete_window = false;
862 protocols.wm_take_focus = false;
864 Atom *proto;
865 int num_return = 0;
867 if (XGetWMProtocols(blackbox->XDisplay(), window,
868 &proto, &num_return)) {
869 for (int i = 0; i < num_return; ++i) {
870 if (proto[i] == blackbox->wmDeleteWindowAtom()) {
871 protocols.wm_delete_window = true;
872 } else if (proto[i] == blackbox->wmTakeFocusAtom()) {
873 protocols.wm_take_focus = true;
876 XFree(proto);
879 return protocols;
884 * Reads the value of the WM_TRANSIENT_FOR property and returns a
885 * pointer to the transient parent for this window. If the
886 * WM_TRANSIENT_FOR is missing or invalid, this function returns 0.
888 * 'client.wmhints' should be properly updated before calling this
889 * function.
891 * Note: a return value of ~0ul signifies a window that should be
892 * transient but has no discernible parent.
894 static Window readTransientInfo(Blackbox *blackbox,
895 Window window,
896 const bt::ScreenInfo &screenInfo,
897 const WMHints &wmhints) {
898 Window trans_for = None;
900 if (!XGetTransientForHint(blackbox->XDisplay(), window, &trans_for)) {
901 // WM_TRANSIENT_FOR hint not set
902 return 0;
905 if (trans_for == window) {
906 // wierd client... treat this window as a normal window
907 return 0;
910 if (trans_for == None || trans_for == screenInfo.rootWindow()) {
912 this is a violation of the ICCCM, yet the EWMH allows this as a
913 way to signify a group transient.
915 trans_for = wmhints.window_group;
918 return trans_for;
922 static bool readState(unsigned long &current_state,
923 Blackbox *blackbox,
924 Window window) {
925 current_state = NormalState;
927 Atom atom_return;
928 bool ret = false;
929 int foo;
930 unsigned long *state, ulfoo, nitems;
932 if ((XGetWindowProperty(blackbox->XDisplay(), window,
933 blackbox->wmStateAtom(),
934 0l, 2l, False, blackbox->wmStateAtom(),
935 &atom_return, &foo, &nitems, &ulfoo,
936 (unsigned char **) &state) != Success) ||
937 (! state)) {
938 return false;
941 if (nitems >= 1) {
942 current_state = static_cast<unsigned long>(state[0]);
943 ret = true;
946 XFree((void *) state);
948 return ret;
952 static void clearState(Blackbox *blackbox, Window window) {
953 XDeleteProperty(blackbox->XDisplay(), window, blackbox->wmStateAtom());
955 const bt::EWMH& ewmh = blackbox->ewmh();
956 ewmh.removeProperty(window, ewmh.wmDesktop());
957 ewmh.removeProperty(window, ewmh.wmState());
958 ewmh.removeProperty(window, ewmh.wmAllowedActions());
959 ewmh.removeProperty(window, ewmh.wmVisibleName());
960 ewmh.removeProperty(window, ewmh.wmVisibleIconName());
965 * Initializes the class with default values/the window's set initial values.
967 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
968 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
969 // sizeof(BlackboxWindow));
971 #ifdef DEBUG
972 fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
973 #endif // DEBUG
976 set timer to zero... it is initialized properly later, so we check
977 if timer is zero in the destructor, and assume that the window is not
978 fully constructed if timer is zero...
980 timer = (bt::Timer*) 0;
981 blackbox = b;
982 client.window = w;
983 _screen = s;
984 lastButtonPressTime = 0;
987 the server needs to be grabbed here to prevent client's from sending
988 events while we are in the process of managing their window.
989 We hold the grab until after we are done moving the window around.
992 blackbox->XGrabServer();
994 // fetch client size and placement
995 XWindowAttributes wattrib;
996 if (! XGetWindowAttributes(blackbox->XDisplay(),
997 client.window, &wattrib) ||
998 ! wattrib.screen || wattrib.override_redirect) {
999 #ifdef DEBUG
1000 fprintf(stderr,
1001 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
1002 #endif // DEBUG
1004 blackbox->XUngrabServer();
1005 delete this;
1006 return;
1009 // set the eventmask early in the game so that we make sure we get
1010 // all the events we are interested in
1011 XSetWindowAttributes attrib_set;
1012 attrib_set.event_mask = ::client_window_event_mask;
1013 attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
1014 ButtonMotionMask;
1015 XChangeWindowAttributes(blackbox->XDisplay(), client.window,
1016 CWEventMask|CWDontPropagate, &attrib_set);
1018 client.colormap = wattrib.colormap;
1019 window_number = bt::BSENTINEL;
1020 client.strut = 0;
1022 set the initial size and location of client window (relative to the
1023 _root window_). This position is the reference point used with the
1024 window's gravity to find the window's initial position.
1026 client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1027 client.premax = client.rect;
1028 client.old_bw = wattrib.border_width;
1029 client.current_state = NormalState;
1031 frame.window = frame.plate = frame.title = frame.handle = None;
1032 frame.close_button = frame.iconify_button = frame.maximize_button = None;
1033 frame.right_grip = frame.left_grip = None;
1034 frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
1035 frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
1036 frame.pbutton = frame.ugrip = frame.fgrip = None;
1038 timer = new bt::Timer(blackbox, this);
1039 timer->setTimeout(blackbox->resource().autoRaiseDelay());
1041 client.title = ::readWMName(blackbox, client.window);
1042 client.icon_title = ::readWMIconName(blackbox, client.window);
1044 // get size, aspect, minimum/maximum size, ewmh and other hints set
1045 // by the client
1046 client.ewmh = ::readEWMH(blackbox->ewmh(), client.window,
1047 _screen->currentWorkspace());
1048 client.motif = ::readMotifWMHints(blackbox, client.window);
1049 client.wmhints = ::readWMHints(blackbox, client.window);
1050 client.wmnormal = ::readWMNormalHints(blackbox, client.window,
1051 _screen->screenInfo());
1052 client.wmprotocols = ::readWMProtocols(blackbox, client.window);
1053 client.transient_for = ::readTransientInfo(blackbox, client.window,
1054 _screen->screenInfo(),
1055 client.wmhints);
1057 if (client.wmhints.window_group != None)
1058 (void) ::update_window_group(client.wmhints.window_group, blackbox, this);
1060 if (isTransient()) {
1061 // add ourselves to our transient_for
1062 BlackboxWindow *win = findTransientFor();
1063 if (win) {
1064 win->addTransient(this);
1065 client.ewmh.workspace = win->workspace();
1066 setLayer(win->layer());
1067 } else if (isGroupTransient()) {
1068 BWindowGroup *group = findWindowGroup();
1069 if (group)
1070 group->addTransient(this);
1071 } else {
1072 // broken client
1073 client.transient_for = 0;
1077 client.state.visible = false;
1078 client.state.iconic = false;
1079 client.state.moving = false;
1080 client.state.resizing = false;
1081 client.state.focused = false;
1083 switch (windowType()) {
1084 case WindowTypeDesktop:
1085 setLayer(StackingList::LayerDesktop);
1086 break;
1088 case WindowTypeDock:
1089 setLayer(StackingList::LayerAbove);
1090 // fallthrough intended
1092 default:
1093 if (client.ewmh.above)
1094 setLayer(StackingList::LayerAbove);
1095 else if (client.ewmh.below)
1096 setLayer(StackingList::LayerBelow);
1097 break;
1098 } // switch
1100 ::update_decorations(client.decorations,
1101 client.functions,
1102 isTransient(),
1103 client.ewmh,
1104 client.motif,
1105 client.wmnormal,
1106 client.wmprotocols);
1108 // sanity checks
1109 if (client.wmhints.initial_state == IconicState
1110 && !hasWindowFunction(WindowFunctionIconify))
1111 client.wmhints.initial_state = NormalState;
1112 if (isMaximized() && !hasWindowFunction(WindowFunctionMaximize))
1113 client.ewmh.maxv = client.ewmh.maxh = false;
1114 if (isFullScreen() && !hasWindowFunction(WindowFunctionFullScreen))
1115 client.ewmh.fullscreen = false;
1117 bt::EWMH::Strut strut;
1118 if (blackbox->ewmh().readWMStrut(client.window, &strut)) {
1119 client.strut = new bt::EWMH::Strut;
1120 *client.strut = strut;
1121 _screen->addStrut(client.strut);
1125 if we just managed the group leader for an existing group, move
1126 all group transients to this window
1129 BWindowGroup *group = blackbox->findWindowGroup(client.window);
1130 if (group) {
1131 BlackboxWindowList transientList = group->transients();
1132 BlackboxWindowList::const_iterator it = transientList.begin();
1133 const BlackboxWindowList::const_iterator end = transientList.end();
1134 for (; it != end; ++it) {
1135 BlackboxWindow * const w1 = *it;
1136 if (w1->client.transient_for != client.window)
1137 continue;
1138 group->removeTransient(w1);
1139 addTransient(w1);
1140 w1->changeWorkspace(workspace());
1141 w1->changeLayer(layer());
1146 frame.window = createToplevelWindow();
1147 blackbox->insertEventHandler(frame.window, this);
1149 frame.plate = createChildWindow(frame.window, NoEventMask);
1150 blackbox->insertEventHandler(frame.plate, this);
1152 if (client.decorations & WindowDecorationTitlebar)
1153 createTitlebar();
1155 if (client.decorations & WindowDecorationHandle)
1156 createHandle();
1158 // apply the size and gravity to the frame
1159 const WindowStyle &style = _screen->resource().windowStyle();
1160 frame.margin = ::update_margin(client.decorations, style);
1161 frame.rect = ::applyGravity(client.rect,
1162 frame.margin,
1163 client.wmnormal.win_gravity);
1165 associateClientWindow();
1167 blackbox->insertEventHandler(client.window, this);
1168 blackbox->insertWindow(client.window, this);
1169 blackbox->insertWindow(frame.plate, this);
1171 // preserve the window's initial state on first map, and its current
1172 // state across a restart
1173 if (!readState(client.current_state, blackbox, client.window))
1174 client.current_state = client.wmhints.initial_state;
1176 if (client.state.iconic) {
1177 // prepare the window to be iconified
1178 client.current_state = IconicState;
1179 client.state.iconic = False;
1180 } else if (workspace() != bt::BSENTINEL &&
1181 workspace() != _screen->currentWorkspace()) {
1182 client.current_state = WithdrawnState;
1185 blackbox->XUngrabServer();
1187 grabButtons();
1189 XMapSubwindows(blackbox->XDisplay(), frame.window);
1191 if (isFullScreen()) {
1192 client.ewmh.fullscreen = false; // trick setFullScreen into working
1193 setFullScreen(true);
1194 } else {
1195 if (isMaximized()) {
1196 remaximize();
1197 } else {
1198 const unsigned long save_state = client.current_state;
1200 bt::Rect r = frame.rect;
1201 // trick configure into working
1202 frame.rect = bt::Rect();
1203 configure(r);
1205 if (isShaded()) {
1206 client.ewmh.shaded = false;
1207 setShaded(true);
1210 if (isShaded() && save_state != IconicState) {
1212 At this point in the life of a window, current_state should
1213 only be set to IconicState if the window was an *icon*, not
1214 if it was shaded.
1216 client.current_state = save_state;
1223 BlackboxWindow::~BlackboxWindow(void) {
1224 #ifdef DEBUG
1225 fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
1226 client.window);
1227 #endif // DEBUG
1229 if (! timer) // window not managed...
1230 return;
1232 if (client.state.moving || client.state.resizing) {
1233 _screen->hideGeometry();
1234 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
1237 delete timer;
1239 if (client.strut) {
1240 _screen->removeStrut(client.strut);
1241 delete client.strut;
1242 client.strut = 0;
1245 BWindowGroup *group = findWindowGroup();
1247 if (isTransient()) {
1248 // remove ourselves from our transient_for
1249 BlackboxWindow *win = findTransientFor();
1250 if (win) {
1251 win->removeTransient(this);
1252 } else if (isGroupTransient()) {
1253 if (group)
1254 group->removeTransient(this);
1256 client.transient_for = 0;
1259 if (group)
1260 group->removeWindow(this);
1262 if (frame.title)
1263 destroyTitlebar();
1265 if (frame.handle)
1266 destroyHandle();
1268 blackbox->removeEventHandler(client.window);
1269 blackbox->removeWindow(client.window);
1271 blackbox->removeEventHandler(frame.plate);
1272 blackbox->removeWindow(frame.plate);
1273 XDestroyWindow(blackbox->XDisplay(), frame.plate);
1275 blackbox->removeEventHandler(frame.window);
1276 XDestroyWindow(blackbox->XDisplay(), frame.window);
1281 * Creates a new top level window, with a given location, size, and border
1282 * width.
1283 * Returns: the newly created window
1285 Window BlackboxWindow::createToplevelWindow(void) {
1286 XSetWindowAttributes attrib_create;
1287 unsigned long create_mask = CWColormap | CWOverrideRedirect | CWEventMask;
1289 attrib_create.colormap = _screen->screenInfo().colormap();
1290 attrib_create.override_redirect = True;
1291 attrib_create.event_mask = EnterWindowMask | LeaveWindowMask;
1293 return XCreateWindow(blackbox->XDisplay(),
1294 _screen->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
1295 _screen->screenInfo().depth(), InputOutput,
1296 _screen->screenInfo().visual(),
1297 create_mask, &attrib_create);
1302 * Creates a child window, and optionally associates a given cursor with
1303 * the new window.
1305 Window BlackboxWindow::createChildWindow(Window parent,
1306 unsigned long event_mask,
1307 Cursor cursor) {
1308 XSetWindowAttributes attrib_create;
1309 unsigned long create_mask = CWEventMask;
1311 attrib_create.event_mask = event_mask;
1313 if (cursor) {
1314 create_mask |= CWCursor;
1315 attrib_create.cursor = cursor;
1318 return XCreateWindow(blackbox->XDisplay(), parent, 0, 0, 1, 1, 0,
1319 _screen->screenInfo().depth(), InputOutput,
1320 _screen->screenInfo().visual(),
1321 create_mask, &attrib_create);
1326 * Reparents the client window into the newly created frame.
1328 * Note: the server must be grabbed before calling this function.
1330 void BlackboxWindow::associateClientWindow(void) {
1331 XSetWindowBorderWidth(blackbox->XDisplay(), client.window, 0);
1332 XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeInsert);
1334 XSelectInput(blackbox->XDisplay(), frame.plate,
1335 FocusChangeMask | SubstructureRedirectMask);
1337 XSelectInput(blackbox->XDisplay(), client.window,
1338 client_window_event_mask & ~StructureNotifyMask);
1339 XReparentWindow(blackbox->XDisplay(), client.window, frame.plate, 0, 0);
1340 XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
1342 #ifdef SHAPE
1343 if (blackbox->hasShapeExtensions()) {
1344 XShapeSelectInput(blackbox->XDisplay(), client.window,
1345 ShapeNotifyMask);
1347 Bool shaped = False;
1348 int foo;
1349 unsigned int ufoo;
1351 XShapeQueryExtents(blackbox->XDisplay(), client.window, &shaped,
1352 &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
1353 &ufoo, &ufoo);
1354 client.state.shaped = shaped;
1356 #endif // SHAPE
1360 void BlackboxWindow::decorate(void) {
1361 const WindowStyle &style = _screen->resource().windowStyle();
1362 if (client.decorations & WindowDecorationTitlebar) {
1363 // render focused button texture
1364 frame.fbutton =
1365 bt::PixmapCache::find(_screen->screenNumber(),
1366 style.focus.button,
1367 style.button_width,
1368 style.button_width,
1369 frame.fbutton);
1371 // render unfocused button texture
1372 frame.ubutton =
1373 bt::PixmapCache::find(_screen->screenNumber(),
1374 style.unfocus.button,
1375 style.button_width,
1376 style.button_width,
1377 frame.ubutton);
1379 // render pressed button texture
1380 frame.pbutton =
1381 bt::PixmapCache::find(_screen->screenNumber(),
1382 style.pressed,
1383 style.button_width,
1384 style.button_width,
1385 frame.pbutton);
1387 // render focused titlebar texture
1388 frame.ftitle =
1389 bt::PixmapCache::find(_screen->screenNumber(),
1390 style.focus.title,
1391 frame.rect.width(),
1392 style.title_height,
1393 frame.ftitle);
1395 // render unfocused titlebar texture
1396 frame.utitle =
1397 bt::PixmapCache::find(_screen->screenNumber(),
1398 style.unfocus.title,
1399 frame.rect.width(),
1400 style.title_height,
1401 frame.utitle);
1403 // render focused label texture
1404 frame.flabel =
1405 bt::PixmapCache::find(_screen->screenNumber(),
1406 style.focus.label,
1407 frame.label_w,
1408 style.label_height,
1409 frame.flabel);
1411 // render unfocused label texture
1412 frame.ulabel =
1413 bt::PixmapCache::find(_screen->screenNumber(),
1414 style.unfocus.label,
1415 frame.label_w,
1416 style.label_height,
1417 frame.ulabel);
1420 if (client.decorations & WindowDecorationHandle) {
1421 frame.fhandle =
1422 bt::PixmapCache::find(_screen->screenNumber(),
1423 style.focus.handle,
1424 frame.rect.width(),
1425 style.handle_height,
1426 frame.fhandle);
1428 frame.uhandle =
1429 bt::PixmapCache::find(_screen->screenNumber(),
1430 style.unfocus.handle,
1431 frame.rect.width(),
1432 style.handle_height,
1433 frame.uhandle);
1436 if (client.decorations & WindowDecorationGrip) {
1437 frame.fgrip =
1438 bt::PixmapCache::find(_screen->screenNumber(),
1439 style.focus.grip,
1440 style.grip_width,
1441 style.handle_height,
1442 frame.fgrip);
1444 frame.ugrip =
1445 bt::PixmapCache::find(_screen->screenNumber(),
1446 style.unfocus.grip,
1447 style.grip_width,
1448 style.handle_height,
1449 frame.ugrip);
1454 void BlackboxWindow::createHandle(void) {
1455 frame.handle = createChildWindow(frame.window,
1456 ButtonPressMask | ButtonReleaseMask |
1457 ButtonMotionMask | ExposureMask);
1458 blackbox->insertEventHandler(frame.handle, this);
1460 if (client.decorations & WindowDecorationGrip)
1461 createGrips();
1465 void BlackboxWindow::destroyHandle(void) {
1466 if (frame.left_grip || frame.right_grip)
1467 destroyGrips();
1469 if (frame.fhandle) bt::PixmapCache::release(frame.fhandle);
1470 if (frame.uhandle) bt::PixmapCache::release(frame.uhandle);
1472 frame.fhandle = frame.uhandle = None;
1474 blackbox->removeEventHandler(frame.handle);
1475 XDestroyWindow(blackbox->XDisplay(), frame.handle);
1476 frame.handle = None;
1480 void BlackboxWindow::createGrips(void) {
1481 frame.left_grip =
1482 createChildWindow(frame.handle,
1483 ButtonPressMask | ButtonReleaseMask |
1484 ButtonMotionMask | ExposureMask,
1485 blackbox->resource().cursors().resize_bottom_left);
1486 blackbox->insertEventHandler(frame.left_grip, this);
1488 frame.right_grip =
1489 createChildWindow(frame.handle,
1490 ButtonPressMask | ButtonReleaseMask |
1491 ButtonMotionMask | ExposureMask,
1492 blackbox->resource().cursors().resize_bottom_right);
1493 blackbox->insertEventHandler(frame.right_grip, this);
1497 void BlackboxWindow::destroyGrips(void) {
1498 if (frame.fgrip) bt::PixmapCache::release(frame.fgrip);
1499 if (frame.ugrip) bt::PixmapCache::release(frame.ugrip);
1501 frame.fgrip = frame.ugrip = None;
1503 blackbox->removeEventHandler(frame.left_grip);
1504 blackbox->removeEventHandler(frame.right_grip);
1506 XDestroyWindow(blackbox->XDisplay(), frame.left_grip);
1507 XDestroyWindow(blackbox->XDisplay(), frame.right_grip);
1508 frame.left_grip = frame.right_grip = None;
1512 void BlackboxWindow::createTitlebar(void) {
1513 frame.title = createChildWindow(frame.window,
1514 ButtonPressMask | ButtonReleaseMask |
1515 ButtonMotionMask | ExposureMask);
1516 frame.label = createChildWindow(frame.title,
1517 ButtonPressMask | ButtonReleaseMask |
1518 ButtonMotionMask | ExposureMask);
1519 blackbox->insertEventHandler(frame.title, this);
1520 blackbox->insertEventHandler(frame.label, this);
1522 if (client.decorations & WindowDecorationIconify) createIconifyButton();
1523 if (client.decorations & WindowDecorationMaximize) createMaximizeButton();
1524 if (client.decorations & WindowDecorationClose) createCloseButton();
1528 void BlackboxWindow::destroyTitlebar(void) {
1529 if (frame.close_button)
1530 destroyCloseButton();
1532 if (frame.iconify_button)
1533 destroyIconifyButton();
1535 if (frame.maximize_button)
1536 destroyMaximizeButton();
1538 if (frame.fbutton) bt::PixmapCache::release(frame.fbutton);
1539 if (frame.ubutton) bt::PixmapCache::release(frame.ubutton);
1540 if (frame.pbutton) bt::PixmapCache::release(frame.pbutton);
1541 if (frame.ftitle) bt::PixmapCache::release(frame.ftitle);
1542 if (frame.utitle) bt::PixmapCache::release(frame.utitle);
1543 if (frame.flabel) bt::PixmapCache::release(frame.flabel);
1544 if (frame.ulabel) bt::PixmapCache::release(frame.ulabel);
1546 frame.fbutton = frame.ubutton = frame.pbutton =
1547 frame.ftitle = frame.utitle =
1548 frame.flabel = frame.ulabel = None;
1550 blackbox->removeEventHandler(frame.title);
1551 blackbox->removeEventHandler(frame.label);
1553 XDestroyWindow(blackbox->XDisplay(), frame.label);
1554 XDestroyWindow(blackbox->XDisplay(), frame.title);
1555 frame.title = frame.label = None;
1559 void BlackboxWindow::createCloseButton(void) {
1560 if (frame.title != None) {
1561 frame.close_button = createChildWindow(frame.title,
1562 ButtonPressMask |
1563 ButtonReleaseMask |
1564 ButtonMotionMask | ExposureMask);
1565 blackbox->insertEventHandler(frame.close_button, this);
1570 void BlackboxWindow::destroyCloseButton(void) {
1571 blackbox->removeEventHandler(frame.close_button);
1572 XDestroyWindow(blackbox->XDisplay(), frame.close_button);
1573 frame.close_button = None;
1577 void BlackboxWindow::createIconifyButton(void) {
1578 if (frame.title != None) {
1579 frame.iconify_button = createChildWindow(frame.title,
1580 ButtonPressMask |
1581 ButtonReleaseMask |
1582 ButtonMotionMask | ExposureMask);
1583 blackbox->insertEventHandler(frame.iconify_button, this);
1588 void BlackboxWindow::destroyIconifyButton(void) {
1589 blackbox->removeEventHandler(frame.iconify_button);
1590 XDestroyWindow(blackbox->XDisplay(), frame.iconify_button);
1591 frame.iconify_button = None;
1595 void BlackboxWindow::createMaximizeButton(void) {
1596 if (frame.title != None) {
1597 frame.maximize_button = createChildWindow(frame.title,
1598 ButtonPressMask |
1599 ButtonReleaseMask |
1600 ButtonMotionMask | ExposureMask);
1601 blackbox->insertEventHandler(frame.maximize_button, this);
1606 void BlackboxWindow::destroyMaximizeButton(void) {
1607 blackbox->removeEventHandler(frame.maximize_button);
1608 XDestroyWindow(blackbox->XDisplay(), frame.maximize_button);
1609 frame.maximize_button = None;
1613 void BlackboxWindow::positionButtons(bool redecorate_label) {
1614 // we need to use signed ints here to detect windows that are too small
1615 const WindowStyle &style = _screen->resource().windowStyle();
1616 const int extra = style.title_margin == 0 ?
1617 style.focus.button.borderWidth() : 0,
1618 bw = style.button_width + style.title_margin
1619 - extra,
1620 by = style.title_margin +
1621 style.focus.title.borderWidth();
1622 int lx = by, lw = frame.rect.width() - by;
1624 if (client.decorations & WindowDecorationIconify) {
1625 if (frame.iconify_button == None) createIconifyButton();
1627 XMoveResizeWindow(blackbox->XDisplay(), frame.iconify_button, by, by,
1628 style.button_width, style.button_width);
1629 XMapWindow(blackbox->XDisplay(), frame.iconify_button);
1631 lx += bw;
1632 lw -= bw;
1633 } else if (frame.iconify_button) {
1634 destroyIconifyButton();
1637 int bx = frame.rect.width() - bw
1638 - style.focus.title.borderWidth() - extra;
1640 if (client.decorations & WindowDecorationClose) {
1641 if (frame.close_button == None) createCloseButton();
1643 XMoveResizeWindow(blackbox->XDisplay(), frame.close_button, bx, by,
1644 style.button_width, style.button_width);
1645 XMapWindow(blackbox->XDisplay(), frame.close_button);
1647 bx -= bw;
1648 lw -= bw;
1649 } else if (frame.close_button) {
1650 destroyCloseButton();
1653 if (client.decorations & WindowDecorationMaximize) {
1654 if (frame.maximize_button == None) createMaximizeButton();
1656 XMoveResizeWindow(blackbox->XDisplay(), frame.maximize_button, bx, by,
1657 style.button_width, style.button_width);
1658 XMapWindow(blackbox->XDisplay(), frame.maximize_button);
1660 bx -= bw;
1661 lw -= bw;
1662 } else if (frame.maximize_button) {
1663 destroyMaximizeButton();
1666 if (lw > by) {
1667 frame.label_w = lw - by;
1668 XMoveResizeWindow(blackbox->XDisplay(), frame.label, lx, by,
1669 frame.label_w, style.label_height);
1670 XMapWindow(blackbox->XDisplay(), frame.label);
1672 if (redecorate_label) {
1673 frame.flabel =
1674 bt::PixmapCache::find(_screen->screenNumber(),
1675 style.focus.label,
1676 frame.label_w, style.label_height,
1677 frame.flabel);
1678 frame.ulabel =
1679 bt::PixmapCache::find(_screen->screenNumber(),
1680 style.unfocus.label,
1681 frame.label_w, style.label_height,
1682 frame.ulabel);
1685 const bt::ustring ellided =
1686 bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
1687 _screen->screenNumber(), style.font);
1689 if (ellided != client.visible_title) {
1690 client.visible_title = ellided;
1691 blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
1693 } else {
1694 frame.label_w = 1;
1695 XUnmapWindow(blackbox->XDisplay(), frame.label);
1698 redrawLabel();
1699 redrawAllButtons();
1703 void BlackboxWindow::reconfigure(void) {
1704 const WindowStyle &style = _screen->resource().windowStyle();
1705 if (isMaximized()) {
1706 // update the frame margin in case the style has changed
1707 frame.margin = ::update_margin(client.decorations, style);
1709 // make sure maximized windows have the correct size after a style
1710 // change
1711 remaximize();
1712 } else {
1713 // get the client window geometry as if it was unmanaged
1714 bt::Rect r = frame.rect;
1715 if (client.ewmh.shaded) {
1716 r.setHeight(client.rect.height() + frame.margin.top
1717 + frame.margin.bottom);
1719 r = ::restoreGravity(r, frame.margin, client.wmnormal.win_gravity);
1721 // update the frame margin in case the style has changed
1722 frame.margin = ::update_margin(client.decorations, style);
1724 // get the frame window geometry from the client window geometry
1725 // calculated above
1726 r = ::applyGravity(r, frame.margin, client.wmnormal.win_gravity);
1727 if (client.ewmh.shaded) {
1728 frame.rect = r;
1730 positionWindows();
1731 decorate();
1733 // keep the window shaded
1734 frame.rect.setHeight(style.title_height);
1735 XResizeWindow(blackbox->XDisplay(), frame.window,
1736 frame.rect.width(), frame.rect.height());
1737 } else {
1738 // trick configure into working
1739 frame.rect = bt::Rect();
1740 configure(r);
1744 ungrabButtons();
1745 grabButtons();
1749 void BlackboxWindow::grabButtons(void) {
1750 if (blackbox->resource().focusModel() == ClickToFocusModel
1751 || blackbox->resource().clickRaise())
1752 // grab button 1 for changing focus/raising
1753 blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
1754 GrabModeSync, GrabModeSync, frame.plate, None,
1755 blackbox->resource().allowScrollLock());
1757 if (hasWindowFunction(WindowFunctionMove))
1758 blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
1759 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1760 GrabModeAsync, frame.window,
1761 blackbox->resource().cursors().move,
1762 blackbox->resource().allowScrollLock());
1763 if (hasWindowFunction(WindowFunctionResize))
1764 blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
1765 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1766 GrabModeAsync, frame.window,
1767 None, blackbox->resource().allowScrollLock());
1768 // alt+middle lowers the window
1769 blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
1770 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
1771 frame.window, None,
1772 blackbox->resource().allowScrollLock());
1774 blackbox->grabButton(Button3, Mod4Mask, frame.window, True,
1775 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
1776 frame.window, None,
1777 blackbox->resource().allowScrollLock());
1781 void BlackboxWindow::ungrabButtons(void) {
1782 blackbox->ungrabButton(Button1, 0, frame.plate);
1783 blackbox->ungrabButton(Button1, Mod1Mask, frame.window);
1784 blackbox->ungrabButton(Button2, Mod1Mask, frame.window);
1785 blackbox->ungrabButton(Button3, Mod1Mask, frame.window);
1786 blackbox->ungrabButton(Button3, Mod4Mask, frame.window);
1790 void BlackboxWindow::positionWindows(void) {
1791 const WindowStyle &style = _screen->resource().windowStyle();
1792 const unsigned int bw = (hasWindowDecoration(WindowDecorationBorder)
1793 ? style.frame_border_width
1794 : 0);
1796 XMoveResizeWindow(blackbox->XDisplay(), frame.plate,
1797 frame.margin.left - bw,
1798 frame.margin.top - bw,
1799 client.rect.width(), client.rect.height());
1800 XSetWindowBorderWidth(blackbox->XDisplay(), frame.plate, bw);
1801 XMoveResizeWindow(blackbox->XDisplay(), client.window,
1802 0, 0, client.rect.width(), client.rect.height());
1803 // ensure client.rect contains the real location
1804 client.rect.setPos(frame.rect.left() + frame.margin.left,
1805 frame.rect.top() + frame.margin.top);
1807 if (client.decorations & WindowDecorationTitlebar) {
1808 if (frame.title == None) createTitlebar();
1810 XMoveResizeWindow(blackbox->XDisplay(), frame.title,
1811 0, 0, frame.rect.width(), style.title_height);
1813 positionButtons();
1814 XMapSubwindows(blackbox->XDisplay(), frame.title);
1815 XMapWindow(blackbox->XDisplay(), frame.title);
1816 } else if (frame.title) {
1817 destroyTitlebar();
1820 if (client.decorations & WindowDecorationHandle) {
1821 if (frame.handle == None) createHandle();
1823 // use client.rect here so the value is correct even if shaded
1824 XMoveResizeWindow(blackbox->XDisplay(), frame.handle,
1825 0, client.rect.height() + frame.margin.top,
1826 frame.rect.width(), style.handle_height);
1828 if (client.decorations & WindowDecorationGrip) {
1829 if (frame.left_grip == None || frame.right_grip == None) createGrips();
1831 XMoveResizeWindow(blackbox->XDisplay(), frame.left_grip, 0, 0,
1832 style.grip_width, style.handle_height);
1834 const int nx = frame.rect.width() - style.grip_width;
1835 XMoveResizeWindow(blackbox->XDisplay(), frame.right_grip, nx, 0,
1836 style.grip_width, style.handle_height);
1838 XMapSubwindows(blackbox->XDisplay(), frame.handle);
1839 } else {
1840 destroyGrips();
1843 XMapWindow(blackbox->XDisplay(), frame.handle);
1844 } else if (frame.handle) {
1845 destroyHandle();
1851 * This function is responsible for updating both the client and the
1852 * frame rectangles. According to the ICCCM a client message is not
1853 * sent for a resize, only a move.
1855 void BlackboxWindow::configure(int dx, int dy,
1856 unsigned int dw, unsigned int dh) {
1857 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1858 ! client.state.moving);
1860 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1861 frame.rect.setRect(dx, dy, dw, dh);
1863 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1864 frame.rect.setPos(0, 0);
1866 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1867 frame.rect.top() + frame.margin.top,
1868 frame.rect.right() - frame.margin.right,
1869 frame.rect.bottom() - frame.margin.bottom);
1871 #ifdef SHAPE
1872 if (client.state.shaped)
1873 configureShape();
1874 #endif // SHAPE
1876 XMoveResizeWindow(blackbox->XDisplay(), frame.window,
1877 frame.rect.x(), frame.rect.y(),
1878 frame.rect.width(), frame.rect.height());
1880 positionWindows();
1881 decorate();
1882 redrawWindowFrame();
1883 } else {
1884 frame.rect.setPos(dx, dy);
1886 XMoveWindow(blackbox->XDisplay(), frame.window,
1887 frame.rect.x(), frame.rect.y());
1889 we may have been called just after an opaque window move, so
1890 even though the old coords match the new ones no ConfigureNotify
1891 has been sent yet. There are likely other times when this will
1892 be relevant as well.
1894 if (! client.state.moving) send_event = True;
1897 if (send_event) {
1898 // if moving, the update and event will occur when the move finishes
1899 client.rect.setPos(frame.rect.left() + frame.margin.left,
1900 frame.rect.top() + frame.margin.top);
1902 XEvent event;
1903 event.type = ConfigureNotify;
1905 event.xconfigure.display = blackbox->XDisplay();
1906 event.xconfigure.event = client.window;
1907 event.xconfigure.window = client.window;
1908 event.xconfigure.x = client.rect.x();
1909 event.xconfigure.y = client.rect.y();
1910 event.xconfigure.width = client.rect.width();
1911 event.xconfigure.height = client.rect.height();
1912 event.xconfigure.border_width = client.old_bw;
1913 event.xconfigure.above = frame.window;
1914 event.xconfigure.override_redirect = False;
1916 XSendEvent(blackbox->XDisplay(), client.window, False,
1917 StructureNotifyMask, &event);
1922 #ifdef SHAPE
1923 void BlackboxWindow::configureShape(void) {
1924 XShapeCombineShape(blackbox->XDisplay(), frame.window, ShapeBounding,
1925 frame.margin.left, frame.margin.top,
1926 client.window, ShapeBounding, ShapeSet);
1928 int num = 0;
1929 XRectangle xrect[2];
1931 const WindowStyle &style = _screen->resource().windowStyle();
1932 if (client.decorations & WindowDecorationTitlebar) {
1933 xrect[0].x = xrect[0].y = 0;
1934 xrect[0].width = frame.rect.width();
1935 xrect[0].height = style.title_height;
1936 ++num;
1939 if (client.decorations & WindowDecorationHandle) {
1940 xrect[1].x = 0;
1941 xrect[1].y = client.rect.height() + frame.margin.top;
1942 xrect[1].width = frame.rect.width();
1943 xrect[1].height = style.handle_height;
1944 ++num;
1947 XShapeCombineRectangles(blackbox->XDisplay(), frame.window,
1948 ShapeBounding, 0, 0, xrect, num,
1949 ShapeUnion, Unsorted);
1951 #endif // SHAPE
1954 void BlackboxWindow::addTransient(BlackboxWindow *win)
1955 { client.transientList.push_front(win); }
1958 void BlackboxWindow::removeTransient(BlackboxWindow *win)
1959 { client.transientList.remove(win); }
1962 BlackboxWindow *BlackboxWindow::findTransientFor(void) const {
1963 BlackboxWindow *win = 0;
1964 if (isTransient()) {
1965 win = blackbox->findWindow(client.transient_for);
1966 if (win && win->_screen != _screen)
1967 win = 0;
1969 return win;
1974 walk up to either 1) a non-transient window 2) a group transient,
1975 watching out for a circular chain
1977 this function returns zero for non-transient windows
1979 BlackboxWindow *BlackboxWindow::findNonTransientParent(void) const {
1980 BlackboxWindowList seen;
1981 seen.push_back(const_cast<BlackboxWindow *>(this));
1983 BlackboxWindow *w = findTransientFor();
1984 if (!w)
1985 return 0;
1987 while (w->isTransient() && !w->isGroupTransient()) {
1988 seen.push_back(w);
1989 BlackboxWindow * const tmp = w->findTransientFor();
1990 if (!tmp)
1991 break;
1992 if (std::find(seen.begin(), seen.end(), tmp) != seen.end()) {
1993 // circular transient chain
1994 break;
1996 w = tmp;
1998 return w;
2003 Returns a list of all transients. This is recursive, so it returns
2004 all transients of transients as well.
2006 BlackboxWindowList BlackboxWindow::buildFullTransientList(void) const {
2007 BlackboxWindowList all = client.transientList;
2008 BlackboxWindowList::const_iterator it = client.transientList.begin(),
2009 end = client.transientList.end();
2010 for (; it != end; ++it) {
2011 BlackboxWindowList x = (*it)->buildFullTransientList();
2012 all.splice(all.end(), x);
2014 return all;
2018 BWindowGroup *BlackboxWindow::findWindowGroup(void) const {
2019 BWindowGroup *group = 0;
2020 if (client.wmhints.window_group)
2021 group = blackbox->findWindowGroup(client.wmhints.window_group);
2022 return group;
2026 void BlackboxWindow::setWorkspace(unsigned int new_workspace) {
2027 client.ewmh.workspace = new_workspace;
2028 blackbox->ewmh().setWMDesktop(client.window, client.ewmh.workspace);
2032 void BlackboxWindow::changeWorkspace(unsigned int new_workspace,
2033 ChangeWorkspaceOption how) {
2034 if (client.ewmh.workspace == new_workspace)
2035 return;
2037 if (isTransient()) {
2038 BlackboxWindow *win = findTransientFor();
2039 if (win) {
2040 if (win->workspace() != new_workspace) {
2041 win->changeWorkspace(new_workspace, how);
2042 return;
2045 } else {
2046 assert(hasWindowFunction(WindowFunctionChangeWorkspace));
2049 Workspace *ws;
2050 if (workspace() != bt::BSENTINEL) {
2051 ws = _screen->findWorkspace(workspace());
2052 assert(ws != 0);
2053 ws->removeWindow(this);
2056 if (new_workspace != bt::BSENTINEL) {
2057 ws = _screen->findWorkspace(new_workspace);
2058 assert(ws != 0);
2059 ws->addWindow(this);
2062 switch (how) {
2063 case StayOnCurrentWorkspace:
2064 if (isVisible() && workspace() != bt::BSENTINEL
2065 && workspace() != _screen->currentWorkspace()) {
2066 hide();
2067 } else if (!isVisible()
2068 && (workspace() == bt::BSENTINEL
2069 || workspace() == _screen->currentWorkspace())) {
2070 show();
2072 break;
2074 case SwitchToNewWorkspace:
2076 we will change to the new workspace soon, so force this window
2077 to be visible
2079 show();
2080 break;
2083 // change workspace on all transients
2084 if (!client.transientList.empty()) {
2085 BlackboxWindowList::iterator it = client.transientList.begin(),
2086 end = client.transientList.end();
2087 for (; it != end; ++it)
2088 (*it)->changeWorkspace(new_workspace, how);
2093 void BlackboxWindow::changeLayer(StackingList::Layer new_layer) {
2094 if (layer() == new_layer)
2095 return;
2097 bool restack = false;
2098 if (isTransient()) {
2099 BlackboxWindow *win = findTransientFor();
2100 if (win) {
2101 if (win->layer() != new_layer) {
2102 win->changeLayer(new_layer);
2103 return;
2104 } else {
2105 restack = true;
2108 } else {
2109 assert(hasWindowFunction(WindowFunctionChangeLayer));
2110 restack = true;
2113 _screen->stackingList().changeLayer(this, new_layer);
2115 if (!client.transientList.empty()) {
2116 BlackboxWindowList::iterator it = client.transientList.begin();
2117 const BlackboxWindowList::iterator end = client.transientList.end();
2118 for (; it != end; ++it)
2119 (*it)->changeLayer(new_layer);
2122 if (restack)
2123 _screen->restackWindows();
2127 bool BlackboxWindow::setInputFocus(void) {
2128 if (!isVisible())
2129 return false;
2130 if (client.state.focused)
2131 return true;
2133 switch (windowType()) {
2134 case WindowTypeDock:
2136 Many docks have auto-hide features similar to the toolbar and
2137 slit... we don't want these things to be moved to the center of
2138 the screen when switching to an empty workspace.
2140 break;
2142 default:
2143 { const bt::Rect &scr = _screen->screenInfo().rect();
2144 if (!frame.rect.intersects(scr)) {
2145 // client is outside the screen, move it to the center
2146 configure(scr.x() + (scr.width() - frame.rect.width()) / 2,
2147 scr.y() + (scr.height() - frame.rect.height()) / 2,
2148 frame.rect.width(), frame.rect.height());
2150 break;
2155 pass focus to any modal transients, giving modal group transients
2156 higher priority
2158 BWindowGroup *group = findWindowGroup();
2159 if (group && !group->transients().empty()) {
2160 BlackboxWindowList::const_iterator it = group->transients().begin(),
2161 end = group->transients().end();
2162 for (; it != end; ++it) {
2163 BlackboxWindow * const tmp = *it;
2164 if (!tmp->isVisible() || !tmp->isModal())
2165 continue;
2166 if (tmp == this) {
2167 // we are the newest modal group transient
2168 break;
2170 if (isTransient()) {
2171 if (tmp == findNonTransientParent()) {
2172 // we are a transient of the modal group transient
2173 break;
2176 return tmp->setInputFocus();
2180 if (!client.transientList.empty()) {
2181 BlackboxWindowList::const_iterator it = client.transientList.begin(),
2182 end = client.transientList.end();
2183 for (; it != end; ++it) {
2184 BlackboxWindow * const tmp = *it;
2185 if (tmp->isVisible() && tmp->isModal())
2186 return tmp->setInputFocus();
2190 switch (windowType()) {
2191 case WindowTypeDock:
2192 return false;
2193 default:
2194 break;
2197 XSetInputFocus(blackbox->XDisplay(), client.window,
2198 RevertToPointerRoot, blackbox->XTime());
2200 if (client.wmprotocols.wm_take_focus) {
2201 XEvent ce;
2202 ce.xclient.type = ClientMessage;
2203 ce.xclient.message_type = blackbox->wmProtocolsAtom();
2204 ce.xclient.display = blackbox->XDisplay();
2205 ce.xclient.window = client.window;
2206 ce.xclient.format = 32;
2207 ce.xclient.data.l[0] = blackbox->wmTakeFocusAtom();
2208 ce.xclient.data.l[1] = blackbox->XTime();
2209 ce.xclient.data.l[2] = 0l;
2210 ce.xclient.data.l[3] = 0l;
2211 ce.xclient.data.l[4] = 0l;
2212 XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
2215 return true;
2219 void BlackboxWindow::show(void) {
2220 if (client.state.visible)
2221 return;
2223 if (client.state.iconic)
2224 _screen->removeIcon(this);
2226 client.state.iconic = false;
2227 client.state.visible = true;
2228 setState(isShaded() ? IconicState : NormalState);
2230 XMapWindow(blackbox->XDisplay(), client.window);
2231 XMapSubwindows(blackbox->XDisplay(), frame.window);
2232 XMapWindow(blackbox->XDisplay(), frame.window);
2234 if (!client.transientList.empty()) {
2235 BlackboxWindowList::iterator it = client.transientList.begin(),
2236 end = client.transientList.end();
2237 for (; it != end; ++it)
2238 (*it)->show();
2241 #ifdef DEBUG
2242 int real_x, real_y;
2243 Window child;
2244 XTranslateCoordinates(blackbox->XDisplay(), client.window,
2245 _screen->screenInfo().rootWindow(),
2246 0, 0, &real_x, &real_y, &child);
2247 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", title().c_str(),
2248 client.rect.left(), client.rect.top(), real_x, real_y);
2249 assert(client.rect.left() == real_x && client.rect.top() == real_y);
2250 #endif
2254 void BlackboxWindow::hide(void) {
2255 if (!client.state.visible)
2256 return;
2258 client.state.visible = false;
2259 setState(client.state.iconic ? IconicState : client.current_state);
2261 XUnmapWindow(blackbox->XDisplay(), frame.window);
2264 * we don't want this XUnmapWindow call to generate an UnmapNotify
2265 * event, so we need to clear the event mask on client.window for a
2266 * split second. HOWEVER, since X11 is asynchronous, the window
2267 * could be destroyed in that split second, leaving us with a ghost
2268 * window... so, we need to do this while the X server is grabbed
2270 blackbox->XGrabServer();
2271 XSelectInput(blackbox->XDisplay(), client.window,
2272 client_window_event_mask & ~StructureNotifyMask);
2273 XUnmapWindow(blackbox->XDisplay(), client.window);
2274 XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
2275 blackbox->XUngrabServer();
2279 void BlackboxWindow::close(void) {
2280 assert(hasWindowFunction(WindowFunctionClose));
2282 XEvent ce;
2283 ce.xclient.type = ClientMessage;
2284 ce.xclient.message_type = blackbox->wmProtocolsAtom();
2285 ce.xclient.display = blackbox->XDisplay();
2286 ce.xclient.window = client.window;
2287 ce.xclient.format = 32;
2288 ce.xclient.data.l[0] = blackbox->wmDeleteWindowAtom();
2289 ce.xclient.data.l[1] = blackbox->XTime();
2290 ce.xclient.data.l[2] = 0l;
2291 ce.xclient.data.l[3] = 0l;
2292 ce.xclient.data.l[4] = 0l;
2293 XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
2297 void BlackboxWindow::activate(void) {
2298 if (workspace() != bt::BSENTINEL
2299 && workspace() != _screen->currentWorkspace())
2300 _screen->setCurrentWorkspace(workspace());
2301 if (client.state.iconic)
2302 show();
2303 if (client.ewmh.shaded)
2304 setShaded(false);
2305 if (setInputFocus())
2306 _screen->raiseWindow(this);
2310 void BlackboxWindow::iconify(void) {
2311 if (client.state.iconic)
2312 return;
2314 if (isTransient()) {
2315 BlackboxWindow *win = findTransientFor();
2316 if (win) {
2317 if (!win->isIconic()) {
2318 win->iconify();
2319 return;
2322 } else {
2323 assert(hasWindowFunction(WindowFunctionIconify));
2326 _screen->addIcon(this);
2328 client.state.iconic = true;
2329 hide();
2331 // iconify all transients
2332 if (!client.transientList.empty()) {
2333 BlackboxWindowList::iterator it = client.transientList.begin(),
2334 end = client.transientList.end();
2335 for (; it != end; ++it)
2336 (*it)->iconify();
2341 void BlackboxWindow::maximize(unsigned int button) {
2342 assert(hasWindowFunction(WindowFunctionMaximize));
2344 // any maximize operation always unshades
2345 client.ewmh.shaded = false;
2346 frame.rect.setHeight(client.rect.height() + frame.margin.top
2347 + frame.margin.bottom);
2349 if (isMaximized()) {
2350 // restore from maximized
2351 client.ewmh.maxh = client.ewmh.maxv = false;
2353 if (!isFullScreen()) {
2355 when a resize is begun, maximize(0) is called to clear any
2356 maximization flags currently set. Otherwise it still thinks
2357 it is maximized. so we do not need to call configure()
2358 because resizing will handle it
2360 if (! client.state.resizing) {
2361 bt::Rect r = ::applyGravity(client.premax,
2362 frame.margin,
2363 client.wmnormal.win_gravity);
2364 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2365 // trick configure into working
2366 frame.rect = bt::Rect();
2367 configure(r);
2370 redrawAllButtons(); // in case it is not called in configure()
2373 updateEWMHState();
2374 updateEWMHAllowedActions();
2375 return;
2378 switch (button) {
2379 case 1:
2380 client.ewmh.maxh = true;
2381 client.ewmh.maxv = true;
2382 break;
2384 case 2:
2385 client.ewmh.maxh = false;
2386 client.ewmh.maxv = true;
2387 break;
2389 case 3:
2390 client.ewmh.maxh = true;
2391 client.ewmh.maxv = false;
2392 break;
2394 default:
2395 assert(0);
2396 break;
2399 if (!isFullScreen()) {
2400 // go go gadget-maximize!
2401 bt::Rect r = _screen->availableArea();
2403 if (!client.ewmh.maxh) {
2404 r.setX(frame.rect.x());
2405 r.setWidth(frame.rect.width());
2407 if (!client.ewmh.maxv) {
2408 r.setY(frame.rect.y());
2409 r.setHeight(frame.rect.height());
2412 // store the current frame geometry, so that we can restore it later
2413 client.premax = ::restoreGravity(frame.rect,
2414 frame.margin,
2415 client.wmnormal.win_gravity);
2417 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2418 // trick configure into working
2419 frame.rect = bt::Rect();
2420 configure(r);
2423 updateEWMHState();
2424 updateEWMHAllowedActions();
2429 re-maximizes the window to take into account availableArea changes.
2431 note that unlike maximize(), the shaded state is preserved.
2433 void BlackboxWindow::remaximize(void) {
2434 if (isShaded()) {
2435 bt::Rect r = _screen->availableArea();
2437 if (!client.ewmh.maxh) {
2438 r.setX(frame.rect.x());
2439 r.setWidth(frame.rect.width());
2441 if (!client.ewmh.maxv) {
2442 r.setY(frame.rect.y());
2443 r.setHeight(frame.rect.height());
2446 frame.rect = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2448 positionWindows();
2449 decorate();
2451 // set the frame rect to the shaded size
2452 const WindowStyle &style = _screen->resource().windowStyle();
2453 frame.rect.setHeight(style.title_height);
2454 XResizeWindow(blackbox->XDisplay(), frame.window,
2455 frame.rect.width(), frame.rect.height());
2456 return;
2459 unsigned int button = 0u;
2460 if (client.ewmh.maxv) {
2461 button = (client.ewmh.maxh) ? 1u : 2u;
2462 } else if (client.ewmh.maxh) {
2463 button = (client.ewmh.maxv) ? 1u : 3u;
2466 // trick maximize() into working
2467 client.ewmh.maxh = client.ewmh.maxv = false;
2468 const bt::Rect tmp = client.premax;
2469 maximize(button);
2470 client.premax = tmp;
2474 void BlackboxWindow::setShaded(bool shaded) {
2475 assert(hasWindowFunction(WindowFunctionShade));
2477 if (client.ewmh.shaded == shaded)
2478 return;
2480 client.ewmh.shaded = shaded;
2481 if (!isShaded()) {
2482 if (isMaximized()) {
2483 remaximize();
2484 } else {
2485 // set the frame rect to the normal size
2486 frame.rect.setHeight(client.rect.height() + frame.margin.top +
2487 frame.margin.bottom);
2489 XResizeWindow(blackbox->XDisplay(), frame.window,
2490 frame.rect.width(), frame.rect.height());
2493 setState(NormalState);
2494 } else {
2495 // set the frame rect to the shaded size
2496 const WindowStyle &style = _screen->resource().windowStyle();
2497 frame.rect.setHeight(style.title_height);
2499 XResizeWindow(blackbox->XDisplay(), frame.window,
2500 frame.rect.width(), frame.rect.height());
2502 setState(IconicState);
2507 void BlackboxWindow::setFullScreen(bool b) {
2508 assert(hasWindowFunction(WindowFunctionFullScreen));
2510 if (client.ewmh.fullscreen == b)
2511 return;
2513 // any fullscreen operation always unshades
2514 client.ewmh.shaded = false;
2515 frame.rect.setHeight(client.rect.height() + frame.margin.top
2516 + frame.margin.bottom);
2518 bool refocus = isFocused();
2519 client.ewmh.fullscreen = b;
2520 if (isFullScreen()) {
2521 // go go gadget-fullscreen!
2522 if (!isMaximized())
2523 client.premax = ::restoreGravity(frame.rect,
2524 frame.margin,
2525 client.wmnormal.win_gravity);
2527 // modify decorations, functions and frame margin
2528 client.decorations = NoWindowDecorations;
2529 client.functions &= ~(WindowFunctionMove |
2530 WindowFunctionResize |
2531 WindowFunctionShade);
2532 const WindowStyle &style = _screen->resource().windowStyle();
2533 frame.margin = ::update_margin(client.decorations, style);
2536 * Note: we don't call ::constrain() here, because many
2537 * applications are broken in this respect. Some specify a
2538 * max-size or aspect-ratio that simply doesn't cover the entire
2539 * screen. Let's try to be smarter than such applications and
2540 * simply cover the entire screen.
2543 // trick configure() into working
2544 frame.rect = bt::Rect();
2545 configure(_screen->screenInfo().rect());
2547 if (isVisible())
2548 changeLayer(StackingList::LayerFullScreen);
2550 updateEWMHState();
2551 updateEWMHAllowedActions();
2552 } else {
2553 // restore from fullscreen
2554 ::update_decorations(client.decorations,
2555 client.functions,
2556 isTransient(),
2557 client.ewmh,
2558 client.motif,
2559 client.wmnormal,
2560 client.wmprotocols);
2561 const WindowStyle &style = _screen->resource().windowStyle();
2562 frame.margin = ::update_margin(client.decorations, style);
2564 if (isVisible())
2565 changeLayer(StackingList::LayerNormal);
2567 if (isMaximized()) {
2568 remaximize();
2569 } else {
2570 bt::Rect r = ::applyGravity(client.premax,
2571 frame.margin,
2572 client.wmnormal.win_gravity);
2573 r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
2575 // trick configure into working
2576 frame.rect = bt::Rect();
2577 configure(r);
2579 updateEWMHState();
2580 updateEWMHAllowedActions();
2584 ungrabButtons();
2585 grabButtons();
2587 if (refocus)
2588 (void) setInputFocus();
2592 void BlackboxWindow::redrawWindowFrame(void) const {
2593 if (client.decorations & WindowDecorationTitlebar) {
2594 redrawTitle();
2595 redrawLabel();
2596 redrawAllButtons();
2599 if (client.decorations & WindowDecorationBorder) {
2600 const WindowStyle &style = _screen->resource().windowStyle();
2601 const bt::Color &c = (isFocused()
2602 ? style.focus.frame_border
2603 : style.unfocus.frame_border);
2604 XSetWindowBorder(blackbox->XDisplay(), frame.plate,
2605 c.pixel(_screen->screenNumber()));
2608 if (client.decorations & WindowDecorationHandle) {
2609 redrawHandle();
2611 if (client.decorations & WindowDecorationGrip)
2612 redrawGrips();
2617 void BlackboxWindow::setFocused(bool focused) {
2618 if (focused == client.state.focused)
2619 return;
2621 client.state.focused = isVisible() ? focused : false;
2623 if (isVisible()) {
2624 redrawWindowFrame();
2626 if (client.state.focused) {
2627 XInstallColormap(blackbox->XDisplay(), client.colormap);
2628 } else {
2629 if (client.ewmh.fullscreen && layer() != StackingList::LayerBelow)
2630 changeLayer(StackingList::LayerBelow);
2636 void BlackboxWindow::setState(unsigned long new_state) {
2637 client.current_state = new_state;
2639 unsigned long state[2];
2640 state[0] = client.current_state;
2641 state[1] = None;
2642 XChangeProperty(blackbox->XDisplay(), client.window,
2643 blackbox->wmStateAtom(), blackbox->wmStateAtom(), 32,
2644 PropModeReplace, (unsigned char *) state, 2);
2646 updateEWMHState();
2647 updateEWMHAllowedActions();
2651 void BlackboxWindow::updateEWMHState() {
2652 const bt::EWMH& ewmh = blackbox->ewmh();
2654 // set _NET_WM_STATE
2655 bt::EWMH::AtomList atoms;
2656 if (isModal())
2657 atoms.push_back(ewmh.wmStateModal());
2658 if (isShaded())
2659 atoms.push_back(ewmh.wmStateShaded());
2660 if (isIconic())
2661 atoms.push_back(ewmh.wmStateHidden());
2662 if (isFullScreen())
2663 atoms.push_back(ewmh.wmStateFullscreen());
2664 if (client.ewmh.maxh)
2665 atoms.push_back(ewmh.wmStateMaximizedHorz());
2666 if (client.ewmh.maxv)
2667 atoms.push_back(ewmh.wmStateMaximizedVert());
2668 if (client.ewmh.skip_taskbar)
2669 atoms.push_back(ewmh.wmStateSkipTaskbar());
2670 if (client.ewmh.skip_pager)
2671 atoms.push_back(ewmh.wmStateSkipPager());
2673 switch (layer()) {
2674 case StackingList::LayerAbove:
2675 atoms.push_back(ewmh.wmStateAbove());
2676 break;
2677 case StackingList::LayerBelow:
2678 atoms.push_back(ewmh.wmStateBelow());
2679 break;
2680 default:
2681 break;
2684 if (atoms.empty())
2685 ewmh.removeProperty(client.window, ewmh.wmState());
2686 else
2687 ewmh.setWMState(client.window, atoms);
2691 void BlackboxWindow::updateEWMHAllowedActions() {
2692 const bt::EWMH& ewmh = blackbox->ewmh();
2694 // set _NET_WM_ALLOWED_ACTIONS
2695 bt::EWMH::AtomList atoms;
2696 if (! client.state.iconic) {
2697 if (hasWindowFunction(WindowFunctionChangeWorkspace))
2698 atoms.push_back(ewmh.wmActionChangeDesktop());
2700 if (hasWindowFunction(WindowFunctionIconify))
2701 atoms.push_back(ewmh.wmActionMinimize());
2703 if (hasWindowFunction(WindowFunctionShade))
2704 atoms.push_back(ewmh.wmActionShade());
2706 if (hasWindowFunction(WindowFunctionMove))
2707 atoms.push_back(ewmh.wmActionMove());
2709 if (hasWindowFunction(WindowFunctionResize))
2710 atoms.push_back(ewmh.wmActionResize());
2712 if (hasWindowFunction(WindowFunctionMaximize)) {
2713 atoms.push_back(ewmh.wmActionMaximizeHorz());
2714 atoms.push_back(ewmh.wmActionMaximizeVert());
2717 atoms.push_back(ewmh.wmActionFullscreen());
2720 if (hasWindowFunction(WindowFunctionClose))
2721 atoms.push_back(ewmh.wmActionClose());
2723 if (atoms.empty())
2724 ewmh.removeProperty(client.window, ewmh.wmAllowedActions());
2725 else
2726 ewmh.setWMAllowedActions(client.window, atoms);
2730 void BlackboxWindow::redrawTitle(void) const {
2731 const WindowStyle &style = _screen->resource().windowStyle();
2732 const bt::Rect u(0, 0, frame.rect.width(), style.title_height);
2733 bt::drawTexture(_screen->screenNumber(),
2734 (client.state.focused
2735 ? style.focus.title
2736 : style.unfocus.title),
2737 frame.title, u, u,
2738 (client.state.focused
2739 ? frame.ftitle
2740 : frame.utitle));
2744 void BlackboxWindow::redrawLabel(void) const {
2745 const WindowStyle &style = _screen->resource().windowStyle();
2746 bt::Rect u(0, 0, frame.label_w, style.label_height);
2747 Pixmap p = (client.state.focused ? frame.flabel : frame.ulabel);
2748 if (p == ParentRelative) {
2749 const bt::Texture &texture =
2750 (isFocused() ? style.focus.title : style.unfocus.title);
2751 int offset = texture.borderWidth();
2752 if (client.decorations & WindowDecorationIconify)
2753 offset += style.button_width + style.title_margin;
2755 const bt::Rect t(-(style.title_margin + offset),
2756 -(style.title_margin + texture.borderWidth()),
2757 frame.rect.width(), style.title_height);
2758 bt::drawTexture(_screen->screenNumber(), texture, frame.label, t, u,
2759 (client.state.focused ? frame.ftitle : frame.utitle));
2760 } else {
2761 bt::drawTexture(_screen->screenNumber(),
2762 (client.state.focused
2763 ? style.focus.label
2764 : style.unfocus.label),
2765 frame.label, u, u, p);
2768 const bt::Pen pen(_screen->screenNumber(),
2769 ((client.state.focused)
2770 ? style.focus.text
2771 : style.unfocus.text));
2772 u.setCoords(u.left() + style.label_margin,
2773 u.top() + style.label_margin,
2774 u.right() - style.label_margin,
2775 u.bottom() - style.label_margin);
2776 bt::drawText(style.font, pen, frame.label, u,
2777 style.alignment, client.visible_title);
2781 void BlackboxWindow::redrawAllButtons(void) const {
2782 if (frame.iconify_button) redrawIconifyButton();
2783 if (frame.maximize_button) redrawMaximizeButton();
2784 if (frame.close_button) redrawCloseButton();
2788 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2789 const WindowStyle &style = _screen->resource().windowStyle();
2790 const bt::Rect u(0, 0, style.button_width, style.button_width);
2791 Pixmap p = (pressed ? frame.pbutton :
2792 (client.state.focused ? frame.fbutton : frame.ubutton));
2793 if (p == ParentRelative) {
2794 const bt::Texture &texture =
2795 (isFocused() ? style.focus.title : style.unfocus.title);
2796 const bt::Rect t(-(style.title_margin + texture.borderWidth()),
2797 -(style.title_margin + texture.borderWidth()),
2798 frame.rect.width(), style.title_height);
2799 bt::drawTexture(_screen->screenNumber(), texture, frame.iconify_button,
2800 t, u, (client.state.focused
2801 ? frame.ftitle
2802 : frame.utitle));
2803 } else {
2804 bt::drawTexture(_screen->screenNumber(),
2805 (pressed ? style.pressed :
2806 (client.state.focused ? style.focus.button :
2807 style.unfocus.button)),
2808 frame.iconify_button, u, u, p);
2811 const bt::Pen pen(_screen->screenNumber(),
2812 (client.state.focused
2813 ? style.focus.foreground
2814 : style.unfocus.foreground));
2815 bt::drawBitmap(style.iconify, pen, frame.iconify_button, u);
2819 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2820 const WindowStyle &style = _screen->resource().windowStyle();
2821 const bt::Rect u(0, 0, style.button_width, style.button_width);
2822 Pixmap p = (pressed ? frame.pbutton :
2823 (client.state.focused ? frame.fbutton : frame.ubutton));
2824 if (p == ParentRelative) {
2825 const bt::Texture &texture =
2826 (isFocused() ? style.focus.title : style.unfocus.title);
2827 int button_w = style.button_width
2828 + style.title_margin + texture.borderWidth();
2829 if (client.decorations & WindowDecorationClose)
2830 button_w *= 2;
2831 const bt::Rect t(-(frame.rect.width() - button_w),
2832 -(style.title_margin + texture.borderWidth()),
2833 frame.rect.width(), style.title_height);
2834 bt::drawTexture(_screen->screenNumber(), texture, frame.maximize_button,
2835 t, u, (client.state.focused
2836 ? frame.ftitle
2837 : frame.utitle));
2838 } else {
2839 bt::drawTexture(_screen->screenNumber(),
2840 (pressed ? style.pressed :
2841 (client.state.focused ? style.focus.button :
2842 style.unfocus.button)),
2843 frame.maximize_button, u, u, p);
2846 const bt::Pen pen(_screen->screenNumber(),
2847 (client.state.focused
2848 ? style.focus.foreground
2849 : style.unfocus.foreground));
2850 bt::drawBitmap(isMaximized() ? style.restore : style.maximize,
2851 pen, frame.maximize_button, u);
2855 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2856 const WindowStyle &style = _screen->resource().windowStyle();
2857 const bt::Rect u(0, 0, style.button_width, style.button_width);
2858 Pixmap p = (pressed ? frame.pbutton :
2859 (client.state.focused ? frame.fbutton : frame.ubutton));
2860 if (p == ParentRelative) {
2861 const bt::Texture &texture =
2862 (isFocused() ? style.focus.title : style.unfocus.title);
2863 const int button_w = style.button_width +
2864 style.title_margin +
2865 texture.borderWidth();
2866 const bt::Rect t(-(frame.rect.width() - button_w),
2867 -(style.title_margin + texture.borderWidth()),
2868 frame.rect.width(), style.title_height);
2869 bt::drawTexture(_screen->screenNumber(),texture, frame.close_button, t, u,
2870 (client.state.focused ? frame.ftitle : frame.utitle));
2871 } else {
2872 bt::drawTexture(_screen->screenNumber(),
2873 (pressed ? style.pressed :
2874 (client.state.focused ? style.focus.button :
2875 style.unfocus.button)),
2876 frame.close_button, u, u, p);
2879 const bt::Pen pen(_screen->screenNumber(),
2880 (client.state.focused
2881 ? style.focus.foreground
2882 : style.unfocus.foreground));
2883 bt::drawBitmap(style.close, pen, frame.close_button, u);
2887 void BlackboxWindow::redrawHandle(void) const {
2888 const WindowStyle &style = _screen->resource().windowStyle();
2889 const bt::Rect u(0, 0, frame.rect.width(), style.handle_height);
2890 bt::drawTexture(_screen->screenNumber(),
2891 (client.state.focused ? style.focus.handle :
2892 style.unfocus.handle),
2893 frame.handle, u, u,
2894 (client.state.focused ? frame.fhandle : frame.uhandle));
2898 void BlackboxWindow::redrawGrips(void) const {
2899 const WindowStyle &style = _screen->resource().windowStyle();
2900 const bt::Rect u(0, 0, style.grip_width, style.handle_height);
2901 Pixmap p = (client.state.focused ? frame.fgrip : frame.ugrip);
2902 if (p == ParentRelative) {
2903 bt::Rect t(0, 0, frame.rect.width(), style.handle_height);
2904 bt::drawTexture(_screen->screenNumber(),
2905 (client.state.focused ? style.focus.handle :
2906 style.unfocus.handle),
2907 frame.right_grip, t, u, p);
2909 t.setPos(-(frame.rect.width() - style.grip_width), 0);
2910 bt::drawTexture(_screen->screenNumber(),
2911 (client.state.focused ? style.focus.handle :
2912 style.unfocus.handle),
2913 frame.right_grip, t, u, p);
2914 } else {
2915 bt::drawTexture(_screen->screenNumber(),
2916 (client.state.focused ? style.focus.grip :
2917 style.unfocus.grip),
2918 frame.left_grip, u, u, p);
2920 bt::drawTexture(_screen->screenNumber(),
2921 (client.state.focused ? style.focus.grip :
2922 style.unfocus.grip),
2923 frame.right_grip, u, u, p);
2928 void
2929 BlackboxWindow::clientMessageEvent(const XClientMessageEvent * const event) {
2930 if (event->format != 32)
2931 return;
2933 const bt::EWMH& ewmh = blackbox->ewmh();
2935 if (event->message_type == blackbox->wmChangeStateAtom()) {
2936 if (event->data.l[0] == IconicState) {
2937 if (hasWindowFunction(WindowFunctionIconify))
2938 iconify();
2939 } else if (event->data.l[0] == NormalState) {
2940 activate();
2942 } else if (event->message_type == ewmh.activeWindow()) {
2943 activate();
2944 } else if (event->message_type == ewmh.closeWindow()) {
2945 if (hasWindowFunction(WindowFunctionClose))
2946 close();
2947 } else if (event->message_type == ewmh.moveresizeWindow()) {
2948 XConfigureRequestEvent request;
2949 request.window = event->window;
2950 request.value_mask =
2951 (event->data.l[0] >> 8) & (CWX | CWY | CWWidth | CWHeight);
2952 request.x = event->data.l[1];
2953 request.y = event->data.l[2];
2954 request.width = event->data.l[3];
2955 request.height = event->data.l[4];
2957 const int gravity = (event->data.l[0] & 0xff);
2958 const int old_gravity = client.wmnormal.win_gravity;
2959 if (event->data.l[0] != 0)
2960 client.wmnormal.win_gravity = gravity;
2962 configureRequestEvent(&request);
2964 client.wmnormal.win_gravity = old_gravity;
2965 } else if (event->message_type == ewmh.wmDesktop()) {
2966 if (hasWindowFunction(WindowFunctionChangeWorkspace)) {
2967 const unsigned int new_workspace = event->data.l[0];
2968 changeWorkspace(new_workspace);
2970 } else if (event->message_type == ewmh.wmState()) {
2971 Atom action = event->data.l[0],
2972 first = event->data.l[1],
2973 second = event->data.l[2];
2975 if (first == ewmh.wmStateModal() || second == ewmh.wmStateModal()) {
2976 if ((action == ewmh.wmStateAdd() ||
2977 (action == ewmh.wmStateToggle() && ! client.ewmh.modal)) &&
2978 isTransient())
2979 client.ewmh.modal = true;
2980 else
2981 client.ewmh.modal = false;
2984 if (hasWindowFunction(WindowFunctionMaximize)) {
2985 int max_horz = 0, max_vert = 0;
2987 if (first == ewmh.wmStateMaximizedHorz() ||
2988 second == ewmh.wmStateMaximizedHorz()) {
2989 max_horz = ((action == ewmh.wmStateAdd()
2990 || (action == ewmh.wmStateToggle()
2991 && !client.ewmh.maxh))
2992 ? 1 : -1);
2995 if (first == ewmh.wmStateMaximizedVert() ||
2996 second == ewmh.wmStateMaximizedVert()) {
2997 max_vert = ((action == ewmh.wmStateAdd()
2998 || (action == ewmh.wmStateToggle()
2999 && !client.ewmh.maxv))
3000 ? 1 : -1);
3003 if (max_horz != 0 || max_vert != 0) {
3004 if (isMaximized())
3005 maximize(0);
3006 unsigned int button = 0u;
3007 if (max_horz == 1 && max_vert != 1)
3008 button = 3u;
3009 else if (max_vert == 1 && max_horz != 1)
3010 button = 2u;
3011 else if (max_vert == 1 && max_horz == 1)
3012 button = 1u;
3013 if (button)
3014 maximize(button);
3018 if (hasWindowFunction(WindowFunctionShade)) {
3019 if (first == ewmh.wmStateShaded() ||
3020 second == ewmh.wmStateShaded()) {
3021 if (action == ewmh.wmStateRemove())
3022 setShaded(false);
3023 else if (action == ewmh.wmStateAdd())
3024 setShaded(true);
3025 else if (action == ewmh.wmStateToggle())
3026 setShaded(!isShaded());
3030 if (first == ewmh.wmStateSkipTaskbar()
3031 || second == ewmh.wmStateSkipTaskbar()
3032 || first == ewmh.wmStateSkipPager()
3033 || second == ewmh.wmStateSkipPager()) {
3034 if (first == ewmh.wmStateSkipTaskbar()
3035 || second == ewmh.wmStateSkipTaskbar()) {
3036 client.ewmh.skip_taskbar = (action == ewmh.wmStateAdd()
3037 || (action == ewmh.wmStateToggle()
3038 && !client.ewmh.skip_taskbar));
3040 if (first == ewmh.wmStateSkipPager()
3041 || second == ewmh.wmStateSkipPager()) {
3042 client.ewmh.skip_pager = (action == ewmh.wmStateAdd()
3043 || (action == ewmh.wmStateToggle()
3044 && !client.ewmh.skip_pager));
3046 // we do nothing with skip_*, but others might... we should at
3047 // least make sure these are present in _NET_WM_STATE
3048 updateEWMHState();
3051 if (first == ewmh.wmStateHidden() ||
3052 second == ewmh.wmStateHidden()) {
3054 ignore _NET_WM_STATE_HIDDEN, the wm sets this state, not the
3055 application
3059 if (hasWindowFunction(WindowFunctionFullScreen)) {
3060 if (first == ewmh.wmStateFullscreen() ||
3061 second == ewmh.wmStateFullscreen()) {
3062 if (action == ewmh.wmStateAdd() ||
3063 (action == ewmh.wmStateToggle() &&
3064 ! client.ewmh.fullscreen)) {
3065 setFullScreen(true);
3066 } else if (action == ewmh.wmStateToggle() ||
3067 action == ewmh.wmStateRemove()) {
3068 setFullScreen(false);
3073 if (hasWindowFunction(WindowFunctionChangeLayer)) {
3074 if (first == ewmh.wmStateAbove() ||
3075 second == ewmh.wmStateAbove()) {
3076 if (action == ewmh.wmStateAdd() ||
3077 (action == ewmh.wmStateToggle() &&
3078 layer() != StackingList::LayerAbove)) {
3079 changeLayer(StackingList::LayerAbove);
3080 } else if (action == ewmh.wmStateToggle() ||
3081 action == ewmh.wmStateRemove()) {
3082 changeLayer(StackingList::LayerNormal);
3086 if (first == ewmh.wmStateBelow() ||
3087 second == ewmh.wmStateBelow()) {
3088 if (action == ewmh.wmStateAdd() ||
3089 (action == ewmh.wmStateToggle() &&
3090 layer() != StackingList::LayerBelow)) {
3091 changeLayer(StackingList::LayerBelow);
3092 } else if (action == ewmh.wmStateToggle() ||
3093 action == ewmh.wmStateRemove()) {
3094 changeLayer(StackingList::LayerNormal);
3102 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent * const event) {
3103 if (event->window != client.window)
3104 return;
3106 #ifdef DEBUG
3107 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
3108 client.window);
3109 #endif // DEBUG
3111 _screen->releaseWindow(this);
3115 void
3116 BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent * const event) {
3117 if (event->window != client.window)
3118 return;
3120 #ifdef DEBUG
3121 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
3122 client.window);
3123 #endif // DEBUG
3125 _screen->releaseWindow(this);
3129 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent * const event) {
3130 if (event->window != client.window || event->parent == frame.plate)
3131 return;
3133 #ifdef DEBUG
3134 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
3135 "0x%lx.\n", client.window, event->parent);
3136 #endif // DEBUG
3139 put the ReparentNotify event back into the queue so that
3140 BlackboxWindow::restore(void) can do the right thing
3142 XEvent replay;
3143 replay.xreparent = *event;
3144 XPutBackEvent(blackbox->XDisplay(), &replay);
3146 _screen->releaseWindow(this);
3150 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent * const event) {
3151 #ifdef DEBUG
3152 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
3153 client.window);
3154 #endif
3156 switch(event->atom) {
3157 case XA_WM_TRANSIENT_FOR: {
3158 if (isTransient()) {
3159 // remove ourselves from our transient_for
3160 BlackboxWindow *win = findTransientFor();
3161 if (win) {
3162 win->removeTransient(this);
3163 } else if (isGroupTransient()) {
3164 BWindowGroup *group = findWindowGroup();
3165 if (group)
3166 group->removeTransient(this);
3170 // determine if this is a transient window
3171 client.transient_for = ::readTransientInfo(blackbox,
3172 client.window,
3173 _screen->screenInfo(),
3174 client.wmhints);
3176 if (isTransient()) {
3177 BlackboxWindow *win = findTransientFor();
3178 if (win) {
3179 // add ourselves to our new transient_for
3180 win->addTransient(this);
3181 changeWorkspace(win->workspace());
3182 changeLayer(win->layer());
3183 } else if (isGroupTransient()) {
3184 BWindowGroup *group = findWindowGroup();
3185 if (group)
3186 group->addTransient(this);
3187 } else {
3188 // broken client
3189 client.transient_for = 0;
3193 ::update_decorations(client.decorations,
3194 client.functions,
3195 isTransient(),
3196 client.ewmh,
3197 client.motif,
3198 client.wmnormal,
3199 client.wmprotocols);
3201 reconfigure();
3202 break;
3205 case XA_WM_HINTS: {
3206 // remove from current window group
3207 BWindowGroup *group = findWindowGroup();
3208 if (group) {
3209 if (isTransient() && !findTransientFor() && isGroupTransient())
3210 group->removeTransient(this);
3211 group->removeWindow(this);
3212 group = 0;
3215 client.wmhints = ::readWMHints(blackbox, client.window);
3217 if (client.wmhints.window_group != None) {
3218 // add to new window group
3219 group = ::update_window_group(client.wmhints.window_group,
3220 blackbox,
3221 this);
3222 if (isTransient() && !findTransientFor() && isGroupTransient()) {
3223 if (group)
3224 group->addTransient(this);
3227 break;
3230 case XA_WM_ICON_NAME: {
3231 client.icon_title = ::readWMIconName(blackbox, client.window);
3232 if (client.state.iconic)
3233 _screen->propagateWindowName(this);
3234 break;
3237 case XA_WM_NAME: {
3238 client.title = ::readWMName(blackbox, client.window);
3240 client.visible_title =
3241 bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
3242 _screen->screenNumber(),
3243 _screen->resource().windowStyle().font);
3244 blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
3246 if (client.decorations & WindowDecorationTitlebar)
3247 redrawLabel();
3249 _screen->propagateWindowName(this);
3250 break;
3253 case XA_WM_NORMAL_HINTS: {
3254 WMNormalHints wmnormal = ::readWMNormalHints(blackbox, client.window,
3255 _screen->screenInfo());
3256 if (wmnormal == client.wmnormal) {
3257 // apps like xv and GNU emacs seem to like to repeatedly set
3258 // this property over and over
3259 break;
3262 client.wmnormal = wmnormal;
3264 ::update_decorations(client.decorations,
3265 client.functions,
3266 isTransient(),
3267 client.ewmh,
3268 client.motif,
3269 client.wmnormal,
3270 client.wmprotocols);
3272 reconfigure();
3273 break;
3276 default: {
3277 if (event->atom == blackbox->wmProtocolsAtom()) {
3278 client.wmprotocols = ::readWMProtocols(blackbox, client.window);
3280 ::update_decorations(client.decorations,
3281 client.functions,
3282 isTransient(),
3283 client.ewmh,
3284 client.motif,
3285 client.wmnormal,
3286 client.wmprotocols);
3288 reconfigure();
3289 } else if (event->atom == blackbox->motifWmHintsAtom()) {
3290 client.motif = ::readMotifWMHints(blackbox, client.window);
3292 ::update_decorations(client.decorations,
3293 client.functions,
3294 isTransient(),
3295 client.ewmh,
3296 client.motif,
3297 client.wmnormal,
3298 client.wmprotocols);
3300 reconfigure();
3301 } else if (event->atom == blackbox->ewmh().wmStrut()) {
3302 if (! client.strut) {
3303 client.strut = new bt::EWMH::Strut;
3304 _screen->addStrut(client.strut);
3307 blackbox->ewmh().readWMStrut(client.window, client.strut);
3308 if (client.strut->left || client.strut->right ||
3309 client.strut->top || client.strut->bottom) {
3310 _screen->updateStrut();
3311 } else {
3312 _screen->removeStrut(client.strut);
3313 delete client.strut;
3314 client.strut = 0;
3318 break;
3320 } // switch
3324 void BlackboxWindow::exposeEvent(const XExposeEvent * const event) {
3325 #ifdef DEBUG
3326 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
3327 #endif
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();
3347 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *
3348 const event) {
3349 if (event->window != client.window || client.state.iconic)
3350 return;
3352 if (event->value_mask & CWBorderWidth)
3353 client.old_bw = event->border_width;
3355 if (event->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
3356 bt::Rect req = frame.rect;
3358 if (event->value_mask & (CWX | CWY)) {
3359 req = ::restoreGravity(req, frame.margin, client.wmnormal.win_gravity);
3361 if (event->value_mask & CWX)
3362 req.setX(event->x);
3363 if (event->value_mask & CWY)
3364 req.setY(event->y);
3366 req = ::applyGravity(req, frame.margin, client.wmnormal.win_gravity);
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);
3376 configure(req);
3379 if (event->value_mask & CWStackMode) {
3380 switch (event->detail) {
3381 case Below:
3382 case BottomIf:
3383 _screen->lowerWindow(this);
3384 break;
3386 case Above:
3387 case TopIf:
3388 default:
3389 _screen->raiseWindow(this);
3390 break;
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
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();
3417 _screen->raiseWindow(this);
3419 if (! client.state.focused)
3420 (void) setInputFocus();
3421 else
3422 XInstallColormap(blackbox->XDisplay(), client.colormap);
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;
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));
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);
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
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();
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();
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();
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());
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
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);
3538 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent * const event) {
3539 if (event->window != frame.window || event->mode != NotifyNormal)
3540 return;
3542 if (blackbox->resource().focusModel() == ClickToFocusModel || !isVisible())
3543 return;
3545 switch (windowType()) {
3546 case WindowTypeDesktop:
3547 case WindowTypeDock:
3548 // these types cannot be focused w/ sloppy focus
3549 return;
3551 default:
3552 break;
3555 XEvent next;
3556 bool leave = False, inferior = False;
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);
3566 if ((! leave || inferior) && ! isFocused())
3567 (void) setInputFocus();
3569 if (blackbox->resource().autoRaise())
3570 timer->start();
3574 void
3575 BlackboxWindow::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
3576 if (!(blackbox->resource().focusModel() == SloppyFocusModel
3577 && blackbox->resource().autoRaise()))
3578 return;
3580 if (timer->isTiming())
3581 timer->stop();
3585 #ifdef SHAPE
3586 void BlackboxWindow::shapeEvent(const XEvent * const /*unused*/)
3587 { if (client.state.shaped) configureShape(); }
3588 #endif // SHAPE
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);
3599 client.state.visible = false;
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).
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);
3612 client.rect = ::restoreGravity(frame.rect, frame.margin,
3613 client.wmnormal.win_gravity);
3615 blackbox->XGrabServer();
3617 XUnmapWindow(blackbox->XDisplay(), frame.window);
3618 XUnmapWindow(blackbox->XDisplay(), client.window);
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());
3635 blackbox->XUngrabServer();
3637 XEvent unused;
3638 if (!XCheckTypedWindowEvent(blackbox->XDisplay(), client.window,
3639 ReparentNotify, &unused)) {
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).
3646 XReparentWindow(blackbox->XDisplay(), client.window,
3647 _screen->screenInfo().rootWindow(),
3648 client.rect.x(), client.rect.y());
3651 if (blackbox->shuttingDown())
3652 XMapWindow(blackbox->XDisplay(), client.window);
3656 // timer for autoraise
3657 void BlackboxWindow::timeout(bt::Timer *)
3658 { _screen->raiseWindow(this); }
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());
3668 client.state.moving = true;
3670 if (! blackbox->resource().opaqueMove()) {
3671 blackbox->XGrabServer();
3673 frame.changing = frame.rect;
3674 _screen->showGeometry(BScreen::Position, frame.changing);
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);
3692 static
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)
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 = abs(wleft - rect.left()),
3705 dinnerright = abs(wright - rect.right()),
3706 dinnertop = abs(wtop - rect.top()),
3707 dinnerbottom = abs(wbottom - rect.bottom()),
3708 douterleft = abs(wright - rect.left()),
3709 douterright = abs(wleft - rect.right()),
3710 doutertop = abs(wbottom - rect.top()),
3711 douterbottom = abs(wtop - rect.bottom());
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));
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));
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 = abs(cwx - crx);
3755 const int cdy = 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));
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();
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;
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 && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
3779 ny = (dy != init_dy && abs(dy) < 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 && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
3784 ny = (dy != init_dy && abs(dy) < abs(ny)) ? dy : ny; dy = init_dy;
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 && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
3797 ny = (dy != init_dy && abs(dy) < abs(ny)) ? dy : ny; dy = init_dy;
3802 *x = (nx != init_dx) ? (*x - nx) : *x;
3803 *y = (ny != init_dy) ? (*y - ny) : *y;
3807 void BlackboxWindow::continueMove(int x_root, int y_root) {
3808 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3810 snapAdjust(&dx, &dy);
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);
3828 frame.changing.setPos(dx, dy);
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);
3838 _screen->showGeometry(BScreen::Position, bt::Rect(dx, dy, 0, 0));
3842 void BlackboxWindow::finishMove() {
3843 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
3845 client.state.moving = false;
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();
3863 configure(frame.changing);
3864 } else {
3865 configure(frame.rect);
3868 _screen->hideGeometry();
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;
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;
3905 // begin a resize
3906 XGrabPointer(blackbox->XDisplay(), window, False,
3907 ButtonMotionMask | ButtonReleaseMask,
3908 GrabModeAsync, GrabModeAsync, None, cursor, blackbox->XTime());
3910 client.state.resizing = true;
3912 frame.changing = constrain(frame.rect, frame.margin, client.wmnormal,
3913 Corner(frame.corner));
3915 if (!blackbox->resource().opaqueResize()) {
3916 blackbox->XGrabServer();
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);
3937 showGeometry(frame.changing);
3941 void BlackboxWindow::continueResize(int x_root, int y_root) {
3942 // continue a resize
3943 const bt::Rect curr = frame.changing;
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;
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;
3991 frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
3992 Corner(frame.corner));
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);
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);
4021 showGeometry(frame.changing);
4026 void BlackboxWindow::finishResize() {
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);
4043 blackbox->XUngrabServer();
4045 // unset maximized state when resized
4046 if (isMaximized())
4047 maximize(0);
4050 client.state.resizing = false;
4052 XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
4054 _screen->hideGeometry();
4056 frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
4057 Corner(frame.corner));
4058 configure(frame.changing);
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.
4069 void BlackboxWindow::showGeometry(const bt::Rect &r) const {
4070 unsigned int w = r.width(), h = r.height();
4072 // remove the window frame
4073 w -= frame.margin.left + frame.margin.right;
4074 h -= frame.margin.top + frame.margin.bottom;
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);
4086 w /= client.wmnormal.width_inc;
4087 h /= client.wmnormal.height_inc;
4090 _screen->showGeometry(BScreen::Size, bt::Rect(0, 0, w, h));
4094 // see my rant above for an explanation of this operator
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);