X: Descubra se uma janela está visível para o usuário, ou seja, não coberta por outras pessoas

5

Eu só quero executar uma determinada ação com xdotool se a janela não estiver visível para o usuário, isso inclui janelas minimizadas, mas também janelas que são 100% cobertas por outras janelas (se nenhuma transparência for usada pelo menos) . Ignorando a questão da transparência, existe uma maneira simples de fazer isso?

xdotool tem uma opção --onlyvisible , mas isso inclui apenas janelas minimizadas, não janelas cobertas. É claro que há a opção de percorrer todas as janelas visíveis, obter suas geometrias de janelas e calcular quanto da janela de interesse elas cobrem, mas espero que haja uma solução mais fácil e rápida do que fazer isso no bash.

Aqui está uma ilustração agradável do problema, mas lá é apenas sobre a listagem de janelas, também é para o Max OS X. Esta pergunta só tem uma resposta que sugere, mas não mostra como fazer isso listando todas as janelas visíveis com suas respectivas ordens z e calculando a área visível manualmente .

    
por mxmlnkn 05.05.2016 / 02:22

1 resposta

3

Para completar, aqui está a solução ingênua / força bruta que eu esperava que já estivesse implementada em alguma outra utilidade. O fullyobscured notify-event no link @Gilles no comentário parece muito promissor, mas eu não sabia ao certo como fazê-lo funcionar, e essa solução também foi muito divertida de implementar.

O script simplesmente calcula a área de cobertura de todas as janelas sobrepostas subtraindo áreas contadas duplas e verifica se é tão grande quanto a área da janela. Por incluir corretamente as bordas dos quadros, o código parece um pouco mais complexo do que poderia. Retorna o código de saída 0 se totalmente coberto e 1 se não. Leva uma identificação de janela como um argumento. Por exemplo. chame-o com if xcovered 0x1a00003; then echo 'covered!'; fi

Sem contar os comentários, os comentários de depuração e a verificação de erros podem ter apenas 40 linhas, com certeza menos ainda. Na verdade, eu queria usar bc em vez de python, mas não consegui encontrar uma maneira fácil de transferir um array bash para um array bc.

#!/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)||; 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]+).*||Ip;
            s|^[ \t]*Absolute upper-left Y:[ \t]*([0-9]+).*||Ip;
            s|^[ \t]*Width:[ \t]*([0-9]+).*||Ip;
            s|^[ \t]*Height:[ \t]*([0-9]+).*||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 $?
    
por 06.05.2016 / 00:20

Tags