ウィンドウがユーザーに表示されない場合にのみ作業を実行したいと思います。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
呼びます)。さらに、これらのウィンドウはすべて同じ座標を持っていると仮定します。次に、与えられた解を最初に加算し、減算して正味面積を計算します。ここでの間違いは、「二重計算」を補償するために実際に「二重計算」をしているということです。これを修正するために、この概念を包含排除原則として定式化します(参照。Z
T
|X ∩ T|+|Y ∩ T|+|Z ∩ T|=3*windowArea
|(X U Y) ∩ T| + |(X U Z) ∩ T| + |(Y U Z) ∩ T| =3*windowArea
0
|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の質問です。これを行うためのより効率的な方法がありますが(ソリューションセクションを確認してください)、個人的には数学的に直感的なアプローチが適切なパフォーマンスを達成すると思います。