-
-
Save oglops/1256e301de34a8e8da5b80c6d058e920 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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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