Created
February 20, 2021 11:36
-
-
Save khalid151/1c9450b5554b641504db5d949fda5964 to your computer and use it in GitHub Desktop.
Wrapper around `xdotool` and `xsetwacom` to toggle wacom tablet focus between different monitors and windows,
This file contains hidden or 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/env python3 | |
from subprocess import check_output as output | |
from subprocess import run | |
from os.path import exists | |
from subprocess import CalledProcessError | |
import argparse | |
import re | |
import sys | |
wacom_max_res = (44800, 29600) # Width, height | |
def status(new=None): | |
if new is not None: | |
with open('/tmp/wacom_focus_status', 'w') as f: | |
f.write(str(new)) | |
else: | |
with open('/tmp/wacom_focus_status', 'r') as f: | |
return int(f.read()) | |
def get_window_info(id=None): | |
try: | |
win_id = id if id is not None else output(['xdotool', 'getactivewindow']).decode() | |
except CalledProcessError: | |
sys.stderr.write("Could not find window\n") | |
exit(1) | |
info = output(['xdotool', 'getwindowgeometry', win_id]).decode() | |
frame = re.findall(r'\b(\d+)+\b', output(['xprop', '_NET_FRAME_EXTENTS', '-id', win_id]).decode()) | |
pos = re.search(r'\b\d+,\d+\b', info).group(0).split(',') | |
geo = re.search(r'\b\d+x\d+\b', info).group(0).split('x') | |
if frame == []: | |
frame = [0, 0, 0, 0] | |
return { | |
'x': int(pos[0]),# - int(frame[0]), | |
'y': int(pos[1]) - int(frame[2]), | |
'w': int(geo[0]) - int(frame[1]), | |
'h': int(geo[1]) - int(frame[3]), | |
} | |
def map_to_area(x, y, w, h): | |
if wacom_max_res[0]/wacom_max_res[1] < w/h: | |
width = wacom_max_res[0] | |
height = int(width * h/w) | |
else: | |
height = wacom_max_res[1] | |
width = int(height * w/h) | |
center_x = int((wacom_max_res[0]-width)/2) | |
center_y = int((wacom_max_res[1]-height)/2) | |
cmd_area = lambda dev: ['xsetwacom', '--set', f'Wacom Intuos Pro M Pen {dev}', 'Area', str(center_x), str(center_y), str(width+center_x), str(height+center_y)] | |
cmd_map = lambda dev: ['xsetwacom', '--set', f'Wacom Intuos Pro M Pen {dev}', 'MapToOutput', f'{w}x{h}+{x}+{y}'] | |
run(cmd_area('stylus')) | |
run(cmd_area('eraser')) | |
run(cmd_map('stylus')) | |
run(cmd_map('eraser')) | |
def get_monitors_res(filter=None): | |
monitors = output(['xrandr', '--listactivemonitors']).decode().strip().split('\n')[1:] | |
mons = [] | |
for mon in monitors: | |
res = re.findall(r'(\d+)/\d+', mon) | |
pos = re.findall(r'\+(\d+)', mon) | |
mons.append({ | |
'x': int(pos[0]), | |
'y': int(pos[1]), | |
'w': int(res[0]), | |
'h': int(res[1]), | |
'name': re.findall(r'\s(\w+-*\d*)$', mon)[0] | |
}) | |
if filter is not None: | |
for m in mons: | |
if m['name'] == filter: | |
return m | |
return None | |
return mons | |
def main(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument('-c', '--current-window', action='store_true', help='Map to currently focused window') | |
parser.add_argument('-t', '--toggle', action='store_true', help='Toggle between monitors and active window') | |
parser.add_argument('-w', '--window', type=str, help='Map to window ID') | |
parser.add_argument('-m', '--monitor', type=str, help='Map to monitor') | |
parser.add_argument('-l', '--list-monitors', action='store_true', help='List active monitors') | |
area = None | |
args = parser.parse_args() | |
if args.toggle: | |
if not exists('/tmp/wacom_focus_status'): | |
status(0) | |
state = status() | |
monitors = get_monitors_res() | |
if state == 0: | |
area = get_window_info() | |
state += 1 | |
status(state) | |
area = get_window_info() | |
else: | |
area = monitors[state-1] | |
state += 1 | |
if state > len(monitors): | |
status(0) | |
else: | |
status(state) | |
elif args.current_window: | |
area = get_window_info() | |
elif args.window: | |
area = get_window_info(args.window) | |
elif args.monitor: | |
if re.match(r'\d+', args.monitor): | |
try: | |
area = get_monitors_res()[int(args.monitor)] | |
except IndexError: | |
sys.stderr.write("Monitor not found. Checked with --list-monitors?\n") | |
exit(1) | |
else: | |
area = get_monitors_res(args.monitor) | |
if area is None: | |
sys.stderr.write("Could not find monitor. Checked --list-monitors?\n") | |
exit(1) | |
elif args.list_monitors: | |
for i,m in enumerate(get_monitors_res()): | |
print(f"{i}: {m['name']} {m['w']}x{m['h']}") | |
if area is not None: | |
map_to_area(area['x'], area['y'], area['w'], area['h']) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment