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 WindowDecorationGrip
);
715 if (prop
->decorations
& MWM_DECOR_TITLE
) {
716 motif
.decorations
|= (WindowDecorationTitlebar
|
717 WindowDecorationClose
);
719 if (prop
->decorations
& MWM_DECOR_MINIMIZE
)
720 motif
.decorations
|= WindowDecorationIconify
;
721 if (prop
->decorations
& MWM_DECOR_MAXIMIZE
)
722 motif
.decorations
|= WindowDecorationMaximize
;
733 * Returns the value of the WM_HINTS property. If the property is not
734 * set, a set of default values is returned instead.
736 static WMHints
readWMHints(Blackbox
*blackbox
, Window window
) {
738 wmh
.accept_focus
= false;
739 wmh
.window_group
= None
;
740 wmh
.initial_state
= NormalState
;
742 XWMHints
*wmhint
= XGetWMHints(blackbox
->XDisplay(), window
);
743 if (!wmhint
) return wmh
;
745 if (wmhint
->flags
& InputHint
)
746 wmh
.accept_focus
= (wmhint
->input
== True
);
747 if (wmhint
->flags
& StateHint
)
748 wmh
.initial_state
= wmhint
->initial_state
;
749 if (wmhint
->flags
& WindowGroupHint
)
750 wmh
.window_group
= wmhint
->window_group
;
759 * Returns the value of the WM_NORMAL_HINTS property. If the property
760 * is not set, a set of default values is returned instead.
762 static WMNormalHints
readWMNormalHints(Blackbox
*blackbox
,
764 const bt::ScreenInfo
&screenInfo
) {
765 WMNormalHints wmnormal
;
767 wmnormal
.min_width
= wmnormal
.min_height
= 1u;
768 wmnormal
.width_inc
= wmnormal
.height_inc
= 1u;
769 wmnormal
.min_aspect_x
= wmnormal
.min_aspect_y
= 1u;
770 wmnormal
.max_aspect_x
= wmnormal
.max_aspect_y
= 1u;
771 wmnormal
.base_width
= wmnormal
.base_height
= 0u;
772 wmnormal
.win_gravity
= NorthWestGravity
;
775 use the full screen, not the strut modified size. otherwise when
776 the availableArea changes max_width/height will be incorrect and
777 lead to odd rendering bugs.
779 const bt::Rect
&rect
= screenInfo
.rect();
780 wmnormal
.max_width
= rect
.width();
781 wmnormal
.max_height
= rect
.height();
785 if (! XGetWMNormalHints(blackbox
->XDisplay(), window
, &sizehint
, &unused
))
788 wmnormal
.flags
= sizehint
.flags
;
790 if (sizehint
.flags
& PMinSize
) {
791 if (sizehint
.min_width
> 0)
792 wmnormal
.min_width
= sizehint
.min_width
;
793 if (sizehint
.min_height
> 0)
794 wmnormal
.min_height
= sizehint
.min_height
;
797 if (sizehint
.flags
& PMaxSize
) {
798 if (sizehint
.max_width
> static_cast<signed>(wmnormal
.min_width
))
799 wmnormal
.max_width
= sizehint
.max_width
;
801 wmnormal
.max_width
= wmnormal
.min_width
;
803 if (sizehint
.max_height
> static_cast<signed>(wmnormal
.min_height
))
804 wmnormal
.max_height
= sizehint
.max_height
;
806 wmnormal
.max_height
= wmnormal
.min_height
;
809 if (sizehint
.flags
& PResizeInc
) {
810 wmnormal
.width_inc
= sizehint
.width_inc
;
811 wmnormal
.height_inc
= sizehint
.height_inc
;
814 if (sizehint
.flags
& PAspect
) {
815 wmnormal
.min_aspect_x
= sizehint
.min_aspect
.x
;
816 wmnormal
.min_aspect_y
= sizehint
.min_aspect
.y
;
817 wmnormal
.max_aspect_x
= sizehint
.max_aspect
.x
;
818 wmnormal
.max_aspect_y
= sizehint
.max_aspect
.y
;
821 if (sizehint
.flags
& PBaseSize
) {
822 wmnormal
.base_width
= sizehint
.base_width
;
823 wmnormal
.base_height
= sizehint
.base_height
;
826 wmnormal
.min_width
= std::max(wmnormal
.min_width
, wmnormal
.base_width
);
827 wmnormal
.min_height
= std::max(wmnormal
.min_height
, wmnormal
.base_height
);
830 if (sizehint
.flags
& PWinGravity
)
831 wmnormal
.win_gravity
= sizehint
.win_gravity
;
838 * Retrieve which Window Manager Protocols are supported by the client
841 static WMProtocols
readWMProtocols(Blackbox
*blackbox
,
843 WMProtocols protocols
;
844 protocols
.wm_delete_window
= false;
845 protocols
.wm_take_focus
= false;
850 if (XGetWMProtocols(blackbox
->XDisplay(), window
,
851 &proto
, &num_return
)) {
852 for (int i
= 0; i
< num_return
; ++i
) {
853 if (proto
[i
] == blackbox
->wmDeleteWindowAtom()) {
854 protocols
.wm_delete_window
= true;
855 } else if (proto
[i
] == blackbox
->wmTakeFocusAtom()) {
856 protocols
.wm_take_focus
= true;
867 * Reads the value of the WM_TRANSIENT_FOR property and returns a
868 * pointer to the transient parent for this window. If the
869 * WM_TRANSIENT_FOR is missing or invalid, this function returns 0.
871 * 'client.wmhints' should be properly updated before calling this
874 * Note: a return value of ~0ul signifies a window that should be
875 * transient but has no discernible parent.
877 static Window
readTransientInfo(Blackbox
*blackbox
,
879 const bt::ScreenInfo
&screenInfo
,
880 const WMHints
&wmhints
) {
881 Window trans_for
= None
;
883 if (!XGetTransientForHint(blackbox
->XDisplay(), window
, &trans_for
)) {
884 // WM_TRANSIENT_FOR hint not set
888 if (trans_for
== window
) {
889 // wierd client... treat this window as a normal window
893 if (trans_for
== None
|| trans_for
== screenInfo
.rootWindow()) {
895 this is a violation of the ICCCM, yet the EWMH allows this as a
896 way to signify a group transient.
898 trans_for
= wmhints
.window_group
;
905 static bool readState(unsigned long ¤t_state
,
908 current_state
= NormalState
;
913 unsigned long *state
, ulfoo
, nitems
;
915 if ((XGetWindowProperty(blackbox
->XDisplay(), window
,
916 blackbox
->wmStateAtom(),
917 0l, 2l, False
, blackbox
->wmStateAtom(),
918 &atom_return
, &foo
, &nitems
, &ulfoo
,
919 (unsigned char **) &state
) != Success
) ||
925 current_state
= static_cast<unsigned long>(state
[0]);
929 XFree((void *) state
);
935 static void clearState(Blackbox
*blackbox
, Window window
) {
936 XDeleteProperty(blackbox
->XDisplay(), window
, blackbox
->wmStateAtom());
938 const bt::EWMH
& ewmh
= blackbox
->ewmh();
939 ewmh
.removeProperty(window
, ewmh
.wmDesktop());
940 ewmh
.removeProperty(window
, ewmh
.wmState());
941 ewmh
.removeProperty(window
, ewmh
.wmAllowedActions());
942 ewmh
.removeProperty(window
, ewmh
.wmVisibleName());
943 ewmh
.removeProperty(window
, ewmh
.wmVisibleIconName());
948 * Initializes the class with default values/the window's set initial values.
950 BlackboxWindow::BlackboxWindow(Blackbox
*b
, Window w
, BScreen
*s
) {
951 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
952 // sizeof(BlackboxWindow));
955 fprintf(stderr
, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w
);
959 set timer to zero... it is initialized properly later, so we check
960 if timer is zero in the destructor, and assume that the window is not
961 fully constructed if timer is zero...
963 timer
= (bt::Timer
*) 0;
967 lastButtonPressTime
= 0;
970 the server needs to be grabbed here to prevent client's from sending
971 events while we are in the process of managing their window.
972 We hold the grab until after we are done moving the window around.
975 blackbox
->XGrabServer();
977 // fetch client size and placement
978 XWindowAttributes wattrib
;
979 if (! XGetWindowAttributes(blackbox
->XDisplay(),
980 client
.window
, &wattrib
) ||
981 ! wattrib
.screen
|| wattrib
.override_redirect
) {
984 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
987 blackbox
->XUngrabServer();
992 // set the eventmask early in the game so that we make sure we get
993 // all the events we are interested in
994 XSetWindowAttributes attrib_set
;
995 attrib_set
.event_mask
= ::client_window_event_mask
;
996 attrib_set
.do_not_propagate_mask
= ButtonPressMask
| ButtonReleaseMask
|
998 XChangeWindowAttributes(blackbox
->XDisplay(), client
.window
,
999 CWEventMask
|CWDontPropagate
, &attrib_set
);
1001 client
.colormap
= wattrib
.colormap
;
1002 window_number
= bt::BSENTINEL
;
1005 set the initial size and location of client window (relative to the
1006 _root window_). This position is the reference point used with the
1007 window's gravity to find the window's initial position.
1009 client
.rect
.setRect(wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
1010 client
.premax
= client
.rect
;
1011 client
.old_bw
= wattrib
.border_width
;
1012 client
.current_state
= NormalState
;
1014 frame
.window
= frame
.plate
= frame
.title
= frame
.handle
= None
;
1015 frame
.close_button
= frame
.iconify_button
= frame
.maximize_button
= None
;
1016 frame
.right_grip
= frame
.left_grip
= None
;
1017 frame
.utitle
= frame
.ftitle
= frame
.uhandle
= frame
.fhandle
= None
;
1018 frame
.ulabel
= frame
.flabel
= frame
.ubutton
= frame
.fbutton
= None
;
1019 frame
.pbutton
= frame
.ugrip
= frame
.fgrip
= None
;
1021 timer
= new bt::Timer(blackbox
, this);
1022 timer
->setTimeout(blackbox
->resource().autoRaiseDelay());
1024 client
.title
= ::readWMName(blackbox
, client
.window
);
1025 client
.icon_title
= ::readWMIconName(blackbox
, client
.window
);
1027 // get size, aspect, minimum/maximum size, ewmh and other hints set
1029 client
.ewmh
= ::readEWMH(blackbox
->ewmh(), client
.window
,
1030 _screen
->currentWorkspace());
1031 client
.motif
= ::readMotifWMHints(blackbox
, client
.window
);
1032 client
.wmhints
= ::readWMHints(blackbox
, client
.window
);
1033 client
.wmnormal
= ::readWMNormalHints(blackbox
, client
.window
,
1034 _screen
->screenInfo());
1035 client
.wmprotocols
= ::readWMProtocols(blackbox
, client
.window
);
1036 client
.transient_for
= ::readTransientInfo(blackbox
, client
.window
,
1037 _screen
->screenInfo(),
1040 if (client
.wmhints
.window_group
!= None
)
1041 (void) ::update_window_group(client
.wmhints
.window_group
, blackbox
, this);
1043 if (isTransient()) {
1044 // add ourselves to our transient_for
1045 BlackboxWindow
*win
= findTransientFor();
1047 win
->addTransient(this);
1048 client
.ewmh
.workspace
= win
->workspace();
1049 setLayer(win
->layer());
1050 } else if (isGroupTransient()) {
1051 BWindowGroup
*group
= findWindowGroup();
1053 group
->addTransient(this);
1056 client
.transient_for
= 0;
1060 client
.state
.visible
= false;
1061 client
.state
.iconic
= false;
1062 client
.state
.moving
= false;
1063 client
.state
.resizing
= false;
1064 client
.state
.focused
= false;
1066 switch (windowType()) {
1067 case WindowTypeDesktop
:
1068 setLayer(StackingList::LayerDesktop
);
1071 case WindowTypeDock
:
1072 setLayer(StackingList::LayerAbove
);
1073 // fallthrough intended
1076 if (client
.ewmh
.above
)
1077 setLayer(StackingList::LayerAbove
);
1078 else if (client
.ewmh
.below
)
1079 setLayer(StackingList::LayerBelow
);
1083 ::update_decorations(client
.decorations
,
1089 client
.wmprotocols
);
1092 if (client
.wmhints
.initial_state
== IconicState
1093 && !hasWindowFunction(WindowFunctionIconify
))
1094 client
.wmhints
.initial_state
= NormalState
;
1095 if (isMaximized() && !hasWindowFunction(WindowFunctionMaximize
))
1096 client
.ewmh
.maxv
= client
.ewmh
.maxh
= false;
1097 if (isFullScreen() && !hasWindowFunction(WindowFunctionFullScreen
))
1098 client
.ewmh
.fullscreen
= false;
1100 bt::EWMH::Strut strut
;
1101 if (blackbox
->ewmh().readWMStrut(client
.window
, &strut
)) {
1102 client
.strut
= new bt::EWMH::Strut
;
1103 *client
.strut
= strut
;
1104 _screen
->addStrut(client
.strut
);
1107 frame
.window
= createToplevelWindow();
1108 blackbox
->insertEventHandler(frame
.window
, this);
1110 frame
.plate
= createChildWindow(frame
.window
, NoEventMask
);
1111 blackbox
->insertEventHandler(frame
.plate
, this);
1113 if (client
.decorations
& WindowDecorationTitlebar
)
1116 if (client
.decorations
& WindowDecorationHandle
)
1119 // apply the size and gravity to the frame
1120 const WindowStyle
&style
= _screen
->resource().windowStyle();
1121 frame
.margin
= ::update_margin(client
.decorations
, style
);
1122 frame
.rect
= ::applyGravity(client
.rect
,
1124 client
.wmnormal
.win_gravity
);
1126 associateClientWindow();
1128 blackbox
->insertEventHandler(client
.window
, this);
1129 blackbox
->insertWindow(client
.window
, this);
1130 blackbox
->insertWindow(frame
.plate
, this);
1132 // preserve the window's initial state on first map, and its current
1133 // state across a restart
1134 if (!readState(client
.current_state
, blackbox
, client
.window
))
1135 client
.current_state
= client
.wmhints
.initial_state
;
1137 if (client
.state
.iconic
) {
1138 // prepare the window to be iconified
1139 client
.current_state
= IconicState
;
1140 client
.state
.iconic
= False
;
1141 } else if (workspace() != bt::BSENTINEL
&&
1142 workspace() != _screen
->currentWorkspace()) {
1143 client
.current_state
= WithdrawnState
;
1146 blackbox
->XUngrabServer();
1150 XMapSubwindows(blackbox
->XDisplay(), frame
.window
);
1152 if (isFullScreen()) {
1153 client
.ewmh
.fullscreen
= false; // trick setFullScreen into working
1154 setFullScreen(true);
1156 if (isMaximized()) {
1159 bt::Rect r
= frame
.rect
;
1160 // trick configure into working
1161 frame
.rect
= bt::Rect();
1166 client
.ewmh
.shaded
= false;
1167 unsigned long save_state
= client
.current_state
;
1171 At this point in the life of a window, current_state should only be set
1172 to IconicState if the window was an *icon*, not if it was shaded.
1174 if (save_state
!= IconicState
)
1175 client
.current_state
= save_state
;
1181 BlackboxWindow::~BlackboxWindow(void) {
1183 fprintf(stderr
, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
1187 if (! timer
) // window not managed...
1190 if (client
.state
.moving
|| client
.state
.resizing
) {
1191 _screen
->hideGeometry();
1192 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
1198 _screen
->removeStrut(client
.strut
);
1199 delete client
.strut
;
1202 BWindowGroup
*group
= findWindowGroup();
1204 if (isTransient()) {
1205 // remove ourselves from our transient_for
1206 BlackboxWindow
*win
= findTransientFor();
1208 win
->removeTransient(this);
1209 } else if (isGroupTransient()) {
1211 group
->removeTransient(this);
1213 client
.transient_for
= 0;
1217 group
->removeWindow(this);
1225 blackbox
->removeEventHandler(client
.window
);
1226 blackbox
->removeWindow(client
.window
);
1228 blackbox
->removeEventHandler(frame
.plate
);
1229 blackbox
->removeWindow(frame
.plate
);
1230 XDestroyWindow(blackbox
->XDisplay(), frame
.plate
);
1232 blackbox
->removeEventHandler(frame
.window
);
1233 XDestroyWindow(blackbox
->XDisplay(), frame
.window
);
1238 * Creates a new top level window, with a given location, size, and border
1240 * Returns: the newly created window
1242 Window
BlackboxWindow::createToplevelWindow(void) {
1243 XSetWindowAttributes attrib_create
;
1244 unsigned long create_mask
= CWColormap
| CWOverrideRedirect
| CWEventMask
;
1246 attrib_create
.colormap
= _screen
->screenInfo().colormap();
1247 attrib_create
.override_redirect
= True
;
1248 attrib_create
.event_mask
= EnterWindowMask
| LeaveWindowMask
;
1250 return XCreateWindow(blackbox
->XDisplay(),
1251 _screen
->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
1252 _screen
->screenInfo().depth(), InputOutput
,
1253 _screen
->screenInfo().visual(),
1254 create_mask
, &attrib_create
);
1259 * Creates a child window, and optionally associates a given cursor with
1262 Window
BlackboxWindow::createChildWindow(Window parent
,
1263 unsigned long event_mask
,
1265 XSetWindowAttributes attrib_create
;
1266 unsigned long create_mask
= CWEventMask
;
1268 attrib_create
.event_mask
= event_mask
;
1271 create_mask
|= CWCursor
;
1272 attrib_create
.cursor
= cursor
;
1275 return XCreateWindow(blackbox
->XDisplay(), parent
, 0, 0, 1, 1, 0,
1276 _screen
->screenInfo().depth(), InputOutput
,
1277 _screen
->screenInfo().visual(),
1278 create_mask
, &attrib_create
);
1283 * Reparents the client window into the newly created frame.
1285 * Note: the server must be grabbed before calling this function.
1287 void BlackboxWindow::associateClientWindow(void) {
1288 XSetWindowBorderWidth(blackbox
->XDisplay(), client
.window
, 0);
1289 XChangeSaveSet(blackbox
->XDisplay(), client
.window
, SetModeInsert
);
1291 XSelectInput(blackbox
->XDisplay(), frame
.plate
,
1292 FocusChangeMask
| SubstructureRedirectMask
);
1294 XSelectInput(blackbox
->XDisplay(), client
.window
,
1295 client_window_event_mask
& ~StructureNotifyMask
);
1296 XReparentWindow(blackbox
->XDisplay(), client
.window
, frame
.plate
, 0, 0);
1297 XSelectInput(blackbox
->XDisplay(), client
.window
, client_window_event_mask
);
1300 if (blackbox
->hasShapeExtensions()) {
1301 XShapeSelectInput(blackbox
->XDisplay(), client
.window
,
1304 Bool shaped
= False
;
1308 XShapeQueryExtents(blackbox
->XDisplay(), client
.window
, &shaped
,
1309 &foo
, &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
,
1311 client
.state
.shaped
= shaped
;
1317 void BlackboxWindow::decorate(void) {
1318 const WindowStyle
&style
= _screen
->resource().windowStyle();
1319 if (client
.decorations
& WindowDecorationTitlebar
) {
1320 // render focused button texture
1322 bt::PixmapCache::find(_screen
->screenNumber(),
1328 // render unfocused button texture
1330 bt::PixmapCache::find(_screen
->screenNumber(),
1331 style
.unfocus
.button
,
1336 // render pressed button texture
1338 bt::PixmapCache::find(_screen
->screenNumber(),
1344 // render focused titlebar texture
1346 bt::PixmapCache::find(_screen
->screenNumber(),
1352 // render unfocused titlebar texture
1354 bt::PixmapCache::find(_screen
->screenNumber(),
1355 style
.unfocus
.title
,
1360 // render focused label texture
1362 bt::PixmapCache::find(_screen
->screenNumber(),
1368 // render unfocused label texture
1370 bt::PixmapCache::find(_screen
->screenNumber(),
1371 style
.unfocus
.label
,
1377 XSetWindowBorder(blackbox
->XDisplay(), frame
.plate
,
1378 style
.frame_border
.pixel(_screen
->screenNumber()));
1380 if (client
.decorations
& WindowDecorationHandle
) {
1382 bt::PixmapCache::find(_screen
->screenNumber(),
1385 style
.handle_height
,
1389 bt::PixmapCache::find(_screen
->screenNumber(),
1390 style
.unfocus
.handle
,
1392 style
.handle_height
,
1396 if (client
.decorations
& WindowDecorationGrip
) {
1398 bt::PixmapCache::find(_screen
->screenNumber(),
1401 style
.handle_height
,
1405 bt::PixmapCache::find(_screen
->screenNumber(),
1408 style
.handle_height
,
1414 void BlackboxWindow::createHandle(void) {
1415 frame
.handle
= createChildWindow(frame
.window
,
1416 ButtonPressMask
| ButtonReleaseMask
|
1417 ButtonMotionMask
| ExposureMask
);
1418 blackbox
->insertEventHandler(frame
.handle
, this);
1420 if (client
.decorations
& WindowDecorationGrip
)
1425 void BlackboxWindow::destroyHandle(void) {
1426 if (frame
.left_grip
|| frame
.right_grip
)
1429 if (frame
.fhandle
) bt::PixmapCache::release(frame
.fhandle
);
1430 if (frame
.uhandle
) bt::PixmapCache::release(frame
.uhandle
);
1432 frame
.fhandle
= frame
.uhandle
= None
;
1434 blackbox
->removeEventHandler(frame
.handle
);
1435 XDestroyWindow(blackbox
->XDisplay(), frame
.handle
);
1436 frame
.handle
= None
;
1440 void BlackboxWindow::createGrips(void) {
1442 createChildWindow(frame
.handle
,
1443 ButtonPressMask
| ButtonReleaseMask
|
1444 ButtonMotionMask
| ExposureMask
,
1445 blackbox
->resource().cursors().resize_bottom_left
);
1446 blackbox
->insertEventHandler(frame
.left_grip
, this);
1449 createChildWindow(frame
.handle
,
1450 ButtonPressMask
| ButtonReleaseMask
|
1451 ButtonMotionMask
| ExposureMask
,
1452 blackbox
->resource().cursors().resize_bottom_right
);
1453 blackbox
->insertEventHandler(frame
.right_grip
, this);
1457 void BlackboxWindow::destroyGrips(void) {
1458 if (frame
.fgrip
) bt::PixmapCache::release(frame
.fgrip
);
1459 if (frame
.ugrip
) bt::PixmapCache::release(frame
.ugrip
);
1461 frame
.fgrip
= frame
.ugrip
= None
;
1463 blackbox
->removeEventHandler(frame
.left_grip
);
1464 blackbox
->removeEventHandler(frame
.right_grip
);
1466 XDestroyWindow(blackbox
->XDisplay(), frame
.left_grip
);
1467 XDestroyWindow(blackbox
->XDisplay(), frame
.right_grip
);
1468 frame
.left_grip
= frame
.right_grip
= None
;
1472 void BlackboxWindow::createTitlebar(void) {
1473 frame
.title
= createChildWindow(frame
.window
,
1474 ButtonPressMask
| ButtonReleaseMask
|
1475 ButtonMotionMask
| ExposureMask
);
1476 frame
.label
= createChildWindow(frame
.title
,
1477 ButtonPressMask
| ButtonReleaseMask
|
1478 ButtonMotionMask
| ExposureMask
);
1479 blackbox
->insertEventHandler(frame
.title
, this);
1480 blackbox
->insertEventHandler(frame
.label
, this);
1482 if (client
.decorations
& WindowDecorationIconify
) createIconifyButton();
1483 if (client
.decorations
& WindowDecorationMaximize
) createMaximizeButton();
1484 if (client
.decorations
& WindowDecorationClose
) createCloseButton();
1488 void BlackboxWindow::destroyTitlebar(void) {
1489 if (frame
.close_button
)
1490 destroyCloseButton();
1492 if (frame
.iconify_button
)
1493 destroyIconifyButton();
1495 if (frame
.maximize_button
)
1496 destroyMaximizeButton();
1498 if (frame
.fbutton
) bt::PixmapCache::release(frame
.fbutton
);
1499 if (frame
.ubutton
) bt::PixmapCache::release(frame
.ubutton
);
1500 if (frame
.pbutton
) bt::PixmapCache::release(frame
.pbutton
);
1501 if (frame
.ftitle
) bt::PixmapCache::release(frame
.ftitle
);
1502 if (frame
.utitle
) bt::PixmapCache::release(frame
.utitle
);
1503 if (frame
.flabel
) bt::PixmapCache::release(frame
.flabel
);
1504 if (frame
.ulabel
) bt::PixmapCache::release(frame
.ulabel
);
1506 frame
.fbutton
= frame
.ubutton
= frame
.pbutton
=
1507 frame
.ftitle
= frame
.utitle
=
1508 frame
.flabel
= frame
.ulabel
= None
;
1510 blackbox
->removeEventHandler(frame
.title
);
1511 blackbox
->removeEventHandler(frame
.label
);
1513 XDestroyWindow(blackbox
->XDisplay(), frame
.label
);
1514 XDestroyWindow(blackbox
->XDisplay(), frame
.title
);
1515 frame
.title
= frame
.label
= None
;
1519 void BlackboxWindow::createCloseButton(void) {
1520 if (frame
.title
!= None
) {
1521 frame
.close_button
= createChildWindow(frame
.title
,
1524 ButtonMotionMask
| ExposureMask
);
1525 blackbox
->insertEventHandler(frame
.close_button
, this);
1530 void BlackboxWindow::destroyCloseButton(void) {
1531 blackbox
->removeEventHandler(frame
.close_button
);
1532 XDestroyWindow(blackbox
->XDisplay(), frame
.close_button
);
1533 frame
.close_button
= None
;
1537 void BlackboxWindow::createIconifyButton(void) {
1538 if (frame
.title
!= None
) {
1539 frame
.iconify_button
= createChildWindow(frame
.title
,
1542 ButtonMotionMask
| ExposureMask
);
1543 blackbox
->insertEventHandler(frame
.iconify_button
, this);
1548 void BlackboxWindow::destroyIconifyButton(void) {
1549 blackbox
->removeEventHandler(frame
.iconify_button
);
1550 XDestroyWindow(blackbox
->XDisplay(), frame
.iconify_button
);
1551 frame
.iconify_button
= None
;
1555 void BlackboxWindow::createMaximizeButton(void) {
1556 if (frame
.title
!= None
) {
1557 frame
.maximize_button
= createChildWindow(frame
.title
,
1560 ButtonMotionMask
| ExposureMask
);
1561 blackbox
->insertEventHandler(frame
.maximize_button
, this);
1566 void BlackboxWindow::destroyMaximizeButton(void) {
1567 blackbox
->removeEventHandler(frame
.maximize_button
);
1568 XDestroyWindow(blackbox
->XDisplay(), frame
.maximize_button
);
1569 frame
.maximize_button
= None
;
1573 void BlackboxWindow::positionButtons(bool redecorate_label
) {
1574 // we need to use signed ints here to detect windows that are too small
1575 const WindowStyle
&style
= _screen
->resource().windowStyle();
1576 const int extra
= style
.title_margin
== 0 ?
1577 style
.focus
.button
.borderWidth() : 0,
1578 bw
= style
.button_width
+ style
.title_margin
1580 by
= style
.title_margin
+
1581 style
.focus
.title
.borderWidth();
1582 int lx
= by
, lw
= frame
.rect
.width() - by
;
1584 if (client
.decorations
& WindowDecorationIconify
) {
1585 if (frame
.iconify_button
== None
) createIconifyButton();
1587 XMoveResizeWindow(blackbox
->XDisplay(), frame
.iconify_button
, by
, by
,
1588 style
.button_width
, style
.button_width
);
1589 XMapWindow(blackbox
->XDisplay(), frame
.iconify_button
);
1593 } else if (frame
.iconify_button
) {
1594 destroyIconifyButton();
1597 int bx
= frame
.rect
.width() - bw
1598 - style
.focus
.title
.borderWidth() - extra
;
1600 if (client
.decorations
& WindowDecorationClose
) {
1601 if (frame
.close_button
== None
) createCloseButton();
1603 XMoveResizeWindow(blackbox
->XDisplay(), frame
.close_button
, bx
, by
,
1604 style
.button_width
, style
.button_width
);
1605 XMapWindow(blackbox
->XDisplay(), frame
.close_button
);
1609 } else if (frame
.close_button
) {
1610 destroyCloseButton();
1613 if (client
.decorations
& WindowDecorationMaximize
) {
1614 if (frame
.maximize_button
== None
) createMaximizeButton();
1616 XMoveResizeWindow(blackbox
->XDisplay(), frame
.maximize_button
, bx
, by
,
1617 style
.button_width
, style
.button_width
);
1618 XMapWindow(blackbox
->XDisplay(), frame
.maximize_button
);
1622 } else if (frame
.maximize_button
) {
1623 destroyMaximizeButton();
1627 frame
.label_w
= lw
- by
;
1628 XMoveResizeWindow(blackbox
->XDisplay(), frame
.label
, lx
, by
,
1629 frame
.label_w
, style
.label_height
);
1630 XMapWindow(blackbox
->XDisplay(), frame
.label
);
1632 if (redecorate_label
) {
1634 bt::PixmapCache::find(_screen
->screenNumber(),
1636 frame
.label_w
, style
.label_height
,
1639 bt::PixmapCache::find(_screen
->screenNumber(),
1640 style
.unfocus
.label
,
1641 frame
.label_w
, style
.label_height
,
1645 const bt::ustring ellided
=
1646 bt::ellideText(client
.title
, frame
.label_w
, bt::toUnicode("..."),
1647 _screen
->screenNumber(), style
.font
);
1649 if (ellided
!= client
.visible_title
) {
1650 client
.visible_title
= ellided
;
1651 blackbox
->ewmh().setWMVisibleName(client
.window
, client
.visible_title
);
1655 XUnmapWindow(blackbox
->XDisplay(), frame
.label
);
1663 void BlackboxWindow::reconfigure(void) {
1664 const WindowStyle
&style
= _screen
->resource().windowStyle();
1665 if (isMaximized()) {
1666 // update the frame margin in case the style has changed
1667 frame
.margin
= ::update_margin(client
.decorations
, style
);
1669 // make sure maximized windows have the correct size after a style
1673 // get the client window geometry as if it was unmanaged
1674 bt::Rect r
= frame
.rect
;
1675 if (client
.ewmh
.shaded
) {
1676 r
.setHeight(client
.rect
.height() + frame
.margin
.top
1677 + frame
.margin
.bottom
);
1679 r
= ::restoreGravity(r
, frame
.margin
, client
.wmnormal
.win_gravity
);
1681 // update the frame margin in case the style has changed
1682 frame
.margin
= ::update_margin(client
.decorations
, style
);
1684 // get the frame window geometry from the client window geometry
1686 r
= ::applyGravity(r
, frame
.margin
, client
.wmnormal
.win_gravity
);
1687 if (client
.ewmh
.shaded
) {
1693 // keep the window shaded
1694 frame
.rect
.setHeight(style
.title_height
);
1695 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
1696 frame
.rect
.width(), frame
.rect
.height());
1698 // trick configure into working
1699 frame
.rect
= bt::Rect();
1709 void BlackboxWindow::grabButtons(void) {
1710 if (blackbox
->resource().focusModel() == ClickToFocusModel
1711 || blackbox
->resource().clickRaise())
1712 // grab button 1 for changing focus/raising
1713 blackbox
->grabButton(Button1
, 0, frame
.plate
, True
, ButtonPressMask
,
1714 GrabModeSync
, GrabModeSync
, frame
.plate
, None
,
1715 blackbox
->resource().allowScrollLock());
1717 if (hasWindowFunction(WindowFunctionMove
))
1718 blackbox
->grabButton(Button1
, Mod1Mask
, frame
.window
, True
,
1719 ButtonReleaseMask
| ButtonMotionMask
, GrabModeAsync
,
1720 GrabModeAsync
, frame
.window
,
1721 blackbox
->resource().cursors().move
,
1722 blackbox
->resource().allowScrollLock());
1723 if (hasWindowFunction(WindowFunctionResize
))
1724 blackbox
->grabButton(Button3
, Mod1Mask
, frame
.window
, True
,
1725 ButtonReleaseMask
| ButtonMotionMask
, GrabModeAsync
,
1726 GrabModeAsync
, frame
.window
,
1727 None
, blackbox
->resource().allowScrollLock());
1728 // alt+middle lowers the window
1729 blackbox
->grabButton(Button2
, Mod1Mask
, frame
.window
, True
,
1730 ButtonReleaseMask
, GrabModeAsync
, GrabModeAsync
,
1732 blackbox
->resource().allowScrollLock());
1736 void BlackboxWindow::ungrabButtons(void) {
1737 blackbox
->ungrabButton(Button1
, 0, frame
.plate
);
1738 blackbox
->ungrabButton(Button1
, Mod1Mask
, frame
.window
);
1739 blackbox
->ungrabButton(Button2
, Mod1Mask
, frame
.window
);
1740 blackbox
->ungrabButton(Button3
, Mod1Mask
, frame
.window
);
1744 void BlackboxWindow::positionWindows(void) {
1745 const WindowStyle
&style
= _screen
->resource().windowStyle();
1746 const unsigned int bw
= (hasWindowDecoration(WindowDecorationBorder
)
1747 ? style
.frame_border_width
1750 XMoveResizeWindow(blackbox
->XDisplay(), frame
.plate
,
1751 frame
.margin
.left
- bw
,
1752 frame
.margin
.top
- bw
,
1753 client
.rect
.width(), client
.rect
.height());
1754 XSetWindowBorderWidth(blackbox
->XDisplay(), frame
.plate
, bw
);
1755 XMoveResizeWindow(blackbox
->XDisplay(), client
.window
,
1756 0, 0, client
.rect
.width(), client
.rect
.height());
1757 // ensure client.rect contains the real location
1758 client
.rect
.setPos(frame
.rect
.left() + frame
.margin
.left
,
1759 frame
.rect
.top() + frame
.margin
.top
);
1761 if (client
.decorations
& WindowDecorationTitlebar
) {
1762 if (frame
.title
== None
) createTitlebar();
1764 XMoveResizeWindow(blackbox
->XDisplay(), frame
.title
,
1765 0, 0, frame
.rect
.width(), style
.title_height
);
1768 XMapSubwindows(blackbox
->XDisplay(), frame
.title
);
1769 XMapWindow(blackbox
->XDisplay(), frame
.title
);
1770 } else if (frame
.title
) {
1774 if (client
.decorations
& WindowDecorationHandle
) {
1775 if (frame
.handle
== None
) createHandle();
1777 // use client.rect here so the value is correct even if shaded
1778 XMoveResizeWindow(blackbox
->XDisplay(), frame
.handle
,
1779 0, client
.rect
.height() + frame
.margin
.top
,
1780 frame
.rect
.width(), style
.handle_height
);
1782 if (client
.decorations
& WindowDecorationGrip
) {
1783 if (frame
.left_grip
== None
|| frame
.right_grip
== None
) createGrips();
1785 XMoveResizeWindow(blackbox
->XDisplay(), frame
.left_grip
, 0, 0,
1786 style
.grip_width
, style
.handle_height
);
1788 const int nx
= frame
.rect
.width() - style
.grip_width
;
1789 XMoveResizeWindow(blackbox
->XDisplay(), frame
.right_grip
, nx
, 0,
1790 style
.grip_width
, style
.handle_height
);
1792 XMapSubwindows(blackbox
->XDisplay(), frame
.handle
);
1797 XMapWindow(blackbox
->XDisplay(), frame
.handle
);
1798 } else if (frame
.handle
) {
1805 * This function is responsible for updating both the client and the
1806 * frame rectangles. According to the ICCCM a client message is not
1807 * sent for a resize, only a move.
1809 void BlackboxWindow::configure(int dx
, int dy
,
1810 unsigned int dw
, unsigned int dh
) {
1811 bool send_event
= ((frame
.rect
.x() != dx
|| frame
.rect
.y() != dy
) &&
1812 ! client
.state
.moving
);
1814 if (dw
!= frame
.rect
.width() || dh
!= frame
.rect
.height()) {
1815 frame
.rect
.setRect(dx
, dy
, dw
, dh
);
1817 if (frame
.rect
.right() <= 0 || frame
.rect
.bottom() <= 0)
1818 frame
.rect
.setPos(0, 0);
1820 client
.rect
.setCoords(frame
.rect
.left() + frame
.margin
.left
,
1821 frame
.rect
.top() + frame
.margin
.top
,
1822 frame
.rect
.right() - frame
.margin
.right
,
1823 frame
.rect
.bottom() - frame
.margin
.bottom
);
1826 if (client
.state
.shaped
)
1830 XMoveResizeWindow(blackbox
->XDisplay(), frame
.window
,
1831 frame
.rect
.x(), frame
.rect
.y(),
1832 frame
.rect
.width(), frame
.rect
.height());
1836 redrawWindowFrame();
1838 frame
.rect
.setPos(dx
, dy
);
1840 XMoveWindow(blackbox
->XDisplay(), frame
.window
,
1841 frame
.rect
.x(), frame
.rect
.y());
1843 we may have been called just after an opaque window move, so
1844 even though the old coords match the new ones no ConfigureNotify
1845 has been sent yet. There are likely other times when this will
1846 be relevant as well.
1848 if (! client
.state
.moving
) send_event
= True
;
1852 // if moving, the update and event will occur when the move finishes
1853 client
.rect
.setPos(frame
.rect
.left() + frame
.margin
.left
,
1854 frame
.rect
.top() + frame
.margin
.top
);
1857 event
.type
= ConfigureNotify
;
1859 event
.xconfigure
.display
= blackbox
->XDisplay();
1860 event
.xconfigure
.event
= client
.window
;
1861 event
.xconfigure
.window
= client
.window
;
1862 event
.xconfigure
.x
= client
.rect
.x();
1863 event
.xconfigure
.y
= client
.rect
.y();
1864 event
.xconfigure
.width
= client
.rect
.width();
1865 event
.xconfigure
.height
= client
.rect
.height();
1866 event
.xconfigure
.border_width
= client
.old_bw
;
1867 event
.xconfigure
.above
= frame
.window
;
1868 event
.xconfigure
.override_redirect
= False
;
1870 XSendEvent(blackbox
->XDisplay(), client
.window
, False
,
1871 StructureNotifyMask
, &event
);
1877 void BlackboxWindow::configureShape(void) {
1878 XShapeCombineShape(blackbox
->XDisplay(), frame
.window
, ShapeBounding
,
1879 frame
.margin
.left
, frame
.margin
.top
,
1880 client
.window
, ShapeBounding
, ShapeSet
);
1883 XRectangle xrect
[2];
1885 const WindowStyle
&style
= _screen
->resource().windowStyle();
1886 if (client
.decorations
& WindowDecorationTitlebar
) {
1887 xrect
[0].x
= xrect
[0].y
= 0;
1888 xrect
[0].width
= frame
.rect
.width();
1889 xrect
[0].height
= style
.title_height
;
1893 if (client
.decorations
& WindowDecorationHandle
) {
1895 xrect
[1].y
= client
.rect
.height() + frame
.margin
.top
;
1896 xrect
[1].width
= frame
.rect
.width();
1897 xrect
[1].height
= style
.handle_height
;
1901 XShapeCombineRectangles(blackbox
->XDisplay(), frame
.window
,
1902 ShapeBounding
, 0, 0, xrect
, num
,
1903 ShapeUnion
, Unsorted
);
1908 void BlackboxWindow::addTransient(BlackboxWindow
*win
)
1909 { client
.transientList
.push_front(win
); }
1912 void BlackboxWindow::removeTransient(BlackboxWindow
*win
)
1913 { client
.transientList
.remove(win
); }
1916 BlackboxWindow
*BlackboxWindow::findTransientFor(void) const {
1917 BlackboxWindow
*win
= 0;
1918 if (isTransient()) {
1919 win
= blackbox
->findWindow(client
.transient_for
);
1920 if (win
&& win
->_screen
!= _screen
)
1928 walk up to either 1) a non-transient window 2) a group transient,
1929 watching out for a circular chain
1931 this function returns zero for non-transient windows
1933 BlackboxWindow
*BlackboxWindow::findNonTransientParent(void) const {
1934 BlackboxWindowList seen
;
1935 seen
.push_back(const_cast<BlackboxWindow
*>(this));
1937 BlackboxWindow
*w
= findTransientFor();
1941 while (w
->isTransient() && !w
->isGroupTransient()) {
1943 BlackboxWindow
* const tmp
= w
->findTransientFor();
1946 if (std::find(seen
.begin(), seen
.end(), tmp
) != seen
.end()) {
1947 // circular transient chain
1957 Returns a list of all transients. This is recursive, so it returns
1958 all transients of transients as well.
1960 BlackboxWindowList
BlackboxWindow::buildFullTransientList(void) const {
1961 BlackboxWindowList all
= client
.transientList
;
1962 BlackboxWindowList::const_iterator it
= client
.transientList
.begin(),
1963 end
= client
.transientList
.end();
1964 for (; it
!= end
; ++it
) {
1965 BlackboxWindowList x
= (*it
)->buildFullTransientList();
1966 all
.splice(all
.end(), x
);
1972 BWindowGroup
*BlackboxWindow::findWindowGroup(void) const {
1973 BWindowGroup
*group
= 0;
1974 if (client
.wmhints
.window_group
)
1975 group
= blackbox
->findWindowGroup(client
.wmhints
.window_group
);
1980 void BlackboxWindow::setWorkspace(unsigned int new_workspace
) {
1981 client
.ewmh
.workspace
= new_workspace
;
1982 blackbox
->ewmh().setWMDesktop(client
.window
, client
.ewmh
.workspace
);
1986 void BlackboxWindow::changeWorkspace(unsigned int new_workspace
,
1987 ChangeWorkspaceOption how
) {
1988 if (client
.ewmh
.workspace
== new_workspace
)
1991 if (isTransient()) {
1992 BlackboxWindow
*win
= findTransientFor();
1994 if (win
->workspace() != new_workspace
) {
1995 win
->changeWorkspace(new_workspace
, how
);
2000 assert(hasWindowFunction(WindowFunctionChangeWorkspace
));
2004 if (workspace() != bt::BSENTINEL
) {
2005 ws
= _screen
->findWorkspace(workspace());
2007 ws
->removeWindow(this);
2010 if (new_workspace
!= bt::BSENTINEL
) {
2011 ws
= _screen
->findWorkspace(new_workspace
);
2013 ws
->addWindow(this);
2017 case StayOnCurrentWorkspace
:
2018 if (isVisible() && workspace() != bt::BSENTINEL
2019 && workspace() != _screen
->currentWorkspace()) {
2021 } else if (!isVisible()
2022 && (workspace() == bt::BSENTINEL
2023 || workspace() == _screen
->currentWorkspace())) {
2028 case SwitchToNewWorkspace
:
2030 we will change to the new workspace soon, so force this window
2037 // change workspace on all transients
2038 if (!client
.transientList
.empty()) {
2039 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2040 end
= client
.transientList
.end();
2041 for (; it
!= end
; ++it
)
2042 (*it
)->changeWorkspace(new_workspace
, how
);
2047 void BlackboxWindow::changeLayer(StackingList::Layer new_layer
) {
2048 if (layer() == new_layer
)
2051 bool restack
= false;
2052 if (isTransient()) {
2053 BlackboxWindow
*win
= findTransientFor();
2055 if (win
->layer() != new_layer
) {
2056 win
->changeLayer(new_layer
);
2063 assert(hasWindowFunction(WindowFunctionChangeLayer
));
2067 _screen
->stackingList().changeLayer(this, new_layer
);
2069 if (!client
.transientList
.empty()) {
2070 BlackboxWindowList::iterator it
= client
.transientList
.begin();
2071 const BlackboxWindowList::iterator end
= client
.transientList
.end();
2072 for (; it
!= end
; ++it
)
2073 (*it
)->changeLayer(new_layer
);
2077 _screen
->restackWindows();
2081 bool BlackboxWindow::setInputFocus(void) {
2084 if (client
.state
.focused
)
2087 switch (windowType()) {
2088 case WindowTypeDock
:
2090 Many docks have auto-hide features similar to the toolbar and
2091 slit... we don't want these things to be moved to the center of
2092 the screen when switching to an empty workspace.
2097 { const bt::Rect
&scr
= _screen
->screenInfo().rect();
2098 if (!frame
.rect
.intersects(scr
)) {
2099 // client is outside the screen, move it to the center
2100 configure(scr
.x() + (scr
.width() - frame
.rect
.width()) / 2,
2101 scr
.y() + (scr
.height() - frame
.rect
.height()) / 2,
2102 frame
.rect
.width(), frame
.rect
.height());
2109 pass focus to any modal transients, giving modal group transients
2112 BWindowGroup
*group
= findWindowGroup();
2113 if (group
&& !group
->transients().empty()) {
2114 BlackboxWindowList::const_iterator it
= group
->transients().begin(),
2115 end
= group
->transients().end();
2116 for (; it
!= end
; ++it
) {
2117 BlackboxWindow
* const tmp
= *it
;
2118 if (!tmp
->isVisible() || !tmp
->isModal())
2121 // we are the newest modal group transient
2124 if (isTransient()) {
2125 if (tmp
== findNonTransientParent()) {
2126 // we are a transient of the modal group transient
2130 return tmp
->setInputFocus();
2134 if (!client
.transientList
.empty()) {
2135 BlackboxWindowList::const_iterator it
= client
.transientList
.begin(),
2136 end
= client
.transientList
.end();
2137 for (; it
!= end
; ++it
) {
2138 BlackboxWindow
* const tmp
= *it
;
2139 if (tmp
->isVisible() && tmp
->isModal())
2140 return tmp
->setInputFocus();
2144 switch (windowType()) {
2145 case WindowTypeDock
:
2151 XSetInputFocus(blackbox
->XDisplay(), client
.window
,
2152 RevertToPointerRoot
, blackbox
->XTime());
2154 if (client
.wmprotocols
.wm_take_focus
) {
2156 ce
.xclient
.type
= ClientMessage
;
2157 ce
.xclient
.message_type
= blackbox
->wmProtocolsAtom();
2158 ce
.xclient
.display
= blackbox
->XDisplay();
2159 ce
.xclient
.window
= client
.window
;
2160 ce
.xclient
.format
= 32;
2161 ce
.xclient
.data
.l
[0] = blackbox
->wmTakeFocusAtom();
2162 ce
.xclient
.data
.l
[1] = blackbox
->XTime();
2163 ce
.xclient
.data
.l
[2] = 0l;
2164 ce
.xclient
.data
.l
[3] = 0l;
2165 ce
.xclient
.data
.l
[4] = 0l;
2166 XSendEvent(blackbox
->XDisplay(), client
.window
, False
, NoEventMask
, &ce
);
2173 void BlackboxWindow::show(void) {
2174 if (client
.state
.visible
)
2177 if (client
.state
.iconic
)
2178 _screen
->removeIcon(this);
2180 client
.state
.iconic
= false;
2181 client
.state
.visible
= true;
2182 setState(isShaded() ? IconicState
: NormalState
);
2184 XMapWindow(blackbox
->XDisplay(), client
.window
);
2185 XMapSubwindows(blackbox
->XDisplay(), frame
.window
);
2186 XMapWindow(blackbox
->XDisplay(), frame
.window
);
2188 if (!client
.transientList
.empty()) {
2189 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2190 end
= client
.transientList
.end();
2191 for (; it
!= end
; ++it
)
2198 XTranslateCoordinates(blackbox
->XDisplay(), client
.window
,
2199 _screen
->screenInfo().rootWindow(),
2200 0, 0, &real_x
, &real_y
, &child
);
2201 fprintf(stderr
, "%s -- assumed: (%d, %d), real: (%d, %d)\n", title().c_str(),
2202 client
.rect
.left(), client
.rect
.top(), real_x
, real_y
);
2203 assert(client
.rect
.left() == real_x
&& client
.rect
.top() == real_y
);
2208 void BlackboxWindow::hide(void) {
2209 if (!client
.state
.visible
)
2212 client
.state
.visible
= false;
2213 setState(client
.state
.iconic
? IconicState
: client
.current_state
);
2215 XUnmapWindow(blackbox
->XDisplay(), frame
.window
);
2218 * we don't want this XUnmapWindow call to generate an UnmapNotify
2219 * event, so we need to clear the event mask on client.window for a
2220 * split second. HOWEVER, since X11 is asynchronous, the window
2221 * could be destroyed in that split second, leaving us with a ghost
2222 * window... so, we need to do this while the X server is grabbed
2224 blackbox
->XGrabServer();
2225 XSelectInput(blackbox
->XDisplay(), client
.window
,
2226 client_window_event_mask
& ~StructureNotifyMask
);
2227 XUnmapWindow(blackbox
->XDisplay(), client
.window
);
2228 XSelectInput(blackbox
->XDisplay(), client
.window
, client_window_event_mask
);
2229 blackbox
->XUngrabServer();
2233 void BlackboxWindow::close(void) {
2234 assert(hasWindowFunction(WindowFunctionClose
));
2237 ce
.xclient
.type
= ClientMessage
;
2238 ce
.xclient
.message_type
= blackbox
->wmProtocolsAtom();
2239 ce
.xclient
.display
= blackbox
->XDisplay();
2240 ce
.xclient
.window
= client
.window
;
2241 ce
.xclient
.format
= 32;
2242 ce
.xclient
.data
.l
[0] = blackbox
->wmDeleteWindowAtom();
2243 ce
.xclient
.data
.l
[1] = blackbox
->XTime();
2244 ce
.xclient
.data
.l
[2] = 0l;
2245 ce
.xclient
.data
.l
[3] = 0l;
2246 ce
.xclient
.data
.l
[4] = 0l;
2247 XSendEvent(blackbox
->XDisplay(), client
.window
, False
, NoEventMask
, &ce
);
2251 void BlackboxWindow::activate(void) {
2252 if (workspace() != bt::BSENTINEL
2253 && workspace() != _screen
->currentWorkspace())
2254 _screen
->setCurrentWorkspace(workspace());
2255 if (client
.state
.iconic
)
2257 if (client
.ewmh
.shaded
)
2259 if (setInputFocus())
2260 _screen
->raiseWindow(this);
2264 void BlackboxWindow::iconify(void) {
2265 if (client
.state
.iconic
)
2268 if (isTransient()) {
2269 BlackboxWindow
*win
= findTransientFor();
2271 if (!win
->isIconic()) {
2277 assert(hasWindowFunction(WindowFunctionIconify
));
2280 _screen
->addIcon(this);
2282 client
.state
.iconic
= true;
2285 // iconify all transients
2286 if (!client
.transientList
.empty()) {
2287 BlackboxWindowList::iterator it
= client
.transientList
.begin(),
2288 end
= client
.transientList
.end();
2289 for (; it
!= end
; ++it
)
2295 void BlackboxWindow::maximize(unsigned int button
) {
2296 assert(hasWindowFunction(WindowFunctionMaximize
));
2298 // any maximize operation always unshades
2299 client
.ewmh
.shaded
= false;
2300 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
2301 + frame
.margin
.bottom
);
2303 if (isMaximized()) {
2304 // restore from maximized
2305 client
.ewmh
.maxh
= client
.ewmh
.maxv
= false;
2307 if (!isFullScreen()) {
2309 when a resize is begun, maximize(0) is called to clear any
2310 maximization flags currently set. Otherwise it still thinks
2311 it is maximized. so we do not need to call configure()
2312 because resizing will handle it
2314 if (! client
.state
.resizing
) {
2315 bt::Rect r
= ::applyGravity(client
.premax
,
2317 client
.wmnormal
.win_gravity
);
2318 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2319 // trick configure into working
2320 frame
.rect
= bt::Rect();
2324 redrawAllButtons(); // in case it is not called in configure()
2328 updateEWMHAllowedActions();
2334 client
.ewmh
.maxh
= true;
2335 client
.ewmh
.maxv
= true;
2339 client
.ewmh
.maxh
= false;
2340 client
.ewmh
.maxv
= true;
2344 client
.ewmh
.maxh
= true;
2345 client
.ewmh
.maxv
= false;
2353 if (!isFullScreen()) {
2354 // go go gadget-maximize!
2355 bt::Rect r
= _screen
->availableArea();
2357 if (!client
.ewmh
.maxh
) {
2358 r
.setX(frame
.rect
.x());
2359 r
.setWidth(frame
.rect
.width());
2361 if (!client
.ewmh
.maxv
) {
2362 r
.setY(frame
.rect
.y());
2363 r
.setHeight(frame
.rect
.height());
2366 // store the current frame geometry, so that we can restore it later
2367 client
.premax
= ::restoreGravity(frame
.rect
,
2369 client
.wmnormal
.win_gravity
);
2371 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2372 // trick configure into working
2373 frame
.rect
= bt::Rect();
2378 updateEWMHAllowedActions();
2382 // re-maximizes the window to take into account availableArea changes
2383 void BlackboxWindow::remaximize(void) {
2384 if (isShaded()) return;
2386 unsigned int button
= 0u;
2387 if (client
.ewmh
.maxv
) {
2388 button
= (client
.ewmh
.maxh
) ? 1u : 2u;
2389 } else if (client
.ewmh
.maxh
) {
2390 button
= (client
.ewmh
.maxv
) ? 1u : 3u;
2393 // trick maximize() into working
2394 client
.ewmh
.maxh
= client
.ewmh
.maxv
= false;
2396 const bt::Rect tmp
= client
.premax
;
2398 client
.premax
= tmp
;
2402 void BlackboxWindow::setShaded(bool shaded
) {
2403 assert(hasWindowFunction(WindowFunctionShade
));
2405 if (client
.ewmh
.shaded
== shaded
)
2408 client
.ewmh
.shaded
= shaded
;
2410 if (isMaximized()) {
2413 // set the frame rect to the normal size
2414 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
+
2415 frame
.margin
.bottom
);
2417 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
2418 frame
.rect
.width(), frame
.rect
.height());
2421 setState(NormalState
);
2423 // set the frame rect to the shaded size
2424 const WindowStyle
&style
= _screen
->resource().windowStyle();
2425 frame
.rect
.setHeight(style
.title_height
);
2427 XResizeWindow(blackbox
->XDisplay(), frame
.window
,
2428 frame
.rect
.width(), frame
.rect
.height());
2430 setState(IconicState
);
2435 void BlackboxWindow::setFullScreen(bool b
) {
2436 assert(hasWindowFunction(WindowFunctionFullScreen
));
2438 if (client
.ewmh
.fullscreen
== b
)
2441 // any fullscreen operation always unshades
2442 client
.ewmh
.shaded
= false;
2443 frame
.rect
.setHeight(client
.rect
.height() + frame
.margin
.top
2444 + frame
.margin
.bottom
);
2446 bool refocus
= isFocused();
2447 client
.ewmh
.fullscreen
= b
;
2448 if (isFullScreen()) {
2449 // go go gadget-fullscreen!
2451 client
.premax
= ::restoreGravity(frame
.rect
,
2453 client
.wmnormal
.win_gravity
);
2455 // modify decorations, functions and frame margin
2456 client
.decorations
= NoWindowDecorations
;
2457 client
.functions
&= ~(WindowFunctionMove
|
2458 WindowFunctionResize
|
2459 WindowFunctionShade
);
2460 const WindowStyle
&style
= _screen
->resource().windowStyle();
2461 frame
.margin
= ::update_margin(client
.decorations
, style
);
2463 bt::Rect r
= ::constrain(_screen
->screenInfo().rect(),
2467 // trick configure() into working
2468 frame
.rect
= bt::Rect();
2472 changeLayer(StackingList::LayerFullScreen
);
2475 updateEWMHAllowedActions();
2477 // restore from fullscreen
2478 ::update_decorations(client
.decorations
,
2484 client
.wmprotocols
);
2485 const WindowStyle
&style
= _screen
->resource().windowStyle();
2486 frame
.margin
= ::update_margin(client
.decorations
, style
);
2489 changeLayer(StackingList::LayerNormal
);
2491 if (isMaximized()) {
2494 bt::Rect r
= ::applyGravity(client
.premax
,
2496 client
.wmnormal
.win_gravity
);
2497 r
= ::constrain(r
, frame
.margin
, client
.wmnormal
, TopLeft
);
2499 // trick configure into working
2500 frame
.rect
= bt::Rect();
2504 updateEWMHAllowedActions();
2512 (void) setInputFocus();
2516 void BlackboxWindow::redrawWindowFrame(void) const {
2517 if (client
.decorations
& WindowDecorationTitlebar
) {
2523 if (client
.decorations
& WindowDecorationHandle
) {
2526 if (client
.decorations
& WindowDecorationGrip
)
2532 void BlackboxWindow::setFocused(bool focused
) {
2533 if (focused
== client
.state
.focused
)
2536 client
.state
.focused
= isVisible() ? focused
: false;
2539 redrawWindowFrame();
2541 if (client
.state
.focused
) {
2542 XInstallColormap(blackbox
->XDisplay(), client
.colormap
);
2544 if (client
.ewmh
.fullscreen
&& layer() != StackingList::LayerBelow
)
2545 changeLayer(StackingList::LayerBelow
);
2551 void BlackboxWindow::setState(unsigned long new_state
) {
2552 client
.current_state
= new_state
;
2554 unsigned long state
[2];
2555 state
[0] = client
.current_state
;
2557 XChangeProperty(blackbox
->XDisplay(), client
.window
,
2558 blackbox
->wmStateAtom(), blackbox
->wmStateAtom(), 32,
2559 PropModeReplace
, (unsigned char *) state
, 2);
2562 updateEWMHAllowedActions();
2566 void BlackboxWindow::updateEWMHState() {
2567 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2569 // set _NET_WM_STATE
2570 bt::EWMH::AtomList atoms
;
2572 atoms
.push_back(ewmh
.wmStateModal());
2574 atoms
.push_back(ewmh
.wmStateShaded());
2576 atoms
.push_back(ewmh
.wmStateHidden());
2578 atoms
.push_back(ewmh
.wmStateFullscreen());
2579 if (client
.ewmh
.maxh
)
2580 atoms
.push_back(ewmh
.wmStateMaximizedHorz());
2581 if (client
.ewmh
.maxv
)
2582 atoms
.push_back(ewmh
.wmStateMaximizedVert());
2583 if (client
.ewmh
.skip_taskbar
)
2584 atoms
.push_back(ewmh
.wmStateSkipTaskbar());
2585 if (client
.ewmh
.skip_pager
)
2586 atoms
.push_back(ewmh
.wmStateSkipPager());
2589 case StackingList::LayerAbove
:
2590 atoms
.push_back(ewmh
.wmStateAbove());
2592 case StackingList::LayerBelow
:
2593 atoms
.push_back(ewmh
.wmStateBelow());
2600 ewmh
.removeProperty(client
.window
, ewmh
.wmState());
2602 ewmh
.setWMState(client
.window
, atoms
);
2606 void BlackboxWindow::updateEWMHAllowedActions() {
2607 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2609 // set _NET_WM_ALLOWED_ACTIONS
2610 bt::EWMH::AtomList atoms
;
2611 if (! client
.state
.iconic
) {
2612 if (hasWindowFunction(WindowFunctionChangeWorkspace
))
2613 atoms
.push_back(ewmh
.wmActionChangeDesktop());
2615 if (hasWindowFunction(WindowFunctionIconify
))
2616 atoms
.push_back(ewmh
.wmActionMinimize());
2618 if (hasWindowFunction(WindowFunctionShade
))
2619 atoms
.push_back(ewmh
.wmActionShade());
2621 if (hasWindowFunction(WindowFunctionMove
))
2622 atoms
.push_back(ewmh
.wmActionMove());
2624 if (hasWindowFunction(WindowFunctionResize
))
2625 atoms
.push_back(ewmh
.wmActionResize());
2627 if (hasWindowFunction(WindowFunctionMaximize
)) {
2628 atoms
.push_back(ewmh
.wmActionMaximizeHorz());
2629 atoms
.push_back(ewmh
.wmActionMaximizeVert());
2632 atoms
.push_back(ewmh
.wmActionFullscreen());
2635 if (hasWindowFunction(WindowFunctionClose
))
2636 atoms
.push_back(ewmh
.wmActionClose());
2639 ewmh
.removeProperty(client
.window
, ewmh
.wmAllowedActions());
2641 ewmh
.setWMAllowedActions(client
.window
, atoms
);
2645 void BlackboxWindow::redrawTitle(void) const {
2646 const WindowStyle
&style
= _screen
->resource().windowStyle();
2647 const bt::Rect
u(0, 0, frame
.rect
.width(), style
.title_height
);
2648 bt::drawTexture(_screen
->screenNumber(),
2649 (client
.state
.focused
2651 : style
.unfocus
.title
),
2653 (client
.state
.focused
2659 void BlackboxWindow::redrawLabel(void) const {
2660 const WindowStyle
&style
= _screen
->resource().windowStyle();
2661 bt::Rect
u(0, 0, frame
.label_w
, style
.label_height
);
2662 Pixmap p
= (client
.state
.focused
? frame
.flabel
: frame
.ulabel
);
2663 if (p
== ParentRelative
) {
2664 const bt::Texture
&texture
=
2665 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2666 int offset
= texture
.borderWidth();
2667 if (client
.decorations
& WindowDecorationIconify
)
2668 offset
+= style
.button_width
+ style
.title_margin
;
2670 const bt::Rect
t(-(style
.title_margin
+ offset
),
2671 -(style
.title_margin
+ texture
.borderWidth()),
2672 frame
.rect
.width(), style
.title_height
);
2673 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.label
, t
, u
,
2674 (client
.state
.focused
? frame
.ftitle
: frame
.utitle
));
2676 bt::drawTexture(_screen
->screenNumber(),
2677 (client
.state
.focused
2679 : style
.unfocus
.label
),
2680 frame
.label
, u
, u
, p
);
2683 const bt::Pen
pen(_screen
->screenNumber(),
2684 ((client
.state
.focused
)
2686 : style
.unfocus
.text
));
2687 u
.setCoords(u
.left() + style
.label_margin
,
2688 u
.top() + style
.label_margin
,
2689 u
.right() - style
.label_margin
,
2690 u
.bottom() - style
.label_margin
);
2691 bt::drawText(style
.font
, pen
, frame
.label
, u
,
2692 style
.alignment
, client
.visible_title
);
2696 void BlackboxWindow::redrawAllButtons(void) const {
2697 if (frame
.iconify_button
) redrawIconifyButton();
2698 if (frame
.maximize_button
) redrawMaximizeButton();
2699 if (frame
.close_button
) redrawCloseButton();
2703 void BlackboxWindow::redrawIconifyButton(bool pressed
) const {
2704 const WindowStyle
&style
= _screen
->resource().windowStyle();
2705 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2706 Pixmap p
= (pressed
? frame
.pbutton
:
2707 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2708 if (p
== ParentRelative
) {
2709 const bt::Texture
&texture
=
2710 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2711 const bt::Rect
t(-(style
.title_margin
+ texture
.borderWidth()),
2712 -(style
.title_margin
+ texture
.borderWidth()),
2713 frame
.rect
.width(), style
.title_height
);
2714 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.iconify_button
,
2715 t
, u
, (client
.state
.focused
2719 bt::drawTexture(_screen
->screenNumber(),
2720 (pressed
? style
.pressed
:
2721 (client
.state
.focused
? style
.focus
.button
:
2722 style
.unfocus
.button
)),
2723 frame
.iconify_button
, u
, u
, p
);
2726 const bt::Pen
pen(_screen
->screenNumber(),
2727 (client
.state
.focused
2728 ? style
.focus
.foreground
2729 : style
.unfocus
.foreground
));
2730 bt::drawBitmap(style
.iconify
, pen
, frame
.iconify_button
, u
);
2734 void BlackboxWindow::redrawMaximizeButton(bool pressed
) const {
2735 const WindowStyle
&style
= _screen
->resource().windowStyle();
2736 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2737 Pixmap p
= (pressed
? frame
.pbutton
:
2738 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2739 if (p
== ParentRelative
) {
2740 const bt::Texture
&texture
=
2741 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2742 int button_w
= style
.button_width
2743 + style
.title_margin
+ texture
.borderWidth();
2744 if (client
.decorations
& WindowDecorationClose
)
2746 const bt::Rect
t(-(frame
.rect
.width() - button_w
),
2747 -(style
.title_margin
+ texture
.borderWidth()),
2748 frame
.rect
.width(), style
.title_height
);
2749 bt::drawTexture(_screen
->screenNumber(), texture
, frame
.maximize_button
,
2750 t
, u
, (client
.state
.focused
2754 bt::drawTexture(_screen
->screenNumber(),
2755 (pressed
? style
.pressed
:
2756 (client
.state
.focused
? style
.focus
.button
:
2757 style
.unfocus
.button
)),
2758 frame
.maximize_button
, u
, u
, p
);
2761 const bt::Pen
pen(_screen
->screenNumber(),
2762 (client
.state
.focused
2763 ? style
.focus
.foreground
2764 : style
.unfocus
.foreground
));
2765 bt::drawBitmap(isMaximized() ? style
.restore
: style
.maximize
,
2766 pen
, frame
.maximize_button
, u
);
2770 void BlackboxWindow::redrawCloseButton(bool pressed
) const {
2771 const WindowStyle
&style
= _screen
->resource().windowStyle();
2772 const bt::Rect
u(0, 0, style
.button_width
, style
.button_width
);
2773 Pixmap p
= (pressed
? frame
.pbutton
:
2774 (client
.state
.focused
? frame
.fbutton
: frame
.ubutton
));
2775 if (p
== ParentRelative
) {
2776 const bt::Texture
&texture
=
2777 (isFocused() ? style
.focus
.title
: style
.unfocus
.title
);
2778 const int button_w
= style
.button_width
+
2779 style
.title_margin
+
2780 texture
.borderWidth();
2781 const bt::Rect
t(-(frame
.rect
.width() - button_w
),
2782 -(style
.title_margin
+ texture
.borderWidth()),
2783 frame
.rect
.width(), style
.title_height
);
2784 bt::drawTexture(_screen
->screenNumber(),texture
, frame
.close_button
, t
, u
,
2785 (client
.state
.focused
? frame
.ftitle
: frame
.utitle
));
2787 bt::drawTexture(_screen
->screenNumber(),
2788 (pressed
? style
.pressed
:
2789 (client
.state
.focused
? style
.focus
.button
:
2790 style
.unfocus
.button
)),
2791 frame
.close_button
, u
, u
, p
);
2794 const bt::Pen
pen(_screen
->screenNumber(),
2795 (client
.state
.focused
2796 ? style
.focus
.foreground
2797 : style
.unfocus
.foreground
));
2798 bt::drawBitmap(style
.close
, pen
, frame
.close_button
, u
);
2802 void BlackboxWindow::redrawHandle(void) const {
2803 const WindowStyle
&style
= _screen
->resource().windowStyle();
2804 const bt::Rect
u(0, 0, frame
.rect
.width(), style
.handle_height
);
2805 bt::drawTexture(_screen
->screenNumber(),
2806 (client
.state
.focused
? style
.focus
.handle
:
2807 style
.unfocus
.handle
),
2809 (client
.state
.focused
? frame
.fhandle
: frame
.uhandle
));
2813 void BlackboxWindow::redrawGrips(void) const {
2814 const WindowStyle
&style
= _screen
->resource().windowStyle();
2815 const bt::Rect
u(0, 0, style
.grip_width
, style
.handle_height
);
2816 Pixmap p
= (client
.state
.focused
? frame
.fgrip
: frame
.ugrip
);
2817 if (p
== ParentRelative
) {
2818 bt::Rect
t(0, 0, frame
.rect
.width(), style
.handle_height
);
2819 bt::drawTexture(_screen
->screenNumber(),
2820 (client
.state
.focused
? style
.focus
.handle
:
2821 style
.unfocus
.handle
),
2822 frame
.right_grip
, t
, u
, p
);
2824 t
.setPos(-(frame
.rect
.width() - style
.grip_width
), 0);
2825 bt::drawTexture(_screen
->screenNumber(),
2826 (client
.state
.focused
? style
.focus
.handle
:
2827 style
.unfocus
.handle
),
2828 frame
.right_grip
, t
, u
, p
);
2830 bt::drawTexture(_screen
->screenNumber(),
2831 (client
.state
.focused
? style
.focus
.grip
:
2832 style
.unfocus
.grip
),
2833 frame
.left_grip
, u
, u
, p
);
2835 bt::drawTexture(_screen
->screenNumber(),
2836 (client
.state
.focused
? style
.focus
.grip
:
2837 style
.unfocus
.grip
),
2838 frame
.right_grip
, u
, u
, p
);
2844 BlackboxWindow::clientMessageEvent(const XClientMessageEvent
* const event
) {
2845 if (event
->format
!= 32)
2848 const bt::EWMH
& ewmh
= blackbox
->ewmh();
2850 if (event
->message_type
== blackbox
->wmChangeStateAtom()) {
2851 if (event
->data
.l
[0] == IconicState
) {
2852 if (hasWindowFunction(WindowFunctionIconify
))
2854 } else if (event
->data
.l
[0] == NormalState
) {
2857 } else if (event
->message_type
== ewmh
.activeWindow()) {
2859 } else if (event
->message_type
== ewmh
.closeWindow()) {
2860 if (hasWindowFunction(WindowFunctionClose
))
2862 } else if (event
->message_type
== ewmh
.moveresizeWindow()) {
2863 XConfigureRequestEvent request
;
2864 request
.window
= event
->window
;
2865 request
.x
= event
->data
.l
[1];
2866 request
.y
= event
->data
.l
[2];
2867 request
.width
= event
->data
.l
[3];
2868 request
.height
= event
->data
.l
[4];
2869 request
.value_mask
= CWX
| CWY
| CWWidth
| CWHeight
;
2871 const int old_gravity
= client
.wmnormal
.win_gravity
;
2872 if (event
->data
.l
[0] != 0)
2873 client
.wmnormal
.win_gravity
= event
->data
.l
[0];
2875 configureRequestEvent(&request
);
2877 client
.wmnormal
.win_gravity
= old_gravity
;
2878 } else if (event
->message_type
== ewmh
.wmDesktop()) {
2879 if (hasWindowFunction(WindowFunctionChangeWorkspace
)) {
2880 const unsigned int new_workspace
= event
->data
.l
[0];
2881 changeWorkspace(new_workspace
);
2883 } else if (event
->message_type
== ewmh
.wmState()) {
2884 Atom action
= event
->data
.l
[0],
2885 first
= event
->data
.l
[1],
2886 second
= event
->data
.l
[2];
2888 if (first
== ewmh
.wmStateModal() || second
== ewmh
.wmStateModal()) {
2889 if ((action
== ewmh
.wmStateAdd() ||
2890 (action
== ewmh
.wmStateToggle() && ! client
.ewmh
.modal
)) &&
2892 client
.ewmh
.modal
= true;
2894 client
.ewmh
.modal
= false;
2897 if (hasWindowFunction(WindowFunctionMaximize
)) {
2898 int max_horz
= 0, max_vert
= 0;
2900 if (first
== ewmh
.wmStateMaximizedHorz() ||
2901 second
== ewmh
.wmStateMaximizedHorz()) {
2902 max_horz
= ((action
== ewmh
.wmStateAdd()
2903 || (action
== ewmh
.wmStateToggle()
2904 && !client
.ewmh
.maxh
))
2908 if (first
== ewmh
.wmStateMaximizedVert() ||
2909 second
== ewmh
.wmStateMaximizedVert()) {
2910 max_vert
= ((action
== ewmh
.wmStateAdd()
2911 || (action
== ewmh
.wmStateToggle()
2912 && !client
.ewmh
.maxv
))
2916 if (max_horz
!= 0 || max_vert
!= 0) {
2919 unsigned int button
= 0u;
2920 if (max_horz
== 1 && max_vert
!= 1)
2922 else if (max_vert
== 1 && max_horz
!= 1)
2924 else if (max_vert
== 1 && max_horz
== 1)
2931 if (hasWindowFunction(WindowFunctionShade
)) {
2932 if (first
== ewmh
.wmStateShaded() ||
2933 second
== ewmh
.wmStateShaded()) {
2934 if (action
== ewmh
.wmStateRemove())
2936 else if (action
== ewmh
.wmStateAdd())
2938 else if (action
== ewmh
.wmStateToggle())
2939 setShaded(!isShaded());
2943 if (first
== ewmh
.wmStateSkipTaskbar()
2944 || second
== ewmh
.wmStateSkipTaskbar()
2945 || first
== ewmh
.wmStateSkipPager()
2946 || second
== ewmh
.wmStateSkipPager()) {
2947 if (first
== ewmh
.wmStateSkipTaskbar()
2948 || second
== ewmh
.wmStateSkipTaskbar()) {
2949 client
.ewmh
.skip_taskbar
= (action
== ewmh
.wmStateAdd()
2950 || (action
== ewmh
.wmStateToggle()
2951 && !client
.ewmh
.skip_taskbar
));
2953 if (first
== ewmh
.wmStateSkipPager()
2954 || second
== ewmh
.wmStateSkipPager()) {
2955 client
.ewmh
.skip_pager
= (action
== ewmh
.wmStateAdd()
2956 || (action
== ewmh
.wmStateToggle()
2957 && !client
.ewmh
.skip_pager
));
2959 // we do nothing with skip_*, but others might... we should at
2960 // least make sure these are present in _NET_WM_STATE
2964 if (first
== ewmh
.wmStateHidden() ||
2965 second
== ewmh
.wmStateHidden()) {
2967 ignore _NET_WM_STATE_HIDDEN, the wm sets this state, not the
2972 if (hasWindowFunction(WindowFunctionFullScreen
)) {
2973 if (first
== ewmh
.wmStateFullscreen() ||
2974 second
== ewmh
.wmStateFullscreen()) {
2975 if (action
== ewmh
.wmStateAdd() ||
2976 (action
== ewmh
.wmStateToggle() &&
2977 ! client
.ewmh
.fullscreen
)) {
2978 setFullScreen(true);
2979 } else if (action
== ewmh
.wmStateToggle() ||
2980 action
== ewmh
.wmStateRemove()) {
2981 setFullScreen(false);
2986 if (hasWindowFunction(WindowFunctionChangeLayer
)) {
2987 if (first
== ewmh
.wmStateAbove() ||
2988 second
== ewmh
.wmStateAbove()) {
2989 if (action
== ewmh
.wmStateAdd() ||
2990 (action
== ewmh
.wmStateToggle() &&
2991 layer() != StackingList::LayerAbove
)) {
2992 changeLayer(StackingList::LayerAbove
);
2993 } else if (action
== ewmh
.wmStateToggle() ||
2994 action
== ewmh
.wmStateRemove()) {
2995 changeLayer(StackingList::LayerNormal
);
2999 if (first
== ewmh
.wmStateBelow() ||
3000 second
== ewmh
.wmStateBelow()) {
3001 if (action
== ewmh
.wmStateAdd() ||
3002 (action
== ewmh
.wmStateToggle() &&
3003 layer() != StackingList::LayerBelow
)) {
3004 changeLayer(StackingList::LayerBelow
);
3005 } else if (action
== ewmh
.wmStateToggle() ||
3006 action
== ewmh
.wmStateRemove()) {
3007 changeLayer(StackingList::LayerNormal
);
3015 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent
* const event
) {
3016 if (event
->window
!= client
.window
)
3020 fprintf(stderr
, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
3024 _screen
->releaseWindow(this);
3029 BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent
* const event
) {
3030 if (event
->window
!= client
.window
)
3034 fprintf(stderr
, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
3038 _screen
->releaseWindow(this);
3042 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent
* const event
) {
3043 if (event
->window
!= client
.window
|| event
->parent
== frame
.plate
)
3047 fprintf(stderr
, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
3048 "0x%lx.\n", client
.window
, event
->parent
);
3052 put the ReparentNotify event back into the queue so that
3053 BlackboxWindow::restore(void) can do the right thing
3056 replay
.xreparent
= *event
;
3057 XPutBackEvent(blackbox
->XDisplay(), &replay
);
3059 _screen
->releaseWindow(this);
3063 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent
* const event
) {
3065 fprintf(stderr
, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
3069 switch(event
->atom
) {
3070 case XA_WM_TRANSIENT_FOR
: {
3071 if (isTransient()) {
3072 // remove ourselves from our transient_for
3073 BlackboxWindow
*win
= findTransientFor();
3075 win
->removeTransient(this);
3076 } else if (isGroupTransient()) {
3077 BWindowGroup
*group
= findWindowGroup();
3079 group
->removeTransient(this);
3083 // determine if this is a transient window
3084 client
.transient_for
= ::readTransientInfo(blackbox
,
3086 _screen
->screenInfo(),
3089 if (isTransient()) {
3090 BlackboxWindow
*win
= findTransientFor();
3092 // add ourselves to our new transient_for
3093 win
->addTransient(this);
3094 changeWorkspace(win
->workspace());
3095 changeLayer(win
->layer());
3096 } else if (isGroupTransient()) {
3097 BWindowGroup
*group
= findWindowGroup();
3099 group
->addTransient(this);
3103 ::update_decorations(client
.decorations
,
3109 client
.wmprotocols
);
3116 // remove from current window group
3117 BWindowGroup
*group
= findWindowGroup();
3119 if (isTransient() && isGroupTransient())
3120 group
->removeTransient(this);
3121 group
->removeWindow(this);
3125 client
.wmhints
= ::readWMHints(blackbox
, client
.window
);
3127 if (client
.wmhints
.window_group
!= None
) {
3128 // add to new window group
3129 group
= ::update_window_group(client
.wmhints
.window_group
,
3132 if (isTransient() && isGroupTransient()) {
3134 group
->addTransient(this);
3140 case XA_WM_ICON_NAME
: {
3141 client
.icon_title
= ::readWMIconName(blackbox
, client
.window
);
3142 if (client
.state
.iconic
)
3143 _screen
->propagateWindowName(this);
3148 client
.title
= ::readWMName(blackbox
, client
.window
);
3150 client
.visible_title
=
3151 bt::ellideText(client
.title
, frame
.label_w
, bt::toUnicode("..."),
3152 _screen
->screenNumber(),
3153 _screen
->resource().windowStyle().font
);
3154 blackbox
->ewmh().setWMVisibleName(client
.window
, client
.visible_title
);
3156 if (client
.decorations
& WindowDecorationTitlebar
)
3159 _screen
->propagateWindowName(this);
3163 case XA_WM_NORMAL_HINTS
: {
3164 WMNormalHints wmnormal
= ::readWMNormalHints(blackbox
, client
.window
,
3165 _screen
->screenInfo());
3166 if (wmnormal
== client
.wmnormal
) {
3167 // apps like xv and GNU emacs seem to like to repeatedly set
3168 // this property over and over
3172 client
.wmnormal
= wmnormal
;
3174 ::update_decorations(client
.decorations
,
3180 client
.wmprotocols
);
3187 if (event
->atom
== blackbox
->wmProtocolsAtom()) {
3188 client
.wmprotocols
= ::readWMProtocols(blackbox
, client
.window
);
3190 ::update_decorations(client
.decorations
,
3196 client
.wmprotocols
);
3199 } else if (event
->atom
== blackbox
->motifWmHintsAtom()) {
3200 client
.motif
= ::readMotifWMHints(blackbox
, client
.window
);
3202 ::update_decorations(client
.decorations
,
3208 client
.wmprotocols
);
3211 } else if (event
->atom
== blackbox
->ewmh().wmStrut()) {
3212 if (! client
.strut
) {
3213 client
.strut
= new bt::EWMH::Strut
;
3214 _screen
->addStrut(client
.strut
);
3217 blackbox
->ewmh().readWMStrut(client
.window
, client
.strut
);
3218 if (client
.strut
->left
|| client
.strut
->right
||
3219 client
.strut
->top
|| client
.strut
->bottom
) {
3220 _screen
->updateStrut();
3222 _screen
->removeStrut(client
.strut
);
3223 delete client
.strut
;
3233 void BlackboxWindow::exposeEvent(const XExposeEvent
* const event
) {
3235 fprintf(stderr
, "BlackboxWindow::exposeEvent() for 0x%lx\n", client
.window
);
3238 if (frame
.title
== event
->window
)
3240 else if (frame
.label
== event
->window
)
3242 else if (frame
.close_button
== event
->window
)
3243 redrawCloseButton();
3244 else if (frame
.maximize_button
== event
->window
)
3245 redrawMaximizeButton();
3246 else if (frame
.iconify_button
== event
->window
)
3247 redrawIconifyButton();
3248 else if (frame
.handle
== event
->window
)
3250 else if (frame
.left_grip
== event
->window
||
3251 frame
.right_grip
== event
->window
)
3256 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent
*
3258 if (event
->window
!= client
.window
|| client
.state
.iconic
)
3261 if (event
->value_mask
& CWBorderWidth
)
3262 client
.old_bw
= event
->border_width
;
3264 if (event
->value_mask
& (CWX
| CWY
| CWWidth
| CWHeight
)) {
3265 bt::Rect req
= frame
.rect
;
3267 if (event
->value_mask
& (CWX
| CWY
)) {
3268 req
= ::restoreGravity(req
, frame
.margin
, client
.wmnormal
.win_gravity
);
3270 if (event
->value_mask
& CWX
)
3272 if (event
->value_mask
& CWY
)
3275 req
= ::applyGravity(req
, frame
.margin
, client
.wmnormal
.win_gravity
);
3278 if (event
->value_mask
& (CWWidth
| CWHeight
)) {
3279 if (event
->value_mask
& CWWidth
)
3280 req
.setWidth(event
->width
+ frame
.margin
.left
+ frame
.margin
.right
);
3281 if (event
->value_mask
& CWHeight
)
3282 req
.setHeight(event
->height
+ frame
.margin
.top
+ frame
.margin
.bottom
);
3288 if (event
->value_mask
& CWStackMode
) {
3289 switch (event
->detail
) {
3292 _screen
->lowerWindow(this);
3298 _screen
->raiseWindow(this);
3305 void BlackboxWindow::buttonPressEvent(const XButtonEvent
* const event
) {
3307 fprintf(stderr
, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3311 if (frame
.maximize_button
== event
->window
) {
3312 if (event
->button
< 4)
3313 redrawMaximizeButton(true);
3314 } else if (frame
.iconify_button
== event
->window
) {
3315 if (event
->button
== 1)
3316 redrawIconifyButton(true);
3317 } else if (frame
.close_button
== event
->window
) {
3318 if (event
->button
== 1)
3319 redrawCloseButton(true);
3321 if (event
->button
== 1
3322 || (event
->button
== 3 && event
->state
== Mod1Mask
)) {
3323 frame
.grab_x
= event
->x_root
- frame
.rect
.x();
3324 frame
.grab_y
= event
->y_root
- frame
.rect
.y();
3326 _screen
->raiseWindow(this);
3328 if (! client
.state
.focused
)
3329 (void) setInputFocus();
3331 XInstallColormap(blackbox
->XDisplay(), client
.colormap
);
3333 if (frame
.plate
== event
->window
) {
3334 XAllowEvents(blackbox
->XDisplay(), ReplayPointer
, event
->time
);
3335 } else if ((frame
.title
== event
->window
3336 || frame
.label
== event
->window
)
3337 && hasWindowFunction(WindowFunctionShade
)) {
3338 if ((event
->time
- lastButtonPressTime
<=
3339 blackbox
->resource().doubleClickInterval()) ||
3340 event
->state
== ControlMask
) {
3341 lastButtonPressTime
= 0;
3342 setShaded(!isShaded());
3344 lastButtonPressTime
= event
->time
;
3347 } else if (event
->button
== 2) {
3348 _screen
->lowerWindow(this);
3349 } else if (event
->button
== 3) {
3350 const int extra
= _screen
->resource().windowStyle().frame_border_width
;
3351 const bt::Rect
rect(client
.rect
.x() - extra
,
3352 client
.rect
.y() - extra
,
3353 client
.rect
.width() + (extra
* 2),
3354 client
.rect
.height() + (extra
* 2));
3356 Windowmenu
*windowmenu
= _screen
->windowmenu(this);
3357 windowmenu
->popup(event
->x_root
, event
->y_root
, rect
);
3363 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent
* const event
) {
3365 fprintf(stderr
, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3369 const WindowStyle
&style
= _screen
->resource().windowStyle();
3370 if (event
->window
== frame
.maximize_button
) {
3371 if (event
->button
< 4) {
3372 if (bt::within(event
->x
, event
->y
,
3373 style
.button_width
, style
.button_width
)) {
3374 maximize(event
->button
);
3375 _screen
->raiseWindow(this);
3377 redrawMaximizeButton();
3380 } else if (event
->window
== frame
.iconify_button
) {
3381 if (event
->button
== 1) {
3382 if (bt::within(event
->x
, event
->y
,
3383 style
.button_width
, style
.button_width
))
3386 redrawIconifyButton();
3388 } else if (event
->window
== frame
.close_button
) {
3389 if (event
->button
== 1) {
3390 if (bt::within(event
->x
, event
->y
,
3391 style
.button_width
, style
.button_width
))
3393 redrawCloseButton();
3395 } else if (client
.state
.moving
) {
3397 } else if (client
.state
.resizing
) {
3399 } else if (event
->window
== frame
.window
) {
3400 if (event
->button
== 2 && event
->state
== Mod1Mask
)
3401 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
3406 void BlackboxWindow::motionNotifyEvent(const XMotionEvent
* const event
) {
3408 fprintf(stderr
, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3412 if (hasWindowFunction(WindowFunctionMove
)
3413 && !client
.state
.resizing
3414 && event
->state
& Button1Mask
3415 && (frame
.title
== event
->window
|| frame
.label
== event
->window
3416 || frame
.handle
== event
->window
|| frame
.window
== event
->window
)) {
3417 if (! client
.state
.moving
)
3420 continueMove(event
->x_root
, event
->y_root
);
3421 } else if (hasWindowFunction(WindowFunctionResize
)
3422 && (event
->state
& Button1Mask
3423 && (event
->window
== frame
.right_grip
3424 || event
->window
== frame
.left_grip
))
3425 || (event
->state
& Button3Mask
3426 && event
->state
& Mod1Mask
3427 && event
->window
== frame
.window
)) {
3428 if (!client
.state
.resizing
)
3429 startResize(event
->window
);
3431 continueResize(event
->x_root
, event
->y_root
);
3436 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent
* const event
) {
3437 if (event
->window
!= frame
.window
|| event
->mode
!= NotifyNormal
)
3440 if (blackbox
->resource().focusModel() == ClickToFocusModel
|| !isVisible())
3443 switch (windowType()) {
3444 case WindowTypeDesktop
:
3445 case WindowTypeDock
:
3446 // these types cannot be focused w/ sloppy focus
3454 bool leave
= False
, inferior
= False
;
3456 while (XCheckTypedWindowEvent(blackbox
->XDisplay(), event
->window
,
3457 LeaveNotify
, &next
)) {
3458 if (next
.type
== LeaveNotify
&& next
.xcrossing
.mode
== NotifyNormal
) {
3460 inferior
= (next
.xcrossing
.detail
== NotifyInferior
);
3464 if ((! leave
|| inferior
) && ! isFocused())
3465 (void) setInputFocus();
3467 if (blackbox
->resource().autoRaise())
3473 BlackboxWindow::leaveNotifyEvent(const XCrossingEvent
* const /*unused*/) {
3474 if (!(blackbox
->resource().focusModel() == SloppyFocusModel
3475 && blackbox
->resource().autoRaise()))
3478 if (timer
->isTiming())
3484 void BlackboxWindow::shapeEvent(const XEvent
* const /*unused*/)
3485 { if (client
.state
.shaped
) configureShape(); }
3492 void BlackboxWindow::restore(void) {
3493 XChangeSaveSet(blackbox
->XDisplay(), client
.window
, SetModeDelete
);
3494 XSelectInput(blackbox
->XDisplay(), client
.window
, NoEventMask
);
3495 XSelectInput(blackbox
->XDisplay(), frame
.plate
, NoEventMask
);
3497 client
.state
.visible
= false;
3500 remove WM_STATE unless the we are shutting down (in which case we
3501 want to make sure we preserve the state across restarts).
3503 if (!blackbox
->shuttingDown()) {
3504 clearState(blackbox
, client
.window
);
3505 } else if (isShaded() && !isIconic()) {
3506 // do not leave a shaded window as an icon unless it was an icon
3507 setState(NormalState
);
3510 client
.rect
= ::restoreGravity(frame
.rect
, frame
.margin
,
3511 client
.wmnormal
.win_gravity
);
3513 blackbox
->XGrabServer();
3515 XUnmapWindow(blackbox
->XDisplay(), frame
.window
);
3516 XUnmapWindow(blackbox
->XDisplay(), client
.window
);
3518 XSetWindowBorderWidth(blackbox
->XDisplay(), client
.window
, client
.old_bw
);
3519 if (isMaximized()) {
3520 // preserve the original size
3521 client
.rect
= client
.premax
;
3522 XMoveResizeWindow(blackbox
->XDisplay(), client
.window
,
3525 client
.premax
.width(),
3526 client
.premax
.height());
3528 XMoveWindow(blackbox
->XDisplay(), client
.window
,
3529 client
.rect
.x() - frame
.rect
.x(),
3530 client
.rect
.y() - frame
.rect
.y());
3533 blackbox
->XUngrabServer();
3536 if (!XCheckTypedWindowEvent(blackbox
->XDisplay(), client
.window
,
3537 ReparentNotify
, &unused
)) {
3539 according to the ICCCM, the window manager is responsible for
3540 reparenting the window back to root... however, we don't want to
3541 do this if the window has been reparented by someone else
3544 XReparentWindow(blackbox
->XDisplay(), client
.window
,
3545 _screen
->screenInfo().rootWindow(),
3546 client
.rect
.x(), client
.rect
.y());
3549 if (blackbox
->shuttingDown())
3550 XMapWindow(blackbox
->XDisplay(), client
.window
);
3554 // timer for autoraise
3555 void BlackboxWindow::timeout(bt::Timer
*)
3556 { _screen
->raiseWindow(this); }
3559 void BlackboxWindow::startMove() {
3561 XGrabPointer(blackbox
->XDisplay(), frame
.window
, false,
3562 Button1MotionMask
| ButtonReleaseMask
,
3563 GrabModeAsync
, GrabModeAsync
, None
,
3564 blackbox
->resource().cursors().move
, blackbox
->XTime());
3566 client
.state
.moving
= true;
3568 if (! blackbox
->resource().opaqueMove()) {
3569 blackbox
->XGrabServer();
3571 frame
.changing
= frame
.rect
;
3572 _screen
->showGeometry(BScreen::Position
, frame
.changing
);
3574 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3575 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3577 pen
.setGCFunction(GXxor
);
3578 pen
.setLineWidth(bw
);
3579 pen
.setSubWindowMode(IncludeInferiors
);
3580 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3582 frame
.changing
.x() + hw
,
3583 frame
.changing
.y() + hw
,
3584 frame
.changing
.width() - bw
,
3585 frame
.changing
.height() - bw
);
3591 void collisionAdjust(int* x
, int* y
, unsigned int width
, unsigned int height
,
3592 const bt::Rect
& rect
, int snap_distance
) {
3594 const int wleft
= *x
,
3595 wright
= *x
+ width
- 1,
3597 wbottom
= *y
+ height
- 1,
3599 dleft
= abs(wleft
- rect
.left()),
3600 dright
= abs(wright
- rect
.right()),
3601 dtop
= abs(wtop
- rect
.top()),
3602 dbottom
= abs(wbottom
- rect
.bottom());
3605 if (dleft
< snap_distance
&& dleft
<= dright
)
3608 else if (dright
< snap_distance
)
3609 *x
= rect
.right() - width
+ 1;
3612 if (dtop
< snap_distance
&& dtop
<= dbottom
)
3615 else if (dbottom
< snap_distance
)
3616 *y
= rect
.bottom() - height
+ 1;
3620 void BlackboxWindow::continueMove(int x_root
, int y_root
) {
3621 int dx
= x_root
- frame
.grab_x
, dy
= y_root
- frame
.grab_y
;
3622 const int snap_distance
= blackbox
->resource().edgeSnapThreshold();
3624 if (snap_distance
) {
3625 collisionAdjust(&dx
, &dy
, frame
.rect
.width(), frame
.rect
.height(),
3626 _screen
->availableArea(), snap_distance
);
3627 if (!blackbox
->resource().fullMaximization())
3628 collisionAdjust(&dx
, &dy
, frame
.rect
.width(), frame
.rect
.height(),
3629 _screen
->screenInfo().rect(), snap_distance
);
3632 if (blackbox
->resource().opaqueMove()) {
3633 configure(dx
, dy
, frame
.rect
.width(), frame
.rect
.height());
3635 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3636 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3638 pen
.setGCFunction(GXxor
);
3639 pen
.setLineWidth(bw
);
3640 pen
.setSubWindowMode(IncludeInferiors
);
3641 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3643 frame
.changing
.x() + hw
,
3644 frame
.changing
.y() + hw
,
3645 frame
.changing
.width() - bw
,
3646 frame
.changing
.height() - bw
);
3648 frame
.changing
.setPos(dx
, dy
);
3650 XDrawRectangle(blackbox
->XDisplay(), _screen
->screenInfo().rootWindow(),
3652 frame
.changing
.x() + hw
,
3653 frame
.changing
.y() + hw
,
3654 frame
.changing
.width() - bw
,
3655 frame
.changing
.height() - bw
);
3658 _screen
->showGeometry(BScreen::Position
, bt::Rect(dx
, dy
, 0, 0));
3662 void BlackboxWindow::finishMove() {
3663 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
3665 client
.state
.moving
= false;
3667 if (!blackbox
->resource().opaqueMove()) {
3668 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3669 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3671 pen
.setGCFunction(GXxor
);
3672 pen
.setLineWidth(bw
);
3673 pen
.setSubWindowMode(IncludeInferiors
);
3674 XDrawRectangle(blackbox
->XDisplay(),
3675 _screen
->screenInfo().rootWindow(),
3677 frame
.changing
.x() + hw
,
3678 frame
.changing
.y() + hw
,
3679 frame
.changing
.width() - bw
,
3680 frame
.changing
.height() - bw
);
3681 blackbox
->XUngrabServer();
3683 configure(frame
.changing
);
3685 configure(frame
.rect
);
3688 _screen
->hideGeometry();
3692 void BlackboxWindow::startResize(Window window
) {
3693 if (frame
.grab_x
< (signed) frame
.rect
.width() / 2) {
3694 if (frame
.grab_y
< (signed) frame
.rect
.height() / 2)
3695 frame
.corner
= BottomRight
;
3697 frame
.corner
= TopRight
;
3699 if (frame
.grab_y
< (signed) frame
.rect
.height() / 2)
3700 frame
.corner
= BottomLeft
;
3702 frame
.corner
= TopLeft
;
3705 Cursor cursor
= None
;
3706 switch (frame
.corner
) {
3708 cursor
= blackbox
->resource().cursors().resize_bottom_right
;
3709 frame
.grab_x
= frame
.rect
.width() - frame
.grab_x
;
3710 frame
.grab_y
= frame
.rect
.height() - frame
.grab_y
;
3713 cursor
= blackbox
->resource().cursors().resize_top_right
;
3714 frame
.grab_x
= frame
.rect
.width() - frame
.grab_x
;
3717 cursor
= blackbox
->resource().cursors().resize_bottom_left
;
3718 frame
.grab_y
= frame
.rect
.height() - frame
.grab_y
;
3721 cursor
= blackbox
->resource().cursors().resize_top_left
;
3726 XGrabPointer(blackbox
->XDisplay(), window
, False
,
3727 ButtonMotionMask
| ButtonReleaseMask
,
3728 GrabModeAsync
, GrabModeAsync
, None
, cursor
, blackbox
->XTime());
3730 client
.state
.resizing
= true;
3732 frame
.changing
= constrain(frame
.rect
, frame
.margin
, client
.wmnormal
,
3733 Corner(frame
.corner
));
3735 if (!blackbox
->resource().opaqueResize()) {
3736 blackbox
->XGrabServer();
3738 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3739 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3741 pen
.setGCFunction(GXxor
);
3742 pen
.setLineWidth(bw
);
3743 pen
.setSubWindowMode(IncludeInferiors
);
3744 XDrawRectangle(blackbox
->XDisplay(),
3745 _screen
->screenInfo().rootWindow(),
3747 frame
.changing
.x() + hw
,
3748 frame
.changing
.y() + hw
,
3749 frame
.changing
.width() - bw
,
3750 frame
.changing
.height() - bw
);
3752 // unset maximized state when resized
3757 showGeometry(frame
.changing
);
3761 void BlackboxWindow::continueResize(int x_root
, int y_root
) {
3762 // continue a resize
3763 const bt::Rect curr
= frame
.changing
;
3765 switch (frame
.corner
) {
3768 frame
.changing
.setCoords(frame
.changing
.left(),
3769 frame
.changing
.top(),
3770 std::max
<signed>(x_root
+ frame
.grab_x
,
3771 frame
.changing
.left()
3772 + (frame
.margin
.left
3773 + frame
.margin
.right
+ 1)),
3774 frame
.changing
.bottom());
3778 frame
.changing
.setCoords(std::min
<signed>(x_root
- frame
.grab_x
,
3779 frame
.changing
.right()
3780 - (frame
.margin
.left
3781 + frame
.margin
.right
+ 1)),
3782 frame
.changing
.top(),
3783 frame
.changing
.right(),
3784 frame
.changing
.bottom());
3788 switch (frame
.corner
) {
3791 frame
.changing
.setCoords(frame
.changing
.left(),
3792 frame
.changing
.top(),
3793 frame
.changing
.right(),
3794 std::max
<signed>(y_root
+ frame
.grab_y
,
3795 frame
.changing
.top()
3797 + frame
.margin
.bottom
+ 1)));
3801 frame
.changing
.setCoords(frame
.changing
.left(),
3802 std::min
<signed>(y_root
- frame
.grab_y
,
3805 + frame
.margin
.bottom
+ 1)),
3806 frame
.changing
.right(),
3807 frame
.changing
.bottom());
3811 frame
.changing
= constrain(frame
.changing
, frame
.margin
, client
.wmnormal
,
3812 Corner(frame
.corner
));
3814 if (curr
!= frame
.changing
) {
3815 if (blackbox
->resource().opaqueResize()) {
3816 configure(frame
.changing
);
3818 bt::Pen
pen(_screen
->screenNumber(), bt::Color(0xff, 0xff, 0xff));
3819 const int bw
= _screen
->resource().windowStyle().frame_border_width
,
3821 pen
.setGCFunction(GXxor
);
3822 pen
.setLineWidth(bw
);
3823 pen
.setSubWindowMode(IncludeInferiors
);
3824 XDrawRectangle(blackbox
->XDisplay(),
3825 _screen
->screenInfo().rootWindow(),
3830 curr
.height() - bw
);
3832 XDrawRectangle(blackbox
->XDisplay(),
3833 _screen
->screenInfo().rootWindow(),
3835 frame
.changing
.x() + hw
,
3836 frame
.changing
.y() + hw
,
3837 frame
.changing
.width() - bw
,
3838 frame
.changing
.height() - bw
);
3841 showGeometry(frame
.changing
);
3846 void BlackboxWindow::finishResize() {
3848 if (!blackbox
->resource().opaqueResize()) {
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
);
3863 blackbox
->XUngrabServer();
3865 // unset maximized state when resized
3870 client
.state
.resizing
= false;
3872 XUngrabPointer(blackbox
->XDisplay(), blackbox
->XTime());
3874 _screen
->hideGeometry();
3876 frame
.changing
= constrain(frame
.changing
, frame
.margin
, client
.wmnormal
,
3877 Corner(frame
.corner
));
3878 configure(frame
.changing
);
3883 * show the geometry of the window based on rectangle r.
3884 * The logical width and height are used here. This refers to the user's
3885 * perception of the window size (for example an xterm resizes in cells,
3886 * not in pixels). No extra work is needed if there is no difference between
3887 * the logical and actual dimensions.
3889 void BlackboxWindow::showGeometry(const bt::Rect
&r
) const {
3890 unsigned int w
= r
.width(), h
= r
.height();
3892 // remove the window frame
3893 w
-= frame
.margin
.left
+ frame
.margin
.right
;
3894 h
-= frame
.margin
.top
+ frame
.margin
.bottom
;
3896 if (client
.wmnormal
.flags
& PResizeInc
) {
3897 if (client
.wmnormal
.flags
& (PMinSize
|PBaseSize
)) {
3898 w
-= ((client
.wmnormal
.base_width
)
3899 ? client
.wmnormal
.base_width
3900 : client
.wmnormal
.min_width
);
3901 h
-= ((client
.wmnormal
.base_height
)
3902 ? client
.wmnormal
.base_height
3903 : client
.wmnormal
.min_height
);
3906 w
/= client
.wmnormal
.width_inc
;
3907 h
/= client
.wmnormal
.height_inc
;
3910 _screen
->showGeometry(BScreen::Size
, bt::Rect(0, 0, w
, h
));
3914 // see my rant above for an explanation of this operator
3915 bool operator==(const WMNormalHints
&x
, const WMNormalHints
&y
) {
3916 return (x
.flags
== y
.flags
3917 && x
.min_width
== y
.min_width
3918 && x
.min_height
== y
.min_height
3919 && x
.max_width
== y
.max_width
3920 && x
.max_height
== y
.max_height
3921 && x
.width_inc
== y
.width_inc
3922 && x
.height_inc
== y
.height_inc
3923 && x
.min_aspect_x
== y
.min_aspect_x
3924 && x
.min_aspect_y
== y
.min_aspect_y
3925 && x
.max_aspect_x
== y
.max_aspect_x
3926 && x
.max_aspect_y
== y
.max_aspect_y
3927 && x
.base_width
== y
.base_width
3928 && x
.base_height
== y
.base_height
3929 && x
.win_gravity
== y
.win_gravity
);