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
;
1245 BWindowGroup
*group
= findWindowGroup();
1247 if (isTransient()) {
1248 // remove ourselves from our transient_for
1249 BlackboxWindow
*win
= findTransientFor();
1251 win
->removeTransient(this);
1252 } else if (isGroupTransient()) {
1254 group
->removeTransient(this);
1256 client
.transient_for
= 0;
1260 group
->removeWindow(this);
1268 blackbox
->removeEventHandler(client
.window
);
1269 blackbox
->removeWindow(client
.window
);
1271 blackbox
->removeEventHandler(frame
.plate
);
1272 blackbox
->removeWindow(frame
.plate
);
1273 XDestroyWindow(blackbox
->XDisplay(), frame
.plate
);
1275 blackbox
->removeEventHandler(frame
.window
);
1276 XDestroyWindow(blackbox
->XDisplay(), frame
.window
);
1281 * Creates a new top level window, with a given location, size, and border
1283 * Returns: the newly created window
1285 Window
BlackboxWindow::createToplevelWindow(void) {
1286 XSetWindowAttributes attrib_create
;
1287 unsigned long create_mask
= CWColormap
| CWOverrideRedirect
| CWEventMask
;
1289 attrib_create
.colormap
= _screen
->screenInfo().colormap();
1290 attrib_create
.override_redirect
= True
;
1291 attrib_create
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
1293 return XCreateWindow(blackbox
->XDisplay(),
1294 _screen
->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
1295 _screen
->screenInfo().depth(), InputOutput
,
1296 _screen
->screenInfo().visual(),
1297 create_mask
, &attrib_create
);
1302 * Creates a child window, and optionally associates a given cursor with
1305 Window
BlackboxWindow::createChildWindow(Window parent
,
1306 unsigned long event_mask
,
1308 XSetWindowAttributes attrib_create
;
1309 unsigned long create_mask
= CWEventMask
;
1311 attrib_create
.event_mask
= event_mask
;
1314 create_mask
|= CWCursor
;
1315 attrib_create
.cursor
= cursor
;
1318 return XCreateWindow(blackbox
->XDisplay(), parent
, 0, 0, 1, 1, 0,
1319 _screen
->screenInfo().depth(), InputOutput
,
1320 _screen
->screenInfo().visual(),
1321 create_mask
, &attrib_create
);
1326 * Reparents the client window into the newly created frame.
1328 * Note: the server must be grabbed before calling this function.
1330 void BlackboxWindow::associateClientWindow(void) {
1331 XSetWindowBorderWidth(blackbox
->XDisplay(), client
.window
, 0);
1332 XChangeSaveSet(blackbox
->XDisplay(), client
.window
, SetModeInsert
);
1334 XSelectInput(blackbox
->XDisplay(), frame
.plate
,
1335 FocusChangeMask
| SubstructureRedirectMask
);
1337 XSelectInput(blackbox
->XDisplay(), client
.window
,
1338 client_window_event_mask
& ~StructureNotifyMask
);
1339 XReparentWindow(blackbox
->XDisplay(), client
.window
, frame
.plate
, 0, 0);
1340 XSelectInput(blackbox
->XDisplay(), client
.window
, client_window_event_mask
);
1343 if (blackbox
->hasShapeExtensions()) {
1344 XShapeSelectInput(blackbox
->XDisplay(), client
.window
,
1347 Bool shaped
= False
;
1351 XShapeQueryExtents(blackbox
->XDisplay(), client
.window
, &shaped
,
1352 &foo
, &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
,
1354 client
.state
.shaped
= shaped
;
1360 void BlackboxWindow::decorate(void) {
1361 const WindowStyle
&style
= _screen
->resource().windowStyle();
1362 if (client
.decorations
& WindowDecorationTitlebar
) {
1363 // render focused button texture
1365 bt::PixmapCache::find(_screen
->screenNumber(),
1371 // render unfocused button texture
1373 bt::PixmapCache::find(_screen
->screenNumber(),
1374 style
.unfocus
.button
,
1379 // render pressed button texture
1381 bt::PixmapCache::find(_screen
->screenNumber(),
1387 // render focused titlebar texture
1389 bt::PixmapCache::find(_screen
->screenNumber(),
1395 // render unfocused titlebar texture
1397 bt::PixmapCache::find(_screen
->screenNumber(),
1398 style
.unfocus
.title
,
1403 // render focused label texture
1405 bt::PixmapCache::find(_screen
->screenNumber(),
1411 // render unfocused label texture
1413 bt::PixmapCache::find(_screen
->screenNumber(),
1414 style
.unfocus
.label
,
1420 if (client
.decorations
& WindowDecorationHandle
) {
1422 bt::PixmapCache::find(_screen
->screenNumber(),
1425 style
.handle_height
,
1429 bt::PixmapCache::find(_screen
->screenNumber(),
1430 style
.unfocus
.handle
,
1432 style
.handle_height
,
1436 if (client
.decorations
& WindowDecorationGrip
) {
1438 bt::PixmapCache::find(_screen
->screenNumber(),
1441 style
.handle_height
,
1445 bt::PixmapCache::find(_screen
->screenNumber(),
1448 style
.handle_height
,
1454 void BlackboxWindow::createHandle(void) {
1455 frame
.handle
= createChildWindow(frame
.window
,
1456 ButtonPressMask
| ButtonReleaseMask
|
1457 ButtonMotionMask
| ExposureMask
);
1458 blackbox
->insertEventHandler(frame
.handle
, this);
1460 if (client
.decorations
& WindowDecorationGrip
)
1465 void BlackboxWindow::destroyHandle(void) {
1466 if (frame
.left_grip
|| frame
.right_grip
)
1469 if (frame
.fhandle
) bt::PixmapCache::release(frame
.fhandle
);
1470 if (frame
.uhandle
) bt::PixmapCache::release(frame
.uhandle
);
1472 frame
.fhandle
= frame
.uhandle
= None
;
1474 blackbox
->removeEventHandler(frame
.handle
);
1475 XDestroyWindow(blackbox
->XDisplay(), frame
.handle
);
1476 frame
.handle
= None
;
1480 void BlackboxWindow::createGrips(void) {
1482 createChildWindow(frame
.handle
,
1483 ButtonPressMask
| ButtonReleaseMask
|
1484 ButtonMotionMask
| ExposureMask
,
1485 blackbox
->resource().cursors().resize_bottom_left
);
1486 blackbox
->insertEventHandler(frame
.left_grip
, this);
1489 createChildWindow(frame
.handle
,
1490 ButtonPressMask
| ButtonReleaseMask
|
1491 ButtonMotionMask
| ExposureMask
,
1492 blackbox
->resource().cursors().resize_bottom_right
);
1493 blackbox
->insertEventHandler(frame
.right_grip
, this);
1497 void BlackboxWindow::destroyGrips(void) {
1498 if (frame
.fgrip
) bt::PixmapCache::release(frame
.fgrip
);
1499 if (frame
.ugrip
) bt::PixmapCache::release(frame
.ugrip
);
1501 frame
.fgrip
= frame
.ugrip
= None
;
1503 blackbox
->removeEventHandler(frame
.left_grip
);
1504 blackbox
->removeEventHandler(frame
.right_grip
);
1506 XDestroyWindow(blackbox
->XDisplay(), frame
.left_grip
);
1507 XDestroyWindow(blackbox
->XDisplay(), frame
.right_grip
);
1508 frame
.left_grip
= frame
.right_grip
= None
;
1512 void BlackboxWindow::createTitlebar(void) {
1513 frame
.title
= createChildWindow(frame
.window
,
1514 ButtonPressMask
| ButtonReleaseMask
|
1515 ButtonMotionMask
| ExposureMask
);
1516 frame
.label
= createChildWindow(frame
.title
,
1517 ButtonPressMask
| ButtonReleaseMask
|
1518 ButtonMotionMask
| ExposureMask
);
1519 blackbox
->insertEventHandler(frame
.title
, this);
1520 blackbox
->insertEventHandler(frame
.label
, this);
1522 if (client
.decorations
& WindowDecorationIconify
) createIconifyButton();
1523 if (client
.decorations
& WindowDecorationMaximize
) createMaximizeButton();
1524 if (client
.decorations
& WindowDecorationClose
) createCloseButton();
1528 void BlackboxWindow::destroyTitlebar(void) {
1529 if (frame
.close_button
)
1530 destroyCloseButton();
1532 if (frame
.iconify_button
)
1533 destroyIconifyButton();
1535 if (frame
.maximize_button
)
1536 destroyMaximizeButton();
1538 if (frame
.fbutton
) bt::PixmapCache::release(frame
.fbutton
);
1539 if (frame
.ubutton
) bt::PixmapCache::release(frame
.ubutton
);
1540 if (frame
.pbutton
) bt::PixmapCache::release(frame
.pbutton
);
1541 if (frame
.ftitle
) bt::PixmapCache::release(frame
.ftitle
);
1542 if (frame
.utitle
) bt::PixmapCache::release(frame
.utitle
);
1543 if (frame
.flabel
) bt::PixmapCache::release(frame
.flabel
);
1544 if (frame
.ulabel
) bt::PixmapCache::release(frame
.ulabel
);
1546 frame
.fbutton
= frame
.ubutton
= frame
.pbutton
=
1547 frame
.ftitle
= frame
.utitle
=
1548 frame
.flabel
= frame
.ulabel
= None
;
1550 blackbox
->removeEventHandler(frame
.title
);
1551 blackbox
->removeEventHandler(frame
.label
);
1553 XDestroyWindow(blackbox
->XDisplay(), frame
.label
);
1554 XDestroyWindow(blackbox
->XDisplay(), frame
.title
);
1555 frame
.title
= frame
.label
= None
;
1559 void BlackboxWindow::createCloseButton(void) {
1560 if (frame
.title
!= None
) {
1561 frame
.close_button
= createChildWindow(frame
.title
,
1564 ButtonMotionMask
| ExposureMask
);
1565 blackbox
->insertEventHandler(frame
.close_button
, this);
1570 void BlackboxWindow::destroyCloseButton(void) {
1571 blackbox
->removeEventHandler(frame
.close_button
);
1572 XDestroyWindow(blackbox
->XDisplay(), frame
.close_button
);
1573 frame
.close_button
= None
;
1577 void BlackboxWindow::createIconifyButton(void) {
1578 if (frame
.title
!= None
) {
1579 frame
.iconify_button
= createChildWindow(frame
.title
,
1582 ButtonMotionMask
| ExposureMask
);
1583 blackbox
->insertEventHandler(frame
.iconify_button
, this);
1588 void BlackboxWindow::destroyIconifyButton(void) {
1589 blackbox
->removeEventHandler(frame
.iconify_button
);
1590 XDestroyWindow(blackbox
->XDisplay(), frame
.iconify_button
);
1591 frame
.iconify_button
= None
;
1595 void BlackboxWindow::createMaximizeButton(void) {
1596 if (frame
.title
!= None
) {
1597 frame
.maximize_button
= createChildWindow(frame
.title
,
1600 ButtonMotionMask
| ExposureMask
);
1601 blackbox
->insertEventHandler(frame
.maximize_button
, this);
1606 void BlackboxWindow::destroyMaximizeButton(void) {
1607 blackbox
->removeEventHandler(frame
.maximize_button
);
1608 XDestroyWindow(blackbox
->XDisplay(), frame
.maximize_button
);
1609 frame
.maximize_button
= None
;
1613 void BlackboxWindow::positionButtons(bool redecorate_label
) {
1614 // we need to use signed ints here to detect windows that are too small
1615 const WindowStyle
&style
= _screen
->resource().windowStyle();
1616 const int extra
= style
.title_margin
== 0 ?
1617 style
.focus
.button
.borderWidth() : 0,
1618 bw
= style
.button_width
+ style
.title_margin
1620 by
= style
.title_margin
+
1621 style
.focus
.title
.borderWidth();
1622 int lx
= by
, lw
= frame
.rect
.width() - by
;
1624 if (client
.decorations
& WindowDecorationIconify
) {
1625 if (frame
.iconify_button
== None
) createIconifyButton();
1627 XMoveResizeWindow(blackbox
->XDisplay(), frame
.iconify_button
, by
, by
,
1628 style
.button_width
, style
.button_width
);
1629 XMapWindow(blackbox
->XDisplay(), frame
.iconify_button
);
1633 } else if (frame
.iconify_button
) {
1634 destroyIconifyButton();
1637 int bx
= frame
.rect
.width() - bw
1638 - style
.focus
.title
.borderWidth() - extra
;
1640 if (client
.decorations
& WindowDecorationClose
) {
1641 if (frame
.close_button
== None
) createCloseButton();
1643 XMoveResizeWindow(blackbox
->XDisplay(), frame
.close_button
, bx
, by
,
1644 style
.button_width
, style
.button_width
);
1645 XMapWindow(blackbox
->XDisplay(), frame
.close_button
);
1649 } else if (frame
.close_button
) {
1650 destroyCloseButton();
1653 if (client
.decorations
& WindowDecorationMaximize
) {
1654 if (frame
.maximize_button
== None
) createMaximizeButton();
1656 XMoveResizeWindow(blackbox
->XDisplay(), frame
.maximize_button
, bx
, by
,
1657 style
.button_width
, style
.button_width
);
1658 XMapWindow(blackbox
->XDisplay(), frame
.maximize_button
);
1662 } else if (frame
.maximize_button
) {
1663 destroyMaximizeButton();
1667 frame
.label_w
= lw
- by
;
1668 XMoveResizeWindow(blackbox
->XDisplay(), frame
.label
, lx
, by
,
1669 frame
.label_w
, style
.label_height
);
1670 XMapWindow(blackbox
->XDisplay(), frame
.label
);
1672 if (redecorate_label
) {
1674 bt::PixmapCache::find(_screen
->screenNumber(),
1676 frame
.label_w
, style
.label_height
,
1679 bt::PixmapCache::find(_screen
->screenNumber(),
1680 style
.unfocus
.label
,
1681 frame
.label_w
, style
.label_height
,
1685 const bt::ustring ellided
=
1686 bt::ellideText(client
.title
, frame
.label_w
, bt::toUnicode("..."),
1687 _screen
->screenNumber(), style
.font
);
1689 if (ellided
!= client
.visible_title
) {
1690 client
.visible_title
= ellided
;
1691 blackbox
->ewmh().setWMVisibleName(client
.window
, client
.visible_title
);
1695 XUnmapWindow(blackbox
->XDisplay(), frame
.label
);
1703 void BlackboxWindow::reconfigure(void) {
1704 const WindowStyle
&style
= _screen
->resource().windowStyle();
1705 if (isMaximized()) {
1706 // update the frame margin in case the style has changed
1707 frame
.margin
= ::update_margin(client
.decorations
, style
);
1709 // make sure maximized windows have the correct size after a style
1713 // get the client window geometry as if it was unmanaged
1714 bt::Rect r
= frame
.rect
;
1715 if (client
.ewmh
.shaded
) {
1716 r
.setHeight(client
.rect
.height() + frame
.margin
.top
1717 + frame
.margin
.bottom
);
1719 r
= ::restoreGravity(r
, frame
.margin
, client
.wmnormal
.win_gravity
);
1721 // update the frame margin in case the style has changed
1722 frame
.margin
= ::update_margin(client
.decorations
, style
);
1724 // get the frame window geometry from the client window geometry
1726 r
= ::applyGravity(r
, frame
.margin
, client
.wmnormal
.win_gravity
);
1727 if (client
.ewmh
.shaded
) {
1733 // keep the window shaded
1734 frame
.rect
.setHeight(style
.title_height
);
1735 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
1736 frame
.rect
.width(), frame
.rect
.height());
1738 // trick configure into working
1739 frame
.rect
= bt::Rect();
1749 void BlackboxWindow::grabButtons(void) {
1750 if (blackbox
->resource().focusModel() == ClickToFocusModel
1751 || blackbox
->resource().clickRaise())
1752 // grab button 1 for changing focus/raising
1753 blackbox
->grabButton(Button1
, 0, frame
.plate
, True
, ButtonPressMask
,
1754 GrabModeSync
, GrabModeSync
, frame
.plate
, None
,
1755 blackbox
->resource().allowScrollLock());
1757 if (hasWindowFunction(WindowFunctionMove
))
1758 blackbox
->grabButton(Button1
, Mod1Mask
, frame
.window
, True
,
1759 ButtonReleaseMask
| ButtonMotionMask
, GrabModeAsync
,
1760 GrabModeAsync
, frame
.window
,
1761 blackbox
->resource().cursors().move
,
1762 blackbox
->resource().allowScrollLock());
1763 if (hasWindowFunction(WindowFunctionResize
))
1764 blackbox
->grabButton(Button3
, Mod1Mask
, frame
.window
, True
,
1765 ButtonReleaseMask
| ButtonMotionMask
, GrabModeAsync
,
1766 GrabModeAsync
, frame
.window
,
1767 None
, blackbox
->resource().allowScrollLock());
1768 // alt+middle lowers the window
1769 blackbox
->grabButton(Button2
, Mod1Mask
, frame
.window
, True
,
1770 ButtonReleaseMask
, GrabModeAsync
, GrabModeAsync
,
1772 blackbox
->resource().allowScrollLock());
1774 blackbox
->grabButton(Button3
, Mod4Mask
, frame
.window
, True
,
1775 ButtonReleaseMask
, GrabModeAsync
, GrabModeAsync
,
1777 blackbox
->resource().allowScrollLock());
1781 void BlackboxWindow::ungrabButtons(void) {
1782 blackbox
->ungrabButton(Button1
, 0, frame
.plate
);
1783 blackbox
->ungrabButton(Button1
, Mod1Mask
, frame
.window
);
1784 blackbox
->ungrabButton(Button2
, Mod1Mask
, frame
.window
);
1785 blackbox
->ungrabButton(Button3
, Mod1Mask
, frame
.window
);
1786 blackbox
->ungrabButton(Button3
, Mod4Mask
, frame
.window
);
1790 void BlackboxWindow::positionWindows(void) {
1791 const WindowStyle
&style
= _screen
->resource().windowStyle();
1792 const unsigned int bw
= (hasWindowDecoration(WindowDecorationBorder
)
1793 ? style
.frame_border_width
1796 XMoveResizeWindow(blackbox
->XDisplay(), frame
.plate
,
1797 frame
.margin
.left
- bw
,
1798 frame
.margin
.top
- bw
,
1799 client
.rect
.width(), client
.rect
.height());
1800 XSetWindowBorderWidth(blackbox
->XDisplay(), frame
.plate
, bw
);
1801 XMoveResizeWindow(blackbox
->XDisplay(), client
.window
,
1802 0, 0, client
.rect
.width(), client
.rect
.height());
1803 // ensure client.rect contains the real location
1804 client
.rect
.setPos(frame
.rect
.left() + frame
.margin
.left
,
1805 frame
.rect
.top() + frame
.margin
.top
);
1807 if (client
.decorations
& WindowDecorationTitlebar
) {
1808 if (frame
.title
== None
) createTitlebar();
1810 XMoveResizeWindow(blackbox
->XDisplay(), frame
.title
,
1811 0, 0, frame
.rect
.width(), style
.title_height
);
1814 XMapSubwindows(blackbox
->XDisplay(), frame
.title
);
1815 XMapWindow(blackbox
->XDisplay(), frame
.title
);
1816 } else if (frame
.title
) {
1820 if (client
.decorations
& WindowDecorationHandle
) {
1821 if (frame
.handle
== None
) createHandle();
1823 // use client.rect here so the value is correct even if shaded
1824 XMoveResizeWindow(blackbox
->XDisplay(), frame
.handle
,
1825 0, client
.rect
.height() + frame
.margin
.top
,
1826 frame
.rect
.width(), style
.handle_height
);
1828 if (client
.decorations
& WindowDecorationGrip
) {
1829 if (frame
.left_grip
== None
|| frame
.right_grip
== None
) createGrips();
1831 XMoveResizeWindow(blackbox
->XDisplay(), frame
.left_grip
, 0, 0,
1832 style
.grip_width
, style
.handle_height
);
1834 const int nx
= frame
.rect
.width() - style
.grip_width
;
1835 XMoveResizeWindow(blackbox
->XDisplay(), frame
.right_grip
, nx
, 0,
1836 style
.grip_width
, style
.handle_height
);
1838 XMapSubwindows(blackbox
->XDisplay(), frame
.handle
);
1843 XMapWindow(blackbox
->XDisplay(), frame
.handle
);
1844 } else if (frame
.handle
) {
1851 * This function is responsible for updating both the client and the
1852 * frame rectangles. According to the ICCCM a client message is not
1853 * sent for a resize, only a move.
1855 void BlackboxWindow::configure(int dx
, int dy
,
1856 unsigned int dw
, unsigned int dh
) {
1857 bool send_event
= ((frame
.rect
.x() != dx
|| frame
.rect
.y() != dy
) &&
1858 ! client
.state
.moving
);
1860 if (dw
!= frame
.rect
.width() || dh
!= frame
.rect
.height()) {
1861 frame
.rect
.setRect(dx
, dy
, dw
, dh
);
1863 if (frame
.rect
.right() <= 0 || frame
.rect
.bottom() <= 0)
1864 frame
.rect
.setPos(0, 0);
1866 client
.rect
.setCoords(frame
.rect
.left() + frame
.margin
.left
,
1867 frame
.rect
.top() + frame
.margin
.top
,
1868 frame
.rect
.right() - frame
.margin
.right
,
1869 frame
.rect
.bottom() - frame
.margin
.bottom
);
1872 if (client
.state
.shaped
)
1876 XMoveResizeWindow(blackbox
->XDisplay(), frame
.window
,
1877 frame
.rect
.x(), frame
.rect
.y(),
1878 frame
.rect
.width(), frame
.rect
.height());
1882 redrawWindowFrame();
1884 frame
.rect
.setPos(dx
, dy
);
1886 XMoveWindow(blackbox
->XDisplay(), frame
.window
,
1887 frame
.rect
.x(), frame
.rect
.y());
1889 we may have been called just after an opaque window move, so
1890 even though the old coords match the new ones no ConfigureNotify
1891 has been sent yet. There are likely other times when this will
1892 be relevant as well.
1894 if (! client
.state
.moving
) send_event
= True
;
1898 // if moving, the update and event will occur when the move finishes
1899 client
.rect
.setPos(frame
.rect
.left() + frame
.margin
.left
,
1900 frame
.rect
.top() + frame
.margin
.top
);
1903 event
.type
= ConfigureNotify
;
1905 event
.xconfigure
.display
= blackbox
->XDisplay();
1906 event
.xconfigure
.event
= client
.window
;
1907 event
.xconfigure
.window
= client
.window
;
1908 event
.xconfigure
.x
= client
.rect
.x();
1909 event
.xconfigure
.y
= client
.rect
.y();
1910 event
.xconfigure
.width
= client
.rect
.width();
1911 event
.xconfigure
.height
= client
.rect
.height();
1912 event
.xconfigure
.border_width
= client
.old_bw
;
1913 event
.xconfigure
.above
= frame
.window
;
1914 event
.xconfigure
.override_redirect
= False
;
1916 XSendEvent(blackbox
->XDisplay(), client
.window
, False
,
1917 StructureNotifyMask
, &event
);
1923 void BlackboxWindow::configureShape(void) {
1924 XShapeCombineShape(blackbox
->XDisplay(), frame
.window
, ShapeBounding
,
1925 frame
.margin
.left
, frame
.margin
.top
,
1926 client
.window
, ShapeBounding
, ShapeSet
);
1929 XRectangle xrect
[2];
1931 const WindowStyle
&style
= _screen
->resource().windowStyle();
1932 if (client
.decorations
& WindowDecorationTitlebar
) {
1933 xrect
[0].x
= xrect
[0].y
= 0;
1934 xrect
[0].width
= frame
.rect
.width();
1935 xrect
[0].height
= style
.title_height
;
1939 if (client
.decorations
& WindowDecorationHandle
) {
1941 xrect
[1].y
= client
.rect
.height() + frame
.margin
.top
;
1942 xrect
[1].width
= frame
.rect
.width();
1943 xrect
[1].height
= style
.handle_height
;
1947 XShapeCombineRectangles(blackbox
->XDisplay(), frame
.window
,
1948 ShapeBounding
, 0, 0, xrect
, num
,
1949 ShapeUnion
, Unsorted
);
1954 void BlackboxWindow::addTransient(BlackboxWindow
*win
)
1955 { client
.transientList
.push_front(win
); }
1958 void BlackboxWindow::removeTransient(BlackboxWindow
*win
)
1959 { client
.transientList
.remove(win
); }
1962 BlackboxWindow
*BlackboxWindow::findTransientFor(void) const {
1963 BlackboxWindow
*win
= 0;
1964 if (isTransient()) {
1965 win
= blackbox
->findWindow(client
.transient_for
);
1966 if (win
&& win
->_screen
!= _screen
)
1974 walk up to either 1) a non-transient window 2) a group transient,
1975 watching out for a circular chain
1977 this function returns zero for non-transient windows
1979 BlackboxWindow
*BlackboxWindow::findNonTransientParent(void) const {
1980 BlackboxWindowList seen
;
1981 seen
.push_back(const_cast<BlackboxWindow
*>(this));
1983 BlackboxWindow
*w
= findTransientFor();
1987 while (w
->isTransient() && !w
->isGroupTransient()) {
1989 BlackboxWindow
* const tmp
= w
->findTransientFor();
1992 if (std::find(seen
.begin(), seen
.end(), tmp
) != seen
.end()) {
1993 // circular transient chain
2003 Returns a list of all transients. This is recursive, so it returns
2004 all transients of transients as well.
2006 BlackboxWindowList
BlackboxWindow::buildFullTransientList(void) const {
2007 BlackboxWindowList all
= client
.transientList
;
2008 BlackboxWindowList::const_iterator it
= client
.transientList
.begin(),
2009 end
= client
.transientList
.end();
2010 for (; it
!= end
; ++it
) {
2011 BlackboxWindowList x
= (*it
)->buildFullTransientList();
2012 all
.splice(all
.end(), x
);
2018 BWindowGroup
*BlackboxWindow::findWindowGroup(void) const {
2019 BWindowGroup
*group
= 0;
2020 if (client
.wmhints
.window_group
)
2021 group
= blackbox
->findWindowGroup(client
.wmhints
.window_group
);
2026 void BlackboxWindow::setWorkspace(unsigned int new_workspace
) {
2027 client
.ewmh
.workspace
= new_workspace
;
2028 blackbox
->ewmh().setWMDesktop(client
.window
, client
.ewmh
.workspace
);
2032 void BlackboxWindow::changeWorkspace(unsigned int new_workspace
,
2033 ChangeWorkspaceOption how
) {
2034 if (client
.ewmh
.workspace
== new_workspace
)
2037 if (isTransient()) {
2038 BlackboxWindow
*win
= findTransientFor();
2040 if (win
->workspace() != new_workspace
) {
2041 win
->changeWorkspace(new_workspace
, how
);
2046 assert(hasWindowFunction(WindowFunctionChangeWorkspace
));
2050 if (workspace() != bt::BSENTINEL
) {
2051 ws
= _screen
->findWorkspace(workspace());
2053 ws
->removeWindow(this);
2056 if (new_workspace
!= bt::BSENTINEL
) {
2057 ws
= _screen
->findWorkspace(new_workspace
);
2059 ws
->addWindow(this);
2063 case StayOnCurrentWorkspace
:
2064 if (isVisible() && workspace() != bt::BSENTINEL
2065 && workspace() != _screen
->currentWorkspace()) {
2067 } else if (!isVisible()
2068 && (workspace() == bt::BSENTINEL
2069 || workspace() == _screen
->currentWorkspace())) {
2074 case SwitchToNewWorkspace
:
2076 we will change to the new workspace soon, so force this window
2083 // change workspace on all transients
2084 if (!client
.transientList
.empty()) {
2085 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2086 end
= client
.transientList
.end();
2087 for (; it
!= end
; ++it
)
2088 (*it
)->changeWorkspace(new_workspace
, how
);
2093 void BlackboxWindow::changeLayer(StackingList::Layer new_layer
) {
2094 if (layer() == new_layer
)
2097 bool restack
= false;
2098 if (isTransient()) {
2099 BlackboxWindow
*win
= findTransientFor();
2101 if (win
->layer() != new_layer
) {
2102 win
->changeLayer(new_layer
);
2109 assert(hasWindowFunction(WindowFunctionChangeLayer
));
2113 _screen
->stackingList().changeLayer(this, new_layer
);
2115 if (!client
.transientList
.empty()) {
2116 BlackboxWindowList::iterator it
= client
.transientList
.begin();
2117 const BlackboxWindowList::iterator end
= client
.transientList
.end();
2118 for (; it
!= end
; ++it
)
2119 (*it
)->changeLayer(new_layer
);
2123 _screen
->restackWindows();
2127 bool BlackboxWindow::setInputFocus(void) {
2130 if (client
.state
.focused
)
2133 switch (windowType()) {
2134 case WindowTypeDock
:
2136 Many docks have auto-hide features similar to the toolbar and
2137 slit... we don't want these things to be moved to the center of
2138 the screen when switching to an empty workspace.
2143 { const bt::Rect
&scr
= _screen
->screenInfo().rect();
2144 if (!frame
.rect
.intersects(scr
)) {
2145 // client is outside the screen, move it to the center
2146 configure(scr
.x() + (scr
.width() - frame
.rect
.width()) / 2,
2147 scr
.y() + (scr
.height() - frame
.rect
.height()) / 2,
2148 frame
.rect
.width(), frame
.rect
.height());
2155 pass focus to any modal transients, giving modal group transients
2158 BWindowGroup
*group
= findWindowGroup();
2159 if (group
&& !group
->transients().empty()) {
2160 BlackboxWindowList::const_iterator it
= group
->transients().begin(),
2161 end
= group
->transients().end();
2162 for (; it
!= end
; ++it
) {
2163 BlackboxWindow
* const tmp
= *it
;
2164 if (!tmp
->isVisible() || !tmp
->isModal())
2167 // we are the newest modal group transient
2170 if (isTransient()) {
2171 if (tmp
== findNonTransientParent()) {
2172 // we are a transient of the modal group transient
2176 return tmp
->setInputFocus();
2180 if (!client
.transientList
.empty()) {
2181 BlackboxWindowList::const_iterator it
= client
.transientList
.begin(),
2182 end
= client
.transientList
.end();
2183 for (; it
!= end
; ++it
) {
2184 BlackboxWindow
* const tmp
= *it
;
2185 if (tmp
->isVisible() && tmp
->isModal())
2186 return tmp
->setInputFocus();
2190 switch (windowType()) {
2191 case WindowTypeDock
:
2197 XSetInputFocus(blackbox
->XDisplay(), client
.window
,
2198 RevertToPointerRoot
, blackbox
->XTime());
2200 if (client
.wmprotocols
.wm_take_focus
) {
2202 ce
.xclient
.type
= ClientMessage
;
2203 ce
.xclient
.message_type
= blackbox
->wmProtocolsAtom();
2204 ce
.xclient
.display
= blackbox
->XDisplay();
2205 ce
.xclient
.window
= client
.window
;
2206 ce
.xclient
.format
= 32;
2207 ce
.xclient
.data
.l
[0] = blackbox
->wmTakeFocusAtom();
2208 ce
.xclient
.data
.l
[1] = blackbox
->XTime();
2209 ce
.xclient
.data
.l
[2] = 0l;
2210 ce
.xclient
.data
.l
[3] = 0l;
2211 ce
.xclient
.data
.l
[4] = 0l;
2212 XSendEvent(blackbox
->XDisplay(), client
.window
, False
, NoEventMask
, &ce
);
2219 void BlackboxWindow::show(void) {
2220 if (client
.state
.visible
)
2223 if (client
.state
.iconic
)
2224 _screen
->removeIcon(this);
2226 client
.state
.iconic
= false;
2227 client
.state
.visible
= true;
2228 setState(isShaded() ? IconicState
: NormalState
);
2230 XMapWindow(blackbox
->XDisplay(), client
.window
);
2231 XMapSubwindows(blackbox
->XDisplay(), frame
.window
);
2232 XMapWindow(blackbox
->XDisplay(), frame
.window
);
2234 if (!client
.transientList
.empty()) {
2235 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2236 end
= client
.transientList
.end();
2237 for (; it
!= end
; ++it
)
2244 XTranslateCoordinates(blackbox
->XDisplay(), client
.window
,
2245 _screen
->screenInfo().rootWindow(),
2246 0, 0, &real_x
, &real_y
, &child
);
2247 fprintf(stderr
, "%s -- assumed: (%d, %d), real: (%d, %d)\n", title().c_str(),
2248 client
.rect
.left(), client
.rect
.top(), real_x
, real_y
);
2249 assert(client
.rect
.left() == real_x
&& client
.rect
.top() == real_y
);
2254 void BlackboxWindow::hide(void) {
2255 if (!client
.state
.visible
)
2258 client
.state
.visible
= false;
2259 setState(client
.state
.iconic
? IconicState
: client
.current_state
);
2261 XUnmapWindow(blackbox
->XDisplay(), frame
.window
);
2264 * we don't want this XUnmapWindow call to generate an UnmapNotify
2265 * event, so we need to clear the event mask on client.window for a
2266 * split second. HOWEVER, since X11 is asynchronous, the window
2267 * could be destroyed in that split second, leaving us with a ghost
2268 * window... so, we need to do this while the X server is grabbed
2270 blackbox
->XGrabServer();
2271 XSelectInput(blackbox
->XDisplay(), client
.window
,
2272 client_window_event_mask
& ~StructureNotifyMask
);
2273 XUnmapWindow(blackbox
->XDisplay(), client
.window
);
2274 XSelectInput(blackbox
->XDisplay(), client
.window
, client_window_event_mask
);
2275 blackbox
->XUngrabServer();
2279 void BlackboxWindow::close(void) {
2280 assert(hasWindowFunction(WindowFunctionClose
));
2283 ce
.xclient
.type
= ClientMessage
;
2284 ce
.xclient
.message_type
= blackbox
->wmProtocolsAtom();
2285 ce
.xclient
.display
= blackbox
->XDisplay();
2286 ce
.xclient
.window
= client
.window
;
2287 ce
.xclient
.format
= 32;
2288 ce
.xclient
.data
.l
[0] = blackbox
->wmDeleteWindowAtom();
2289 ce
.xclient
.data
.l
[1] = blackbox
->XTime();
2290 ce
.xclient
.data
.l
[2] = 0l;
2291 ce
.xclient
.data
.l
[3] = 0l;
2292 ce
.xclient
.data
.l
[4] = 0l;
2293 XSendEvent(blackbox
->XDisplay(), client
.window
, False
, NoEventMask
, &ce
);
2297 void BlackboxWindow::activate(void) {
2298 if (workspace() != bt::BSENTINEL
2299 && workspace() != _screen
->currentWorkspace())
2300 _screen
->setCurrentWorkspace(workspace());
2301 if (client
.state
.iconic
)
2303 if (client
.ewmh
.shaded
)
2305 if (setInputFocus())
2306 _screen
->raiseWindow(this);
2310 void BlackboxWindow::iconify(void) {
2311 if (client
.state
.iconic
)
2314 if (isTransient()) {
2315 BlackboxWindow
*win
= findTransientFor();
2317 if (!win
->isIconic()) {
2323 assert(hasWindowFunction(WindowFunctionIconify
));
2326 _screen
->addIcon(this);
2328 client
.state
.iconic
= true;
2331 // iconify all transients
2332 if (!client
.transientList
.empty()) {
2333 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2334 end
= client
.transientList
.end();
2335 for (; it
!= end
; ++it
)
2341 void BlackboxWindow::maximize(unsigned int button
) {
2342 assert(hasWindowFunction(WindowFunctionMaximize
));
2344 // any maximize operation always unshades
2345 client
.ewmh
.shaded
= false;
2346 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
2347 + frame
.margin
.bottom
);
2349 if (isMaximized()) {
2350 // restore from maximized
2351 client
.ewmh
.maxh
= client
.ewmh
.maxv
= false;
2353 if (!isFullScreen()) {
2355 when a resize is begun, maximize(0) is called to clear any
2356 maximization flags currently set. Otherwise it still thinks
2357 it is maximized. so we do not need to call configure()
2358 because resizing will handle it
2360 if (! client
.state
.resizing
) {
2361 bt::Rect r
= ::applyGravity(client
.premax
,
2363 client
.wmnormal
.win_gravity
);
2364 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2365 // trick configure into working
2366 frame
.rect
= bt::Rect();
2370 redrawAllButtons(); // in case it is not called in configure()
2374 updateEWMHAllowedActions();
2380 client
.ewmh
.maxh
= true;
2381 client
.ewmh
.maxv
= true;
2385 client
.ewmh
.maxh
= false;
2386 client
.ewmh
.maxv
= true;
2390 client
.ewmh
.maxh
= true;
2391 client
.ewmh
.maxv
= false;
2399 if (!isFullScreen()) {
2400 // go go gadget-maximize!
2401 bt::Rect r
= _screen
->availableArea();
2403 if (!client
.ewmh
.maxh
) {
2404 r
.setX(frame
.rect
.x());
2405 r
.setWidth(frame
.rect
.width());
2407 if (!client
.ewmh
.maxv
) {
2408 r
.setY(frame
.rect
.y());
2409 r
.setHeight(frame
.rect
.height());
2412 // store the current frame geometry, so that we can restore it later
2413 client
.premax
= ::restoreGravity(frame
.rect
,
2415 client
.wmnormal
.win_gravity
);
2417 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2418 // trick configure into working
2419 frame
.rect
= bt::Rect();
2424 updateEWMHAllowedActions();
2429 re-maximizes the window to take into account availableArea changes.
2431 note that unlike maximize(), the shaded state is preserved.
2433 void BlackboxWindow::remaximize(void) {
2435 bt::Rect r
= _screen
->availableArea();
2437 if (!client
.ewmh
.maxh
) {
2438 r
.setX(frame
.rect
.x());
2439 r
.setWidth(frame
.rect
.width());
2441 if (!client
.ewmh
.maxv
) {
2442 r
.setY(frame
.rect
.y());
2443 r
.setHeight(frame
.rect
.height());
2446 frame
.rect
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2451 // set the frame rect to the shaded size
2452 const WindowStyle
&style
= _screen
->resource().windowStyle();
2453 frame
.rect
.setHeight(style
.title_height
);
2454 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
2455 frame
.rect
.width(), frame
.rect
.height());
2459 unsigned int button
= 0u;
2460 if (client
.ewmh
.maxv
) {
2461 button
= (client
.ewmh
.maxh
) ? 1u : 2u;
2462 } else if (client
.ewmh
.maxh
) {
2463 button
= (client
.ewmh
.maxv
) ? 1u : 3u;
2466 // trick maximize() into working
2467 client
.ewmh
.maxh
= client
.ewmh
.maxv
= false;
2468 const bt::Rect tmp
= client
.premax
;
2470 client
.premax
= tmp
;
2474 void BlackboxWindow::setShaded(bool shaded
) {
2475 assert(hasWindowFunction(WindowFunctionShade
));
2477 if (client
.ewmh
.shaded
== shaded
)
2480 client
.ewmh
.shaded
= shaded
;
2482 if (isMaximized()) {
2485 // set the frame rect to the normal size
2486 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
+
2487 frame
.margin
.bottom
);
2489 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
2490 frame
.rect
.width(), frame
.rect
.height());
2493 setState(NormalState
);
2495 // set the frame rect to the shaded size
2496 const WindowStyle
&style
= _screen
->resource().windowStyle();
2497 frame
.rect
.setHeight(style
.title_height
);
2499 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
2500 frame
.rect
.width(), frame
.rect
.height());
2502 setState(IconicState
);
2507 void BlackboxWindow::setFullScreen(bool b
) {
2508 assert(hasWindowFunction(WindowFunctionFullScreen
));
2510 if (client
.ewmh
.fullscreen
== b
)
2513 // any fullscreen operation always unshades
2514 client
.ewmh
.shaded
= false;
2515 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
2516 + frame
.margin
.bottom
);
2518 bool refocus
= isFocused();
2519 client
.ewmh
.fullscreen
= b
;
2520 if (isFullScreen()) {
2521 // go go gadget-fullscreen!
2523 client
.premax
= ::restoreGravity(frame
.rect
,
2525 client
.wmnormal
.win_gravity
);
2527 // modify decorations, functions and frame margin
2528 client
.decorations
= NoWindowDecorations
;
2529 client
.functions
&= ~(WindowFunctionMove
|
2530 WindowFunctionResize
|
2531 WindowFunctionShade
);
2532 const WindowStyle
&style
= _screen
->resource().windowStyle();
2533 frame
.margin
= ::update_margin(client
.decorations
, style
);
2536 * Note: we don't call ::constrain() here, because many
2537 * applications are broken in this respect. Some specify a
2538 * max-size or aspect-ratio that simply doesn't cover the entire
2539 * screen. Let's try to be smarter than such applications and
2540 * simply cover the entire screen.
2543 // trick configure() into working
2544 frame
.rect
= bt::Rect();
2545 configure(_screen
->screenInfo().rect());
2548 changeLayer(StackingList::LayerFullScreen
);
2551 updateEWMHAllowedActions();
2553 // restore from fullscreen
2554 ::update_decorations(client
.decorations
,
2560 client
.wmprotocols
);
2561 const WindowStyle
&style
= _screen
->resource().windowStyle();
2562 frame
.margin
= ::update_margin(client
.decorations
, style
);
2565 changeLayer(StackingList::LayerNormal
);
2567 if (isMaximized()) {
2570 bt::Rect r
= ::applyGravity(client
.premax
,
2572 client
.wmnormal
.win_gravity
);
2573 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2575 // trick configure into working
2576 frame
.rect
= bt::Rect();
2580 updateEWMHAllowedActions();
2588 (void) setInputFocus();
2592 void BlackboxWindow::redrawWindowFrame(void) const {
2593 if (client
.decorations
& WindowDecorationTitlebar
) {
2599 if (client
.decorations
& WindowDecorationBorder
) {
2600 const WindowStyle
&style
= _screen
->resource().windowStyle();
2601 const bt::Color
&c
= (isFocused()
2602 ? style
.focus
.frame_border
2603 : style
.unfocus
.frame_border
);
2604 XSetWindowBorder(blackbox
->XDisplay(), frame
.plate
,
2605 c
.pixel(_screen
->screenNumber()));
2608 if (client
.decorations
& WindowDecorationHandle
) {
2611 if (client
.decorations
& WindowDecorationGrip
)
2617 void BlackboxWindow::setFocused(bool focused
) {
2618 if (focused
== client
.state
.focused
)
2621 client
.state
.focused
= isVisible() ? focused
: false;
2624 redrawWindowFrame();
2626 if (client
.state
.focused
) {
2627 XInstallColormap(blackbox
->XDisplay(), client
.colormap
);
2629 if (client
.ewmh
.fullscreen
&& layer() != StackingList::LayerBelow
)
2630 changeLayer(StackingList::LayerBelow
);
2636 void BlackboxWindow::setState(unsigned long new_state
) {
2637 client
.current_state
= new_state
;
2639 unsigned long state
[2];
2640 state
[0] = client
.current_state
;
2642 XChangeProperty(blackbox
->XDisplay(), client
.window
,
2643 blackbox
->wmStateAtom(), blackbox
->wmStateAtom(), 32,
2644 PropModeReplace
, (unsigned char *) state
, 2);
2647 updateEWMHAllowedActions();
2651 void BlackboxWindow::updateEWMHState() {
2652 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2654 // set _NET_WM_STATE
2655 bt::EWMH::AtomList atoms
;
2657 atoms
.push_back(ewmh
.wmStateModal());
2659 atoms
.push_back(ewmh
.wmStateShaded());
2661 atoms
.push_back(ewmh
.wmStateHidden());
2663 atoms
.push_back(ewmh
.wmStateFullscreen());
2664 if (client
.ewmh
.maxh
)
2665 atoms
.push_back(ewmh
.wmStateMaximizedHorz());
2666 if (client
.ewmh
.maxv
)
2667 atoms
.push_back(ewmh
.wmStateMaximizedVert());
2668 if (client
.ewmh
.skip_taskbar
)
2669 atoms
.push_back(ewmh
.wmStateSkipTaskbar());
2670 if (client
.ewmh
.skip_pager
)
2671 atoms
.push_back(ewmh
.wmStateSkipPager());
2674 case StackingList::LayerAbove
:
2675 atoms
.push_back(ewmh
.wmStateAbove());
2677 case StackingList::LayerBelow
:
2678 atoms
.push_back(ewmh
.wmStateBelow());
2685 ewmh
.removeProperty(client
.window
, ewmh
.wmState());
2687 ewmh
.setWMState(client
.window
, atoms
);
2691 void BlackboxWindow::updateEWMHAllowedActions() {
2692 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2694 // set _NET_WM_ALLOWED_ACTIONS
2695 bt::EWMH::AtomList atoms
;
2696 if (! client
.state
.iconic
) {
2697 if (hasWindowFunction(WindowFunctionChangeWorkspace
))
2698 atoms
.push_back(ewmh
.wmActionChangeDesktop());
2700 if (hasWindowFunction(WindowFunctionIconify
))
2701 atoms
.push_back(ewmh
.wmActionMinimize());
2703 if (hasWindowFunction(WindowFunctionShade
))
2704 atoms
.push_back(ewmh
.wmActionShade());
2706 if (hasWindowFunction(WindowFunctionMove
))
2707 atoms
.push_back(ewmh
.wmActionMove());
2709 if (hasWindowFunction(WindowFunctionResize
))
2710 atoms
.push_back(ewmh
.wmActionResize());
2712 if (hasWindowFunction(WindowFunctionMaximize
)) {
2713 atoms
.push_back(ewmh
.wmActionMaximizeHorz());
2714 atoms
.push_back(ewmh
.wmActionMaximizeVert());
2717 atoms
.push_back(ewmh
.wmActionFullscreen());
2720 if (hasWindowFunction(WindowFunctionClose
))
2721 atoms
.push_back(ewmh
.wmActionClose());
2724 ewmh
.removeProperty(client
.window
, ewmh
.wmAllowedActions());
2726 ewmh
.setWMAllowedActions(client
.window
, atoms
);
2730 void BlackboxWindow::redrawTitle(void) const {
2731 const WindowStyle
&style
= _screen
->resource().windowStyle();
2732 const bt::Rect
u(0, 0, frame
.rect
.width(), style
.title_height
);
2733 bt::drawTexture(_screen
->screenNumber(),
2734 (client
.state
.focused
2736 : style
.unfocus
.title
),
2738 (client
.state
.focused
2744 void BlackboxWindow::redrawLabel(void) const {
2745 const WindowStyle
&style
= _screen
->resource().windowStyle();
2746 bt::Rect
u(0, 0, frame
.label_w
, style
.label_height
);
2747 Pixmap p
= (client
.state
.focused
? frame
.flabel
: frame
.ulabel
);
2748 if (p
== ParentRelative
) {
2749 const bt::Texture
&texture
=
2750 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2751 int offset
= texture
.borderWidth();
2752 if (client
.decorations
& WindowDecorationIconify
)
2753 offset
+= style
.button_width
+ style
.title_margin
;
2755 const bt::Rect
t(-(style
.title_margin
+ offset
),
2756 -(style
.title_margin
+ texture
.borderWidth()),
2757 frame
.rect
.width(), style
.title_height
);
2758 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.label
, t
, u
,
2759 (client
.state
.focused
? frame
.ftitle
: frame
.utitle
));
2761 bt::drawTexture(_screen
->screenNumber(),
2762 (client
.state
.focused
2764 : style
.unfocus
.label
),
2765 frame
.label
, u
, u
, p
);
2768 const bt::Pen
pen(_screen
->screenNumber(),
2769 ((client
.state
.focused
)
2771 : style
.unfocus
.text
));
2772 u
.setCoords(u
.left() + style
.label_margin
,
2773 u
.top() + style
.label_margin
,
2774 u
.right() - style
.label_margin
,
2775 u
.bottom() - style
.label_margin
);
2776 bt::drawText(style
.font
, pen
, frame
.label
, u
,
2777 style
.alignment
, client
.visible_title
);
2781 void BlackboxWindow::redrawAllButtons(void) const {
2782 if (frame
.iconify_button
) redrawIconifyButton();
2783 if (frame
.maximize_button
) redrawMaximizeButton();
2784 if (frame
.close_button
) redrawCloseButton();
2788 void BlackboxWindow::redrawIconifyButton(bool pressed
) const {
2789 const WindowStyle
&style
= _screen
->resource().windowStyle();
2790 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2791 Pixmap p
= (pressed
? frame
.pbutton
:
2792 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2793 if (p
== ParentRelative
) {
2794 const bt::Texture
&texture
=
2795 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2796 const bt::Rect
t(-(style
.title_margin
+ texture
.borderWidth()),
2797 -(style
.title_margin
+ texture
.borderWidth()),
2798 frame
.rect
.width(), style
.title_height
);
2799 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.iconify_button
,
2800 t
, u
, (client
.state
.focused
2804 bt::drawTexture(_screen
->screenNumber(),
2805 (pressed
? style
.pressed
:
2806 (client
.state
.focused
? style
.focus
.button
:
2807 style
.unfocus
.button
)),
2808 frame
.iconify_button
, u
, u
, p
);
2811 const bt::Pen
pen(_screen
->screenNumber(),
2812 (client
.state
.focused
2813 ? style
.focus
.foreground
2814 : style
.unfocus
.foreground
));
2815 bt::drawBitmap(style
.iconify
, pen
, frame
.iconify_button
, u
);
2819 void BlackboxWindow::redrawMaximizeButton(bool pressed
) const {
2820 const WindowStyle
&style
= _screen
->resource().windowStyle();
2821 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2822 Pixmap p
= (pressed
? frame
.pbutton
:
2823 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2824 if (p
== ParentRelative
) {
2825 const bt::Texture
&texture
=
2826 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2827 int button_w
= style
.button_width
2828 + style
.title_margin
+ texture
.borderWidth();
2829 if (client
.decorations
& WindowDecorationClose
)
2831 const bt::Rect
t(-(frame
.rect
.width() - button_w
),
2832 -(style
.title_margin
+ texture
.borderWidth()),
2833 frame
.rect
.width(), style
.title_height
);
2834 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.maximize_button
,
2835 t
, u
, (client
.state
.focused
2839 bt::drawTexture(_screen
->screenNumber(),
2840 (pressed
? style
.pressed
:
2841 (client
.state
.focused
? style
.focus
.button
:
2842 style
.unfocus
.button
)),
2843 frame
.maximize_button
, u
, u
, p
);
2846 const bt::Pen
pen(_screen
->screenNumber(),
2847 (client
.state
.focused
2848 ? style
.focus
.foreground
2849 : style
.unfocus
.foreground
));
2850 bt::drawBitmap(isMaximized() ? style
.restore
: style
.maximize
,
2851 pen
, frame
.maximize_button
, u
);
2855 void BlackboxWindow::redrawCloseButton(bool pressed
) const {
2856 const WindowStyle
&style
= _screen
->resource().windowStyle();
2857 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2858 Pixmap p
= (pressed
? frame
.pbutton
:
2859 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2860 if (p
== ParentRelative
) {
2861 const bt::Texture
&texture
=
2862 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2863 const int button_w
= style
.button_width
+
2864 style
.title_margin
+
2865 texture
.borderWidth();
2866 const bt::Rect
t(-(frame
.rect
.width() - button_w
),
2867 -(style
.title_margin
+ texture
.borderWidth()),
2868 frame
.rect
.width(), style
.title_height
);
2869 bt::drawTexture(_screen
->screenNumber(),texture
, frame
.close_button
, t
, u
,
2870 (client
.state
.focused
? frame
.ftitle
: frame
.utitle
));
2872 bt::drawTexture(_screen
->screenNumber(),
2873 (pressed
? style
.pressed
:
2874 (client
.state
.focused
? style
.focus
.button
:
2875 style
.unfocus
.button
)),
2876 frame
.close_button
, u
, u
, p
);
2879 const bt::Pen
pen(_screen
->screenNumber(),
2880 (client
.state
.focused
2881 ? style
.focus
.foreground
2882 : style
.unfocus
.foreground
));
2883 bt::drawBitmap(style
.close
, pen
, frame
.close_button
, u
);
2887 void BlackboxWindow::redrawHandle(void) const {
2888 const WindowStyle
&style
= _screen
->resource().windowStyle();
2889 const bt::Rect
u(0, 0, frame
.rect
.width(), style
.handle_height
);
2890 bt::drawTexture(_screen
->screenNumber(),
2891 (client
.state
.focused
? style
.focus
.handle
:
2892 style
.unfocus
.handle
),
2894 (client
.state
.focused
? frame
.fhandle
: frame
.uhandle
));
2898 void BlackboxWindow::redrawGrips(void) const {
2899 const WindowStyle
&style
= _screen
->resource().windowStyle();
2900 const bt::Rect
u(0, 0, style
.grip_width
, style
.handle_height
);
2901 Pixmap p
= (client
.state
.focused
? frame
.fgrip
: frame
.ugrip
);
2902 if (p
== ParentRelative
) {
2903 bt::Rect
t(0, 0, frame
.rect
.width(), style
.handle_height
);
2904 bt::drawTexture(_screen
->screenNumber(),
2905 (client
.state
.focused
? style
.focus
.handle
:
2906 style
.unfocus
.handle
),
2907 frame
.right_grip
, t
, u
, p
);
2909 t
.setPos(-(frame
.rect
.width() - style
.grip_width
), 0);
2910 bt::drawTexture(_screen
->screenNumber(),
2911 (client
.state
.focused
? style
.focus
.handle
:
2912 style
.unfocus
.handle
),
2913 frame
.right_grip
, t
, u
, p
);
2915 bt::drawTexture(_screen
->screenNumber(),
2916 (client
.state
.focused
? style
.focus
.grip
:
2917 style
.unfocus
.grip
),
2918 frame
.left_grip
, u
, u
, p
);
2920 bt::drawTexture(_screen
->screenNumber(),
2921 (client
.state
.focused
? style
.focus
.grip
:
2922 style
.unfocus
.grip
),
2923 frame
.right_grip
, u
, u
, p
);
2929 BlackboxWindow::clientMessageEvent(const XClientMessageEvent
* const event
) {
2930 if (event
->format
!= 32)
2933 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2935 if (event
->message_type
== blackbox
->wmChangeStateAtom()) {
2936 if (event
->data
.l
[0] == IconicState
) {
2937 if (hasWindowFunction(WindowFunctionIconify
))
2939 } else if (event
->data
.l
[0] == NormalState
) {
2942 } else if (event
->message_type
== ewmh
.activeWindow()) {
2944 } else if (event
->message_type
== ewmh
.closeWindow()) {
2945 if (hasWindowFunction(WindowFunctionClose
))
2947 } else if (event
->message_type
== ewmh
.moveresizeWindow()) {
2948 XConfigureRequestEvent request
;
2949 request
.window
= event
->window
;
2950 request
.value_mask
=
2951 (event
->data
.l
[0] >> 8) & (CWX
| CWY
| CWWidth
| CWHeight
);
2952 request
.x
= event
->data
.l
[1];
2953 request
.y
= event
->data
.l
[2];
2954 request
.width
= event
->data
.l
[3];
2955 request
.height
= event
->data
.l
[4];
2957 const int gravity
= (event
->data
.l
[0] & 0xff);
2958 const int old_gravity
= client
.wmnormal
.win_gravity
;
2959 if (event
->data
.l
[0] != 0)
2960 client
.wmnormal
.win_gravity
= gravity
;
2962 configureRequestEvent(&request
);
2964 client
.wmnormal
.win_gravity
= old_gravity
;
2965 } else if (event
->message_type
== ewmh
.wmDesktop()) {
2966 if (hasWindowFunction(WindowFunctionChangeWorkspace
)) {
2967 const unsigned int new_workspace
= event
->data
.l
[0];
2968 changeWorkspace(new_workspace
);
2970 } else if (event
->message_type
== ewmh
.wmState()) {
2971 Atom action
= event
->data
.l
[0],
2972 first
= event
->data
.l
[1],
2973 second
= event
->data
.l
[2];
2975 if (first
== ewmh
.wmStateModal() || second
== ewmh
.wmStateModal()) {
2976 if ((action
== ewmh
.wmStateAdd() ||
2977 (action
== ewmh
.wmStateToggle() && ! client
.ewmh
.modal
)) &&
2979 client
.ewmh
.modal
= true;
2981 client
.ewmh
.modal
= false;
2984 if (hasWindowFunction(WindowFunctionMaximize
)) {
2985 int max_horz
= 0, max_vert
= 0;
2987 if (first
== ewmh
.wmStateMaximizedHorz() ||
2988 second
== ewmh
.wmStateMaximizedHorz()) {
2989 max_horz
= ((action
== ewmh
.wmStateAdd()
2990 || (action
== ewmh
.wmStateToggle()
2991 && !client
.ewmh
.maxh
))
2995 if (first
== ewmh
.wmStateMaximizedVert() ||
2996 second
== ewmh
.wmStateMaximizedVert()) {
2997 max_vert
= ((action
== ewmh
.wmStateAdd()
2998 || (action
== ewmh
.wmStateToggle()
2999 && !client
.ewmh
.maxv
))
3003 if (max_horz
!= 0 || max_vert
!= 0) {
3006 unsigned int button
= 0u;
3007 if (max_horz
== 1 && max_vert
!= 1)
3009 else if (max_vert
== 1 && max_horz
!= 1)
3011 else if (max_vert
== 1 && max_horz
== 1)
3018 if (hasWindowFunction(WindowFunctionShade
)) {
3019 if (first
== ewmh
.wmStateShaded() ||
3020 second
== ewmh
.wmStateShaded()) {
3021 if (action
== ewmh
.wmStateRemove())
3023 else if (action
== ewmh
.wmStateAdd())
3025 else if (action
== ewmh
.wmStateToggle())
3026 setShaded(!isShaded());
3030 if (first
== ewmh
.wmStateSkipTaskbar()
3031 || second
== ewmh
.wmStateSkipTaskbar()
3032 || first
== ewmh
.wmStateSkipPager()
3033 || second
== ewmh
.wmStateSkipPager()) {
3034 if (first
== ewmh
.wmStateSkipTaskbar()
3035 || second
== ewmh
.wmStateSkipTaskbar()) {
3036 client
.ewmh
.skip_taskbar
= (action
== ewmh
.wmStateAdd()
3037 || (action
== ewmh
.wmStateToggle()
3038 && !client
.ewmh
.skip_taskbar
));
3040 if (first
== ewmh
.wmStateSkipPager()
3041 || second
== ewmh
.wmStateSkipPager()) {
3042 client
.ewmh
.skip_pager
= (action
== ewmh
.wmStateAdd()
3043 || (action
== ewmh
.wmStateToggle()
3044 && !client
.ewmh
.skip_pager
));
3046 // we do nothing with skip_*, but others might... we should at
3047 // least make sure these are present in _NET_WM_STATE
3051 if (first
== ewmh
.wmStateHidden() ||
3052 second
== ewmh
.wmStateHidden()) {
3054 ignore _NET_WM_STATE_HIDDEN, the wm sets this state, not the
3059 if (hasWindowFunction(WindowFunctionFullScreen
)) {
3060 if (first
== ewmh
.wmStateFullscreen() ||
3061 second
== ewmh
.wmStateFullscreen()) {
3062 if (action
== ewmh
.wmStateAdd() ||
3063 (action
== ewmh
.wmStateToggle() &&
3064 ! client
.ewmh
.fullscreen
)) {
3065 setFullScreen(true);
3066 } else if (action
== ewmh
.wmStateToggle() ||
3067 action
== ewmh
.wmStateRemove()) {
3068 setFullScreen(false);
3073 if (hasWindowFunction(WindowFunctionChangeLayer
)) {
3074 if (first
== ewmh
.wmStateAbove() ||
3075 second
== ewmh
.wmStateAbove()) {
3076 if (action
== ewmh
.wmStateAdd() ||
3077 (action
== ewmh
.wmStateToggle() &&
3078 layer() != StackingList::LayerAbove
)) {
3079 changeLayer(StackingList::LayerAbove
);
3080 } else if (action
== ewmh
.wmStateToggle() ||
3081 action
== ewmh
.wmStateRemove()) {
3082 changeLayer(StackingList::LayerNormal
);
3086 if (first
== ewmh
.wmStateBelow() ||
3087 second
== ewmh
.wmStateBelow()) {
3088 if (action
== ewmh
.wmStateAdd() ||
3089 (action
== ewmh
.wmStateToggle() &&
3090 layer() != StackingList::LayerBelow
)) {
3091 changeLayer(StackingList::LayerBelow
);
3092 } else if (action
== ewmh
.wmStateToggle() ||
3093 action
== ewmh
.wmStateRemove()) {
3094 changeLayer(StackingList::LayerNormal
);
3102 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent
* const event
) {
3103 if (event
->window
!= client
.window
)
3107 fprintf(stderr
, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
3111 _screen
->releaseWindow(this);
3116 BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent
* const event
) {
3117 if (event
->window
!= client
.window
)
3121 fprintf(stderr
, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
3125 _screen
->releaseWindow(this);
3129 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent
* const event
) {
3130 if (event
->window
!= client
.window
|| event
->parent
== frame
.plate
)
3134 fprintf(stderr
, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
3135 "0x%lx.\n", client
.window
, event
->parent
);
3139 put the ReparentNotify event back into the queue so that
3140 BlackboxWindow::restore(void) can do the right thing
3143 replay
.xreparent
= *event
;
3144 XPutBackEvent(blackbox
->XDisplay(), &replay
);
3146 _screen
->releaseWindow(this);
3150 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent
* const event
) {
3152 fprintf(stderr
, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
3156 switch(event
->atom
) {
3157 case XA_WM_TRANSIENT_FOR
: {
3158 if (isTransient()) {
3159 // remove ourselves from our transient_for
3160 BlackboxWindow
*win
= findTransientFor();
3162 win
->removeTransient(this);
3163 } else if (isGroupTransient()) {
3164 BWindowGroup
*group
= findWindowGroup();
3166 group
->removeTransient(this);
3170 // determine if this is a transient window
3171 client
.transient_for
= ::readTransientInfo(blackbox
,
3173 _screen
->screenInfo(),
3176 if (isTransient()) {
3177 BlackboxWindow
*win
= findTransientFor();
3179 // add ourselves to our new transient_for
3180 win
->addTransient(this);
3181 changeWorkspace(win
->workspace());
3182 changeLayer(win
->layer());
3183 } else if (isGroupTransient()) {
3184 BWindowGroup
*group
= findWindowGroup();
3186 group
->addTransient(this);
3189 client
.transient_for
= 0;
3193 ::update_decorations(client
.decorations
,
3199 client
.wmprotocols
);
3206 // remove from current window group
3207 BWindowGroup
*group
= findWindowGroup();
3209 if (isTransient() && !findTransientFor() && isGroupTransient())
3210 group
->removeTransient(this);
3211 group
->removeWindow(this);
3215 client
.wmhints
= ::readWMHints(blackbox
, client
.window
);
3217 if (client
.wmhints
.window_group
!= None
) {
3218 // add to new window group
3219 group
= ::update_window_group(client
.wmhints
.window_group
,
3222 if (isTransient() && !findTransientFor() && isGroupTransient()) {
3224 group
->addTransient(this);
3230 case XA_WM_ICON_NAME
: {
3231 client
.icon_title
= ::readWMIconName(blackbox
, client
.window
);
3232 if (client
.state
.iconic
)
3233 _screen
->propagateWindowName(this);
3238 client
.title
= ::readWMName(blackbox
, client
.window
);
3240 client
.visible_title
=
3241 bt::ellideText(client
.title
, frame
.label_w
, bt::toUnicode("..."),
3242 _screen
->screenNumber(),
3243 _screen
->resource().windowStyle().font
);
3244 blackbox
->ewmh().setWMVisibleName(client
.window
, client
.visible_title
);
3246 if (client
.decorations
& WindowDecorationTitlebar
)
3249 _screen
->propagateWindowName(this);
3253 case XA_WM_NORMAL_HINTS
: {
3254 WMNormalHints wmnormal
= ::readWMNormalHints(blackbox
, client
.window
,
3255 _screen
->screenInfo());
3256 if (wmnormal
== client
.wmnormal
) {
3257 // apps like xv and GNU emacs seem to like to repeatedly set
3258 // this property over and over
3262 client
.wmnormal
= wmnormal
;
3264 ::update_decorations(client
.decorations
,
3270 client
.wmprotocols
);
3277 if (event
->atom
== blackbox
->wmProtocolsAtom()) {
3278 client
.wmprotocols
= ::readWMProtocols(blackbox
, client
.window
);
3280 ::update_decorations(client
.decorations
,
3286 client
.wmprotocols
);
3289 } else if (event
->atom
== blackbox
->motifWmHintsAtom()) {
3290 client
.motif
= ::readMotifWMHints(blackbox
, client
.window
);
3292 ::update_decorations(client
.decorations
,
3298 client
.wmprotocols
);
3301 } else if (event
->atom
== blackbox
->ewmh().wmStrut()) {
3302 if (! client
.strut
) {
3303 client
.strut
= new bt::EWMH::Strut
;
3304 _screen
->addStrut(client
.strut
);
3307 blackbox
->ewmh().readWMStrut(client
.window
, client
.strut
);
3308 if (client
.strut
->left
|| client
.strut
->right
||
3309 client
.strut
->top
|| client
.strut
->bottom
) {
3310 _screen
->updateStrut();
3312 _screen
->removeStrut(client
.strut
);
3313 delete client
.strut
;
3324 void BlackboxWindow::exposeEvent(const XExposeEvent
* const event
) {
3326 fprintf(stderr
, "BlackboxWindow::exposeEvent() for 0x%lx\n", client
.window
);
3329 if (frame
.title
== event
->window
)
3331 else if (frame
.label
== event
->window
)
3333 else if (frame
.close_button
== event
->window
)
3334 redrawCloseButton();
3335 else if (frame
.maximize_button
== event
->window
)
3336 redrawMaximizeButton();
3337 else if (frame
.iconify_button
== event
->window
)
3338 redrawIconifyButton();
3339 else if (frame
.handle
== event
->window
)
3341 else if (frame
.left_grip
== event
->window
||
3342 frame
.right_grip
== event
->window
)
3347 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent
*
3349 if (event
->window
!= client
.window
|| client
.state
.iconic
)
3352 if (event
->value_mask
& CWBorderWidth
)
3353 client
.old_bw
= event
->border_width
;
3355 if (event
->value_mask
& (CWX
| CWY
| CWWidth
| CWHeight
)) {
3356 bt::Rect req
= frame
.rect
;
3358 if (event
->value_mask
& (CWX
| CWY
)) {
3359 req
= ::restoreGravity(req
, frame
.margin
, client
.wmnormal
.win_gravity
);
3361 if (event
->value_mask
& CWX
)
3363 if (event
->value_mask
& CWY
)
3366 req
= ::applyGravity(req
, frame
.margin
, client
.wmnormal
.win_gravity
);
3369 if (event
->value_mask
& (CWWidth
| CWHeight
)) {
3370 if (event
->value_mask
& CWWidth
)
3371 req
.setWidth(event
->width
+ frame
.margin
.left
+ frame
.margin
.right
);
3372 if (event
->value_mask
& CWHeight
)
3373 req
.setHeight(event
->height
+ frame
.margin
.top
+ frame
.margin
.bottom
);
3379 if (event
->value_mask
& CWStackMode
) {
3380 switch (event
->detail
) {
3383 _screen
->lowerWindow(this);
3389 _screen
->raiseWindow(this);
3396 void BlackboxWindow::buttonPressEvent(const XButtonEvent
* const event
) {
3398 fprintf(stderr
, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3402 if (frame
.maximize_button
== event
->window
) {
3403 if (event
->button
< 4)
3404 redrawMaximizeButton(true);
3405 } else if (frame
.iconify_button
== event
->window
) {
3406 if (event
->button
== 1)
3407 redrawIconifyButton(true);
3408 } else if (frame
.close_button
== event
->window
) {
3409 if (event
->button
== 1)
3410 redrawCloseButton(true);
3412 if (event
->button
== 1
3413 || (event
->button
== 3 && event
->state
== Mod1Mask
)) {
3414 frame
.grab_x
= event
->x_root
- frame
.rect
.x();
3415 frame
.grab_y
= event
->y_root
- frame
.rect
.y();
3417 _screen
->raiseWindow(this);
3419 if (! client
.state
.focused
)
3420 (void) setInputFocus();
3422 XInstallColormap(blackbox
->XDisplay(), client
.colormap
);
3424 if (frame
.plate
== event
->window
) {
3425 XAllowEvents(blackbox
->XDisplay(), ReplayPointer
, event
->time
);
3426 } else if ((frame
.title
== event
->window
3427 || frame
.label
== event
->window
)
3428 && hasWindowFunction(WindowFunctionShade
)) {
3429 if ((event
->time
- lastButtonPressTime
<=
3430 blackbox
->resource().doubleClickInterval()) ||
3431 event
->state
== ControlMask
) {
3432 lastButtonPressTime
= 0;
3433 setShaded(!isShaded());
3435 lastButtonPressTime
= event
->time
;
3438 } else if (event
->button
== 2) {
3439 _screen
->lowerWindow(this);
3440 } else if (event
->button
== 3
3441 || (event
->button
== 3 && event
->state
== Mod4Mask
)) {
3442 const int extra
= _screen
->resource().windowStyle().frame_border_width
;
3443 const bt::Rect
rect(client
.rect
.x() - extra
,
3444 client
.rect
.y() - extra
,
3445 client
.rect
.width() + (extra
* 2),
3446 client
.rect
.height() + (extra
* 2));
3448 Windowmenu
*windowmenu
= _screen
->windowmenu(this);
3449 windowmenu
->popup(event
->x_root
, event
->y_root
, rect
);
3450 } else if (blackbox
->resource().shadeWindowWithMouseWheel()) {
3451 if (event
->button
== 4
3452 && hasWindowFunction(WindowFunctionShade
)
3455 } else if (event
->button
== 5
3456 && hasWindowFunction(WindowFunctionShade
)
3465 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent
* const event
) {
3467 fprintf(stderr
, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3471 const WindowStyle
&style
= _screen
->resource().windowStyle();
3472 if (event
->window
== frame
.maximize_button
) {
3473 if (event
->button
< 4) {
3474 if (bt::within(event
->x
, event
->y
,
3475 style
.button_width
, style
.button_width
)) {
3476 maximize(event
->button
);
3477 _screen
->raiseWindow(this);
3479 redrawMaximizeButton();
3482 } else if (event
->window
== frame
.iconify_button
) {
3483 if (event
->button
== 1) {
3484 if (bt::within(event
->x
, event
->y
,
3485 style
.button_width
, style
.button_width
))
3488 redrawIconifyButton();
3490 } else if (event
->window
== frame
.close_button
) {
3491 if (event
->button
== 1) {
3492 if (bt::within(event
->x
, event
->y
,
3493 style
.button_width
, style
.button_width
))
3495 redrawCloseButton();
3497 } else if (client
.state
.moving
) {
3499 } else if (client
.state
.resizing
) {
3501 } else if (event
->window
== frame
.window
) {
3502 if (event
->button
== 2 && event
->state
== Mod1Mask
)
3503 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
3508 void BlackboxWindow::motionNotifyEvent(const XMotionEvent
* const event
) {
3510 fprintf(stderr
, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3514 if (hasWindowFunction(WindowFunctionMove
)
3515 && !client
.state
.resizing
3516 && event
->state
& Button1Mask
3517 && (frame
.title
== event
->window
|| frame
.label
== event
->window
3518 || frame
.handle
== event
->window
|| frame
.window
== event
->window
)) {
3519 if (! client
.state
.moving
)
3522 continueMove(event
->x_root
, event
->y_root
);
3523 } else if (hasWindowFunction(WindowFunctionResize
)
3524 && (event
->state
& Button1Mask
3525 && (event
->window
== frame
.right_grip
3526 || event
->window
== frame
.left_grip
))
3527 || (event
->state
& Button3Mask
3528 && event
->state
& Mod1Mask
3529 && event
->window
== frame
.window
)) {
3530 if (!client
.state
.resizing
)
3531 startResize(event
->window
);
3533 continueResize(event
->x_root
, event
->y_root
);
3538 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent
* const event
) {
3539 if (event
->window
!= frame
.window
|| event
->mode
!= NotifyNormal
)
3542 if (blackbox
->resource().focusModel() == ClickToFocusModel
|| !isVisible())
3545 switch (windowType()) {
3546 case WindowTypeDesktop
:
3547 case WindowTypeDock
:
3548 // these types cannot be focused w/ sloppy focus
3556 bool leave
= False
, inferior
= False
;
3558 while (XCheckTypedWindowEvent(blackbox
->XDisplay(), event
->window
,
3559 LeaveNotify
, &next
)) {
3560 if (next
.type
== LeaveNotify
&& next
.xcrossing
.mode
== NotifyNormal
) {
3562 inferior
= (next
.xcrossing
.detail
== NotifyInferior
);
3566 if ((! leave
|| inferior
) && ! isFocused())
3567 (void) setInputFocus();
3569 if (blackbox
->resource().autoRaise())
3575 BlackboxWindow::leaveNotifyEvent(const XCrossingEvent
* const /*unused*/) {
3576 if (!(blackbox
->resource().focusModel() == SloppyFocusModel
3577 && blackbox
->resource().autoRaise()))
3580 if (timer
->isTiming())
3586 void BlackboxWindow::shapeEvent(const XEvent
* const /*unused*/)
3587 { if (client
.state
.shaped
) configureShape(); }
3594 void BlackboxWindow::restore(void) {
3595 XChangeSaveSet(blackbox
->XDisplay(), client
.window
, SetModeDelete
);
3596 XSelectInput(blackbox
->XDisplay(), client
.window
, NoEventMask
);
3597 XSelectInput(blackbox
->XDisplay(), frame
.plate
, NoEventMask
);
3599 client
.state
.visible
= false;
3602 remove WM_STATE unless the we are shutting down (in which case we
3603 want to make sure we preserve the state across restarts).
3605 if (!blackbox
->shuttingDown()) {
3606 clearState(blackbox
, client
.window
);
3607 } else if (isShaded() && !isIconic()) {
3608 // do not leave a shaded window as an icon unless it was an icon
3609 setState(NormalState
);
3612 client
.rect
= ::restoreGravity(frame
.rect
, frame
.margin
,
3613 client
.wmnormal
.win_gravity
);
3615 blackbox
->XGrabServer();
3617 XUnmapWindow(blackbox
->XDisplay(), frame
.window
);
3618 XUnmapWindow(blackbox
->XDisplay(), client
.window
);
3620 XSetWindowBorderWidth(blackbox
->XDisplay(), client
.window
, client
.old_bw
);
3621 if (isMaximized()) {
3622 // preserve the original size
3623 client
.rect
= client
.premax
;
3624 XMoveResizeWindow(blackbox
->XDisplay(), client
.window
,
3627 client
.premax
.width(),
3628 client
.premax
.height());
3630 XMoveWindow(blackbox
->XDisplay(), client
.window
,
3631 client
.rect
.x() - frame
.rect
.x(),
3632 client
.rect
.y() - frame
.rect
.y());
3635 blackbox
->XUngrabServer();
3638 if (!XCheckTypedWindowEvent(blackbox
->XDisplay(), client
.window
,
3639 ReparentNotify
, &unused
)) {
3641 according to the ICCCM, the window manager is responsible for
3642 reparenting the window back to root... however, we don't want to
3643 do this if the window has been reparented by someone else
3646 XReparentWindow(blackbox
->XDisplay(), client
.window
,
3647 _screen
->screenInfo().rootWindow(),
3648 client
.rect
.x(), client
.rect
.y());
3651 if (blackbox
->shuttingDown())
3652 XMapWindow(blackbox
->XDisplay(), client
.window
);
3656 // timer for autoraise
3657 void BlackboxWindow::timeout(bt::Timer
*)
3658 { _screen
->raiseWindow(this); }
3661 void BlackboxWindow::startMove() {
3663 XGrabPointer(blackbox
->XDisplay(), frame
.window
, false,
3664 Button1MotionMask
| ButtonReleaseMask
,
3665 GrabModeAsync
, GrabModeAsync
, None
,
3666 blackbox
->resource().cursors().move
, blackbox
->XTime());
3668 client
.state
.moving
= true;
3670 if (! blackbox
->resource().opaqueMove()) {
3671 blackbox
->XGrabServer();
3673 frame
.changing
= frame
.rect
;
3674 _screen
->showGeometry(BScreen::Position
, frame
.changing
);
3676 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3677 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3679 pen
.setGCFunction(GXxor
);
3680 pen
.setLineWidth(bw
);
3681 pen
.setSubWindowMode(IncludeInferiors
);
3682 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3684 frame
.changing
.x() + hw
,
3685 frame
.changing
.y() + hw
,
3686 frame
.changing
.width() - bw
,
3687 frame
.changing
.height() - bw
);
3693 void collisionAdjust(int *dx
, int *dy
, int x
, int y
,
3694 unsigned int width
, unsigned int height
,
3695 const bt::Rect
& rect
, int snap_distance
,
3696 bool snapCenter
= false)
3699 const int wleft
= x
,
3700 wright
= x
+ width
- 1,
3702 wbottom
= y
+ height
- 1,
3703 // left, right, top + bottom are for rect, douterleft = left border of rect
3704 dinnerleft
= abs(wleft
- rect
.left()),
3705 dinnerright
= abs(wright
- rect
.right()),
3706 dinnertop
= abs(wtop
- rect
.top()),
3707 dinnerbottom
= abs(wbottom
- rect
.bottom()),
3708 douterleft
= abs(wright
- rect
.left()),
3709 douterright
= abs(wleft
- rect
.right()),
3710 doutertop
= abs(wbottom
- rect
.top()),
3711 douterbottom
= abs(wtop
- rect
.bottom());
3713 if ((wtop
<= rect
.bottom() && wbottom
>= rect
.top())
3714 || doutertop
<= snap_distance
3715 || douterbottom
<= snap_distance
) {
3716 // snap left or right
3717 if (douterleft
<= dinnerleft
&& douterleft
<= snap_distance
)
3719 *dx
= (x
- (rect
.left() - width
));
3720 else if (douterright
<= dinnerright
&& douterright
<= snap_distance
)
3722 *dx
= (x
- rect
.right() - 1);
3723 else if (dinnerleft
<= dinnerright
&& dinnerleft
< snap_distance
)
3725 *dx
= (x
- rect
.left());
3726 else if (dinnerright
< snap_distance
)
3728 *dx
= (x
- (rect
.right() - width
+ 1));
3731 if ((wleft
<= rect
.right() && wright
>= rect
.left())
3732 || douterleft
<= snap_distance
3733 || douterright
<= snap_distance
) {
3734 // snap top or bottom
3735 if (doutertop
<= dinnertop
&& doutertop
<= snap_distance
)
3737 *dy
= (y
- (rect
.top() - height
));
3738 else if (douterbottom
<= dinnerbottom
&& douterbottom
<= snap_distance
)
3739 // snap outer bottom
3740 *dy
= (y
- rect
.bottom() - 1);
3741 else if (dinnertop
<= dinnerbottom
&& dinnertop
< snap_distance
)
3743 *dy
= (y
- rect
.top());
3744 else if (dinnerbottom
< snap_distance
)
3745 // snap inner bottom
3746 *dy
= (y
- (rect
.bottom() - height
+ 1));
3750 const int cwx
= x
+ width
/ 2;
3751 const int cwy
= y
+ height
/ 2;
3752 const int crx
= rect
.x() + rect
.width() / 2;
3753 const int cry
= rect
.y() + rect
.height() / 2;
3754 const int cdx
= abs(cwx
- crx
);
3755 const int cdy
= abs(cwy
- cry
);
3756 if (cdx
<= snap_distance
)
3757 // snap to horizontal center
3758 *dx
= x
- (rect
.x() + ((rect
.width() - width
) / 2));
3759 if (cdy
<= snap_distance
)
3760 // snap to vertical center
3761 *dy
= y
- (rect
.y() + ((rect
.height() - height
) / 2));
3766 void BlackboxWindow::snapAdjust(int *x
, int *y
) {
3767 int nx
, ny
, dx
, dy
, init_dx
, init_dy
;
3768 const int edge_distance
= blackbox
->resource().edgeSnapThreshold();
3769 const int win_distance
= blackbox
->resource().windowSnapThreshold();
3771 nx
= (win_distance
> edge_distance
) ? win_distance
: edge_distance
;
3772 ny
= (win_distance
> edge_distance
) ? win_distance
: edge_distance
;
3773 dx
= init_dx
= ++nx
; dy
= init_dy
= ++ny
;
3775 if (edge_distance
) {
3776 collisionAdjust(&dx
, &dy
, *x
, *y
, frame
.rect
.width(), frame
.rect
.height(),
3777 _screen
->availableArea(), edge_distance
, true);
3778 nx
= (dx
!= init_dx
&& abs(dx
) < abs(nx
)) ? dx
: nx
; dx
= init_dx
;
3779 ny
= (dy
!= init_dy
&& abs(dy
) < abs(ny
)) ? dy
: ny
; dy
= init_dy
;
3780 if (!blackbox
->resource().fullMaximization()) {
3781 collisionAdjust(&dx
, &dy
, *x
, *y
, frame
.rect
.width(), frame
.rect
.height(),
3782 _screen
->screenInfo().rect(), edge_distance
);
3783 nx
= (dx
!= init_dx
&& abs(dx
) < abs(nx
)) ? dx
: nx
; dx
= init_dx
;
3784 ny
= (dy
!= init_dy
&& abs(dy
) < abs(ny
)) ? dy
: ny
; dy
= init_dy
;
3788 StackingList::const_iterator it
= _screen
->stackingList().begin(),
3789 end
= _screen
->stackingList().end();
3790 for (; it
!= end
; ++it
) {
3791 BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
3792 if (win
&& win
!= this &&
3793 (win
->workspace() == _screen
->currentWorkspace()
3794 || win
->workspace() == bt::BSENTINEL
)) {
3795 collisionAdjust(&dx
, &dy
, *x
, *y
, frame
.rect
.width(),
3796 frame
.rect
.height(), win
->frame
.rect
, win_distance
);
3797 nx
= (dx
!= init_dx
&& abs(dx
) < abs(nx
)) ? dx
: nx
; dx
= init_dx
;
3798 ny
= (dy
!= init_dy
&& abs(dy
) < abs(ny
)) ? dy
: ny
; dy
= init_dy
;
3803 *x
= (nx
!= init_dx
) ? (*x
- nx
) : *x
;
3804 *y
= (ny
!= init_dy
) ? (*y
- ny
) : *y
;
3808 void BlackboxWindow::continueMove(int x_root
, int y_root
) {
3809 int dx
= x_root
- frame
.grab_x
, dy
= y_root
- frame
.grab_y
;
3811 snapAdjust(&dx
, &dy
);
3813 if (blackbox
->resource().opaqueMove()) {
3814 configure(dx
, dy
, frame
.rect
.width(), frame
.rect
.height());
3816 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3817 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3819 pen
.setGCFunction(GXxor
);
3820 pen
.setLineWidth(bw
);
3821 pen
.setSubWindowMode(IncludeInferiors
);
3822 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3824 frame
.changing
.x() + hw
,
3825 frame
.changing
.y() + hw
,
3826 frame
.changing
.width() - bw
,
3827 frame
.changing
.height() - bw
);
3829 frame
.changing
.setPos(dx
, dy
);
3831 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3833 frame
.changing
.x() + hw
,
3834 frame
.changing
.y() + hw
,
3835 frame
.changing
.width() - bw
,
3836 frame
.changing
.height() - bw
);
3839 _screen
->showGeometry(BScreen::Position
, bt::Rect(dx
, dy
, 0, 0));
3843 void BlackboxWindow::finishMove() {
3844 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
3846 client
.state
.moving
= false;
3848 if (!blackbox
->resource().opaqueMove()) {
3849 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3850 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3852 pen
.setGCFunction(GXxor
);
3853 pen
.setLineWidth(bw
);
3854 pen
.setSubWindowMode(IncludeInferiors
);
3855 XDrawRectangle(blackbox
->XDisplay(),
3856 _screen
->screenInfo().rootWindow(),
3858 frame
.changing
.x() + hw
,
3859 frame
.changing
.y() + hw
,
3860 frame
.changing
.width() - bw
,
3861 frame
.changing
.height() - bw
);
3862 blackbox
->XUngrabServer();
3864 configure(frame
.changing
);
3866 configure(frame
.rect
);
3869 _screen
->hideGeometry();
3873 void BlackboxWindow::startResize(Window window
) {
3874 if (frame
.grab_x
< (signed) frame
.rect
.width() / 2) {
3875 if (frame
.grab_y
< (signed) frame
.rect
.height() / 2)
3876 frame
.corner
= BottomRight
;
3878 frame
.corner
= TopRight
;
3880 if (frame
.grab_y
< (signed) frame
.rect
.height() / 2)
3881 frame
.corner
= BottomLeft
;
3883 frame
.corner
= TopLeft
;
3886 Cursor cursor
= None
;
3887 switch (frame
.corner
) {
3889 cursor
= blackbox
->resource().cursors().resize_bottom_right
;
3890 frame
.grab_x
= frame
.rect
.width() - frame
.grab_x
;
3891 frame
.grab_y
= frame
.rect
.height() - frame
.grab_y
;
3894 cursor
= blackbox
->resource().cursors().resize_top_right
;
3895 frame
.grab_x
= frame
.rect
.width() - frame
.grab_x
;
3898 cursor
= blackbox
->resource().cursors().resize_bottom_left
;
3899 frame
.grab_y
= frame
.rect
.height() - frame
.grab_y
;
3902 cursor
= blackbox
->resource().cursors().resize_top_left
;
3907 XGrabPointer(blackbox
->XDisplay(), window
, False
,
3908 ButtonMotionMask
| ButtonReleaseMask
,
3909 GrabModeAsync
, GrabModeAsync
, None
, cursor
, blackbox
->XTime());
3911 client
.state
.resizing
= true;
3913 frame
.changing
= constrain(frame
.rect
, frame
.margin
, client
.wmnormal
,
3914 Corner(frame
.corner
));
3916 if (!blackbox
->resource().opaqueResize()) {
3917 blackbox
->XGrabServer();
3919 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3920 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3922 pen
.setGCFunction(GXxor
);
3923 pen
.setLineWidth(bw
);
3924 pen
.setSubWindowMode(IncludeInferiors
);
3925 XDrawRectangle(blackbox
->XDisplay(),
3926 _screen
->screenInfo().rootWindow(),
3928 frame
.changing
.x() + hw
,
3929 frame
.changing
.y() + hw
,
3930 frame
.changing
.width() - bw
,
3931 frame
.changing
.height() - bw
);
3933 // unset maximized state when resized
3938 showGeometry(frame
.changing
);
3942 void BlackboxWindow::continueResize(int x_root
, int y_root
) {
3943 // continue a resize
3944 const bt::Rect curr
= frame
.changing
;
3946 switch (frame
.corner
) {
3949 frame
.changing
.setCoords(frame
.changing
.left(),
3950 frame
.changing
.top(),
3951 std::max
<signed>(x_root
+ frame
.grab_x
,
3952 frame
.changing
.left()
3953 + (frame
.margin
.left
3954 + frame
.margin
.right
+ 1)),
3955 frame
.changing
.bottom());
3959 frame
.changing
.setCoords(std::min
<signed>(x_root
- frame
.grab_x
,
3960 frame
.changing
.right()
3961 - (frame
.margin
.left
3962 + frame
.margin
.right
+ 1)),
3963 frame
.changing
.top(),
3964 frame
.changing
.right(),
3965 frame
.changing
.bottom());
3969 switch (frame
.corner
) {
3972 frame
.changing
.setCoords(frame
.changing
.left(),
3973 frame
.changing
.top(),
3974 frame
.changing
.right(),
3975 std::max
<signed>(y_root
+ frame
.grab_y
,
3976 frame
.changing
.top()
3978 + frame
.margin
.bottom
+ 1)));
3982 frame
.changing
.setCoords(frame
.changing
.left(),
3983 std::min
<signed>(y_root
- frame
.grab_y
,
3986 + frame
.margin
.bottom
+ 1)),
3987 frame
.changing
.right(),
3988 frame
.changing
.bottom());
3992 frame
.changing
= constrain(frame
.changing
, frame
.margin
, client
.wmnormal
,
3993 Corner(frame
.corner
));
3995 if (curr
!= frame
.changing
) {
3996 if (blackbox
->resource().opaqueResize()) {
3997 configure(frame
.changing
);
3999 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
4000 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
4002 pen
.setGCFunction(GXxor
);
4003 pen
.setLineWidth(bw
);
4004 pen
.setSubWindowMode(IncludeInferiors
);
4005 XDrawRectangle(blackbox
->XDisplay(),
4006 _screen
->screenInfo().rootWindow(),
4011 curr
.height() - bw
);
4013 XDrawRectangle(blackbox
->XDisplay(),
4014 _screen
->screenInfo().rootWindow(),
4016 frame
.changing
.x() + hw
,
4017 frame
.changing
.y() + hw
,
4018 frame
.changing
.width() - bw
,
4019 frame
.changing
.height() - bw
);
4022 showGeometry(frame
.changing
);
4027 void BlackboxWindow::finishResize() {
4029 if (!blackbox
->resource().opaqueResize()) {
4030 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
4031 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
4033 pen
.setGCFunction(GXxor
);
4034 pen
.setLineWidth(bw
);
4035 pen
.setSubWindowMode(IncludeInferiors
);
4036 XDrawRectangle(blackbox
->XDisplay(),
4037 _screen
->screenInfo().rootWindow(),
4039 frame
.changing
.x() + hw
,
4040 frame
.changing
.y() + hw
,
4041 frame
.changing
.width() - bw
,
4042 frame
.changing
.height() - bw
);
4044 blackbox
->XUngrabServer();
4046 // unset maximized state when resized
4051 client
.state
.resizing
= false;
4053 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
4055 _screen
->hideGeometry();
4057 frame
.changing
= constrain(frame
.changing
, frame
.margin
, client
.wmnormal
,
4058 Corner(frame
.corner
));
4059 configure(frame
.changing
);
4064 * show the geometry of the window based on rectangle r.
4065 * The logical width and height are used here. This refers to the user's
4066 * perception of the window size (for example an xterm resizes in cells,
4067 * not in pixels). No extra work is needed if there is no difference between
4068 * the logical and actual dimensions.
4070 void BlackboxWindow::showGeometry(const bt::Rect
&r
) const {
4071 unsigned int w
= r
.width(), h
= r
.height();
4073 // remove the window frame
4074 w
-= frame
.margin
.left
+ frame
.margin
.right
;
4075 h
-= frame
.margin
.top
+ frame
.margin
.bottom
;
4077 if (client
.wmnormal
.flags
& PResizeInc
) {
4078 if (client
.wmnormal
.flags
& (PMinSize
|PBaseSize
)) {
4079 w
-= ((client
.wmnormal
.base_width
)
4080 ? client
.wmnormal
.base_width
4081 : client
.wmnormal
.min_width
);
4082 h
-= ((client
.wmnormal
.base_height
)
4083 ? client
.wmnormal
.base_height
4084 : client
.wmnormal
.min_height
);
4087 w
/= client
.wmnormal
.width_inc
;
4088 h
/= client
.wmnormal
.height_inc
;
4091 _screen
->showGeometry(BScreen::Size
, bt::Rect(0, 0, w
, h
));
4095 // see my rant above for an explanation of this operator
4096 bool operator==(const WMNormalHints
&x
, const WMNormalHints
&y
) {
4097 return (x
.flags
== y
.flags
4098 && x
.min_width
== y
.min_width
4099 && x
.min_height
== y
.min_height
4100 && x
.max_width
== y
.max_width
4101 && x
.max_height
== y
.max_height
4102 && x
.width_inc
== y
.width_inc
4103 && x
.height_inc
== y
.height_inc
4104 && x
.min_aspect_x
== y
.min_aspect_x
4105 && x
.min_aspect_y
== y
.min_aspect_y
4106 && x
.max_aspect_x
== y
.max_aspect_x
4107 && x
.max_aspect_y
== y
.max_aspect_y
4108 && x
.base_width
== y
.base_width
4109 && x
.base_height
== y
.base_height
4110 && x
.win_gravity
== y
.win_gravity
);