Skip to content

Instantly share code, notes, and snippets.

@aou
Created May 25, 2022 16:14
Show Gist options
  • Save aou/043bdd6a44bfebb0b42bc4e40c0a8913 to your computer and use it in GitHub Desktop.
Save aou/043bdd6a44bfebb0b42bc4e40c0a8913 to your computer and use it in GitHub Desktop.
python scripts for managing multi-monitor independent fractional scaling on X11 with xrandr
#!/usr/bin/python3
import argparse
import re
import subprocess
presets = {
'intel1': {
'dim1': '3200x1800',
'dim2': '2560x1440',
'scale': 1.5,
'dpi': 192,
},
'intel2': {
'dim1': '3200x1800',
'dim2': '2560x1440',
'scale': 1.7,
'dpi': 192,
},
'nv': {
'dim1': '3840x2160',
'dim2': '2560x1440',
'scale': 1.7,
'dpi': 235,
'scale': 1.7,
},
'mini': {
'dim1': '3200x1800',
'dim2': '1920x1080',
'scale': 1.5,
'dpi': 192,
},
'rathian': {
'dim1': '2560x1600',
'sm': True,
'scale': 1.25,
'dpi': 192,
},
}
parser = argparse.ArgumentParser(description='dual monitors')
parser.add_argument('--preset', dest='preset_name', type=str, choices=presets.keys())
parser.add_argument('--scale', default=2.2, type=float)
parser.add_argument('--dpi', default=211, type=int)
parser.add_argument('--dim1', type=str)
parser.add_argument('--dim2', type=str)
parser.add_argument('--primary', type=int)
parser.add_argument('--sm', action='store_true')
parser.add_argument('--flicker', action='store_true')
parser.add_argument('-d', '--dry-run', action='store_true')
args = parser.parse_args()
preset = {}
if args.preset_name:
preset = presets[args.preset_name]
scale = preset.get('scale', args.scale)
dpi = preset.get('scale', args.scale)
sm = preset.get('sm', args.sm)
dim1 = preset.get('dim1', args.dim1)
dim2 = preset.get('dim2', args.dim2)
primary = preset.get('primary', args.primary)
flicker = preset.get('flicker', args.flicker)
cp = subprocess.run(['xrandr'], universal_newlines=True, stdout=subprocess.PIPE)
lines = cp.stdout.split('\n')
d1 = None
d2 = None
d1_dims = None
d2_dims = None
next_line_dims = False
for line in lines:
if next_line_dims is True:
match = re.match(r'\s+(\d+)x(\d+)\s+', line)
if d2 is None:
d1_dims = (int(match[1]), int(match[2]))
else:
d2_dims = (int(match[1]), int(match[2]))
next_line_dims = False
else:
match = re.match(r'(\S+) connected.* (\d+)x(\d+)+(\d+)+(\d+)', line)
if match:
if match[4] == '0' and match[5] == '0':
d1 = match[1]
else:
d2 = match[1]
# if d1 is None:
# d1 = match[1]
# else:
# d2 = match[1]
next_line_dims = True
if dim1:
match = re.match(r'(\d+)x(\d+)', dim1)
d1_dims = (int(match[1]), int(match[2]))
if dim2:
match = re.match(r'(\d+)x(\d+)', dim2)
d2_dims = (int(match[1]), int(match[2]))
xrandr_args = None
if sm:
if dim2:
off = d1
d1 = d2
d1_dims = d2_dims
d2 = off
xrandr_args = [
'xrandr --dpi {} --fb {}x{}'.format(dpi, *d1_dims),
'--output {} --mode {}x{}'.format(d1, *d1_dims),
'--output {} --off'.format(d2)
]
else:
d2_dims = tuple(map(lambda x: int(x * scale), d2_dims))
fb = (d1_dims[0] + d2_dims[0],
d1_dims[1] + d2_dims[1])
xrandr_args = [
'xrandr --dpi {} --fb {}x{}'.format(dpi, *fb),
'--output {} --mode {}x{}'.format(d1, *d1_dims),
'--output {} --scale {}x{} --pos {}x0 --panning {}x{}+{}+0'.format(
d2, scale, scale, d1_dims[0], *d2_dims, d1_dims[0]),
]
if flicker:
xrandr_args = [
'xrandr --output {} --scale 0.9999x0.9999 && '.format(d1), #stops flickering for some reason
] + xrandr_args
print(' '.join(xrandr_args))
if not args.dry_run:
subprocess.run(' '.join(xrandr_args), shell=True)
if primary:
primary_output = ''
if primary == 1:
primary_output = d1
elif primary == 2:
primary_output = d2
xrandr_args = [
'xrandr --output {} --primary'.format(primary_output)
]
subprocess.run(' '.join(xrandr_args), shell=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment