Skip to content

Instantly share code, notes, and snippets.

@ShadowKyogre
Last active April 22, 2017 20:09
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save ShadowKyogre/00997f67205af2675a84 to your computer and use it in GitHub Desktop.
Select multiple windows with slop. Needs slop, xwininfo, and xprop. Edit batch_keys.sh if you want to pass different params to the slop wrapper.
#!/bin/bash
happened=1
for window in $(recsel_windows.py -l -c 0,0.5,1,0.6)
do
happened=0
xdotool windowactivate --sync $window key "$1"
done
if [ $happened -a -n "$2" ];then
xdotool key "$2"
fi
#!/usr/bin/env python3
import subprocess
import re
import sys
VIEWPORT_SHAPE = re.compile(r"^\s+(?:Width|Height):\s+(\d+)", re.MULTILINE)
GEOM = re.compile(r"^\s+(?:Width|Height|Absolute upper-left [XY]):\s+([0-9-]+)", re.MULTILINE)
WIN_DOCK_DESKTOP = re.compile(r"_NET_WM_WINDOW_TYPE_DOCK|_NET_WM_WINDOW_TYPE_DESKTOP")
WIN_HIDDEN = re.compile(r"_NET_WM_STATE_HIDDEN")
WID_AT_POINT=re.compile(r"(?:window:)(\d+)")
#http://unix.stackexchange.com/questions/179743/xdotool-how-to-get-window-id-given-its-x-and-y
def window_at_point(x, y):
window_at_point = subprocess.check_output(["xdotool", "mousemove", x, y, "getmouselocation", "mousemove", "restore"]).decode('utf-8')
window_at_point = WID_AT_POINT.findall(window_at_point)[0]
return int(window_at_point)
def rec_contains(select_rec, cur_rec):
return cur_rec[2] <= select_rec[2] and cur_rec[0] >= select_rec[0] \
and cur_rec[1] >= select_rec[1] and cur_rec[3] <= select_rec[3]
def rec_overlap(select_rec, cur_rec):
return select_rec[0] <= cur_rec[2] and select_rec[2] >= cur_rec[0] \
and select_rec[1] <= cur_rec[3] and select_rec[3] >= cur_rec[1]
def rec_overlap_area(select_rec, cur_rec):
if rec_contains(select_rec, cur_rec):
return cur_rec
res = [-1, -1, -1, -1]
#trim left edge
if select_rec[0] >= cur_rec[0]:
res[0] = select_rec[0]
else:
res[0] = cur_rec[0]
#trim top edge
if select_rec[1] >= cur_rec[1]:
res[1] = select_rec[1]
else:
res[1] = cur_rec[1]
#trim right edge
if select_rec[2] <= cur_rec[2]:
res[2] = select_rec[2]
else:
res[2] = cur_rec[2]
#trim bottom edge
if select_rec[3] <= cur_rec[3]:
res[3] = select_rec[3]
else:
res[3] = cur_rec[3]
return res
viewport = VIEWPORT_SHAPE.findall(subprocess.check_output(["xwininfo", "-root", "-shape"]).decode('utf-8'))
viewport_dims = (0, 0, int(viewport[0]), int(viewport[1]))
try:
args = ["slop"]
args.extend(sys.argv[1:])
args.extend(("-f", "%x %y %w %h"))
rectangle_sel = subprocess.check_output(args).decode('utf-8').split()
except subprocess.CalledProcessError as e:
exit(0)
rectangle = [int(rectangle_sel[0]), int(rectangle_sel[1])]
rectangle.append(rectangle[0]+int(rectangle_sel[2]))
rectangle.append(rectangle[1]+int(rectangle_sel[3]))
visible_windows = subprocess.check_output(["xprop", "-root", "_NET_CLIENT_LIST_STACKING"]).decode('utf-8')
visible_windows = visible_windows.replace("_NET_CLIENT_LIST_STACKING(WINDOW): window id # ", "").split(", ")
visible_windows = [int(vw, 16) for vw in visible_windows]
# filter out all windows that have negative offsets or are docks
geom_cache = {}
for window in visible_windows[:]:
strwindow = str(window)
wintype = subprocess.check_output(["xprop", "-id", strwindow, "_NET_WM_WINDOW_TYPE"]).decode('utf-8')
wintype = wintype.replace("_NET_WM_WINDOW_TYPE(ATOM) = ", "")
winstate = subprocess.check_output(["xprop", "-id", strwindow, "_NET_WM_STATE"]).decode('utf-8')
winstate = winstate.replace("_NET_WM_STATE(ATOM) = ", "")
if WIN_DOCK_DESKTOP.search(wintype):
visible_windows.remove(window)
continue
if WIN_HIDDEN.search(winstate):
visible_windows.remove(window)
continue
geominfo = GEOM.findall(subprocess.check_output(["xwininfo", "-id", strwindow, "-shape"]).decode('utf-8'))
geominfo[0] = int(geominfo[0])
geominfo[1] = int(geominfo[1])
geominfo[2] = geominfo[0]+int(geominfo[2])
geominfo[3] = geominfo[1]+int(geominfo[3])
#print(geominfo, window, subprocess.check_output(["xprop", "-id", window, "WM_CLASS"]).decode('utf-8'))
if not rec_overlap(viewport_dims, geominfo):
visible_windows.remove(window)
continue
if not rec_overlap(rectangle, geominfo):
visible_windows.remove(window)
continue
#print(geominfo, '&', rectangle, '->', rec_overlap_area(rectangle, geominfo))
geom_cache[window] = rec_overlap_area(rectangle, geominfo)
vis_cpy = visible_windows[:]
for window in vis_cpy:
# windows beyond this are in higher zorder
mid_x = str(int((geom_cache[window][0]+geom_cache[window][2])/2))
mid_y = str(int((geom_cache[window][1]+geom_cache[window][3])/2))
win_at_north = window_at_point(mid_x, str(geom_cache[window][1]))
win_at_south = window_at_point(mid_x, str(geom_cache[window][3]))
win_at_west = window_at_point(str(geom_cache[window][0]), mid_y)
win_at_east = window_at_point(str(geom_cache[window][2]), mid_y)
win_at_center = window_at_point(mid_x, mid_y)
true_wins_at_points = {win_at_north, win_at_south, win_at_west, win_at_east, win_at_center}
#print(true_wins_at_points)
for other_win in true_wins_at_points.copy():
if other_win == window or other_win not in geom_cache:
continue
if rec_contains(geom_cache[window], geom_cache[other_win]):
true_wins_at_points.remove(other_win)
true_wins_at_points.add(window)
if window not in true_wins_at_points:
#print("Popping", window, true_wins_at_points)
visible_windows.remove(window)
"""
for other_window in vis_cpy[i+1:]:
#print(rec_contains(geom_cache[other_window], geom_cache[window]), other_window, window)
if rec_contains(geom_cache[other_window], geom_cache[window]):
visible_windows.remove(window)
break
"""
# the earlier the window, the further behind it is in stacking order
print('\n'.join(str(x) for x in visible_windows))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment