X: Find out if a window is visible to the user i.e. not covered by othersComputing the visible area of a...
Why did Tony's Arc Reactor do this?
PWM on 5V GPIO pin
How invisible hand adjusts stock prices if company is listed on multiple exchanges, under multiple currencies, and one of the currencies plunges?
What is the purpose of the rotating plate in front of the lock?
More than three domains hosted on the same IP address
How would two worlds first establish an exchange rate between their currencies
Should I tip on the Amtrak train?
Why is Sojdlg123aljg a common password?
Do aarakocra have arms as well as wings?
Dragons and gems
Did the Byzantines ever attempt to move their capital to Rome?
How to finish my PhD?
Colorize specific region in plane
When calculating averages, why can we treat exploding die as if they're independent?
Why are UK MPs allowed to abstain (but it counts as a no)?
Do you need to burn fuel between gravity assists?
Capacitors with same voltage, same capacitance, same temp, different diameter?
Galilean transformation vs simple translation
Why do the Brexit opposition parties not want a new election?
Is mountain bike good for long distances?
How should Thaumaturgy's "three times as loud as normal" be interpreted?
Why is it that I have to play this note on the piano as A sharp?
Why does PAUSE key have a long make code and no break code?
What's the biggest difference between these two photos?
X: Find out if a window is visible to the user i.e. not covered by others
Computing the visible area of a partly hidden windowCheck if an application is running and is visible to the userDoes window minimizing free memory usage?How Do I Shrink the Visible Area of the X11 Root Window?Computing the visible area of a partly hidden windowCan xdotool be used on some window not in the front?How are window-ids given out by MATE window manager?How do I find out the window name of fullscreen internet content (eg Flash)?Output of ssh control command ~#Make xzoom float in i3wmnodm display manager - cursor is not visiblehow to focus menu window on the object (instead the mouse cursor position) with a xdotool?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
I only want to execute a certain action with xdotool
if the window is not visible to the user, this includes minimized windows, but also windows which are 100% covered by other windows (if no transparency is used at least). Ignoring the transparency issue is there a simple way to do this?
xdotool
has a --onlyvisible
option, but that only includes minimized windows, not covered windows. Of course there is the option of cycling through all visible windows, getting their window geometries and calculating how much of the window of interest they cover, but I do hope there is an easier and faster solution than doing this in bash.
Here is a nice illustration of the problem, but there it is only about listing windows, also it is for Max OS X. This question only has an answer which hints, but not shows how to do it by listing all visible windows with their respective z-order and calculating the visible area manually.
x11 xdotool
add a comment |
I only want to execute a certain action with xdotool
if the window is not visible to the user, this includes minimized windows, but also windows which are 100% covered by other windows (if no transparency is used at least). Ignoring the transparency issue is there a simple way to do this?
xdotool
has a --onlyvisible
option, but that only includes minimized windows, not covered windows. Of course there is the option of cycling through all visible windows, getting their window geometries and calculating how much of the window of interest they cover, but I do hope there is an easier and faster solution than doing this in bash.
Here is a nice illustration of the problem, but there it is only about listing windows, also it is for Max OS X. This question only has an answer which hints, but not shows how to do it by listing all visible windows with their respective z-order and calculating the visible area manually.
x11 xdotool
1
Computing the visible area of a partly hidden window may help.
– Gilles
May 5 '16 at 0:51
add a comment |
I only want to execute a certain action with xdotool
if the window is not visible to the user, this includes minimized windows, but also windows which are 100% covered by other windows (if no transparency is used at least). Ignoring the transparency issue is there a simple way to do this?
xdotool
has a --onlyvisible
option, but that only includes minimized windows, not covered windows. Of course there is the option of cycling through all visible windows, getting their window geometries and calculating how much of the window of interest they cover, but I do hope there is an easier and faster solution than doing this in bash.
Here is a nice illustration of the problem, but there it is only about listing windows, also it is for Max OS X. This question only has an answer which hints, but not shows how to do it by listing all visible windows with their respective z-order and calculating the visible area manually.
x11 xdotool
I only want to execute a certain action with xdotool
if the window is not visible to the user, this includes minimized windows, but also windows which are 100% covered by other windows (if no transparency is used at least). Ignoring the transparency issue is there a simple way to do this?
xdotool
has a --onlyvisible
option, but that only includes minimized windows, not covered windows. Of course there is the option of cycling through all visible windows, getting their window geometries and calculating how much of the window of interest they cover, but I do hope there is an easier and faster solution than doing this in bash.
Here is a nice illustration of the problem, but there it is only about listing windows, also it is for Max OS X. This question only has an answer which hints, but not shows how to do it by listing all visible windows with their respective z-order and calculating the visible area manually.
x11 xdotool
x11 xdotool
edited May 23 '17 at 12:39
Community♦
1
1
asked May 5 '16 at 0:22
mxmlnknmxmlnkn
2411 silver badge9 bronze badges
2411 silver badge9 bronze badges
1
Computing the visible area of a partly hidden window may help.
– Gilles
May 5 '16 at 0:51
add a comment |
1
Computing the visible area of a partly hidden window may help.
– Gilles
May 5 '16 at 0:51
1
1
Computing the visible area of a partly hidden window may help.
– Gilles
May 5 '16 at 0:51
Computing the visible area of a partly hidden window may help.
– Gilles
May 5 '16 at 0:51
add a comment |
2 Answers
2
active
oldest
votes
For completeness sake here is the naive / brute-force solution which I hoped is already implemented in some other utility. The fullyobscured
notify-event in @Gilles link in the comment sounds very promising, but I wasn't sure how to get it to work, and this solution was also quite fun to implement.
The script simply calculates the coverage area of all overlapping window subtracting double counted areas and checks if it is as large as the window area. Because it correctly includes the frame borders the code looks a bit more complex than it could. It returns exit code 0 if fully covered and 1 if not. It takes a window ID as an argument. E.g. call it with if xcovered 0x1a00003; then echo 'covered!'; fi
Not counting the comments, debug comments and error-checking it could be only 40 lines long, surely even less.
I actually wanted to use bc instead of python, but I couldn't find an easy way to transfer a bash array to a bc array.
#!/bin/bash
# Name: xcovered.sh
# Find out if C is completely or only partially covered by A or B
# +-----------+
# | +===+ |
# | | +-------+
# | C | | B |
# | | +-------+
# +---| A |---+
# +---+
# @return 0 if window ist not visible, 1 if visible
# Note: Only tested with three windows like in sketch above, but
# it should also work for an arbitrary amount of overlapping windwows
wid=$1
if ! xwininfo -id $wid -stats | 'grep' -q 'IsViewable'; then return 0; fi
# get all stacked window ids after and including the given wid
wids=($(xprop -root | 'sed' -nE "/_NET_CLIENT_LIST_STACKING(WINDOW)/{ s|.*($wid)|1|; s|,||g; p }"))
if [ ${#wids} -eq 0 ]; then
echo -e "e[31mCouldn't find specified window id $wid in _NET_CLIENT_LIST_STACKING(WINDOW)"'!'"e[0m"
return 2
fi
if [ ${#wids} -eq 1 ]; then return 0; fi
# Gather geometry of all windows in higher zorder / possibly lying on top
coords=(); frames=()
for owid in ${wids[@]}; do
#xwininfo -id $owid | grep xwininfo
if xwininfo -id $owid -stats | 'grep' -q 'IsViewable'; then
# _NET_WM_ICON_GEOMETRY doesn't exist for xfce4-panel, thereby making this more difficult
#coords=$(xprop -id $owid _NET_WM_ICON_GEOMETRY)
#frames=$(xprop -id $owid _NET_FRAME_EXTENTS)
x=($(xwininfo -id $owid -stats -wm | sed -nE '
s|^[ t]*Absolute upper-left X:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Absolute upper-left Y:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Width:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Height:[ t]*([0-9]+).*|1|Ip;
/Frame extents:/I{ s|^[ t}Frame Extents:[ t]*||I; s|,||g; p; };
' | sed ':a; N; $!b a; s/n/ /g '))
if [ ! ${#x[@]} -eq 8 ]; then
echo -e "e[31mSomething went wrong when parsing the output of 'xwininfo -id $owid -stats -wm':e[0m"
xwininfo -id $owid -stats -wm
exit 1
fi
# apply the frame width to the coordinates and window width
# 0:x 1:y 2:w 3:h, border widths 4:left 5:right 6:top 7:bottom
coords+=( "${x[0]}-${x[4]}, ${x[1]}-${x[6]}, ${x[2]}+${x[4]}+${x[5]}, ${x[3]}+${x[6]}+${x[7]}" )
fi
done
IFS=','; python - <<EOF #| python
# Calculates the area of the union of all overlapping areas. If that area
# is equal to the window of interest area / size, then the window is covered.
# Note that the calcualted area can't be larger than that!
# 1
# D---C => overlap given by H and B
# | H-|---G x-overlap: max(0, xleft2-xright1)
# A---B | -> '1' and '2' is not known, that's why for left and right
# | 2 | use min, each
# E-----F -> max(0, min(xright1,xright2) - max(xleft1,xleft2) )
# Note that because of xleft<xright this can only
# result in xright1-xleft2 or xright2-xleft1
# All cases: 1 | +--+ | +--+ | +--+ | +--+ |
# 2 | +--+ | +--+ | +--+ | +--+ |
# overlap | 0 | 2 | 2 | 0 |
def overlap( x1,y1,w1,h1, x2,y2,w2,h2, x3=0,y3=0,w3=65535,h3=65535 ):
return max( 0, min(x1+w1,x2+w2,x3+w3) - max(x1,x2,x3) ) *
max( 0, min(y1+h1,y2+h2,y3+h3) - max(y1,y2,y3) )
x=[ ${coords[*]} ]
area=0
# Calculate overlap with window in question
# 0:x 1:y 2:w 3:h, border widths 0:left 1:right 2:top 3:bottom
for i in range( 4,len(x),4 ):
area += overlap( *( x[0:4]+x[i:i+4] ) )
# subtract double counted areas i.e. areas overlapping to the window
# of interest and two other windows on top ... This is n**2
for i in range( 4,len(x),4 ):
for j in range( i+4,len(x),4 ):
area -= overlap( *( x[0:4]+x[i:i+4]+x[j:j+4] ) )
print "area =",area
print "woi =",x[2]*x[3]
# exit code 0: if not fully covered, 1: if fully covered
exit( area < x[2]*x[3] )
EOF
exit $?
1
Beware that this doesn't take non-rectangular windows into account. Those are admittedly rare. Does this correctly report windows visible only on another desktop or entirely outside the screen area as invisible?
– Gilles
May 6 '16 at 0:50
1
I used your code to make it a Python module. Maybe you like to check it out: github.com/ikem-krueger/sleep-walker/blob/develop/src/usr/lib/…
– Ikem Krueger
Sep 15 '17 at 23:44
@user82110 Oh that's cool, thanks for the link!
– mxmlnkn
Sep 16 '17 at 22:18
Note that the "covered area" calculation here is not correct when there are more than three windows on top of the target window. See my solution here.
– user12173
28 mins ago
add a comment |
The answer given by @mxmlnkn is a great start, but unfortunately the methodology for calculating the covered area is not correct for when 3 or more windows are on top of the target window.
To see why, imagine that there are 3 windows (which we'll call X
, Y
, and Z
) on top of a target window T
); further suppose that all these windows have the same coordinates. Then the given solution would first add |X ∩ T|+|Y ∩ T|+|Z ∩ T|=3*windowArea
, and then subtract |(X U Y) ∩ T| + |(X U Z) ∩ T| + |(Y U Z) ∩ T| =3*windowArea
, resulting in a net area calculation of 0
. The error here is we are actually "double counting" the attempt to compensate for "double counting". To correct this, we add |X ∩ Y ∩ Z ∩ T|
. This notion is formalized with the Principle of Inclusion-Exclusion (see here).
The "covered area" can be defined as (forgive the haphazard mathematical notation, unix.stackexchange.com
does not allow LaTeX
)
(A_1 U A_2 U ... U A_n) ∩ B
where A_1, A_2, ..., A_n
are the windows that lie on top of the target window, and B
is the target window.
We can use the Principle of Inclusion-Exclusion to expand (A_1 U A_2 U ... U A_n)
. We can then distribute the intersection with B
across this result.
Concretely, this results in the following algorithm (C++):
bool windowIsVisible(Display *display, Window window, float threshold) {
// Indicates whether a window is fully covered
if (!windowIsViewable(display, window)) {
return false;
}
auto rootWindow = DefaultRootWindow(display);
auto coords = getWindowCoords(display, rootWindow, window);
if (coords.size() <= 1) {
return true;
}
float area = (coords[0][2]-coords[0][0]) * (coords[0][3]-coords[0][1]);
float coveredArea = 0;
auto selector = std::vector<bool>(coords.size()-1);
for (int i = 0; i < selector.size(); i++) {
std::fill(selector.begin(), selector.begin()+i+1, true);
std::fill(selector.begin()+i+1, selector.end(), false);
auto selectedWindows = std::vector<std::vector<int>>(i);
do {
selectedWindows.clear();
for (int j = 0; j < selector.size(); j++) {
if (selector[j]) selectedWindows.push_back(coords[j+1]);
}
selectedWindows.push_back(coords[0]);
coveredArea += pow(-1, i)*calculateWindowOverlap(selectedWindows);
} while (std::prev_permutation(selector.begin(), selector.end()));
}
float tol = 1e-4;
return (1 - ((float)coveredArea)/((float)area) + tol) >= threshold;
}
int calculateWindowOverlap(std::vector<std::vector<int>> windowCoords) {
if (windowCoords.size() == 0) {
return 0;
}
std::vector<int> intersect = windowCoords[0];
for (int i = 1; i < windowCoords.size(); i++) {
intersect[0] = std::max(intersect[0], windowCoords[i][0]);
intersect[1] = std::max(intersect[1], windowCoords[i][1]);
intersect[2] = std::min(intersect[2], windowCoords[i][2]);
intersect[3] = std::min(intersect[3], windowCoords[i][3]);
}
return std::max(0, intersect[2]-intersect[0]) *
std::max(0, intersect[3]-intersect[1]);
}
std::vector<std::vector<int>> getWindowCoords(Display *display,
Window queryWindow, Window targetWindow,
bool *reachedTargetPtr = nullptr, int absX = 0, int absY = 0) {
// Gather geometry of all windows in higher zorder
std::vector<std::vector<int>> coords = {};
bool reachedTarget = false;
if (!reachedTargetPtr) {
reachedTargetPtr = &reachedTarget;
}
Window rWindow;
Window parentWindow;
Window *childrenWindows;
unsigned int numChildren;
XQueryTree(display, queryWindow, &rWindow, &parentWindow,
&childrenWindows, &numChildren);
for (int i = 0; i < numChildren; i++) {
if (childrenWindows[i] == targetWindow) {
*reachedTargetPtr = true;
}
XWindowAttributes windowAttributes;
XGetWindowAttributes(display, childrenWindows[i], &windowAttributes);
if (*reachedTargetPtr && windowAttributes.map_state == IsViewable &&
windowAttributes.c_class != InputOnly) {
coords.push_back(std::vector<int> {
windowAttributes.x + absX,
windowAttributes.y + absY,
windowAttributes.x + absX + windowAttributes.width,
windowAttributes.y + absY + windowAttributes.height });
}
if (childrenWindows[i] != targetWindow) {
auto childCoords = getWindowCoords(display, childrenWindows[i],
targetWindow, reachedTargetPtr, absX + windowAttributes.x,
absY + windowAttributes.y);
coords.reserve(coords.size() + childCoords.size());
coords.insert(coords.end(), childCoords.begin(), childCoords.end());
}
}
return coords;
}
Basically, for k=1,2,...,n
, we find all combinations of n choose k
. We then calculate the area of the intersections of these windows, along with the target window, and add/subtract that result from the running area (in accordance with the (-1)^(k-1)
term from the Principle of Inclusion-Exclusion.
I have implemented this in a simple tool I made here. Also, this is essentially an extension of the Rectangle Area II problem from leetcode. There are some more efficient ways to do this (check the solutions section), but I personally found that the mathematically intuitive way achieved adequate performance.
New contributor
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f281168%2fx-find-out-if-a-window-is-visible-to-the-user-i-e-not-covered-by-others%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
For completeness sake here is the naive / brute-force solution which I hoped is already implemented in some other utility. The fullyobscured
notify-event in @Gilles link in the comment sounds very promising, but I wasn't sure how to get it to work, and this solution was also quite fun to implement.
The script simply calculates the coverage area of all overlapping window subtracting double counted areas and checks if it is as large as the window area. Because it correctly includes the frame borders the code looks a bit more complex than it could. It returns exit code 0 if fully covered and 1 if not. It takes a window ID as an argument. E.g. call it with if xcovered 0x1a00003; then echo 'covered!'; fi
Not counting the comments, debug comments and error-checking it could be only 40 lines long, surely even less.
I actually wanted to use bc instead of python, but I couldn't find an easy way to transfer a bash array to a bc array.
#!/bin/bash
# Name: xcovered.sh
# Find out if C is completely or only partially covered by A or B
# +-----------+
# | +===+ |
# | | +-------+
# | C | | B |
# | | +-------+
# +---| A |---+
# +---+
# @return 0 if window ist not visible, 1 if visible
# Note: Only tested with three windows like in sketch above, but
# it should also work for an arbitrary amount of overlapping windwows
wid=$1
if ! xwininfo -id $wid -stats | 'grep' -q 'IsViewable'; then return 0; fi
# get all stacked window ids after and including the given wid
wids=($(xprop -root | 'sed' -nE "/_NET_CLIENT_LIST_STACKING(WINDOW)/{ s|.*($wid)|1|; s|,||g; p }"))
if [ ${#wids} -eq 0 ]; then
echo -e "e[31mCouldn't find specified window id $wid in _NET_CLIENT_LIST_STACKING(WINDOW)"'!'"e[0m"
return 2
fi
if [ ${#wids} -eq 1 ]; then return 0; fi
# Gather geometry of all windows in higher zorder / possibly lying on top
coords=(); frames=()
for owid in ${wids[@]}; do
#xwininfo -id $owid | grep xwininfo
if xwininfo -id $owid -stats | 'grep' -q 'IsViewable'; then
# _NET_WM_ICON_GEOMETRY doesn't exist for xfce4-panel, thereby making this more difficult
#coords=$(xprop -id $owid _NET_WM_ICON_GEOMETRY)
#frames=$(xprop -id $owid _NET_FRAME_EXTENTS)
x=($(xwininfo -id $owid -stats -wm | sed -nE '
s|^[ t]*Absolute upper-left X:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Absolute upper-left Y:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Width:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Height:[ t]*([0-9]+).*|1|Ip;
/Frame extents:/I{ s|^[ t}Frame Extents:[ t]*||I; s|,||g; p; };
' | sed ':a; N; $!b a; s/n/ /g '))
if [ ! ${#x[@]} -eq 8 ]; then
echo -e "e[31mSomething went wrong when parsing the output of 'xwininfo -id $owid -stats -wm':e[0m"
xwininfo -id $owid -stats -wm
exit 1
fi
# apply the frame width to the coordinates and window width
# 0:x 1:y 2:w 3:h, border widths 4:left 5:right 6:top 7:bottom
coords+=( "${x[0]}-${x[4]}, ${x[1]}-${x[6]}, ${x[2]}+${x[4]}+${x[5]}, ${x[3]}+${x[6]}+${x[7]}" )
fi
done
IFS=','; python - <<EOF #| python
# Calculates the area of the union of all overlapping areas. If that area
# is equal to the window of interest area / size, then the window is covered.
# Note that the calcualted area can't be larger than that!
# 1
# D---C => overlap given by H and B
# | H-|---G x-overlap: max(0, xleft2-xright1)
# A---B | -> '1' and '2' is not known, that's why for left and right
# | 2 | use min, each
# E-----F -> max(0, min(xright1,xright2) - max(xleft1,xleft2) )
# Note that because of xleft<xright this can only
# result in xright1-xleft2 or xright2-xleft1
# All cases: 1 | +--+ | +--+ | +--+ | +--+ |
# 2 | +--+ | +--+ | +--+ | +--+ |
# overlap | 0 | 2 | 2 | 0 |
def overlap( x1,y1,w1,h1, x2,y2,w2,h2, x3=0,y3=0,w3=65535,h3=65535 ):
return max( 0, min(x1+w1,x2+w2,x3+w3) - max(x1,x2,x3) ) *
max( 0, min(y1+h1,y2+h2,y3+h3) - max(y1,y2,y3) )
x=[ ${coords[*]} ]
area=0
# Calculate overlap with window in question
# 0:x 1:y 2:w 3:h, border widths 0:left 1:right 2:top 3:bottom
for i in range( 4,len(x),4 ):
area += overlap( *( x[0:4]+x[i:i+4] ) )
# subtract double counted areas i.e. areas overlapping to the window
# of interest and two other windows on top ... This is n**2
for i in range( 4,len(x),4 ):
for j in range( i+4,len(x),4 ):
area -= overlap( *( x[0:4]+x[i:i+4]+x[j:j+4] ) )
print "area =",area
print "woi =",x[2]*x[3]
# exit code 0: if not fully covered, 1: if fully covered
exit( area < x[2]*x[3] )
EOF
exit $?
1
Beware that this doesn't take non-rectangular windows into account. Those are admittedly rare. Does this correctly report windows visible only on another desktop or entirely outside the screen area as invisible?
– Gilles
May 6 '16 at 0:50
1
I used your code to make it a Python module. Maybe you like to check it out: github.com/ikem-krueger/sleep-walker/blob/develop/src/usr/lib/…
– Ikem Krueger
Sep 15 '17 at 23:44
@user82110 Oh that's cool, thanks for the link!
– mxmlnkn
Sep 16 '17 at 22:18
Note that the "covered area" calculation here is not correct when there are more than three windows on top of the target window. See my solution here.
– user12173
28 mins ago
add a comment |
For completeness sake here is the naive / brute-force solution which I hoped is already implemented in some other utility. The fullyobscured
notify-event in @Gilles link in the comment sounds very promising, but I wasn't sure how to get it to work, and this solution was also quite fun to implement.
The script simply calculates the coverage area of all overlapping window subtracting double counted areas and checks if it is as large as the window area. Because it correctly includes the frame borders the code looks a bit more complex than it could. It returns exit code 0 if fully covered and 1 if not. It takes a window ID as an argument. E.g. call it with if xcovered 0x1a00003; then echo 'covered!'; fi
Not counting the comments, debug comments and error-checking it could be only 40 lines long, surely even less.
I actually wanted to use bc instead of python, but I couldn't find an easy way to transfer a bash array to a bc array.
#!/bin/bash
# Name: xcovered.sh
# Find out if C is completely or only partially covered by A or B
# +-----------+
# | +===+ |
# | | +-------+
# | C | | B |
# | | +-------+
# +---| A |---+
# +---+
# @return 0 if window ist not visible, 1 if visible
# Note: Only tested with three windows like in sketch above, but
# it should also work for an arbitrary amount of overlapping windwows
wid=$1
if ! xwininfo -id $wid -stats | 'grep' -q 'IsViewable'; then return 0; fi
# get all stacked window ids after and including the given wid
wids=($(xprop -root | 'sed' -nE "/_NET_CLIENT_LIST_STACKING(WINDOW)/{ s|.*($wid)|1|; s|,||g; p }"))
if [ ${#wids} -eq 0 ]; then
echo -e "e[31mCouldn't find specified window id $wid in _NET_CLIENT_LIST_STACKING(WINDOW)"'!'"e[0m"
return 2
fi
if [ ${#wids} -eq 1 ]; then return 0; fi
# Gather geometry of all windows in higher zorder / possibly lying on top
coords=(); frames=()
for owid in ${wids[@]}; do
#xwininfo -id $owid | grep xwininfo
if xwininfo -id $owid -stats | 'grep' -q 'IsViewable'; then
# _NET_WM_ICON_GEOMETRY doesn't exist for xfce4-panel, thereby making this more difficult
#coords=$(xprop -id $owid _NET_WM_ICON_GEOMETRY)
#frames=$(xprop -id $owid _NET_FRAME_EXTENTS)
x=($(xwininfo -id $owid -stats -wm | sed -nE '
s|^[ t]*Absolute upper-left X:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Absolute upper-left Y:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Width:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Height:[ t]*([0-9]+).*|1|Ip;
/Frame extents:/I{ s|^[ t}Frame Extents:[ t]*||I; s|,||g; p; };
' | sed ':a; N; $!b a; s/n/ /g '))
if [ ! ${#x[@]} -eq 8 ]; then
echo -e "e[31mSomething went wrong when parsing the output of 'xwininfo -id $owid -stats -wm':e[0m"
xwininfo -id $owid -stats -wm
exit 1
fi
# apply the frame width to the coordinates and window width
# 0:x 1:y 2:w 3:h, border widths 4:left 5:right 6:top 7:bottom
coords+=( "${x[0]}-${x[4]}, ${x[1]}-${x[6]}, ${x[2]}+${x[4]}+${x[5]}, ${x[3]}+${x[6]}+${x[7]}" )
fi
done
IFS=','; python - <<EOF #| python
# Calculates the area of the union of all overlapping areas. If that area
# is equal to the window of interest area / size, then the window is covered.
# Note that the calcualted area can't be larger than that!
# 1
# D---C => overlap given by H and B
# | H-|---G x-overlap: max(0, xleft2-xright1)
# A---B | -> '1' and '2' is not known, that's why for left and right
# | 2 | use min, each
# E-----F -> max(0, min(xright1,xright2) - max(xleft1,xleft2) )
# Note that because of xleft<xright this can only
# result in xright1-xleft2 or xright2-xleft1
# All cases: 1 | +--+ | +--+ | +--+ | +--+ |
# 2 | +--+ | +--+ | +--+ | +--+ |
# overlap | 0 | 2 | 2 | 0 |
def overlap( x1,y1,w1,h1, x2,y2,w2,h2, x3=0,y3=0,w3=65535,h3=65535 ):
return max( 0, min(x1+w1,x2+w2,x3+w3) - max(x1,x2,x3) ) *
max( 0, min(y1+h1,y2+h2,y3+h3) - max(y1,y2,y3) )
x=[ ${coords[*]} ]
area=0
# Calculate overlap with window in question
# 0:x 1:y 2:w 3:h, border widths 0:left 1:right 2:top 3:bottom
for i in range( 4,len(x),4 ):
area += overlap( *( x[0:4]+x[i:i+4] ) )
# subtract double counted areas i.e. areas overlapping to the window
# of interest and two other windows on top ... This is n**2
for i in range( 4,len(x),4 ):
for j in range( i+4,len(x),4 ):
area -= overlap( *( x[0:4]+x[i:i+4]+x[j:j+4] ) )
print "area =",area
print "woi =",x[2]*x[3]
# exit code 0: if not fully covered, 1: if fully covered
exit( area < x[2]*x[3] )
EOF
exit $?
1
Beware that this doesn't take non-rectangular windows into account. Those are admittedly rare. Does this correctly report windows visible only on another desktop or entirely outside the screen area as invisible?
– Gilles
May 6 '16 at 0:50
1
I used your code to make it a Python module. Maybe you like to check it out: github.com/ikem-krueger/sleep-walker/blob/develop/src/usr/lib/…
– Ikem Krueger
Sep 15 '17 at 23:44
@user82110 Oh that's cool, thanks for the link!
– mxmlnkn
Sep 16 '17 at 22:18
Note that the "covered area" calculation here is not correct when there are more than three windows on top of the target window. See my solution here.
– user12173
28 mins ago
add a comment |
For completeness sake here is the naive / brute-force solution which I hoped is already implemented in some other utility. The fullyobscured
notify-event in @Gilles link in the comment sounds very promising, but I wasn't sure how to get it to work, and this solution was also quite fun to implement.
The script simply calculates the coverage area of all overlapping window subtracting double counted areas and checks if it is as large as the window area. Because it correctly includes the frame borders the code looks a bit more complex than it could. It returns exit code 0 if fully covered and 1 if not. It takes a window ID as an argument. E.g. call it with if xcovered 0x1a00003; then echo 'covered!'; fi
Not counting the comments, debug comments and error-checking it could be only 40 lines long, surely even less.
I actually wanted to use bc instead of python, but I couldn't find an easy way to transfer a bash array to a bc array.
#!/bin/bash
# Name: xcovered.sh
# Find out if C is completely or only partially covered by A or B
# +-----------+
# | +===+ |
# | | +-------+
# | C | | B |
# | | +-------+
# +---| A |---+
# +---+
# @return 0 if window ist not visible, 1 if visible
# Note: Only tested with three windows like in sketch above, but
# it should also work for an arbitrary amount of overlapping windwows
wid=$1
if ! xwininfo -id $wid -stats | 'grep' -q 'IsViewable'; then return 0; fi
# get all stacked window ids after and including the given wid
wids=($(xprop -root | 'sed' -nE "/_NET_CLIENT_LIST_STACKING(WINDOW)/{ s|.*($wid)|1|; s|,||g; p }"))
if [ ${#wids} -eq 0 ]; then
echo -e "e[31mCouldn't find specified window id $wid in _NET_CLIENT_LIST_STACKING(WINDOW)"'!'"e[0m"
return 2
fi
if [ ${#wids} -eq 1 ]; then return 0; fi
# Gather geometry of all windows in higher zorder / possibly lying on top
coords=(); frames=()
for owid in ${wids[@]}; do
#xwininfo -id $owid | grep xwininfo
if xwininfo -id $owid -stats | 'grep' -q 'IsViewable'; then
# _NET_WM_ICON_GEOMETRY doesn't exist for xfce4-panel, thereby making this more difficult
#coords=$(xprop -id $owid _NET_WM_ICON_GEOMETRY)
#frames=$(xprop -id $owid _NET_FRAME_EXTENTS)
x=($(xwininfo -id $owid -stats -wm | sed -nE '
s|^[ t]*Absolute upper-left X:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Absolute upper-left Y:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Width:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Height:[ t]*([0-9]+).*|1|Ip;
/Frame extents:/I{ s|^[ t}Frame Extents:[ t]*||I; s|,||g; p; };
' | sed ':a; N; $!b a; s/n/ /g '))
if [ ! ${#x[@]} -eq 8 ]; then
echo -e "e[31mSomething went wrong when parsing the output of 'xwininfo -id $owid -stats -wm':e[0m"
xwininfo -id $owid -stats -wm
exit 1
fi
# apply the frame width to the coordinates and window width
# 0:x 1:y 2:w 3:h, border widths 4:left 5:right 6:top 7:bottom
coords+=( "${x[0]}-${x[4]}, ${x[1]}-${x[6]}, ${x[2]}+${x[4]}+${x[5]}, ${x[3]}+${x[6]}+${x[7]}" )
fi
done
IFS=','; python - <<EOF #| python
# Calculates the area of the union of all overlapping areas. If that area
# is equal to the window of interest area / size, then the window is covered.
# Note that the calcualted area can't be larger than that!
# 1
# D---C => overlap given by H and B
# | H-|---G x-overlap: max(0, xleft2-xright1)
# A---B | -> '1' and '2' is not known, that's why for left and right
# | 2 | use min, each
# E-----F -> max(0, min(xright1,xright2) - max(xleft1,xleft2) )
# Note that because of xleft<xright this can only
# result in xright1-xleft2 or xright2-xleft1
# All cases: 1 | +--+ | +--+ | +--+ | +--+ |
# 2 | +--+ | +--+ | +--+ | +--+ |
# overlap | 0 | 2 | 2 | 0 |
def overlap( x1,y1,w1,h1, x2,y2,w2,h2, x3=0,y3=0,w3=65535,h3=65535 ):
return max( 0, min(x1+w1,x2+w2,x3+w3) - max(x1,x2,x3) ) *
max( 0, min(y1+h1,y2+h2,y3+h3) - max(y1,y2,y3) )
x=[ ${coords[*]} ]
area=0
# Calculate overlap with window in question
# 0:x 1:y 2:w 3:h, border widths 0:left 1:right 2:top 3:bottom
for i in range( 4,len(x),4 ):
area += overlap( *( x[0:4]+x[i:i+4] ) )
# subtract double counted areas i.e. areas overlapping to the window
# of interest and two other windows on top ... This is n**2
for i in range( 4,len(x),4 ):
for j in range( i+4,len(x),4 ):
area -= overlap( *( x[0:4]+x[i:i+4]+x[j:j+4] ) )
print "area =",area
print "woi =",x[2]*x[3]
# exit code 0: if not fully covered, 1: if fully covered
exit( area < x[2]*x[3] )
EOF
exit $?
For completeness sake here is the naive / brute-force solution which I hoped is already implemented in some other utility. The fullyobscured
notify-event in @Gilles link in the comment sounds very promising, but I wasn't sure how to get it to work, and this solution was also quite fun to implement.
The script simply calculates the coverage area of all overlapping window subtracting double counted areas and checks if it is as large as the window area. Because it correctly includes the frame borders the code looks a bit more complex than it could. It returns exit code 0 if fully covered and 1 if not. It takes a window ID as an argument. E.g. call it with if xcovered 0x1a00003; then echo 'covered!'; fi
Not counting the comments, debug comments and error-checking it could be only 40 lines long, surely even less.
I actually wanted to use bc instead of python, but I couldn't find an easy way to transfer a bash array to a bc array.
#!/bin/bash
# Name: xcovered.sh
# Find out if C is completely or only partially covered by A or B
# +-----------+
# | +===+ |
# | | +-------+
# | C | | B |
# | | +-------+
# +---| A |---+
# +---+
# @return 0 if window ist not visible, 1 if visible
# Note: Only tested with three windows like in sketch above, but
# it should also work for an arbitrary amount of overlapping windwows
wid=$1
if ! xwininfo -id $wid -stats | 'grep' -q 'IsViewable'; then return 0; fi
# get all stacked window ids after and including the given wid
wids=($(xprop -root | 'sed' -nE "/_NET_CLIENT_LIST_STACKING(WINDOW)/{ s|.*($wid)|1|; s|,||g; p }"))
if [ ${#wids} -eq 0 ]; then
echo -e "e[31mCouldn't find specified window id $wid in _NET_CLIENT_LIST_STACKING(WINDOW)"'!'"e[0m"
return 2
fi
if [ ${#wids} -eq 1 ]; then return 0; fi
# Gather geometry of all windows in higher zorder / possibly lying on top
coords=(); frames=()
for owid in ${wids[@]}; do
#xwininfo -id $owid | grep xwininfo
if xwininfo -id $owid -stats | 'grep' -q 'IsViewable'; then
# _NET_WM_ICON_GEOMETRY doesn't exist for xfce4-panel, thereby making this more difficult
#coords=$(xprop -id $owid _NET_WM_ICON_GEOMETRY)
#frames=$(xprop -id $owid _NET_FRAME_EXTENTS)
x=($(xwininfo -id $owid -stats -wm | sed -nE '
s|^[ t]*Absolute upper-left X:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Absolute upper-left Y:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Width:[ t]*([0-9]+).*|1|Ip;
s|^[ t]*Height:[ t]*([0-9]+).*|1|Ip;
/Frame extents:/I{ s|^[ t}Frame Extents:[ t]*||I; s|,||g; p; };
' | sed ':a; N; $!b a; s/n/ /g '))
if [ ! ${#x[@]} -eq 8 ]; then
echo -e "e[31mSomething went wrong when parsing the output of 'xwininfo -id $owid -stats -wm':e[0m"
xwininfo -id $owid -stats -wm
exit 1
fi
# apply the frame width to the coordinates and window width
# 0:x 1:y 2:w 3:h, border widths 4:left 5:right 6:top 7:bottom
coords+=( "${x[0]}-${x[4]}, ${x[1]}-${x[6]}, ${x[2]}+${x[4]}+${x[5]}, ${x[3]}+${x[6]}+${x[7]}" )
fi
done
IFS=','; python - <<EOF #| python
# Calculates the area of the union of all overlapping areas. If that area
# is equal to the window of interest area / size, then the window is covered.
# Note that the calcualted area can't be larger than that!
# 1
# D---C => overlap given by H and B
# | H-|---G x-overlap: max(0, xleft2-xright1)
# A---B | -> '1' and '2' is not known, that's why for left and right
# | 2 | use min, each
# E-----F -> max(0, min(xright1,xright2) - max(xleft1,xleft2) )
# Note that because of xleft<xright this can only
# result in xright1-xleft2 or xright2-xleft1
# All cases: 1 | +--+ | +--+ | +--+ | +--+ |
# 2 | +--+ | +--+ | +--+ | +--+ |
# overlap | 0 | 2 | 2 | 0 |
def overlap( x1,y1,w1,h1, x2,y2,w2,h2, x3=0,y3=0,w3=65535,h3=65535 ):
return max( 0, min(x1+w1,x2+w2,x3+w3) - max(x1,x2,x3) ) *
max( 0, min(y1+h1,y2+h2,y3+h3) - max(y1,y2,y3) )
x=[ ${coords[*]} ]
area=0
# Calculate overlap with window in question
# 0:x 1:y 2:w 3:h, border widths 0:left 1:right 2:top 3:bottom
for i in range( 4,len(x),4 ):
area += overlap( *( x[0:4]+x[i:i+4] ) )
# subtract double counted areas i.e. areas overlapping to the window
# of interest and two other windows on top ... This is n**2
for i in range( 4,len(x),4 ):
for j in range( i+4,len(x),4 ):
area -= overlap( *( x[0:4]+x[i:i+4]+x[j:j+4] ) )
print "area =",area
print "woi =",x[2]*x[3]
# exit code 0: if not fully covered, 1: if fully covered
exit( area < x[2]*x[3] )
EOF
exit $?
edited May 5 '16 at 22:41
answered May 5 '16 at 22:20
mxmlnknmxmlnkn
2411 silver badge9 bronze badges
2411 silver badge9 bronze badges
1
Beware that this doesn't take non-rectangular windows into account. Those are admittedly rare. Does this correctly report windows visible only on another desktop or entirely outside the screen area as invisible?
– Gilles
May 6 '16 at 0:50
1
I used your code to make it a Python module. Maybe you like to check it out: github.com/ikem-krueger/sleep-walker/blob/develop/src/usr/lib/…
– Ikem Krueger
Sep 15 '17 at 23:44
@user82110 Oh that's cool, thanks for the link!
– mxmlnkn
Sep 16 '17 at 22:18
Note that the "covered area" calculation here is not correct when there are more than three windows on top of the target window. See my solution here.
– user12173
28 mins ago
add a comment |
1
Beware that this doesn't take non-rectangular windows into account. Those are admittedly rare. Does this correctly report windows visible only on another desktop or entirely outside the screen area as invisible?
– Gilles
May 6 '16 at 0:50
1
I used your code to make it a Python module. Maybe you like to check it out: github.com/ikem-krueger/sleep-walker/blob/develop/src/usr/lib/…
– Ikem Krueger
Sep 15 '17 at 23:44
@user82110 Oh that's cool, thanks for the link!
– mxmlnkn
Sep 16 '17 at 22:18
Note that the "covered area" calculation here is not correct when there are more than three windows on top of the target window. See my solution here.
– user12173
28 mins ago
1
1
Beware that this doesn't take non-rectangular windows into account. Those are admittedly rare. Does this correctly report windows visible only on another desktop or entirely outside the screen area as invisible?
– Gilles
May 6 '16 at 0:50
Beware that this doesn't take non-rectangular windows into account. Those are admittedly rare. Does this correctly report windows visible only on another desktop or entirely outside the screen area as invisible?
– Gilles
May 6 '16 at 0:50
1
1
I used your code to make it a Python module. Maybe you like to check it out: github.com/ikem-krueger/sleep-walker/blob/develop/src/usr/lib/…
– Ikem Krueger
Sep 15 '17 at 23:44
I used your code to make it a Python module. Maybe you like to check it out: github.com/ikem-krueger/sleep-walker/blob/develop/src/usr/lib/…
– Ikem Krueger
Sep 15 '17 at 23:44
@user82110 Oh that's cool, thanks for the link!
– mxmlnkn
Sep 16 '17 at 22:18
@user82110 Oh that's cool, thanks for the link!
– mxmlnkn
Sep 16 '17 at 22:18
Note that the "covered area" calculation here is not correct when there are more than three windows on top of the target window. See my solution here.
– user12173
28 mins ago
Note that the "covered area" calculation here is not correct when there are more than three windows on top of the target window. See my solution here.
– user12173
28 mins ago
add a comment |
The answer given by @mxmlnkn is a great start, but unfortunately the methodology for calculating the covered area is not correct for when 3 or more windows are on top of the target window.
To see why, imagine that there are 3 windows (which we'll call X
, Y
, and Z
) on top of a target window T
); further suppose that all these windows have the same coordinates. Then the given solution would first add |X ∩ T|+|Y ∩ T|+|Z ∩ T|=3*windowArea
, and then subtract |(X U Y) ∩ T| + |(X U Z) ∩ T| + |(Y U Z) ∩ T| =3*windowArea
, resulting in a net area calculation of 0
. The error here is we are actually "double counting" the attempt to compensate for "double counting". To correct this, we add |X ∩ Y ∩ Z ∩ T|
. This notion is formalized with the Principle of Inclusion-Exclusion (see here).
The "covered area" can be defined as (forgive the haphazard mathematical notation, unix.stackexchange.com
does not allow LaTeX
)
(A_1 U A_2 U ... U A_n) ∩ B
where A_1, A_2, ..., A_n
are the windows that lie on top of the target window, and B
is the target window.
We can use the Principle of Inclusion-Exclusion to expand (A_1 U A_2 U ... U A_n)
. We can then distribute the intersection with B
across this result.
Concretely, this results in the following algorithm (C++):
bool windowIsVisible(Display *display, Window window, float threshold) {
// Indicates whether a window is fully covered
if (!windowIsViewable(display, window)) {
return false;
}
auto rootWindow = DefaultRootWindow(display);
auto coords = getWindowCoords(display, rootWindow, window);
if (coords.size() <= 1) {
return true;
}
float area = (coords[0][2]-coords[0][0]) * (coords[0][3]-coords[0][1]);
float coveredArea = 0;
auto selector = std::vector<bool>(coords.size()-1);
for (int i = 0; i < selector.size(); i++) {
std::fill(selector.begin(), selector.begin()+i+1, true);
std::fill(selector.begin()+i+1, selector.end(), false);
auto selectedWindows = std::vector<std::vector<int>>(i);
do {
selectedWindows.clear();
for (int j = 0; j < selector.size(); j++) {
if (selector[j]) selectedWindows.push_back(coords[j+1]);
}
selectedWindows.push_back(coords[0]);
coveredArea += pow(-1, i)*calculateWindowOverlap(selectedWindows);
} while (std::prev_permutation(selector.begin(), selector.end()));
}
float tol = 1e-4;
return (1 - ((float)coveredArea)/((float)area) + tol) >= threshold;
}
int calculateWindowOverlap(std::vector<std::vector<int>> windowCoords) {
if (windowCoords.size() == 0) {
return 0;
}
std::vector<int> intersect = windowCoords[0];
for (int i = 1; i < windowCoords.size(); i++) {
intersect[0] = std::max(intersect[0], windowCoords[i][0]);
intersect[1] = std::max(intersect[1], windowCoords[i][1]);
intersect[2] = std::min(intersect[2], windowCoords[i][2]);
intersect[3] = std::min(intersect[3], windowCoords[i][3]);
}
return std::max(0, intersect[2]-intersect[0]) *
std::max(0, intersect[3]-intersect[1]);
}
std::vector<std::vector<int>> getWindowCoords(Display *display,
Window queryWindow, Window targetWindow,
bool *reachedTargetPtr = nullptr, int absX = 0, int absY = 0) {
// Gather geometry of all windows in higher zorder
std::vector<std::vector<int>> coords = {};
bool reachedTarget = false;
if (!reachedTargetPtr) {
reachedTargetPtr = &reachedTarget;
}
Window rWindow;
Window parentWindow;
Window *childrenWindows;
unsigned int numChildren;
XQueryTree(display, queryWindow, &rWindow, &parentWindow,
&childrenWindows, &numChildren);
for (int i = 0; i < numChildren; i++) {
if (childrenWindows[i] == targetWindow) {
*reachedTargetPtr = true;
}
XWindowAttributes windowAttributes;
XGetWindowAttributes(display, childrenWindows[i], &windowAttributes);
if (*reachedTargetPtr && windowAttributes.map_state == IsViewable &&
windowAttributes.c_class != InputOnly) {
coords.push_back(std::vector<int> {
windowAttributes.x + absX,
windowAttributes.y + absY,
windowAttributes.x + absX + windowAttributes.width,
windowAttributes.y + absY + windowAttributes.height });
}
if (childrenWindows[i] != targetWindow) {
auto childCoords = getWindowCoords(display, childrenWindows[i],
targetWindow, reachedTargetPtr, absX + windowAttributes.x,
absY + windowAttributes.y);
coords.reserve(coords.size() + childCoords.size());
coords.insert(coords.end(), childCoords.begin(), childCoords.end());
}
}
return coords;
}
Basically, for k=1,2,...,n
, we find all combinations of n choose k
. We then calculate the area of the intersections of these windows, along with the target window, and add/subtract that result from the running area (in accordance with the (-1)^(k-1)
term from the Principle of Inclusion-Exclusion.
I have implemented this in a simple tool I made here. Also, this is essentially an extension of the Rectangle Area II problem from leetcode. There are some more efficient ways to do this (check the solutions section), but I personally found that the mathematically intuitive way achieved adequate performance.
New contributor
add a comment |
The answer given by @mxmlnkn is a great start, but unfortunately the methodology for calculating the covered area is not correct for when 3 or more windows are on top of the target window.
To see why, imagine that there are 3 windows (which we'll call X
, Y
, and Z
) on top of a target window T
); further suppose that all these windows have the same coordinates. Then the given solution would first add |X ∩ T|+|Y ∩ T|+|Z ∩ T|=3*windowArea
, and then subtract |(X U Y) ∩ T| + |(X U Z) ∩ T| + |(Y U Z) ∩ T| =3*windowArea
, resulting in a net area calculation of 0
. The error here is we are actually "double counting" the attempt to compensate for "double counting". To correct this, we add |X ∩ Y ∩ Z ∩ T|
. This notion is formalized with the Principle of Inclusion-Exclusion (see here).
The "covered area" can be defined as (forgive the haphazard mathematical notation, unix.stackexchange.com
does not allow LaTeX
)
(A_1 U A_2 U ... U A_n) ∩ B
where A_1, A_2, ..., A_n
are the windows that lie on top of the target window, and B
is the target window.
We can use the Principle of Inclusion-Exclusion to expand (A_1 U A_2 U ... U A_n)
. We can then distribute the intersection with B
across this result.
Concretely, this results in the following algorithm (C++):
bool windowIsVisible(Display *display, Window window, float threshold) {
// Indicates whether a window is fully covered
if (!windowIsViewable(display, window)) {
return false;
}
auto rootWindow = DefaultRootWindow(display);
auto coords = getWindowCoords(display, rootWindow, window);
if (coords.size() <= 1) {
return true;
}
float area = (coords[0][2]-coords[0][0]) * (coords[0][3]-coords[0][1]);
float coveredArea = 0;
auto selector = std::vector<bool>(coords.size()-1);
for (int i = 0; i < selector.size(); i++) {
std::fill(selector.begin(), selector.begin()+i+1, true);
std::fill(selector.begin()+i+1, selector.end(), false);
auto selectedWindows = std::vector<std::vector<int>>(i);
do {
selectedWindows.clear();
for (int j = 0; j < selector.size(); j++) {
if (selector[j]) selectedWindows.push_back(coords[j+1]);
}
selectedWindows.push_back(coords[0]);
coveredArea += pow(-1, i)*calculateWindowOverlap(selectedWindows);
} while (std::prev_permutation(selector.begin(), selector.end()));
}
float tol = 1e-4;
return (1 - ((float)coveredArea)/((float)area) + tol) >= threshold;
}
int calculateWindowOverlap(std::vector<std::vector<int>> windowCoords) {
if (windowCoords.size() == 0) {
return 0;
}
std::vector<int> intersect = windowCoords[0];
for (int i = 1; i < windowCoords.size(); i++) {
intersect[0] = std::max(intersect[0], windowCoords[i][0]);
intersect[1] = std::max(intersect[1], windowCoords[i][1]);
intersect[2] = std::min(intersect[2], windowCoords[i][2]);
intersect[3] = std::min(intersect[3], windowCoords[i][3]);
}
return std::max(0, intersect[2]-intersect[0]) *
std::max(0, intersect[3]-intersect[1]);
}
std::vector<std::vector<int>> getWindowCoords(Display *display,
Window queryWindow, Window targetWindow,
bool *reachedTargetPtr = nullptr, int absX = 0, int absY = 0) {
// Gather geometry of all windows in higher zorder
std::vector<std::vector<int>> coords = {};
bool reachedTarget = false;
if (!reachedTargetPtr) {
reachedTargetPtr = &reachedTarget;
}
Window rWindow;
Window parentWindow;
Window *childrenWindows;
unsigned int numChildren;
XQueryTree(display, queryWindow, &rWindow, &parentWindow,
&childrenWindows, &numChildren);
for (int i = 0; i < numChildren; i++) {
if (childrenWindows[i] == targetWindow) {
*reachedTargetPtr = true;
}
XWindowAttributes windowAttributes;
XGetWindowAttributes(display, childrenWindows[i], &windowAttributes);
if (*reachedTargetPtr && windowAttributes.map_state == IsViewable &&
windowAttributes.c_class != InputOnly) {
coords.push_back(std::vector<int> {
windowAttributes.x + absX,
windowAttributes.y + absY,
windowAttributes.x + absX + windowAttributes.width,
windowAttributes.y + absY + windowAttributes.height });
}
if (childrenWindows[i] != targetWindow) {
auto childCoords = getWindowCoords(display, childrenWindows[i],
targetWindow, reachedTargetPtr, absX + windowAttributes.x,
absY + windowAttributes.y);
coords.reserve(coords.size() + childCoords.size());
coords.insert(coords.end(), childCoords.begin(), childCoords.end());
}
}
return coords;
}
Basically, for k=1,2,...,n
, we find all combinations of n choose k
. We then calculate the area of the intersections of these windows, along with the target window, and add/subtract that result from the running area (in accordance with the (-1)^(k-1)
term from the Principle of Inclusion-Exclusion.
I have implemented this in a simple tool I made here. Also, this is essentially an extension of the Rectangle Area II problem from leetcode. There are some more efficient ways to do this (check the solutions section), but I personally found that the mathematically intuitive way achieved adequate performance.
New contributor
add a comment |
The answer given by @mxmlnkn is a great start, but unfortunately the methodology for calculating the covered area is not correct for when 3 or more windows are on top of the target window.
To see why, imagine that there are 3 windows (which we'll call X
, Y
, and Z
) on top of a target window T
); further suppose that all these windows have the same coordinates. Then the given solution would first add |X ∩ T|+|Y ∩ T|+|Z ∩ T|=3*windowArea
, and then subtract |(X U Y) ∩ T| + |(X U Z) ∩ T| + |(Y U Z) ∩ T| =3*windowArea
, resulting in a net area calculation of 0
. The error here is we are actually "double counting" the attempt to compensate for "double counting". To correct this, we add |X ∩ Y ∩ Z ∩ T|
. This notion is formalized with the Principle of Inclusion-Exclusion (see here).
The "covered area" can be defined as (forgive the haphazard mathematical notation, unix.stackexchange.com
does not allow LaTeX
)
(A_1 U A_2 U ... U A_n) ∩ B
where A_1, A_2, ..., A_n
are the windows that lie on top of the target window, and B
is the target window.
We can use the Principle of Inclusion-Exclusion to expand (A_1 U A_2 U ... U A_n)
. We can then distribute the intersection with B
across this result.
Concretely, this results in the following algorithm (C++):
bool windowIsVisible(Display *display, Window window, float threshold) {
// Indicates whether a window is fully covered
if (!windowIsViewable(display, window)) {
return false;
}
auto rootWindow = DefaultRootWindow(display);
auto coords = getWindowCoords(display, rootWindow, window);
if (coords.size() <= 1) {
return true;
}
float area = (coords[0][2]-coords[0][0]) * (coords[0][3]-coords[0][1]);
float coveredArea = 0;
auto selector = std::vector<bool>(coords.size()-1);
for (int i = 0; i < selector.size(); i++) {
std::fill(selector.begin(), selector.begin()+i+1, true);
std::fill(selector.begin()+i+1, selector.end(), false);
auto selectedWindows = std::vector<std::vector<int>>(i);
do {
selectedWindows.clear();
for (int j = 0; j < selector.size(); j++) {
if (selector[j]) selectedWindows.push_back(coords[j+1]);
}
selectedWindows.push_back(coords[0]);
coveredArea += pow(-1, i)*calculateWindowOverlap(selectedWindows);
} while (std::prev_permutation(selector.begin(), selector.end()));
}
float tol = 1e-4;
return (1 - ((float)coveredArea)/((float)area) + tol) >= threshold;
}
int calculateWindowOverlap(std::vector<std::vector<int>> windowCoords) {
if (windowCoords.size() == 0) {
return 0;
}
std::vector<int> intersect = windowCoords[0];
for (int i = 1; i < windowCoords.size(); i++) {
intersect[0] = std::max(intersect[0], windowCoords[i][0]);
intersect[1] = std::max(intersect[1], windowCoords[i][1]);
intersect[2] = std::min(intersect[2], windowCoords[i][2]);
intersect[3] = std::min(intersect[3], windowCoords[i][3]);
}
return std::max(0, intersect[2]-intersect[0]) *
std::max(0, intersect[3]-intersect[1]);
}
std::vector<std::vector<int>> getWindowCoords(Display *display,
Window queryWindow, Window targetWindow,
bool *reachedTargetPtr = nullptr, int absX = 0, int absY = 0) {
// Gather geometry of all windows in higher zorder
std::vector<std::vector<int>> coords = {};
bool reachedTarget = false;
if (!reachedTargetPtr) {
reachedTargetPtr = &reachedTarget;
}
Window rWindow;
Window parentWindow;
Window *childrenWindows;
unsigned int numChildren;
XQueryTree(display, queryWindow, &rWindow, &parentWindow,
&childrenWindows, &numChildren);
for (int i = 0; i < numChildren; i++) {
if (childrenWindows[i] == targetWindow) {
*reachedTargetPtr = true;
}
XWindowAttributes windowAttributes;
XGetWindowAttributes(display, childrenWindows[i], &windowAttributes);
if (*reachedTargetPtr && windowAttributes.map_state == IsViewable &&
windowAttributes.c_class != InputOnly) {
coords.push_back(std::vector<int> {
windowAttributes.x + absX,
windowAttributes.y + absY,
windowAttributes.x + absX + windowAttributes.width,
windowAttributes.y + absY + windowAttributes.height });
}
if (childrenWindows[i] != targetWindow) {
auto childCoords = getWindowCoords(display, childrenWindows[i],
targetWindow, reachedTargetPtr, absX + windowAttributes.x,
absY + windowAttributes.y);
coords.reserve(coords.size() + childCoords.size());
coords.insert(coords.end(), childCoords.begin(), childCoords.end());
}
}
return coords;
}
Basically, for k=1,2,...,n
, we find all combinations of n choose k
. We then calculate the area of the intersections of these windows, along with the target window, and add/subtract that result from the running area (in accordance with the (-1)^(k-1)
term from the Principle of Inclusion-Exclusion.
I have implemented this in a simple tool I made here. Also, this is essentially an extension of the Rectangle Area II problem from leetcode. There are some more efficient ways to do this (check the solutions section), but I personally found that the mathematically intuitive way achieved adequate performance.
New contributor
The answer given by @mxmlnkn is a great start, but unfortunately the methodology for calculating the covered area is not correct for when 3 or more windows are on top of the target window.
To see why, imagine that there are 3 windows (which we'll call X
, Y
, and Z
) on top of a target window T
); further suppose that all these windows have the same coordinates. Then the given solution would first add |X ∩ T|+|Y ∩ T|+|Z ∩ T|=3*windowArea
, and then subtract |(X U Y) ∩ T| + |(X U Z) ∩ T| + |(Y U Z) ∩ T| =3*windowArea
, resulting in a net area calculation of 0
. The error here is we are actually "double counting" the attempt to compensate for "double counting". To correct this, we add |X ∩ Y ∩ Z ∩ T|
. This notion is formalized with the Principle of Inclusion-Exclusion (see here).
The "covered area" can be defined as (forgive the haphazard mathematical notation, unix.stackexchange.com
does not allow LaTeX
)
(A_1 U A_2 U ... U A_n) ∩ B
where A_1, A_2, ..., A_n
are the windows that lie on top of the target window, and B
is the target window.
We can use the Principle of Inclusion-Exclusion to expand (A_1 U A_2 U ... U A_n)
. We can then distribute the intersection with B
across this result.
Concretely, this results in the following algorithm (C++):
bool windowIsVisible(Display *display, Window window, float threshold) {
// Indicates whether a window is fully covered
if (!windowIsViewable(display, window)) {
return false;
}
auto rootWindow = DefaultRootWindow(display);
auto coords = getWindowCoords(display, rootWindow, window);
if (coords.size() <= 1) {
return true;
}
float area = (coords[0][2]-coords[0][0]) * (coords[0][3]-coords[0][1]);
float coveredArea = 0;
auto selector = std::vector<bool>(coords.size()-1);
for (int i = 0; i < selector.size(); i++) {
std::fill(selector.begin(), selector.begin()+i+1, true);
std::fill(selector.begin()+i+1, selector.end(), false);
auto selectedWindows = std::vector<std::vector<int>>(i);
do {
selectedWindows.clear();
for (int j = 0; j < selector.size(); j++) {
if (selector[j]) selectedWindows.push_back(coords[j+1]);
}
selectedWindows.push_back(coords[0]);
coveredArea += pow(-1, i)*calculateWindowOverlap(selectedWindows);
} while (std::prev_permutation(selector.begin(), selector.end()));
}
float tol = 1e-4;
return (1 - ((float)coveredArea)/((float)area) + tol) >= threshold;
}
int calculateWindowOverlap(std::vector<std::vector<int>> windowCoords) {
if (windowCoords.size() == 0) {
return 0;
}
std::vector<int> intersect = windowCoords[0];
for (int i = 1; i < windowCoords.size(); i++) {
intersect[0] = std::max(intersect[0], windowCoords[i][0]);
intersect[1] = std::max(intersect[1], windowCoords[i][1]);
intersect[2] = std::min(intersect[2], windowCoords[i][2]);
intersect[3] = std::min(intersect[3], windowCoords[i][3]);
}
return std::max(0, intersect[2]-intersect[0]) *
std::max(0, intersect[3]-intersect[1]);
}
std::vector<std::vector<int>> getWindowCoords(Display *display,
Window queryWindow, Window targetWindow,
bool *reachedTargetPtr = nullptr, int absX = 0, int absY = 0) {
// Gather geometry of all windows in higher zorder
std::vector<std::vector<int>> coords = {};
bool reachedTarget = false;
if (!reachedTargetPtr) {
reachedTargetPtr = &reachedTarget;
}
Window rWindow;
Window parentWindow;
Window *childrenWindows;
unsigned int numChildren;
XQueryTree(display, queryWindow, &rWindow, &parentWindow,
&childrenWindows, &numChildren);
for (int i = 0; i < numChildren; i++) {
if (childrenWindows[i] == targetWindow) {
*reachedTargetPtr = true;
}
XWindowAttributes windowAttributes;
XGetWindowAttributes(display, childrenWindows[i], &windowAttributes);
if (*reachedTargetPtr && windowAttributes.map_state == IsViewable &&
windowAttributes.c_class != InputOnly) {
coords.push_back(std::vector<int> {
windowAttributes.x + absX,
windowAttributes.y + absY,
windowAttributes.x + absX + windowAttributes.width,
windowAttributes.y + absY + windowAttributes.height });
}
if (childrenWindows[i] != targetWindow) {
auto childCoords = getWindowCoords(display, childrenWindows[i],
targetWindow, reachedTargetPtr, absX + windowAttributes.x,
absY + windowAttributes.y);
coords.reserve(coords.size() + childCoords.size());
coords.insert(coords.end(), childCoords.begin(), childCoords.end());
}
}
return coords;
}
Basically, for k=1,2,...,n
, we find all combinations of n choose k
. We then calculate the area of the intersections of these windows, along with the target window, and add/subtract that result from the running area (in accordance with the (-1)^(k-1)
term from the Principle of Inclusion-Exclusion.
I have implemented this in a simple tool I made here. Also, this is essentially an extension of the Rectangle Area II problem from leetcode. There are some more efficient ways to do this (check the solutions section), but I personally found that the mathematically intuitive way achieved adequate performance.
New contributor
edited 19 mins ago
New contributor
answered 30 mins ago
user12173user12173
1012 bronze badges
1012 bronze badges
New contributor
New contributor
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f281168%2fx-find-out-if-a-window-is-visible-to-the-user-i-e-not-covered-by-others%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Computing the visible area of a partly hidden window may help.
– Gilles
May 5 '16 at 0:51