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;
}







4















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.










share|improve this question






















  • 1





    Computing the visible area of a partly hidden window may help.

    – Gilles
    May 5 '16 at 0:51


















4















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.










share|improve this question






















  • 1





    Computing the visible area of a partly hidden window may help.

    – Gilles
    May 5 '16 at 0:51














4












4








4


1






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.










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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














  • 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










2 Answers
2






active

oldest

votes


















4
















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 $?





share|improve this answer























  • 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



















0
















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.






share|improve this answer










New contributor



user12173 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
























    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
    });


    }
    });















    draft saved

    draft discarded
















    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









    4
















    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 $?





    share|improve this answer























    • 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
















    4
















    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 $?





    share|improve this answer























    • 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














    4














    4










    4









    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 $?





    share|improve this answer















    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 $?






    share|improve this answer














    share|improve this answer



    share|improve this answer








    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














    • 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













    0
















    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.






    share|improve this answer










    New contributor



    user12173 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.


























      0
















      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.






      share|improve this answer










      New contributor



      user12173 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.
























        0














        0










        0









        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.






        share|improve this answer










        New contributor



        user12173 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        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.







        share|improve this answer










        New contributor



        user12173 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.








        share|improve this answer



        share|improve this answer








        edited 19 mins ago





















        New contributor



        user12173 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.








        answered 30 mins ago









        user12173user12173

        1012 bronze badges




        1012 bronze badges




        New contributor



        user12173 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.




        New contributor




        user12173 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.




































            draft saved

            draft discarded



















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            Taj Mahal Inhaltsverzeichnis Aufbau | Geschichte | 350-Jahr-Feier | Heutige Bedeutung | Siehe auch |...

            Baia Sprie Cuprins Etimologie | Istorie | Demografie | Politică și administrație | Arii naturale...

            Nicolae Petrescu-Găină Cuprins Biografie | Opera | In memoriam | Varia | Controverse, incertitudini...