X: 창이 사용자에게 보이는지 확인합니다. 즉, 다른 창에 가려지지 않습니다.

X: 창이 사용자에게 보이는지 확인합니다. 즉, 다른 창에 가려지지 않습니다.

창이 사용자에게 보이지 않을 때만 작업을 수행하고 싶습니다. xdotool여기에는 최소화된 창뿐만 아니라 다른 창에 의해 100% 가려지는 창도 포함됩니다(적어도 투명도가 사용되지 않는 경우). 투명성 문제를 무시하고 이를 수행하는 쉬운 방법이 있습니까?

xdotool옵션 이 있지만 --onlyvisible가려진 창은 포함되지 않고 최소화된 창만 포함됩니다. 물론 보이는 모든 창을 반복하여 창 형상을 가져오고 관심 창의 크기를 계산하는 옵션이 있지만 Bash에서 이 작업을 수행하는 것보다 더 간단하고 빠른 솔루션을 정말로 바랐습니다.

여기 하나 있어요좋아요문제에 대한 설명은 단지 창만 나열하는 것일 뿐이며 Max OS X에서도 작동합니다.이것질문에는 힌트 답변만 있지만 표시되는 모든 창과 해당 z 순서를 나열하고 표시 영역을 수동으로 계산하여 이를 수행하는 방법을 보여주지 않습니다.

답변1

완전성을 기하기 위해 여기에 순진한/무차별적인 솔루션이 있습니다. 다른 유틸리티에서 이미 구현되었으면 좋겠습니다. 댓글에 있는 @Gilles 링크의 알림 이벤트가 fullyobscured유망해 보이지만 어떻게 작동하게 해야 할지 잘 모르겠고 이 솔루션을 구현하면 흥미로울 것입니다.

스크립트는 단순히 겹치는 모든 창의 덮힌 영역에서 이중 계산 영역을 뺀 영역을 계산하고 창 영역만큼 큰지 확인합니다. 프레임 테두리가 올바르게 포함되어 있기 때문에 코드가 실제보다 조금 더 복잡해 보입니다. 완전히 덮어쓰면 종료 코드 0을 반환하고, 그렇지 않으면 종료 코드 1을 반환합니다. 창 ID를 매개변수로 사용합니다. 예를 들어if xcovered 0x1a00003; then echo 'covered!'; fi

주석, 디버깅 주석 및 오류 검사를 포함하지 않으면 길이는 아마도 40줄 또는 그 미만일 것입니다. 실제로 Python 대신 bc를 사용하고 싶지만 bash 배열을 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)|\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 $?

답변2

@mxmlnkn의 답변은 좋은 시작이지만 안타깝게도 대상 창 위에 창이 3개 이상 있을 경우 적용 범위를 계산하는 방법이 올바르지 않습니다.

이유를 알아보기 위해 대상 창 위에 3개의 창이 있다고 가정합니다(이들을 X, Y및 이라고 부르겠습니다). 또한 이러한 창은 모두 동일한 좌표를 가지고 있다고 가정합니다. 그런 다음 주어진 솔루션을 먼저 더한 다음 빼서 순 면적을 계산합니다. 여기서 실수는 "이중 계산"을 보상하기 위해 실제로 "이중 계산"을 하고 있다는 것입니다. 이를 수정하기 위해 이 개념을 포함-배제 원칙으로 공식화합니다(참조.ZT|X ∩ T|+|Y ∩ T|+|Z ∩ T|=3*windowArea|(X U Y) ∩ T| + |(X U Z) ∩ T| + |(Y U Z) ∩ T| =3*windowArea0|X ∩ Y ∩ Z ∩ T|여기).

"적용 범위"는 다음과 같이 정의할 수 있습니다(임의의 수학 표기법은 제외, unix.stackexchange.com허용되지 않음 LaTeX).

(A_1 U A_2 U ... U A_n) ∩ B

A_1, A_2, ..., A_n는 대상 창의 상단에 있는 창 이고 B는 대상 창입니다.

포함-배제 원칙을 사용하여 이를 확장할 수 있습니다 (A_1 U A_2 U ... U A_n). 그런 다음 이 결과에 대한 교차점을 배포할 수 있습니다 B.

구체적으로 다음 알고리즘이 생성됩니다(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;
}

기본적으로 k=1,2,...,n우리가 찾은 모든 조합 에 대해 n choose k. 그런 다음 이러한 창과 대상 창의 교차 영역을 계산하고 ( (-1)^(k-1)포함-배제 원칙의 용어에 따라) 실행 영역의 결과를 더하거나 뺍니다.

나는 이것을 내가 만든 간단한 도구로 구현했습니다.여기. 게다가 이는 본질적으로직사각형 영역 2Leetcode님의 질문입니다. 이를 수행하는 좀 더 효율적인 방법이 있지만(솔루션 섹션 확인) 개인적으로는 수학적으로 직관적인 접근 방식이 적절한 성능을 달성한다고 생각합니다.

관련 정보