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>
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()
27 #include <X11/Xutil.h>
31 #include "WindowGroup.hh"
32 #include "Windowmenu.hh"
33 #include "Workspace.hh"
34 #include "blackbox.hh"
37 #include <PixmapCache.hh>
40 #include <X11/Xatom.h>
42 # include <X11/extensions/shape.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;
60 BUT, BUT, BUT, forget about doing this:
64 return; // nothing to do
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
,
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
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
);
129 case WindowTypeDesktop
:
131 case WindowTypeSplash
:
132 decorations
= NoWindowDecorations
;
133 functions
= NoWindowFunctions
;
136 case WindowTypeToolbar
:
138 decorations
&= ~(WindowDecorationHandle
|
139 WindowDecorationGrip
|
140 WindowDecorationBorder
|
141 WindowDecorationIconify
|
142 WindowDecorationMaximize
);
143 functions
&= ~(WindowFunctionResize
|
144 WindowFunctionShade
|
145 WindowFunctionIconify
|
146 WindowFunctionMaximize
|
147 WindowFunctionFullScreen
);
150 case WindowTypeUtility
:
151 decorations
&= ~(WindowDecorationIconify
|
152 WindowDecorationMaximize
);
153 functions
&= ~(WindowFunctionShade
|
154 WindowFunctionIconify
|
155 WindowFunctionMaximize
);
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
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
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
;
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
,
218 BlackboxWindow
*win
) {
219 BWindowGroup
*group
= win
->findWindowGroup();
221 new BWindowGroup(blackbox
, window_group
);
222 group
= win
->findWindowGroup();
225 group
->addWindow(win
);
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.
242 static bt::Rect
constrain(const bt::Rect
&rect
,
243 const bt::EWMH::Strut
&margin
,
244 const WMNormalHints
&wmnormal
,
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
) {
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
) {
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
) {
323 delta
= ((dw
- (dh
* max_asp_x
) / max_asp_y
) * w_inc
) / w_inc
;
324 if (dw
- delta
>= wmnormal
.min_width
) dw
-= delta
;
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();
345 r
.setPos(r
.x() + dx
, r
.y());
349 r
.setPos(r
.x(), r
.y() + dy
);
353 r
.setPos(r
.x() + dx
, r
.y() + dy
);
362 * Positions 'rect' according to the client window geometry and window
365 static bt::Rect
applyGravity(const bt::Rect
&rect
,
366 const bt::EWMH::Strut
&margin
,
370 // apply horizontal window gravity
373 case NorthWestGravity
:
374 case SouthWestGravity
:
382 r
.setX(rect
.x() - (margin
.left
+ margin
.right
) / 2);
385 case NorthEastGravity
:
386 case SouthEastGravity
:
388 r
.setX(rect
.x() - (margin
.left
+ margin
.right
) + 2);
393 r
.setX(rect
.x() - margin
.left
);
397 // apply vertical window gravity
400 case NorthWestGravity
:
401 case NorthEastGravity
:
409 r
.setY(rect
.y() - ((margin
.top
+ margin
.bottom
) / 2));
412 case SouthWestGravity
:
413 case SouthEastGravity
:
415 r
.setY(rect
.y() - (margin
.bottom
+ margin
.top
) + 2);
420 r
.setY(rect
.y() - margin
.top
);
424 r
.setSize(rect
.width() + margin
.left
+ margin
.right
,
425 rect
.height() + margin
.top
+ margin
.bottom
);
431 * The reverse of the applyGravity function.
433 * Positions 'rect' according to the frame window geometry and window
436 static bt::Rect
restoreGravity(const bt::Rect
&rect
,
437 const bt::EWMH::Strut
&margin
,
441 // restore horizontal window gravity
444 case NorthWestGravity
:
445 case SouthWestGravity
:
453 r
.setX(rect
.x() + (margin
.left
+ margin
.right
) / 2);
456 case NorthEastGravity
:
457 case SouthEastGravity
:
459 r
.setX(rect
.x() + (margin
.left
+ margin
.right
) - 2);
464 r
.setX(rect
.x() + margin
.left
);
468 // restore vertical window gravity
471 case NorthWestGravity
:
472 case NorthEastGravity
:
480 r
.setY(rect
.y() + (margin
.top
+ margin
.bottom
) / 2);
483 case SouthWestGravity
:
484 case SouthEastGravity
:
486 r
.setY(rect
.y() + (margin
.top
+ margin
.bottom
) - 2);
491 r
.setY(rect
.y() + margin
.top
);
495 r
.setSize(rect
.width() - margin
.left
- margin
.right
,
496 rect
.height() - margin
.top
- margin
.bottom
);
501 static bt::ustring
readWMName(Blackbox
*blackbox
, Window window
) {
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(),
510 XFree((char *) text_prop
.value
);
515 name
= bt::toUnicode("Unnamed");
521 static bt::ustring
readWMIconName(Blackbox
*blackbox
, Window window
) {
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(),
529 XFree((char *) text_prop
.value
);
534 return bt::ustring();
540 static EWMH
readEWMH(const bt::EWMH
&bewmh
,
542 int currentWorkspace
) {
544 ewmh
.window_type
= WindowTypeNormal
;
545 ewmh
.workspace
= 0; // initialized properly below
550 ewmh
.skip_taskbar
= false;
551 ewmh
.skip_pager
= false;
553 ewmh
.fullscreen
= false;
557 // note: wm_name and wm_icon_name are read separately
561 bt::EWMH::AtomList atoms
;
562 ret
= bewmh
.readWMWindowType(window
, atoms
);
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
);
574 ret
= bewmh
.readWMState(window
, atoms
);
576 bt::EWMH::AtomList::iterator it
= atoms
.begin(), end
= atoms
.end();
577 for (; it
!= end
; ++it
) {
579 if (state
== bewmh
.wmStateModal()) {
581 } else if (state
== bewmh
.wmStateMaximizedVert()) {
583 } else if (state
== bewmh
.wmStateMaximizedHorz()) {
585 } else if (state
== bewmh
.wmStateShaded()) {
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()) {
600 } else if (state
== bewmh
.wmStateBelow()) {
606 switch (ewmh
.window_type
) {
607 case WindowTypeDesktop
:
609 // these types should occupy all workspaces by default
610 ewmh
.workspace
= bt::BSENTINEL
;
614 if (!bewmh
.readWMDesktop(window
, ewmh
.workspace
))
615 ewmh
.workspace
= currentWorkspace
;
624 * Returns the MotifWM hints for the specified window.
626 static MotifHints
readMotifWMHints(Blackbox
*blackbox
, Window window
) {
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
{
637 unsigned long functions
;
638 unsigned long decorations
;
640 static const unsigned int PROP_MWM_HINTS_ELEMENTS
= 3u;
642 MWM_HINTS_FUNCTIONS
= 1<<0,
643 MWM_HINTS_DECORATIONS
= 1<<1
645 enum { // MWM functions
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
664 PropMotifhints
*prop
= 0;
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
,
672 (unsigned char **) &prop
);
674 if (ret
!= Success
|| !prop
|| num
!= PROP_MWM_HINTS_ELEMENTS
) {
675 if (prop
) XFree(prop
);
679 if (prop
->flags
& MWM_HINTS_FUNCTIONS
) {
680 if (prop
->functions
& MWM_FUNC_ALL
) {
681 motif
.functions
= AllWindowFunctions
;
683 // default to the functions that cannot be set through
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
;
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
;
726 motif
.decorations
&= ~WindowDecorationGrip
;
729 if (motif
.decorations
& WindowDecorationTitlebar
) {
730 if (motif
.functions
& WindowFunctionClose
)
731 motif
.decorations
|= WindowDecorationClose
;
733 motif
.decorations
&= ~WindowDecorationClose
;
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
) {
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
;
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
,
774 const bt::ScreenInfo
&screenInfo
) {
775 WMNormalHints wmnormal
;
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();
795 if (! XGetWMNormalHints(blackbox
->XDisplay(), window
, &sizehint
, &unused
))
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
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
;
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
;
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
;
855 * Retrieve which Window Manager Protocols are supported by the client
858 static WMProtocols
readWMProtocols(Blackbox
*blackbox
,
860 WMProtocols protocols
;
861 protocols
.wm_delete_window
= false;
862 protocols
.wm_take_focus
= false;
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;
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
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
,
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
905 if (trans_for
== window
) {
906 // wierd client... treat this window as a normal window
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
;
922 static bool readState(unsigned long ¤t_state
,
925 current_state
= NormalState
;
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
) ||
942 current_state
= static_cast<unsigned long>(state
[0]);
946 XFree((void *) state
);
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));
972 fprintf(stderr
, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w
);
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;
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
) {
1001 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
1004 blackbox
->XUngrabServer();
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
|
1015 XChangeWindowAttributes(blackbox
->XDisplay(), client
.window
,
1016 CWEventMask
|CWDontPropagate
, &attrib_set
);
1018 client
.colormap
= wattrib
.colormap
;
1019 window_number
= bt::BSENTINEL
;
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
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(),
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();
1064 win
->addTransient(this);
1065 client
.ewmh
.workspace
= win
->workspace();
1066 setLayer(win
->layer());
1067 } else if (isGroupTransient()) {
1068 BWindowGroup
*group
= findWindowGroup();
1070 group
->addTransient(this);
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
);
1088 case WindowTypeDock
:
1089 setLayer(StackingList::LayerAbove
);
1090 // fallthrough intended
1093 if (client
.ewmh
.above
)
1094 setLayer(StackingList::LayerAbove
);
1095 else if (client
.ewmh
.below
)
1096 setLayer(StackingList::LayerBelow
);
1100 ::update_decorations(client
.decorations
,
1106 client
.wmprotocols
);
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
);
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
)
1138 group
->removeTransient(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
)
1155 if (client
.decorations
& WindowDecorationHandle
)
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
,
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();
1189 XMapSubwindows(blackbox
->XDisplay(), frame
.window
);
1191 if (isFullScreen()) {
1192 client
.ewmh
.fullscreen
= false; // trick setFullScreen into working
1193 setFullScreen(true);
1195 if (isMaximized()) {
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();
1206 client
.ewmh
.shaded
= false;
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
1216 client
.current_state
= save_state
;
1223 BlackboxWindow::~BlackboxWindow(void) {
1225 fprintf(stderr
, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
1229 if (! timer
) // window not managed...
1232 if (client
.state
.moving
|| client
.state
.resizing
) {
1233 _screen
->hideGeometry();
1234 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
1240 _screen
->removeStrut(client
.strut
);
1241 delete client
.strut
;
1244 BWindowGroup
*group
= findWindowGroup();
1246 if (isTransient()) {
1247 // remove ourselves from our transient_for
1248 BlackboxWindow
*win
= findTransientFor();
1250 win
->removeTransient(this);
1251 } else if (isGroupTransient()) {
1253 group
->removeTransient(this);
1255 client
.transient_for
= 0;
1259 group
->removeWindow(this);
1267 blackbox
->removeEventHandler(client
.window
);
1268 blackbox
->removeWindow(client
.window
);
1270 blackbox
->removeEventHandler(frame
.plate
);
1271 blackbox
->removeWindow(frame
.plate
);
1272 XDestroyWindow(blackbox
->XDisplay(), frame
.plate
);
1274 blackbox
->removeEventHandler(frame
.window
);
1275 XDestroyWindow(blackbox
->XDisplay(), frame
.window
);
1280 * Creates a new top level window, with a given location, size, and border
1282 * Returns: the newly created window
1284 Window
BlackboxWindow::createToplevelWindow(void) {
1285 XSetWindowAttributes attrib_create
;
1286 unsigned long create_mask
= CWColormap
| CWOverrideRedirect
| CWEventMask
;
1288 attrib_create
.colormap
= _screen
->screenInfo().colormap();
1289 attrib_create
.override_redirect
= True
;
1290 attrib_create
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
1292 return XCreateWindow(blackbox
->XDisplay(),
1293 _screen
->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
1294 _screen
->screenInfo().depth(), InputOutput
,
1295 _screen
->screenInfo().visual(),
1296 create_mask
, &attrib_create
);
1301 * Creates a child window, and optionally associates a given cursor with
1304 Window
BlackboxWindow::createChildWindow(Window parent
,
1305 unsigned long event_mask
,
1307 XSetWindowAttributes attrib_create
;
1308 unsigned long create_mask
= CWEventMask
;
1310 attrib_create
.event_mask
= event_mask
;
1313 create_mask
|= CWCursor
;
1314 attrib_create
.cursor
= cursor
;
1317 return XCreateWindow(blackbox
->XDisplay(), parent
, 0, 0, 1, 1, 0,
1318 _screen
->screenInfo().depth(), InputOutput
,
1319 _screen
->screenInfo().visual(),
1320 create_mask
, &attrib_create
);
1325 * Reparents the client window into the newly created frame.
1327 * Note: the server must be grabbed before calling this function.
1329 void BlackboxWindow::associateClientWindow(void) {
1330 XSetWindowBorderWidth(blackbox
->XDisplay(), client
.window
, 0);
1331 XChangeSaveSet(blackbox
->XDisplay(), client
.window
, SetModeInsert
);
1333 XSelectInput(blackbox
->XDisplay(), frame
.plate
,
1334 FocusChangeMask
| SubstructureRedirectMask
);
1336 XSelectInput(blackbox
->XDisplay(), client
.window
,
1337 client_window_event_mask
& ~StructureNotifyMask
);
1338 XReparentWindow(blackbox
->XDisplay(), client
.window
, frame
.plate
, 0, 0);
1339 XSelectInput(blackbox
->XDisplay(), client
.window
, client_window_event_mask
);
1342 if (blackbox
->hasShapeExtensions()) {
1343 XShapeSelectInput(blackbox
->XDisplay(), client
.window
,
1346 Bool shaped
= False
;
1350 XShapeQueryExtents(blackbox
->XDisplay(), client
.window
, &shaped
,
1351 &foo
, &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
,
1353 client
.state
.shaped
= shaped
;
1359 void BlackboxWindow::decorate(void) {
1360 const WindowStyle
&style
= _screen
->resource().windowStyle();
1361 if (client
.decorations
& WindowDecorationTitlebar
) {
1362 // render focused button texture
1364 bt::PixmapCache::find(_screen
->screenNumber(),
1370 // render unfocused button texture
1372 bt::PixmapCache::find(_screen
->screenNumber(),
1373 style
.unfocus
.button
,
1378 // render pressed button texture
1380 bt::PixmapCache::find(_screen
->screenNumber(),
1386 // render focused titlebar texture
1388 bt::PixmapCache::find(_screen
->screenNumber(),
1394 // render unfocused titlebar texture
1396 bt::PixmapCache::find(_screen
->screenNumber(),
1397 style
.unfocus
.title
,
1402 // render focused label texture
1404 bt::PixmapCache::find(_screen
->screenNumber(),
1410 // render unfocused label texture
1412 bt::PixmapCache::find(_screen
->screenNumber(),
1413 style
.unfocus
.label
,
1419 if (client
.decorations
& WindowDecorationHandle
) {
1421 bt::PixmapCache::find(_screen
->screenNumber(),
1424 style
.handle_height
,
1428 bt::PixmapCache::find(_screen
->screenNumber(),
1429 style
.unfocus
.handle
,
1431 style
.handle_height
,
1435 if (client
.decorations
& WindowDecorationGrip
) {
1437 bt::PixmapCache::find(_screen
->screenNumber(),
1440 style
.handle_height
,
1444 bt::PixmapCache::find(_screen
->screenNumber(),
1447 style
.handle_height
,
1453 void BlackboxWindow::createHandle(void) {
1454 frame
.handle
= createChildWindow(frame
.window
,
1455 ButtonPressMask
| ButtonReleaseMask
|
1456 ButtonMotionMask
| ExposureMask
);
1457 blackbox
->insertEventHandler(frame
.handle
, this);
1459 if (client
.decorations
& WindowDecorationGrip
)
1464 void BlackboxWindow::destroyHandle(void) {
1465 if (frame
.left_grip
|| frame
.right_grip
)
1468 if (frame
.fhandle
) bt::PixmapCache::release(frame
.fhandle
);
1469 if (frame
.uhandle
) bt::PixmapCache::release(frame
.uhandle
);
1471 frame
.fhandle
= frame
.uhandle
= None
;
1473 blackbox
->removeEventHandler(frame
.handle
);
1474 XDestroyWindow(blackbox
->XDisplay(), frame
.handle
);
1475 frame
.handle
= None
;
1479 void BlackboxWindow::createGrips(void) {
1481 createChildWindow(frame
.handle
,
1482 ButtonPressMask
| ButtonReleaseMask
|
1483 ButtonMotionMask
| ExposureMask
,
1484 blackbox
->resource().cursors().resize_bottom_left
);
1485 blackbox
->insertEventHandler(frame
.left_grip
, this);
1488 createChildWindow(frame
.handle
,
1489 ButtonPressMask
| ButtonReleaseMask
|
1490 ButtonMotionMask
| ExposureMask
,
1491 blackbox
->resource().cursors().resize_bottom_right
);
1492 blackbox
->insertEventHandler(frame
.right_grip
, this);
1496 void BlackboxWindow::destroyGrips(void) {
1497 if (frame
.fgrip
) bt::PixmapCache::release(frame
.fgrip
);
1498 if (frame
.ugrip
) bt::PixmapCache::release(frame
.ugrip
);
1500 frame
.fgrip
= frame
.ugrip
= None
;
1502 blackbox
->removeEventHandler(frame
.left_grip
);
1503 blackbox
->removeEventHandler(frame
.right_grip
);
1505 XDestroyWindow(blackbox
->XDisplay(), frame
.left_grip
);
1506 XDestroyWindow(blackbox
->XDisplay(), frame
.right_grip
);
1507 frame
.left_grip
= frame
.right_grip
= None
;
1511 void BlackboxWindow::createTitlebar(void) {
1512 frame
.title
= createChildWindow(frame
.window
,
1513 ButtonPressMask
| ButtonReleaseMask
|
1514 ButtonMotionMask
| ExposureMask
);
1515 frame
.label
= createChildWindow(frame
.title
,
1516 ButtonPressMask
| ButtonReleaseMask
|
1517 ButtonMotionMask
| ExposureMask
);
1518 blackbox
->insertEventHandler(frame
.title
, this);
1519 blackbox
->insertEventHandler(frame
.label
, this);
1521 if (client
.decorations
& WindowDecorationIconify
) createIconifyButton();
1522 if (client
.decorations
& WindowDecorationMaximize
) createMaximizeButton();
1523 if (client
.decorations
& WindowDecorationClose
) createCloseButton();
1527 void BlackboxWindow::destroyTitlebar(void) {
1528 if (frame
.close_button
)
1529 destroyCloseButton();
1531 if (frame
.iconify_button
)
1532 destroyIconifyButton();
1534 if (frame
.maximize_button
)
1535 destroyMaximizeButton();
1537 if (frame
.fbutton
) bt::PixmapCache::release(frame
.fbutton
);
1538 if (frame
.ubutton
) bt::PixmapCache::release(frame
.ubutton
);
1539 if (frame
.pbutton
) bt::PixmapCache::release(frame
.pbutton
);
1540 if (frame
.ftitle
) bt::PixmapCache::release(frame
.ftitle
);
1541 if (frame
.utitle
) bt::PixmapCache::release(frame
.utitle
);
1542 if (frame
.flabel
) bt::PixmapCache::release(frame
.flabel
);
1543 if (frame
.ulabel
) bt::PixmapCache::release(frame
.ulabel
);
1545 frame
.fbutton
= frame
.ubutton
= frame
.pbutton
=
1546 frame
.ftitle
= frame
.utitle
=
1547 frame
.flabel
= frame
.ulabel
= None
;
1549 blackbox
->removeEventHandler(frame
.title
);
1550 blackbox
->removeEventHandler(frame
.label
);
1552 XDestroyWindow(blackbox
->XDisplay(), frame
.label
);
1553 XDestroyWindow(blackbox
->XDisplay(), frame
.title
);
1554 frame
.title
= frame
.label
= None
;
1558 void BlackboxWindow::createCloseButton(void) {
1559 if (frame
.title
!= None
) {
1560 frame
.close_button
= createChildWindow(frame
.title
,
1563 ButtonMotionMask
| ExposureMask
);
1564 blackbox
->insertEventHandler(frame
.close_button
, this);
1569 void BlackboxWindow::destroyCloseButton(void) {
1570 blackbox
->removeEventHandler(frame
.close_button
);
1571 XDestroyWindow(blackbox
->XDisplay(), frame
.close_button
);
1572 frame
.close_button
= None
;
1576 void BlackboxWindow::createIconifyButton(void) {
1577 if (frame
.title
!= None
) {
1578 frame
.iconify_button
= createChildWindow(frame
.title
,
1581 ButtonMotionMask
| ExposureMask
);
1582 blackbox
->insertEventHandler(frame
.iconify_button
, this);
1587 void BlackboxWindow::destroyIconifyButton(void) {
1588 blackbox
->removeEventHandler(frame
.iconify_button
);
1589 XDestroyWindow(blackbox
->XDisplay(), frame
.iconify_button
);
1590 frame
.iconify_button
= None
;
1594 void BlackboxWindow::createMaximizeButton(void) {
1595 if (frame
.title
!= None
) {
1596 frame
.maximize_button
= createChildWindow(frame
.title
,
1599 ButtonMotionMask
| ExposureMask
);
1600 blackbox
->insertEventHandler(frame
.maximize_button
, this);
1605 void BlackboxWindow::destroyMaximizeButton(void) {
1606 blackbox
->removeEventHandler(frame
.maximize_button
);
1607 XDestroyWindow(blackbox
->XDisplay(), frame
.maximize_button
);
1608 frame
.maximize_button
= None
;
1612 void BlackboxWindow::positionButtons(bool redecorate_label
) {
1613 // we need to use signed ints here to detect windows that are too small
1614 const WindowStyle
&style
= _screen
->resource().windowStyle();
1615 const int extra
= style
.title_margin
== 0 ?
1616 style
.focus
.button
.borderWidth() : 0,
1617 bw
= style
.button_width
+ style
.title_margin
1619 by
= style
.title_margin
+
1620 style
.focus
.title
.borderWidth();
1621 int lx
= by
, lw
= frame
.rect
.width() - by
;
1623 if (client
.decorations
& WindowDecorationIconify
) {
1624 if (frame
.iconify_button
== None
) createIconifyButton();
1626 XMoveResizeWindow(blackbox
->XDisplay(), frame
.iconify_button
, by
, by
,
1627 style
.button_width
, style
.button_width
);
1628 XMapWindow(blackbox
->XDisplay(), frame
.iconify_button
);
1632 } else if (frame
.iconify_button
) {
1633 destroyIconifyButton();
1636 int bx
= frame
.rect
.width() - bw
1637 - style
.focus
.title
.borderWidth() - extra
;
1639 if (client
.decorations
& WindowDecorationClose
) {
1640 if (frame
.close_button
== None
) createCloseButton();
1642 XMoveResizeWindow(blackbox
->XDisplay(), frame
.close_button
, bx
, by
,
1643 style
.button_width
, style
.button_width
);
1644 XMapWindow(blackbox
->XDisplay(), frame
.close_button
);
1648 } else if (frame
.close_button
) {
1649 destroyCloseButton();
1652 if (client
.decorations
& WindowDecorationMaximize
) {
1653 if (frame
.maximize_button
== None
) createMaximizeButton();
1655 XMoveResizeWindow(blackbox
->XDisplay(), frame
.maximize_button
, bx
, by
,
1656 style
.button_width
, style
.button_width
);
1657 XMapWindow(blackbox
->XDisplay(), frame
.maximize_button
);
1661 } else if (frame
.maximize_button
) {
1662 destroyMaximizeButton();
1666 frame
.label_w
= lw
- by
;
1667 XMoveResizeWindow(blackbox
->XDisplay(), frame
.label
, lx
, by
,
1668 frame
.label_w
, style
.label_height
);
1669 XMapWindow(blackbox
->XDisplay(), frame
.label
);
1671 if (redecorate_label
) {
1673 bt::PixmapCache::find(_screen
->screenNumber(),
1675 frame
.label_w
, style
.label_height
,
1678 bt::PixmapCache::find(_screen
->screenNumber(),
1679 style
.unfocus
.label
,
1680 frame
.label_w
, style
.label_height
,
1684 const bt::ustring ellided
=
1685 bt::ellideText(client
.title
, frame
.label_w
, bt::toUnicode("..."),
1686 _screen
->screenNumber(), style
.font
);
1688 if (ellided
!= client
.visible_title
) {
1689 client
.visible_title
= ellided
;
1690 blackbox
->ewmh().setWMVisibleName(client
.window
, client
.visible_title
);
1694 XUnmapWindow(blackbox
->XDisplay(), frame
.label
);
1702 void BlackboxWindow::reconfigure(void) {
1703 const WindowStyle
&style
= _screen
->resource().windowStyle();
1704 if (isMaximized()) {
1705 // update the frame margin in case the style has changed
1706 frame
.margin
= ::update_margin(client
.decorations
, style
);
1708 // make sure maximized windows have the correct size after a style
1712 // get the client window geometry as if it was unmanaged
1713 bt::Rect r
= frame
.rect
;
1714 if (client
.ewmh
.shaded
) {
1715 r
.setHeight(client
.rect
.height() + frame
.margin
.top
1716 + frame
.margin
.bottom
);
1718 r
= ::restoreGravity(r
, frame
.margin
, client
.wmnormal
.win_gravity
);
1720 // update the frame margin in case the style has changed
1721 frame
.margin
= ::update_margin(client
.decorations
, style
);
1723 // get the frame window geometry from the client window geometry
1725 r
= ::applyGravity(r
, frame
.margin
, client
.wmnormal
.win_gravity
);
1726 if (client
.ewmh
.shaded
) {
1732 // keep the window shaded
1733 frame
.rect
.setHeight(style
.title_height
);
1734 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
1735 frame
.rect
.width(), frame
.rect
.height());
1737 // trick configure into working
1738 frame
.rect
= bt::Rect();
1748 void BlackboxWindow::grabButtons(void) {
1749 if (blackbox
->resource().focusModel() == ClickToFocusModel
1750 || blackbox
->resource().clickRaise())
1751 // grab button 1 for changing focus/raising
1752 blackbox
->grabButton(Button1
, 0, frame
.plate
, True
, ButtonPressMask
,
1753 GrabModeSync
, GrabModeSync
, frame
.plate
, None
,
1754 blackbox
->resource().allowScrollLock());
1756 if (hasWindowFunction(WindowFunctionMove
))
1757 blackbox
->grabButton(Button1
, Mod1Mask
, frame
.window
, True
,
1758 ButtonReleaseMask
| ButtonMotionMask
, GrabModeAsync
,
1759 GrabModeAsync
, frame
.window
,
1760 blackbox
->resource().cursors().move
,
1761 blackbox
->resource().allowScrollLock());
1762 if (hasWindowFunction(WindowFunctionResize
))
1763 blackbox
->grabButton(Button3
, Mod1Mask
, frame
.window
, True
,
1764 ButtonReleaseMask
| ButtonMotionMask
, GrabModeAsync
,
1765 GrabModeAsync
, frame
.window
,
1766 None
, blackbox
->resource().allowScrollLock());
1767 // alt+middle lowers the window
1768 blackbox
->grabButton(Button2
, Mod1Mask
, frame
.window
, True
,
1769 ButtonReleaseMask
, GrabModeAsync
, GrabModeAsync
,
1771 blackbox
->resource().allowScrollLock());
1773 blackbox
->grabButton(Button3
, Mod4Mask
, frame
.window
, True
,
1774 ButtonReleaseMask
, GrabModeAsync
, GrabModeAsync
,
1776 blackbox
->resource().allowScrollLock());
1780 void BlackboxWindow::ungrabButtons(void) {
1781 blackbox
->ungrabButton(Button1
, 0, frame
.plate
);
1782 blackbox
->ungrabButton(Button1
, Mod1Mask
, frame
.window
);
1783 blackbox
->ungrabButton(Button2
, Mod1Mask
, frame
.window
);
1784 blackbox
->ungrabButton(Button3
, Mod1Mask
, frame
.window
);
1785 blackbox
->ungrabButton(Button3
, Mod4Mask
, frame
.window
);
1789 void BlackboxWindow::positionWindows(void) {
1790 const WindowStyle
&style
= _screen
->resource().windowStyle();
1791 const unsigned int bw
= (hasWindowDecoration(WindowDecorationBorder
)
1792 ? style
.frame_border_width
1795 XMoveResizeWindow(blackbox
->XDisplay(), frame
.plate
,
1796 frame
.margin
.left
- bw
,
1797 frame
.margin
.top
- bw
,
1798 client
.rect
.width(), client
.rect
.height());
1799 XSetWindowBorderWidth(blackbox
->XDisplay(), frame
.plate
, bw
);
1800 XMoveResizeWindow(blackbox
->XDisplay(), client
.window
,
1801 0, 0, client
.rect
.width(), client
.rect
.height());
1802 // ensure client.rect contains the real location
1803 client
.rect
.setPos(frame
.rect
.left() + frame
.margin
.left
,
1804 frame
.rect
.top() + frame
.margin
.top
);
1806 if (client
.decorations
& WindowDecorationTitlebar
) {
1807 if (frame
.title
== None
) createTitlebar();
1809 XMoveResizeWindow(blackbox
->XDisplay(), frame
.title
,
1810 0, 0, frame
.rect
.width(), style
.title_height
);
1813 XMapSubwindows(blackbox
->XDisplay(), frame
.title
);
1814 XMapWindow(blackbox
->XDisplay(), frame
.title
);
1815 } else if (frame
.title
) {
1819 if (client
.decorations
& WindowDecorationHandle
) {
1820 if (frame
.handle
== None
) createHandle();
1822 // use client.rect here so the value is correct even if shaded
1823 XMoveResizeWindow(blackbox
->XDisplay(), frame
.handle
,
1824 0, client
.rect
.height() + frame
.margin
.top
,
1825 frame
.rect
.width(), style
.handle_height
);
1827 if (client
.decorations
& WindowDecorationGrip
) {
1828 if (frame
.left_grip
== None
|| frame
.right_grip
== None
) createGrips();
1830 XMoveResizeWindow(blackbox
->XDisplay(), frame
.left_grip
, 0, 0,
1831 style
.grip_width
, style
.handle_height
);
1833 const int nx
= frame
.rect
.width() - style
.grip_width
;
1834 XMoveResizeWindow(blackbox
->XDisplay(), frame
.right_grip
, nx
, 0,
1835 style
.grip_width
, style
.handle_height
);
1837 XMapSubwindows(blackbox
->XDisplay(), frame
.handle
);
1842 XMapWindow(blackbox
->XDisplay(), frame
.handle
);
1843 } else if (frame
.handle
) {
1850 * This function is responsible for updating both the client and the
1851 * frame rectangles. According to the ICCCM a client message is not
1852 * sent for a resize, only a move.
1854 void BlackboxWindow::configure(int dx
, int dy
,
1855 unsigned int dw
, unsigned int dh
) {
1856 bool send_event
= ((frame
.rect
.x() != dx
|| frame
.rect
.y() != dy
) &&
1857 ! client
.state
.moving
);
1859 if (dw
!= frame
.rect
.width() || dh
!= frame
.rect
.height()) {
1860 frame
.rect
.setRect(dx
, dy
, dw
, dh
);
1862 if (frame
.rect
.right() <= 0 || frame
.rect
.bottom() <= 0)
1863 frame
.rect
.setPos(0, 0);
1865 client
.rect
.setCoords(frame
.rect
.left() + frame
.margin
.left
,
1866 frame
.rect
.top() + frame
.margin
.top
,
1867 frame
.rect
.right() - frame
.margin
.right
,
1868 frame
.rect
.bottom() - frame
.margin
.bottom
);
1871 if (client
.state
.shaped
)
1875 XMoveResizeWindow(blackbox
->XDisplay(), frame
.window
,
1876 frame
.rect
.x(), frame
.rect
.y(),
1877 frame
.rect
.width(), frame
.rect
.height());
1881 redrawWindowFrame();
1883 frame
.rect
.setPos(dx
, dy
);
1885 XMoveWindow(blackbox
->XDisplay(), frame
.window
,
1886 frame
.rect
.x(), frame
.rect
.y());
1888 we may have been called just after an opaque window move, so
1889 even though the old coords match the new ones no ConfigureNotify
1890 has been sent yet. There are likely other times when this will
1891 be relevant as well.
1893 if (! client
.state
.moving
) send_event
= True
;
1897 // if moving, the update and event will occur when the move finishes
1898 client
.rect
.setPos(frame
.rect
.left() + frame
.margin
.left
,
1899 frame
.rect
.top() + frame
.margin
.top
);
1902 event
.type
= ConfigureNotify
;
1904 event
.xconfigure
.display
= blackbox
->XDisplay();
1905 event
.xconfigure
.event
= client
.window
;
1906 event
.xconfigure
.window
= client
.window
;
1907 event
.xconfigure
.x
= client
.rect
.x();
1908 event
.xconfigure
.y
= client
.rect
.y();
1909 event
.xconfigure
.width
= client
.rect
.width();
1910 event
.xconfigure
.height
= client
.rect
.height();
1911 event
.xconfigure
.border_width
= client
.old_bw
;
1912 event
.xconfigure
.above
= frame
.window
;
1913 event
.xconfigure
.override_redirect
= False
;
1915 XSendEvent(blackbox
->XDisplay(), client
.window
, False
,
1916 StructureNotifyMask
, &event
);
1922 void BlackboxWindow::configureShape(void) {
1923 XShapeCombineShape(blackbox
->XDisplay(), frame
.window
, ShapeBounding
,
1924 frame
.margin
.left
, frame
.margin
.top
,
1925 client
.window
, ShapeBounding
, ShapeSet
);
1928 XRectangle xrect
[2];
1930 const WindowStyle
&style
= _screen
->resource().windowStyle();
1931 if (client
.decorations
& WindowDecorationTitlebar
) {
1932 xrect
[0].x
= xrect
[0].y
= 0;
1933 xrect
[0].width
= frame
.rect
.width();
1934 xrect
[0].height
= style
.title_height
;
1938 if (client
.decorations
& WindowDecorationHandle
) {
1940 xrect
[1].y
= client
.rect
.height() + frame
.margin
.top
;
1941 xrect
[1].width
= frame
.rect
.width();
1942 xrect
[1].height
= style
.handle_height
;
1946 XShapeCombineRectangles(blackbox
->XDisplay(), frame
.window
,
1947 ShapeBounding
, 0, 0, xrect
, num
,
1948 ShapeUnion
, Unsorted
);
1953 void BlackboxWindow::addTransient(BlackboxWindow
*win
)
1954 { client
.transientList
.push_front(win
); }
1957 void BlackboxWindow::removeTransient(BlackboxWindow
*win
)
1958 { client
.transientList
.remove(win
); }
1961 BlackboxWindow
*BlackboxWindow::findTransientFor(void) const {
1962 BlackboxWindow
*win
= 0;
1963 if (isTransient()) {
1964 win
= blackbox
->findWindow(client
.transient_for
);
1965 if (win
&& win
->_screen
!= _screen
)
1973 walk up to either 1) a non-transient window 2) a group transient,
1974 watching out for a circular chain
1976 this function returns zero for non-transient windows
1978 BlackboxWindow
*BlackboxWindow::findNonTransientParent(void) const {
1979 BlackboxWindowList seen
;
1980 seen
.push_back(const_cast<BlackboxWindow
*>(this));
1982 BlackboxWindow
*w
= findTransientFor();
1986 while (w
->isTransient() && !w
->isGroupTransient()) {
1988 BlackboxWindow
* const tmp
= w
->findTransientFor();
1991 if (std::find(seen
.begin(), seen
.end(), tmp
) != seen
.end()) {
1992 // circular transient chain
2002 Returns a list of all transients. This is recursive, so it returns
2003 all transients of transients as well.
2005 BlackboxWindowList
BlackboxWindow::buildFullTransientList(void) const {
2006 BlackboxWindowList all
= client
.transientList
;
2007 BlackboxWindowList::const_iterator it
= client
.transientList
.begin(),
2008 end
= client
.transientList
.end();
2009 for (; it
!= end
; ++it
) {
2010 BlackboxWindowList x
= (*it
)->buildFullTransientList();
2011 all
.splice(all
.end(), x
);
2017 BWindowGroup
*BlackboxWindow::findWindowGroup(void) const {
2018 BWindowGroup
*group
= 0;
2019 if (client
.wmhints
.window_group
)
2020 group
= blackbox
->findWindowGroup(client
.wmhints
.window_group
);
2025 void BlackboxWindow::setWorkspace(unsigned int new_workspace
) {
2026 client
.ewmh
.workspace
= new_workspace
;
2027 blackbox
->ewmh().setWMDesktop(client
.window
, client
.ewmh
.workspace
);
2031 void BlackboxWindow::changeWorkspace(unsigned int new_workspace
,
2032 ChangeWorkspaceOption how
) {
2033 if (client
.ewmh
.workspace
== new_workspace
)
2036 if (isTransient()) {
2037 BlackboxWindow
*win
= findTransientFor();
2039 if (win
->workspace() != new_workspace
) {
2040 win
->changeWorkspace(new_workspace
, how
);
2045 assert(hasWindowFunction(WindowFunctionChangeWorkspace
));
2049 if (workspace() != bt::BSENTINEL
) {
2050 ws
= _screen
->findWorkspace(workspace());
2052 ws
->removeWindow(this);
2055 if (new_workspace
!= bt::BSENTINEL
) {
2056 ws
= _screen
->findWorkspace(new_workspace
);
2058 ws
->addWindow(this);
2062 case StayOnCurrentWorkspace
:
2063 if (isVisible() && workspace() != bt::BSENTINEL
2064 && workspace() != _screen
->currentWorkspace()) {
2066 } else if (!isVisible()
2067 && (workspace() == bt::BSENTINEL
2068 || workspace() == _screen
->currentWorkspace())) {
2073 case SwitchToNewWorkspace
:
2075 we will change to the new workspace soon, so force this window
2082 // change workspace on all transients
2083 if (!client
.transientList
.empty()) {
2084 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2085 end
= client
.transientList
.end();
2086 for (; it
!= end
; ++it
)
2087 (*it
)->changeWorkspace(new_workspace
, how
);
2092 void BlackboxWindow::changeLayer(StackingList::Layer new_layer
) {
2093 if (layer() == new_layer
)
2096 bool restack
= false;
2097 if (isTransient()) {
2098 BlackboxWindow
*win
= findTransientFor();
2100 if (win
->layer() != new_layer
) {
2101 win
->changeLayer(new_layer
);
2108 assert(hasWindowFunction(WindowFunctionChangeLayer
));
2112 _screen
->stackingList().changeLayer(this, new_layer
);
2114 if (!client
.transientList
.empty()) {
2115 BlackboxWindowList::iterator it
= client
.transientList
.begin();
2116 const BlackboxWindowList::iterator end
= client
.transientList
.end();
2117 for (; it
!= end
; ++it
)
2118 (*it
)->changeLayer(new_layer
);
2122 _screen
->restackWindows();
2126 bool BlackboxWindow::setInputFocus(void) {
2129 if (client
.state
.focused
)
2132 switch (windowType()) {
2133 case WindowTypeDock
:
2135 Many docks have auto-hide features similar to the toolbar and
2136 slit... we don't want these things to be moved to the center of
2137 the screen when switching to an empty workspace.
2142 { const bt::Rect
&scr
= _screen
->screenInfo().rect();
2143 if (!frame
.rect
.intersects(scr
)) {
2144 // client is outside the screen, move it to the center
2145 configure(scr
.x() + (scr
.width() - frame
.rect
.width()) / 2,
2146 scr
.y() + (scr
.height() - frame
.rect
.height()) / 2,
2147 frame
.rect
.width(), frame
.rect
.height());
2154 pass focus to any modal transients, giving modal group transients
2157 BWindowGroup
*group
= findWindowGroup();
2158 if (group
&& !group
->transients().empty()) {
2159 BlackboxWindowList::const_iterator it
= group
->transients().begin(),
2160 end
= group
->transients().end();
2161 for (; it
!= end
; ++it
) {
2162 BlackboxWindow
* const tmp
= *it
;
2163 if (!tmp
->isVisible() || !tmp
->isModal())
2166 // we are the newest modal group transient
2169 if (isTransient()) {
2170 if (tmp
== findNonTransientParent()) {
2171 // we are a transient of the modal group transient
2175 return tmp
->setInputFocus();
2179 if (!client
.transientList
.empty()) {
2180 BlackboxWindowList::const_iterator it
= client
.transientList
.begin(),
2181 end
= client
.transientList
.end();
2182 for (; it
!= end
; ++it
) {
2183 BlackboxWindow
* const tmp
= *it
;
2184 if (tmp
->isVisible() && tmp
->isModal())
2185 return tmp
->setInputFocus();
2189 switch (windowType()) {
2190 case WindowTypeDock
:
2196 XSetInputFocus(blackbox
->XDisplay(), client
.window
,
2197 RevertToPointerRoot
, blackbox
->XTime());
2199 if (client
.wmprotocols
.wm_take_focus
) {
2201 ce
.xclient
.type
= ClientMessage
;
2202 ce
.xclient
.message_type
= blackbox
->wmProtocolsAtom();
2203 ce
.xclient
.display
= blackbox
->XDisplay();
2204 ce
.xclient
.window
= client
.window
;
2205 ce
.xclient
.format
= 32;
2206 ce
.xclient
.data
.l
[0] = blackbox
->wmTakeFocusAtom();
2207 ce
.xclient
.data
.l
[1] = blackbox
->XTime();
2208 ce
.xclient
.data
.l
[2] = 0l;
2209 ce
.xclient
.data
.l
[3] = 0l;
2210 ce
.xclient
.data
.l
[4] = 0l;
2211 XSendEvent(blackbox
->XDisplay(), client
.window
, False
, NoEventMask
, &ce
);
2218 void BlackboxWindow::show(void) {
2219 if (client
.state
.visible
)
2222 if (client
.state
.iconic
)
2223 _screen
->removeIcon(this);
2225 client
.state
.iconic
= false;
2226 client
.state
.visible
= true;
2227 setState(isShaded() ? IconicState
: NormalState
);
2229 XMapWindow(blackbox
->XDisplay(), client
.window
);
2230 XMapSubwindows(blackbox
->XDisplay(), frame
.window
);
2231 XMapWindow(blackbox
->XDisplay(), frame
.window
);
2233 if (!client
.transientList
.empty()) {
2234 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2235 end
= client
.transientList
.end();
2236 for (; it
!= end
; ++it
)
2243 XTranslateCoordinates(blackbox
->XDisplay(), client
.window
,
2244 _screen
->screenInfo().rootWindow(),
2245 0, 0, &real_x
, &real_y
, &child
);
2246 fprintf(stderr
, "%s -- assumed: (%d, %d), real: (%d, %d)\n", title().c_str(),
2247 client
.rect
.left(), client
.rect
.top(), real_x
, real_y
);
2248 assert(client
.rect
.left() == real_x
&& client
.rect
.top() == real_y
);
2253 void BlackboxWindow::hide(void) {
2254 if (!client
.state
.visible
)
2257 client
.state
.visible
= false;
2258 setState(client
.state
.iconic
? IconicState
: client
.current_state
);
2260 XUnmapWindow(blackbox
->XDisplay(), frame
.window
);
2263 * we don't want this XUnmapWindow call to generate an UnmapNotify
2264 * event, so we need to clear the event mask on client.window for a
2265 * split second. HOWEVER, since X11 is asynchronous, the window
2266 * could be destroyed in that split second, leaving us with a ghost
2267 * window... so, we need to do this while the X server is grabbed
2269 blackbox
->XGrabServer();
2270 XSelectInput(blackbox
->XDisplay(), client
.window
,
2271 client_window_event_mask
& ~StructureNotifyMask
);
2272 XUnmapWindow(blackbox
->XDisplay(), client
.window
);
2273 XSelectInput(blackbox
->XDisplay(), client
.window
, client_window_event_mask
);
2274 blackbox
->XUngrabServer();
2278 void BlackboxWindow::close(void) {
2279 assert(hasWindowFunction(WindowFunctionClose
));
2282 ce
.xclient
.type
= ClientMessage
;
2283 ce
.xclient
.message_type
= blackbox
->wmProtocolsAtom();
2284 ce
.xclient
.display
= blackbox
->XDisplay();
2285 ce
.xclient
.window
= client
.window
;
2286 ce
.xclient
.format
= 32;
2287 ce
.xclient
.data
.l
[0] = blackbox
->wmDeleteWindowAtom();
2288 ce
.xclient
.data
.l
[1] = blackbox
->XTime();
2289 ce
.xclient
.data
.l
[2] = 0l;
2290 ce
.xclient
.data
.l
[3] = 0l;
2291 ce
.xclient
.data
.l
[4] = 0l;
2292 XSendEvent(blackbox
->XDisplay(), client
.window
, False
, NoEventMask
, &ce
);
2296 void BlackboxWindow::activate(void) {
2297 if (workspace() != bt::BSENTINEL
2298 && workspace() != _screen
->currentWorkspace())
2299 _screen
->setCurrentWorkspace(workspace());
2300 if (client
.state
.iconic
)
2302 if (client
.ewmh
.shaded
)
2304 if (setInputFocus())
2305 _screen
->raiseWindow(this);
2309 void BlackboxWindow::iconify(void) {
2310 if (client
.state
.iconic
)
2313 if (isTransient()) {
2314 BlackboxWindow
*win
= findTransientFor();
2316 if (!win
->isIconic()) {
2322 assert(hasWindowFunction(WindowFunctionIconify
));
2325 _screen
->addIcon(this);
2327 client
.state
.iconic
= true;
2330 // iconify all transients
2331 if (!client
.transientList
.empty()) {
2332 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2333 end
= client
.transientList
.end();
2334 for (; it
!= end
; ++it
)
2340 void BlackboxWindow::maximize(unsigned int button
) {
2341 assert(hasWindowFunction(WindowFunctionMaximize
));
2343 // any maximize operation always unshades
2344 client
.ewmh
.shaded
= false;
2345 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
2346 + frame
.margin
.bottom
);
2348 if (isMaximized()) {
2349 // restore from maximized
2350 client
.ewmh
.maxh
= client
.ewmh
.maxv
= false;
2352 if (!isFullScreen()) {
2354 when a resize is begun, maximize(0) is called to clear any
2355 maximization flags currently set. Otherwise it still thinks
2356 it is maximized. so we do not need to call configure()
2357 because resizing will handle it
2359 if (! client
.state
.resizing
) {
2360 bt::Rect r
= ::applyGravity(client
.premax
,
2362 client
.wmnormal
.win_gravity
);
2363 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2364 // trick configure into working
2365 frame
.rect
= bt::Rect();
2369 redrawAllButtons(); // in case it is not called in configure()
2373 updateEWMHAllowedActions();
2379 client
.ewmh
.maxh
= true;
2380 client
.ewmh
.maxv
= true;
2384 client
.ewmh
.maxh
= false;
2385 client
.ewmh
.maxv
= true;
2389 client
.ewmh
.maxh
= true;
2390 client
.ewmh
.maxv
= false;
2398 if (!isFullScreen()) {
2399 // go go gadget-maximize!
2400 bt::Rect r
= _screen
->availableArea();
2402 if (!client
.ewmh
.maxh
) {
2403 r
.setX(frame
.rect
.x());
2404 r
.setWidth(frame
.rect
.width());
2406 if (!client
.ewmh
.maxv
) {
2407 r
.setY(frame
.rect
.y());
2408 r
.setHeight(frame
.rect
.height());
2411 // store the current frame geometry, so that we can restore it later
2412 client
.premax
= ::restoreGravity(frame
.rect
,
2414 client
.wmnormal
.win_gravity
);
2416 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2417 // trick configure into working
2418 frame
.rect
= bt::Rect();
2423 updateEWMHAllowedActions();
2428 re-maximizes the window to take into account availableArea changes.
2430 note that unlike maximize(), the shaded state is preserved.
2432 void BlackboxWindow::remaximize(void) {
2434 bt::Rect r
= _screen
->availableArea();
2436 if (!client
.ewmh
.maxh
) {
2437 r
.setX(frame
.rect
.x());
2438 r
.setWidth(frame
.rect
.width());
2440 if (!client
.ewmh
.maxv
) {
2441 r
.setY(frame
.rect
.y());
2442 r
.setHeight(frame
.rect
.height());
2445 frame
.rect
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2450 // set the frame rect to the shaded size
2451 const WindowStyle
&style
= _screen
->resource().windowStyle();
2452 frame
.rect
.setHeight(style
.title_height
);
2453 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
2454 frame
.rect
.width(), frame
.rect
.height());
2458 unsigned int button
= 0u;
2459 if (client
.ewmh
.maxv
) {
2460 button
= (client
.ewmh
.maxh
) ? 1u : 2u;
2461 } else if (client
.ewmh
.maxh
) {
2462 button
= (client
.ewmh
.maxv
) ? 1u : 3u;
2465 // trick maximize() into working
2466 client
.ewmh
.maxh
= client
.ewmh
.maxv
= false;
2467 const bt::Rect tmp
= client
.premax
;
2469 client
.premax
= tmp
;
2473 void BlackboxWindow::setShaded(bool shaded
) {
2474 assert(hasWindowFunction(WindowFunctionShade
));
2476 if (client
.ewmh
.shaded
== shaded
)
2479 client
.ewmh
.shaded
= shaded
;
2481 if (isMaximized()) {
2484 // set the frame rect to the normal size
2485 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
+
2486 frame
.margin
.bottom
);
2488 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
2489 frame
.rect
.width(), frame
.rect
.height());
2492 setState(NormalState
);
2494 // set the frame rect to the shaded size
2495 const WindowStyle
&style
= _screen
->resource().windowStyle();
2496 frame
.rect
.setHeight(style
.title_height
);
2498 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
2499 frame
.rect
.width(), frame
.rect
.height());
2501 setState(IconicState
);
2506 void BlackboxWindow::setFullScreen(bool b
) {
2507 assert(hasWindowFunction(WindowFunctionFullScreen
));
2509 if (client
.ewmh
.fullscreen
== b
)
2512 // any fullscreen operation always unshades
2513 client
.ewmh
.shaded
= false;
2514 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
2515 + frame
.margin
.bottom
);
2517 bool refocus
= isFocused();
2518 client
.ewmh
.fullscreen
= b
;
2519 if (isFullScreen()) {
2520 // go go gadget-fullscreen!
2522 client
.premax
= ::restoreGravity(frame
.rect
,
2524 client
.wmnormal
.win_gravity
);
2526 // modify decorations, functions and frame margin
2527 client
.decorations
= NoWindowDecorations
;
2528 client
.functions
&= ~(WindowFunctionMove
|
2529 WindowFunctionResize
|
2530 WindowFunctionShade
);
2531 const WindowStyle
&style
= _screen
->resource().windowStyle();
2532 frame
.margin
= ::update_margin(client
.decorations
, style
);
2535 * Note: we don't call ::constrain() here, because many
2536 * applications are broken in this respect. Some specify a
2537 * max-size or aspect-ratio that simply doesn't cover the entire
2538 * screen. Let's try to be smarter than such applications and
2539 * simply cover the entire screen.
2542 // trick configure() into working
2543 frame
.rect
= bt::Rect();
2544 configure(_screen
->screenInfo().rect());
2547 changeLayer(StackingList::LayerFullScreen
);
2550 updateEWMHAllowedActions();
2552 // restore from fullscreen
2553 ::update_decorations(client
.decorations
,
2559 client
.wmprotocols
);
2560 const WindowStyle
&style
= _screen
->resource().windowStyle();
2561 frame
.margin
= ::update_margin(client
.decorations
, style
);
2564 changeLayer(StackingList::LayerNormal
);
2566 if (isMaximized()) {
2569 bt::Rect r
= ::applyGravity(client
.premax
,
2571 client
.wmnormal
.win_gravity
);
2572 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2574 // trick configure into working
2575 frame
.rect
= bt::Rect();
2579 updateEWMHAllowedActions();
2587 (void) setInputFocus();
2591 void BlackboxWindow::redrawWindowFrame(void) const {
2592 if (client
.decorations
& WindowDecorationTitlebar
) {
2598 if (client
.decorations
& WindowDecorationBorder
) {
2599 const WindowStyle
&style
= _screen
->resource().windowStyle();
2600 const bt::Color
&c
= (isFocused()
2601 ? style
.focus
.frame_border
2602 : style
.unfocus
.frame_border
);
2603 XSetWindowBorder(blackbox
->XDisplay(), frame
.plate
,
2604 c
.pixel(_screen
->screenNumber()));
2607 if (client
.decorations
& WindowDecorationHandle
) {
2610 if (client
.decorations
& WindowDecorationGrip
)
2616 void BlackboxWindow::setFocused(bool focused
) {
2617 if (focused
== client
.state
.focused
)
2620 client
.state
.focused
= isVisible() ? focused
: false;
2623 redrawWindowFrame();
2625 if (client
.state
.focused
) {
2626 XInstallColormap(blackbox
->XDisplay(), client
.colormap
);
2628 if (client
.ewmh
.fullscreen
&& layer() != StackingList::LayerBelow
)
2629 changeLayer(StackingList::LayerBelow
);
2635 void BlackboxWindow::setState(unsigned long new_state
) {
2636 client
.current_state
= new_state
;
2638 unsigned long state
[2];
2639 state
[0] = client
.current_state
;
2641 XChangeProperty(blackbox
->XDisplay(), client
.window
,
2642 blackbox
->wmStateAtom(), blackbox
->wmStateAtom(), 32,
2643 PropModeReplace
, (unsigned char *) state
, 2);
2646 updateEWMHAllowedActions();
2650 void BlackboxWindow::updateEWMHState() {
2651 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2653 // set _NET_WM_STATE
2654 bt::EWMH::AtomList atoms
;
2656 atoms
.push_back(ewmh
.wmStateModal());
2658 atoms
.push_back(ewmh
.wmStateShaded());
2660 atoms
.push_back(ewmh
.wmStateHidden());
2662 atoms
.push_back(ewmh
.wmStateFullscreen());
2663 if (client
.ewmh
.maxh
)
2664 atoms
.push_back(ewmh
.wmStateMaximizedHorz());
2665 if (client
.ewmh
.maxv
)
2666 atoms
.push_back(ewmh
.wmStateMaximizedVert());
2667 if (client
.ewmh
.skip_taskbar
)
2668 atoms
.push_back(ewmh
.wmStateSkipTaskbar());
2669 if (client
.ewmh
.skip_pager
)
2670 atoms
.push_back(ewmh
.wmStateSkipPager());
2673 case StackingList::LayerAbove
:
2674 atoms
.push_back(ewmh
.wmStateAbove());
2676 case StackingList::LayerBelow
:
2677 atoms
.push_back(ewmh
.wmStateBelow());
2684 ewmh
.removeProperty(client
.window
, ewmh
.wmState());
2686 ewmh
.setWMState(client
.window
, atoms
);
2690 void BlackboxWindow::updateEWMHAllowedActions() {
2691 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2693 // set _NET_WM_ALLOWED_ACTIONS
2694 bt::EWMH::AtomList atoms
;
2695 if (! client
.state
.iconic
) {
2696 if (hasWindowFunction(WindowFunctionChangeWorkspace
))
2697 atoms
.push_back(ewmh
.wmActionChangeDesktop());
2699 if (hasWindowFunction(WindowFunctionIconify
))
2700 atoms
.push_back(ewmh
.wmActionMinimize());
2702 if (hasWindowFunction(WindowFunctionShade
))
2703 atoms
.push_back(ewmh
.wmActionShade());
2705 if (hasWindowFunction(WindowFunctionMove
))
2706 atoms
.push_back(ewmh
.wmActionMove());
2708 if (hasWindowFunction(WindowFunctionResize
))
2709 atoms
.push_back(ewmh
.wmActionResize());
2711 if (hasWindowFunction(WindowFunctionMaximize
)) {
2712 atoms
.push_back(ewmh
.wmActionMaximizeHorz());
2713 atoms
.push_back(ewmh
.wmActionMaximizeVert());
2716 atoms
.push_back(ewmh
.wmActionFullscreen());
2719 if (hasWindowFunction(WindowFunctionClose
))
2720 atoms
.push_back(ewmh
.wmActionClose());
2723 ewmh
.removeProperty(client
.window
, ewmh
.wmAllowedActions());
2725 ewmh
.setWMAllowedActions(client
.window
, atoms
);
2729 void BlackboxWindow::redrawTitle(void) const {
2730 const WindowStyle
&style
= _screen
->resource().windowStyle();
2731 const bt::Rect
u(0, 0, frame
.rect
.width(), style
.title_height
);
2732 bt::drawTexture(_screen
->screenNumber(),
2733 (client
.state
.focused
2735 : style
.unfocus
.title
),
2737 (client
.state
.focused
2743 void BlackboxWindow::redrawLabel(void) const {
2744 const WindowStyle
&style
= _screen
->resource().windowStyle();
2745 bt::Rect
u(0, 0, frame
.label_w
, style
.label_height
);
2746 Pixmap p
= (client
.state
.focused
? frame
.flabel
: frame
.ulabel
);
2747 if (p
== ParentRelative
) {
2748 const bt::Texture
&texture
=
2749 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2750 int offset
= texture
.borderWidth();
2751 if (client
.decorations
& WindowDecorationIconify
)
2752 offset
+= style
.button_width
+ style
.title_margin
;
2754 const bt::Rect
t(-(style
.title_margin
+ offset
),
2755 -(style
.title_margin
+ texture
.borderWidth()),
2756 frame
.rect
.width(), style
.title_height
);
2757 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.label
, t
, u
,
2758 (client
.state
.focused
? frame
.ftitle
: frame
.utitle
));
2760 bt::drawTexture(_screen
->screenNumber(),
2761 (client
.state
.focused
2763 : style
.unfocus
.label
),
2764 frame
.label
, u
, u
, p
);
2767 const bt::Pen
pen(_screen
->screenNumber(),
2768 ((client
.state
.focused
)
2770 : style
.unfocus
.text
));
2771 u
.setCoords(u
.left() + style
.label_margin
,
2772 u
.top() + style
.label_margin
,
2773 u
.right() - style
.label_margin
,
2774 u
.bottom() - style
.label_margin
);
2775 bt::drawText(style
.font
, pen
, frame
.label
, u
,
2776 style
.alignment
, client
.visible_title
);
2780 void BlackboxWindow::redrawAllButtons(void) const {
2781 if (frame
.iconify_button
) redrawIconifyButton();
2782 if (frame
.maximize_button
) redrawMaximizeButton();
2783 if (frame
.close_button
) redrawCloseButton();
2787 void BlackboxWindow::redrawIconifyButton(bool pressed
) const {
2788 const WindowStyle
&style
= _screen
->resource().windowStyle();
2789 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2790 Pixmap p
= (pressed
? frame
.pbutton
:
2791 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2792 if (p
== ParentRelative
) {
2793 const bt::Texture
&texture
=
2794 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2795 const bt::Rect
t(-(style
.title_margin
+ texture
.borderWidth()),
2796 -(style
.title_margin
+ texture
.borderWidth()),
2797 frame
.rect
.width(), style
.title_height
);
2798 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.iconify_button
,
2799 t
, u
, (client
.state
.focused
2803 bt::drawTexture(_screen
->screenNumber(),
2804 (pressed
? style
.pressed
:
2805 (client
.state
.focused
? style
.focus
.button
:
2806 style
.unfocus
.button
)),
2807 frame
.iconify_button
, u
, u
, p
);
2810 const bt::Pen
pen(_screen
->screenNumber(),
2811 (client
.state
.focused
2812 ? style
.focus
.foreground
2813 : style
.unfocus
.foreground
));
2814 bt::drawBitmap(style
.iconify
, pen
, frame
.iconify_button
, u
);
2818 void BlackboxWindow::redrawMaximizeButton(bool pressed
) const {
2819 const WindowStyle
&style
= _screen
->resource().windowStyle();
2820 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2821 Pixmap p
= (pressed
? frame
.pbutton
:
2822 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2823 if (p
== ParentRelative
) {
2824 const bt::Texture
&texture
=
2825 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2826 int button_w
= style
.button_width
2827 + style
.title_margin
+ texture
.borderWidth();
2828 if (client
.decorations
& WindowDecorationClose
)
2830 const bt::Rect
t(-(frame
.rect
.width() - button_w
),
2831 -(style
.title_margin
+ texture
.borderWidth()),
2832 frame
.rect
.width(), style
.title_height
);
2833 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.maximize_button
,
2834 t
, u
, (client
.state
.focused
2838 bt::drawTexture(_screen
->screenNumber(),
2839 (pressed
? style
.pressed
:
2840 (client
.state
.focused
? style
.focus
.button
:
2841 style
.unfocus
.button
)),
2842 frame
.maximize_button
, u
, u
, p
);
2845 const bt::Pen
pen(_screen
->screenNumber(),
2846 (client
.state
.focused
2847 ? style
.focus
.foreground
2848 : style
.unfocus
.foreground
));
2849 bt::drawBitmap(isMaximized() ? style
.restore
: style
.maximize
,
2850 pen
, frame
.maximize_button
, u
);
2854 void BlackboxWindow::redrawCloseButton(bool pressed
) const {
2855 const WindowStyle
&style
= _screen
->resource().windowStyle();
2856 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2857 Pixmap p
= (pressed
? frame
.pbutton
:
2858 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2859 if (p
== ParentRelative
) {
2860 const bt::Texture
&texture
=
2861 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2862 const int button_w
= style
.button_width
+
2863 style
.title_margin
+
2864 texture
.borderWidth();
2865 const bt::Rect
t(-(frame
.rect
.width() - button_w
),
2866 -(style
.title_margin
+ texture
.borderWidth()),
2867 frame
.rect
.width(), style
.title_height
);
2868 bt::drawTexture(_screen
->screenNumber(),texture
, frame
.close_button
, t
, u
,
2869 (client
.state
.focused
? frame
.ftitle
: frame
.utitle
));
2871 bt::drawTexture(_screen
->screenNumber(),
2872 (pressed
? style
.pressed
:
2873 (client
.state
.focused
? style
.focus
.button
:
2874 style
.unfocus
.button
)),
2875 frame
.close_button
, u
, u
, p
);
2878 const bt::Pen
pen(_screen
->screenNumber(),
2879 (client
.state
.focused
2880 ? style
.focus
.foreground
2881 : style
.unfocus
.foreground
));
2882 bt::drawBitmap(style
.close
, pen
, frame
.close_button
, u
);
2886 void BlackboxWindow::redrawHandle(void) const {
2887 const WindowStyle
&style
= _screen
->resource().windowStyle();
2888 const bt::Rect
u(0, 0, frame
.rect
.width(), style
.handle_height
);
2889 bt::drawTexture(_screen
->screenNumber(),
2890 (client
.state
.focused
? style
.focus
.handle
:
2891 style
.unfocus
.handle
),
2893 (client
.state
.focused
? frame
.fhandle
: frame
.uhandle
));
2897 void BlackboxWindow::redrawGrips(void) const {
2898 const WindowStyle
&style
= _screen
->resource().windowStyle();
2899 const bt::Rect
u(0, 0, style
.grip_width
, style
.handle_height
);
2900 Pixmap p
= (client
.state
.focused
? frame
.fgrip
: frame
.ugrip
);
2901 if (p
== ParentRelative
) {
2902 bt::Rect
t(0, 0, frame
.rect
.width(), style
.handle_height
);
2903 bt::drawTexture(_screen
->screenNumber(),
2904 (client
.state
.focused
? style
.focus
.handle
:
2905 style
.unfocus
.handle
),
2906 frame
.right_grip
, t
, u
, p
);
2908 t
.setPos(-(frame
.rect
.width() - style
.grip_width
), 0);
2909 bt::drawTexture(_screen
->screenNumber(),
2910 (client
.state
.focused
? style
.focus
.handle
:
2911 style
.unfocus
.handle
),
2912 frame
.right_grip
, t
, u
, p
);
2914 bt::drawTexture(_screen
->screenNumber(),
2915 (client
.state
.focused
? style
.focus
.grip
:
2916 style
.unfocus
.grip
),
2917 frame
.left_grip
, u
, u
, p
);
2919 bt::drawTexture(_screen
->screenNumber(),
2920 (client
.state
.focused
? style
.focus
.grip
:
2921 style
.unfocus
.grip
),
2922 frame
.right_grip
, u
, u
, p
);
2928 BlackboxWindow::clientMessageEvent(const XClientMessageEvent
* const event
) {
2929 if (event
->format
!= 32)
2932 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2934 if (event
->message_type
== blackbox
->wmChangeStateAtom()) {
2935 if (event
->data
.l
[0] == IconicState
) {
2936 if (hasWindowFunction(WindowFunctionIconify
))
2938 } else if (event
->data
.l
[0] == NormalState
) {
2941 } else if (event
->message_type
== ewmh
.activeWindow()) {
2943 } else if (event
->message_type
== ewmh
.closeWindow()) {
2944 if (hasWindowFunction(WindowFunctionClose
))
2946 } else if (event
->message_type
== ewmh
.moveresizeWindow()) {
2947 XConfigureRequestEvent request
;
2948 request
.window
= event
->window
;
2949 request
.value_mask
=
2950 (event
->data
.l
[0] >> 8) & (CWX
| CWY
| CWWidth
| CWHeight
);
2951 request
.x
= event
->data
.l
[1];
2952 request
.y
= event
->data
.l
[2];
2953 request
.width
= event
->data
.l
[3];
2954 request
.height
= event
->data
.l
[4];
2956 const int gravity
= (event
->data
.l
[0] & 0xff);
2957 const int old_gravity
= client
.wmnormal
.win_gravity
;
2958 if (event
->data
.l
[0] != 0)
2959 client
.wmnormal
.win_gravity
= gravity
;
2961 configureRequestEvent(&request
);
2963 client
.wmnormal
.win_gravity
= old_gravity
;
2964 } else if (event
->message_type
== ewmh
.wmDesktop()) {
2965 if (hasWindowFunction(WindowFunctionChangeWorkspace
)) {
2966 const unsigned int new_workspace
= event
->data
.l
[0];
2967 changeWorkspace(new_workspace
);
2969 } else if (event
->message_type
== ewmh
.wmState()) {
2970 Atom action
= event
->data
.l
[0],
2971 first
= event
->data
.l
[1],
2972 second
= event
->data
.l
[2];
2974 if (first
== ewmh
.wmStateModal() || second
== ewmh
.wmStateModal()) {
2975 if ((action
== ewmh
.wmStateAdd() ||
2976 (action
== ewmh
.wmStateToggle() && ! client
.ewmh
.modal
)) &&
2978 client
.ewmh
.modal
= true;
2980 client
.ewmh
.modal
= false;
2983 if (hasWindowFunction(WindowFunctionMaximize
)) {
2984 int max_horz
= 0, max_vert
= 0;
2986 if (first
== ewmh
.wmStateMaximizedHorz() ||
2987 second
== ewmh
.wmStateMaximizedHorz()) {
2988 max_horz
= ((action
== ewmh
.wmStateAdd()
2989 || (action
== ewmh
.wmStateToggle()
2990 && !client
.ewmh
.maxh
))
2994 if (first
== ewmh
.wmStateMaximizedVert() ||
2995 second
== ewmh
.wmStateMaximizedVert()) {
2996 max_vert
= ((action
== ewmh
.wmStateAdd()
2997 || (action
== ewmh
.wmStateToggle()
2998 && !client
.ewmh
.maxv
))
3002 if (max_horz
!= 0 || max_vert
!= 0) {
3005 unsigned int button
= 0u;
3006 if (max_horz
== 1 && max_vert
!= 1)
3008 else if (max_vert
== 1 && max_horz
!= 1)
3010 else if (max_vert
== 1 && max_horz
== 1)
3017 if (hasWindowFunction(WindowFunctionShade
)) {
3018 if (first
== ewmh
.wmStateShaded() ||
3019 second
== ewmh
.wmStateShaded()) {
3020 if (action
== ewmh
.wmStateRemove())
3022 else if (action
== ewmh
.wmStateAdd())
3024 else if (action
== ewmh
.wmStateToggle())
3025 setShaded(!isShaded());
3029 if (first
== ewmh
.wmStateSkipTaskbar()
3030 || second
== ewmh
.wmStateSkipTaskbar()
3031 || first
== ewmh
.wmStateSkipPager()
3032 || second
== ewmh
.wmStateSkipPager()) {
3033 if (first
== ewmh
.wmStateSkipTaskbar()
3034 || second
== ewmh
.wmStateSkipTaskbar()) {
3035 client
.ewmh
.skip_taskbar
= (action
== ewmh
.wmStateAdd()
3036 || (action
== ewmh
.wmStateToggle()
3037 && !client
.ewmh
.skip_taskbar
));
3039 if (first
== ewmh
.wmStateSkipPager()
3040 || second
== ewmh
.wmStateSkipPager()) {
3041 client
.ewmh
.skip_pager
= (action
== ewmh
.wmStateAdd()
3042 || (action
== ewmh
.wmStateToggle()
3043 && !client
.ewmh
.skip_pager
));
3045 // we do nothing with skip_*, but others might... we should at
3046 // least make sure these are present in _NET_WM_STATE
3050 if (first
== ewmh
.wmStateHidden() ||
3051 second
== ewmh
.wmStateHidden()) {
3053 ignore _NET_WM_STATE_HIDDEN, the wm sets this state, not the
3058 if (hasWindowFunction(WindowFunctionFullScreen
)) {
3059 if (first
== ewmh
.wmStateFullscreen() ||
3060 second
== ewmh
.wmStateFullscreen()) {
3061 if (action
== ewmh
.wmStateAdd() ||
3062 (action
== ewmh
.wmStateToggle() &&
3063 ! client
.ewmh
.fullscreen
)) {
3064 setFullScreen(true);
3065 } else if (action
== ewmh
.wmStateToggle() ||
3066 action
== ewmh
.wmStateRemove()) {
3067 setFullScreen(false);
3072 if (hasWindowFunction(WindowFunctionChangeLayer
)) {
3073 if (first
== ewmh
.wmStateAbove() ||
3074 second
== ewmh
.wmStateAbove()) {
3075 if (action
== ewmh
.wmStateAdd() ||
3076 (action
== ewmh
.wmStateToggle() &&
3077 layer() != StackingList::LayerAbove
)) {
3078 changeLayer(StackingList::LayerAbove
);
3079 } else if (action
== ewmh
.wmStateToggle() ||
3080 action
== ewmh
.wmStateRemove()) {
3081 changeLayer(StackingList::LayerNormal
);
3085 if (first
== ewmh
.wmStateBelow() ||
3086 second
== ewmh
.wmStateBelow()) {
3087 if (action
== ewmh
.wmStateAdd() ||
3088 (action
== ewmh
.wmStateToggle() &&
3089 layer() != StackingList::LayerBelow
)) {
3090 changeLayer(StackingList::LayerBelow
);
3091 } else if (action
== ewmh
.wmStateToggle() ||
3092 action
== ewmh
.wmStateRemove()) {
3093 changeLayer(StackingList::LayerNormal
);
3101 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent
* const event
) {
3102 if (event
->window
!= client
.window
)
3106 fprintf(stderr
, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
3110 _screen
->releaseWindow(this);
3115 BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent
* const event
) {
3116 if (event
->window
!= client
.window
)
3120 fprintf(stderr
, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
3124 _screen
->releaseWindow(this);
3128 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent
* const event
) {
3129 if (event
->window
!= client
.window
|| event
->parent
== frame
.plate
)
3133 fprintf(stderr
, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
3134 "0x%lx.\n", client
.window
, event
->parent
);
3138 put the ReparentNotify event back into the queue so that
3139 BlackboxWindow::restore(void) can do the right thing
3142 replay
.xreparent
= *event
;
3143 XPutBackEvent(blackbox
->XDisplay(), &replay
);
3145 _screen
->releaseWindow(this);
3149 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent
* const event
) {
3151 fprintf(stderr
, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
3155 switch(event
->atom
) {
3156 case XA_WM_TRANSIENT_FOR
: {
3157 if (isTransient()) {
3158 // remove ourselves from our transient_for
3159 BlackboxWindow
*win
= findTransientFor();
3161 win
->removeTransient(this);
3162 } else if (isGroupTransient()) {
3163 BWindowGroup
*group
= findWindowGroup();
3165 group
->removeTransient(this);
3169 // determine if this is a transient window
3170 client
.transient_for
= ::readTransientInfo(blackbox
,
3172 _screen
->screenInfo(),
3175 if (isTransient()) {
3176 BlackboxWindow
*win
= findTransientFor();
3178 // add ourselves to our new transient_for
3179 win
->addTransient(this);
3180 changeWorkspace(win
->workspace());
3181 changeLayer(win
->layer());
3182 } else if (isGroupTransient()) {
3183 BWindowGroup
*group
= findWindowGroup();
3185 group
->addTransient(this);
3188 client
.transient_for
= 0;
3192 ::update_decorations(client
.decorations
,
3198 client
.wmprotocols
);
3205 // remove from current window group
3206 BWindowGroup
*group
= findWindowGroup();
3208 if (isTransient() && !findTransientFor() && isGroupTransient())
3209 group
->removeTransient(this);
3210 group
->removeWindow(this);
3214 client
.wmhints
= ::readWMHints(blackbox
, client
.window
);
3216 if (client
.wmhints
.window_group
!= None
) {
3217 // add to new window group
3218 group
= ::update_window_group(client
.wmhints
.window_group
,
3221 if (isTransient() && !findTransientFor() && isGroupTransient()) {
3223 group
->addTransient(this);
3229 case XA_WM_ICON_NAME
: {
3230 client
.icon_title
= ::readWMIconName(blackbox
, client
.window
);
3231 if (client
.state
.iconic
)
3232 _screen
->propagateWindowName(this);
3237 client
.title
= ::readWMName(blackbox
, client
.window
);
3239 client
.visible_title
=
3240 bt::ellideText(client
.title
, frame
.label_w
, bt::toUnicode("..."),
3241 _screen
->screenNumber(),
3242 _screen
->resource().windowStyle().font
);
3243 blackbox
->ewmh().setWMVisibleName(client
.window
, client
.visible_title
);
3245 if (client
.decorations
& WindowDecorationTitlebar
)
3248 _screen
->propagateWindowName(this);
3252 case XA_WM_NORMAL_HINTS
: {
3253 WMNormalHints wmnormal
= ::readWMNormalHints(blackbox
, client
.window
,
3254 _screen
->screenInfo());
3255 if (wmnormal
== client
.wmnormal
) {
3256 // apps like xv and GNU emacs seem to like to repeatedly set
3257 // this property over and over
3261 client
.wmnormal
= wmnormal
;
3263 ::update_decorations(client
.decorations
,
3269 client
.wmprotocols
);
3276 if (event
->atom
== blackbox
->wmProtocolsAtom()) {
3277 client
.wmprotocols
= ::readWMProtocols(blackbox
, client
.window
);
3279 ::update_decorations(client
.decorations
,
3285 client
.wmprotocols
);
3288 } else if (event
->atom
== blackbox
->motifWmHintsAtom()) {
3289 client
.motif
= ::readMotifWMHints(blackbox
, client
.window
);
3291 ::update_decorations(client
.decorations
,
3297 client
.wmprotocols
);
3300 } else if (event
->atom
== blackbox
->ewmh().wmStrut()) {
3301 if (! client
.strut
) {
3302 client
.strut
= new bt::EWMH::Strut
;
3303 _screen
->addStrut(client
.strut
);
3306 blackbox
->ewmh().readWMStrut(client
.window
, client
.strut
);
3307 if (client
.strut
->left
|| client
.strut
->right
||
3308 client
.strut
->top
|| client
.strut
->bottom
) {
3309 _screen
->updateStrut();
3311 _screen
->removeStrut(client
.strut
);
3312 delete client
.strut
;
3322 void BlackboxWindow::exposeEvent(const XExposeEvent
* const event
) {
3324 fprintf(stderr
, "BlackboxWindow::exposeEvent() for 0x%lx\n", client
.window
);
3327 if (frame
.title
== event
->window
)
3329 else if (frame
.label
== event
->window
)
3331 else if (frame
.close_button
== event
->window
)
3332 redrawCloseButton();
3333 else if (frame
.maximize_button
== event
->window
)
3334 redrawMaximizeButton();
3335 else if (frame
.iconify_button
== event
->window
)
3336 redrawIconifyButton();
3337 else if (frame
.handle
== event
->window
)
3339 else if (frame
.left_grip
== event
->window
||
3340 frame
.right_grip
== event
->window
)
3345 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent
*
3347 if (event
->window
!= client
.window
|| client
.state
.iconic
)
3350 if (event
->value_mask
& CWBorderWidth
)
3351 client
.old_bw
= event
->border_width
;
3353 if (event
->value_mask
& (CWX
| CWY
| CWWidth
| CWHeight
)) {
3354 bt::Rect req
= frame
.rect
;
3356 if (event
->value_mask
& (CWX
| CWY
)) {
3357 req
= ::restoreGravity(req
, frame
.margin
, client
.wmnormal
.win_gravity
);
3359 if (event
->value_mask
& CWX
)
3361 if (event
->value_mask
& CWY
)
3364 req
= ::applyGravity(req
, frame
.margin
, client
.wmnormal
.win_gravity
);
3367 if (event
->value_mask
& (CWWidth
| CWHeight
)) {
3368 if (event
->value_mask
& CWWidth
)
3369 req
.setWidth(event
->width
+ frame
.margin
.left
+ frame
.margin
.right
);
3370 if (event
->value_mask
& CWHeight
)
3371 req
.setHeight(event
->height
+ frame
.margin
.top
+ frame
.margin
.bottom
);
3377 if (event
->value_mask
& CWStackMode
) {
3378 switch (event
->detail
) {
3381 _screen
->lowerWindow(this);
3387 _screen
->raiseWindow(this);
3394 void BlackboxWindow::buttonPressEvent(const XButtonEvent
* const event
) {
3396 fprintf(stderr
, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3400 if (frame
.maximize_button
== event
->window
) {
3401 if (event
->button
< 4)
3402 redrawMaximizeButton(true);
3403 } else if (frame
.iconify_button
== event
->window
) {
3404 if (event
->button
== 1)
3405 redrawIconifyButton(true);
3406 } else if (frame
.close_button
== event
->window
) {
3407 if (event
->button
== 1)
3408 redrawCloseButton(true);
3410 if (event
->button
== 1
3411 || (event
->button
== 3 && event
->state
== Mod1Mask
)) {
3412 frame
.grab_x
= event
->x_root
- frame
.rect
.x();
3413 frame
.grab_y
= event
->y_root
- frame
.rect
.y();
3415 _screen
->raiseWindow(this);
3417 if (! client
.state
.focused
)
3418 (void) setInputFocus();
3420 XInstallColormap(blackbox
->XDisplay(), client
.colormap
);
3422 if (frame
.plate
== event
->window
) {
3423 XAllowEvents(blackbox
->XDisplay(), ReplayPointer
, event
->time
);
3424 } else if ((frame
.title
== event
->window
3425 || frame
.label
== event
->window
)
3426 && hasWindowFunction(WindowFunctionShade
)) {
3427 if ((event
->time
- lastButtonPressTime
<=
3428 blackbox
->resource().doubleClickInterval()) ||
3429 event
->state
== ControlMask
) {
3430 lastButtonPressTime
= 0;
3431 setShaded(!isShaded());
3433 lastButtonPressTime
= event
->time
;
3436 } else if (event
->button
== 2) {
3437 _screen
->lowerWindow(this);
3438 } else if (event
->button
== 3
3439 || (event
->button
== 3 && event
->state
== Mod4Mask
)) {
3440 const int extra
= _screen
->resource().windowStyle().frame_border_width
;
3441 const bt::Rect
rect(client
.rect
.x() - extra
,
3442 client
.rect
.y() - extra
,
3443 client
.rect
.width() + (extra
* 2),
3444 client
.rect
.height() + (extra
* 2));
3446 Windowmenu
*windowmenu
= _screen
->windowmenu(this);
3447 windowmenu
->popup(event
->x_root
, event
->y_root
, rect
);
3448 } else if (blackbox
->resource().shadeWindowWithMouseWheel()) {
3449 if (event
->button
== 4
3450 && hasWindowFunction(WindowFunctionShade
)
3453 } else if (event
->button
== 5
3454 && hasWindowFunction(WindowFunctionShade
)
3463 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent
* const event
) {
3465 fprintf(stderr
, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3469 const WindowStyle
&style
= _screen
->resource().windowStyle();
3470 if (event
->window
== frame
.maximize_button
) {
3471 if (event
->button
< 4) {
3472 if (bt::within(event
->x
, event
->y
,
3473 style
.button_width
, style
.button_width
)) {
3474 maximize(event
->button
);
3475 _screen
->raiseWindow(this);
3477 redrawMaximizeButton();
3480 } else if (event
->window
== frame
.iconify_button
) {
3481 if (event
->button
== 1) {
3482 if (bt::within(event
->x
, event
->y
,
3483 style
.button_width
, style
.button_width
))
3486 redrawIconifyButton();
3488 } else if (event
->window
== frame
.close_button
) {
3489 if (event
->button
== 1) {
3490 if (bt::within(event
->x
, event
->y
,
3491 style
.button_width
, style
.button_width
))
3493 redrawCloseButton();
3495 } else if (client
.state
.moving
) {
3497 } else if (client
.state
.resizing
) {
3499 } else if (event
->window
== frame
.window
) {
3500 if (event
->button
== 2 && event
->state
== Mod1Mask
)
3501 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
3506 void BlackboxWindow::motionNotifyEvent(const XMotionEvent
* const event
) {
3508 fprintf(stderr
, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3512 if (hasWindowFunction(WindowFunctionMove
)
3513 && !client
.state
.resizing
3514 && event
->state
& Button1Mask
3515 && (frame
.title
== event
->window
|| frame
.label
== event
->window
3516 || frame
.handle
== event
->window
|| frame
.window
== event
->window
)) {
3517 if (! client
.state
.moving
)
3520 continueMove(event
->x_root
, event
->y_root
);
3521 } else if (hasWindowFunction(WindowFunctionResize
)
3522 && (event
->state
& Button1Mask
3523 && (event
->window
== frame
.right_grip
3524 || event
->window
== frame
.left_grip
))
3525 || (event
->state
& Button3Mask
3526 && event
->state
& Mod1Mask
3527 && event
->window
== frame
.window
)) {
3528 if (!client
.state
.resizing
)
3529 startResize(event
->window
);
3531 continueResize(event
->x_root
, event
->y_root
);
3536 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent
* const event
) {
3537 if (event
->window
!= frame
.window
|| event
->mode
!= NotifyNormal
)
3540 if (blackbox
->resource().focusModel() == ClickToFocusModel
|| !isVisible())
3543 switch (windowType()) {
3544 case WindowTypeDesktop
:
3545 case WindowTypeDock
:
3546 // these types cannot be focused w/ sloppy focus
3554 bool leave
= False
, inferior
= False
;
3556 while (XCheckTypedWindowEvent(blackbox
->XDisplay(), event
->window
,
3557 LeaveNotify
, &next
)) {
3558 if (next
.type
== LeaveNotify
&& next
.xcrossing
.mode
== NotifyNormal
) {
3560 inferior
= (next
.xcrossing
.detail
== NotifyInferior
);
3564 if ((! leave
|| inferior
) && ! isFocused())
3565 (void) setInputFocus();
3567 if (blackbox
->resource().autoRaise())
3573 BlackboxWindow::leaveNotifyEvent(const XCrossingEvent
* const /*unused*/) {
3574 if (!(blackbox
->resource().focusModel() == SloppyFocusModel
3575 && blackbox
->resource().autoRaise()))
3578 if (timer
->isTiming())
3584 void BlackboxWindow::shapeEvent(const XEvent
* const /*unused*/)
3585 { if (client
.state
.shaped
) configureShape(); }
3592 void BlackboxWindow::restore(void) {
3593 XChangeSaveSet(blackbox
->XDisplay(), client
.window
, SetModeDelete
);
3594 XSelectInput(blackbox
->XDisplay(), client
.window
, NoEventMask
);
3595 XSelectInput(blackbox
->XDisplay(), frame
.plate
, NoEventMask
);
3597 client
.state
.visible
= false;
3600 remove WM_STATE unless the we are shutting down (in which case we
3601 want to make sure we preserve the state across restarts).
3603 if (!blackbox
->shuttingDown()) {
3604 clearState(blackbox
, client
.window
);
3605 } else if (isShaded() && !isIconic()) {
3606 // do not leave a shaded window as an icon unless it was an icon
3607 setState(NormalState
);
3610 client
.rect
= ::restoreGravity(frame
.rect
, frame
.margin
,
3611 client
.wmnormal
.win_gravity
);
3613 blackbox
->XGrabServer();
3615 XUnmapWindow(blackbox
->XDisplay(), frame
.window
);
3616 XUnmapWindow(blackbox
->XDisplay(), client
.window
);
3618 XSetWindowBorderWidth(blackbox
->XDisplay(), client
.window
, client
.old_bw
);
3619 if (isMaximized()) {
3620 // preserve the original size
3621 client
.rect
= client
.premax
;
3622 XMoveResizeWindow(blackbox
->XDisplay(), client
.window
,
3625 client
.premax
.width(),
3626 client
.premax
.height());
3628 XMoveWindow(blackbox
->XDisplay(), client
.window
,
3629 client
.rect
.x() - frame
.rect
.x(),
3630 client
.rect
.y() - frame
.rect
.y());
3633 blackbox
->XUngrabServer();
3636 if (!XCheckTypedWindowEvent(blackbox
->XDisplay(), client
.window
,
3637 ReparentNotify
, &unused
)) {
3639 according to the ICCCM, the window manager is responsible for
3640 reparenting the window back to root... however, we don't want to
3641 do this if the window has been reparented by someone else
3644 XReparentWindow(blackbox
->XDisplay(), client
.window
,
3645 _screen
->screenInfo().rootWindow(),
3646 client
.rect
.x(), client
.rect
.y());
3649 if (blackbox
->shuttingDown())
3650 XMapWindow(blackbox
->XDisplay(), client
.window
);
3654 // timer for autoraise
3655 void BlackboxWindow::timeout(bt::Timer
*)
3656 { _screen
->raiseWindow(this); }
3659 void BlackboxWindow::startMove() {
3661 XGrabPointer(blackbox
->XDisplay(), frame
.window
, false,
3662 Button1MotionMask
| ButtonReleaseMask
,
3663 GrabModeAsync
, GrabModeAsync
, None
,
3664 blackbox
->resource().cursors().move
, blackbox
->XTime());
3666 client
.state
.moving
= true;
3668 if (! blackbox
->resource().opaqueMove()) {
3669 blackbox
->XGrabServer();
3671 frame
.changing
= frame
.rect
;
3672 _screen
->showGeometry(BScreen::Position
, frame
.changing
);
3674 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3675 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3677 pen
.setGCFunction(GXxor
);
3678 pen
.setLineWidth(bw
);
3679 pen
.setSubWindowMode(IncludeInferiors
);
3680 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3682 frame
.changing
.x() + hw
,
3683 frame
.changing
.y() + hw
,
3684 frame
.changing
.width() - bw
,
3685 frame
.changing
.height() - bw
);
3691 void collisionAdjust(int *dx
, int *dy
, int x
, int y
,
3692 unsigned int width
, unsigned int height
,
3693 const bt::Rect
& rect
, int snap_distance
,
3694 bool snapCenter
= false)
3697 const int wleft
= x
,
3698 wright
= x
+ width
- 1,
3700 wbottom
= y
+ height
- 1,
3701 // left, right, top + bottom are for rect, douterleft = left border of rect
3702 dinnerleft
= abs(wleft
- rect
.left()),
3703 dinnerright
= abs(wright
- rect
.right()),
3704 dinnertop
= abs(wtop
- rect
.top()),
3705 dinnerbottom
= abs(wbottom
- rect
.bottom()),
3706 douterleft
= abs(wright
- rect
.left()),
3707 douterright
= abs(wleft
- rect
.right()),
3708 doutertop
= abs(wbottom
- rect
.top()),
3709 douterbottom
= abs(wtop
- rect
.bottom());
3711 if ((wtop
<= rect
.bottom() && wbottom
>= rect
.top())
3712 || doutertop
<= snap_distance
3713 || douterbottom
<= snap_distance
) {
3714 // snap left or right
3715 if (douterleft
<= dinnerleft
&& douterleft
<= snap_distance
)
3717 *dx
= (x
- (rect
.left() - width
));
3718 else if (douterright
<= dinnerright
&& douterright
<= snap_distance
)
3720 *dx
= (x
- rect
.right() - 1);
3721 else if (dinnerleft
<= dinnerright
&& dinnerleft
< snap_distance
)
3723 *dx
= (x
- rect
.left());
3724 else if (dinnerright
< snap_distance
)
3726 *dx
= (x
- (rect
.right() - width
+ 1));
3729 if ((wleft
<= rect
.right() && wright
>= rect
.left())
3730 || douterleft
<= snap_distance
3731 || douterright
<= snap_distance
) {
3732 // snap top or bottom
3733 if (doutertop
<= dinnertop
&& doutertop
<= snap_distance
)
3735 *dy
= (y
- (rect
.top() - height
));
3736 else if (douterbottom
<= dinnerbottom
&& douterbottom
<= snap_distance
)
3737 // snap outer bottom
3738 *dy
= (y
- rect
.bottom() - 1);
3739 else if (dinnertop
<= dinnerbottom
&& dinnertop
< snap_distance
)
3741 *dy
= (y
- rect
.top());
3742 else if (dinnerbottom
< snap_distance
)
3743 // snap inner bottom
3744 *dy
= (y
- (rect
.bottom() - height
+ 1));
3748 const int cwx
= x
+ width
/ 2;
3749 const int cwy
= y
+ height
/ 2;
3750 const int crx
= rect
.x() + rect
.width() / 2;
3751 const int cry
= rect
.y() + rect
.height() / 2;
3752 const int cdx
= abs(cwx
- crx
);
3753 const int cdy
= abs(cwy
- cry
);
3754 if (cdx
<= snap_distance
)
3755 // snap to horizontal center
3756 *dx
= x
- (rect
.x() + ((rect
.width() - width
) / 2));
3757 if (cdy
<= snap_distance
)
3758 // snap to vertical center
3759 *dy
= y
- (rect
.y() + ((rect
.height() - height
) / 2));
3764 void BlackboxWindow::snapAdjust(int *x
, int *y
) {
3765 int nx
, ny
, dx
, dy
, init_dx
, init_dy
;
3766 const int edge_distance
= blackbox
->resource().edgeSnapThreshold();
3767 const int win_distance
= blackbox
->resource().windowSnapThreshold();
3769 nx
= (win_distance
> edge_distance
) ? win_distance
: edge_distance
;
3770 ny
= (win_distance
> edge_distance
) ? win_distance
: edge_distance
;
3771 dx
= init_dx
= ++nx
; dy
= init_dy
= ++ny
;
3773 if (edge_distance
) {
3774 collisionAdjust(&dx
, &dy
, *x
, *y
, frame
.rect
.width(), frame
.rect
.height(),
3775 _screen
->availableArea(), edge_distance
, true);
3776 nx
= (dx
!= init_dx
&& abs(dx
) < abs(nx
)) ? dx
: nx
; dx
= init_dx
;
3777 ny
= (dy
!= init_dy
&& abs(dy
) < abs(ny
)) ? dy
: ny
; dy
= init_dy
;
3778 if (!blackbox
->resource().fullMaximization()) {
3779 collisionAdjust(&dx
, &dy
, *x
, *y
, frame
.rect
.width(), frame
.rect
.height(),
3780 _screen
->screenInfo().rect(), edge_distance
);
3781 nx
= (dx
!= init_dx
&& abs(dx
) < abs(nx
)) ? dx
: nx
; dx
= init_dx
;
3782 ny
= (dy
!= init_dy
&& abs(dy
) < abs(ny
)) ? dy
: ny
; dy
= init_dy
;
3786 StackingList::const_iterator it
= _screen
->stackingList().begin(),
3787 end
= _screen
->stackingList().end();
3788 for (; it
!= end
; ++it
) {
3789 BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
3790 if (win
&& win
!= this &&
3791 win
->workspace() == _screen
->currentWorkspace()) {
3792 collisionAdjust(&dx
, &dy
, *x
, *y
, frame
.rect
.width(),
3793 frame
.rect
.height(), win
->frame
.rect
, win_distance
);
3794 nx
= (dx
!= init_dx
&& abs(dx
) < abs(nx
)) ? dx
: nx
; dx
= init_dx
;
3795 ny
= (dy
!= init_dy
&& abs(dy
) < abs(ny
)) ? dy
: ny
; dy
= init_dy
;
3800 *x
= (nx
!= init_dx
) ? (*x
- nx
) : *x
;
3801 *y
= (ny
!= init_dy
) ? (*y
- ny
) : *y
;
3805 void BlackboxWindow::continueMove(int x_root
, int y_root
) {
3806 int dx
= x_root
- frame
.grab_x
, dy
= y_root
- frame
.grab_y
;
3808 snapAdjust(&dx
, &dy
);
3810 if (blackbox
->resource().opaqueMove()) {
3811 configure(dx
, dy
, frame
.rect
.width(), frame
.rect
.height());
3813 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3814 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3816 pen
.setGCFunction(GXxor
);
3817 pen
.setLineWidth(bw
);
3818 pen
.setSubWindowMode(IncludeInferiors
);
3819 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3821 frame
.changing
.x() + hw
,
3822 frame
.changing
.y() + hw
,
3823 frame
.changing
.width() - bw
,
3824 frame
.changing
.height() - bw
);
3826 frame
.changing
.setPos(dx
, dy
);
3828 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3830 frame
.changing
.x() + hw
,
3831 frame
.changing
.y() + hw
,
3832 frame
.changing
.width() - bw
,
3833 frame
.changing
.height() - bw
);
3836 _screen
->showGeometry(BScreen::Position
, bt::Rect(dx
, dy
, 0, 0));
3840 void BlackboxWindow::finishMove() {
3841 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
3843 client
.state
.moving
= false;
3845 if (!blackbox
->resource().opaqueMove()) {
3846 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3847 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3849 pen
.setGCFunction(GXxor
);
3850 pen
.setLineWidth(bw
);
3851 pen
.setSubWindowMode(IncludeInferiors
);
3852 XDrawRectangle(blackbox
->XDisplay(),
3853 _screen
->screenInfo().rootWindow(),
3855 frame
.changing
.x() + hw
,
3856 frame
.changing
.y() + hw
,
3857 frame
.changing
.width() - bw
,
3858 frame
.changing
.height() - bw
);
3859 blackbox
->XUngrabServer();
3861 configure(frame
.changing
);
3863 configure(frame
.rect
);
3866 _screen
->hideGeometry();
3870 void BlackboxWindow::startResize(Window window
) {
3871 if (frame
.grab_x
< (signed) frame
.rect
.width() / 2) {
3872 if (frame
.grab_y
< (signed) frame
.rect
.height() / 2)
3873 frame
.corner
= BottomRight
;
3875 frame
.corner
= TopRight
;
3877 if (frame
.grab_y
< (signed) frame
.rect
.height() / 2)
3878 frame
.corner
= BottomLeft
;
3880 frame
.corner
= TopLeft
;
3883 Cursor cursor
= None
;
3884 switch (frame
.corner
) {
3886 cursor
= blackbox
->resource().cursors().resize_bottom_right
;
3887 frame
.grab_x
= frame
.rect
.width() - frame
.grab_x
;
3888 frame
.grab_y
= frame
.rect
.height() - frame
.grab_y
;
3891 cursor
= blackbox
->resource().cursors().resize_top_right
;
3892 frame
.grab_x
= frame
.rect
.width() - frame
.grab_x
;
3895 cursor
= blackbox
->resource().cursors().resize_bottom_left
;
3896 frame
.grab_y
= frame
.rect
.height() - frame
.grab_y
;
3899 cursor
= blackbox
->resource().cursors().resize_top_left
;
3904 XGrabPointer(blackbox
->XDisplay(), window
, False
,
3905 ButtonMotionMask
| ButtonReleaseMask
,
3906 GrabModeAsync
, GrabModeAsync
, None
, cursor
, blackbox
->XTime());
3908 client
.state
.resizing
= true;
3910 frame
.changing
= constrain(frame
.rect
, frame
.margin
, client
.wmnormal
,
3911 Corner(frame
.corner
));
3913 if (!blackbox
->resource().opaqueResize()) {
3914 blackbox
->XGrabServer();
3916 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3917 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3919 pen
.setGCFunction(GXxor
);
3920 pen
.setLineWidth(bw
);
3921 pen
.setSubWindowMode(IncludeInferiors
);
3922 XDrawRectangle(blackbox
->XDisplay(),
3923 _screen
->screenInfo().rootWindow(),
3925 frame
.changing
.x() + hw
,
3926 frame
.changing
.y() + hw
,
3927 frame
.changing
.width() - bw
,
3928 frame
.changing
.height() - bw
);
3930 // unset maximized state when resized
3935 showGeometry(frame
.changing
);
3939 void BlackboxWindow::continueResize(int x_root
, int y_root
) {
3940 // continue a resize
3941 const bt::Rect curr
= frame
.changing
;
3943 switch (frame
.corner
) {
3946 frame
.changing
.setCoords(frame
.changing
.left(),
3947 frame
.changing
.top(),
3948 std::max
<signed>(x_root
+ frame
.grab_x
,
3949 frame
.changing
.left()
3950 + (frame
.margin
.left
3951 + frame
.margin
.right
+ 1)),
3952 frame
.changing
.bottom());
3956 frame
.changing
.setCoords(std::min
<signed>(x_root
- frame
.grab_x
,
3957 frame
.changing
.right()
3958 - (frame
.margin
.left
3959 + frame
.margin
.right
+ 1)),
3960 frame
.changing
.top(),
3961 frame
.changing
.right(),
3962 frame
.changing
.bottom());
3966 switch (frame
.corner
) {
3969 frame
.changing
.setCoords(frame
.changing
.left(),
3970 frame
.changing
.top(),
3971 frame
.changing
.right(),
3972 std::max
<signed>(y_root
+ frame
.grab_y
,
3973 frame
.changing
.top()
3975 + frame
.margin
.bottom
+ 1)));
3979 frame
.changing
.setCoords(frame
.changing
.left(),
3980 std::min
<signed>(y_root
- frame
.grab_y
,
3983 + frame
.margin
.bottom
+ 1)),
3984 frame
.changing
.right(),
3985 frame
.changing
.bottom());
3989 frame
.changing
= constrain(frame
.changing
, frame
.margin
, client
.wmnormal
,
3990 Corner(frame
.corner
));
3992 if (curr
!= frame
.changing
) {
3993 if (blackbox
->resource().opaqueResize()) {
3994 configure(frame
.changing
);
3996 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3997 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3999 pen
.setGCFunction(GXxor
);
4000 pen
.setLineWidth(bw
);
4001 pen
.setSubWindowMode(IncludeInferiors
);
4002 XDrawRectangle(blackbox
->XDisplay(),
4003 _screen
->screenInfo().rootWindow(),
4008 curr
.height() - bw
);
4010 XDrawRectangle(blackbox
->XDisplay(),
4011 _screen
->screenInfo().rootWindow(),
4013 frame
.changing
.x() + hw
,
4014 frame
.changing
.y() + hw
,
4015 frame
.changing
.width() - bw
,
4016 frame
.changing
.height() - bw
);
4019 showGeometry(frame
.changing
);
4024 void BlackboxWindow::finishResize() {
4026 if (!blackbox
->resource().opaqueResize()) {
4027 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
4028 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
4030 pen
.setGCFunction(GXxor
);
4031 pen
.setLineWidth(bw
);
4032 pen
.setSubWindowMode(IncludeInferiors
);
4033 XDrawRectangle(blackbox
->XDisplay(),
4034 _screen
->screenInfo().rootWindow(),
4036 frame
.changing
.x() + hw
,
4037 frame
.changing
.y() + hw
,
4038 frame
.changing
.width() - bw
,
4039 frame
.changing
.height() - bw
);
4041 blackbox
->XUngrabServer();
4043 // unset maximized state when resized
4048 client
.state
.resizing
= false;
4050 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
4052 _screen
->hideGeometry();
4054 frame
.changing
= constrain(frame
.changing
, frame
.margin
, client
.wmnormal
,
4055 Corner(frame
.corner
));
4056 configure(frame
.changing
);
4061 * show the geometry of the window based on rectangle r.
4062 * The logical width and height are used here. This refers to the user's
4063 * perception of the window size (for example an xterm resizes in cells,
4064 * not in pixels). No extra work is needed if there is no difference between
4065 * the logical and actual dimensions.
4067 void BlackboxWindow::showGeometry(const bt::Rect
&r
) const {
4068 unsigned int w
= r
.width(), h
= r
.height();
4070 // remove the window frame
4071 w
-= frame
.margin
.left
+ frame
.margin
.right
;
4072 h
-= frame
.margin
.top
+ frame
.margin
.bottom
;
4074 if (client
.wmnormal
.flags
& PResizeInc
) {
4075 if (client
.wmnormal
.flags
& (PMinSize
|PBaseSize
)) {
4076 w
-= ((client
.wmnormal
.base_width
)
4077 ? client
.wmnormal
.base_width
4078 : client
.wmnormal
.min_width
);
4079 h
-= ((client
.wmnormal
.base_height
)
4080 ? client
.wmnormal
.base_height
4081 : client
.wmnormal
.min_height
);
4084 w
/= client
.wmnormal
.width_inc
;
4085 h
/= client
.wmnormal
.height_inc
;
4088 _screen
->showGeometry(BScreen::Size
, bt::Rect(0, 0, w
, h
));
4092 // see my rant above for an explanation of this operator
4093 bool operator==(const WMNormalHints
&x
, const WMNormalHints
&y
) {
4094 return (x
.flags
== y
.flags
4095 && x
.min_width
== y
.min_width
4096 && x
.min_height
== y
.min_height
4097 && x
.max_width
== y
.max_width
4098 && x
.max_height
== y
.max_height
4099 && x
.width_inc
== y
.width_inc
4100 && x
.height_inc
== y
.height_inc
4101 && x
.min_aspect_x
== y
.min_aspect_x
4102 && x
.min_aspect_y
== y
.min_aspect_y
4103 && x
.max_aspect_x
== y
.max_aspect_x
4104 && x
.max_aspect_y
== y
.max_aspect_y
4105 && x
.base_width
== y
.base_width
4106 && x
.base_height
== y
.base_height
4107 && x
.win_gravity
== y
.win_gravity
);