Created
January 15, 2019 11:23
-
-
Save mic-e/481a1dd86146ace2224d08afe701dfc3 to your computer and use it in GitHub Desktop.
Monitor autoconfiguration script
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 | |
""" | |
CLI script for setting display modes through xrandr. | |
""" | |
from argparse import ArgumentParser | |
from collections import OrderedDict | |
import re | |
from subprocess import call, Popen, PIPE | |
from shlex import quote | |
from time import sleep | |
import pyudev | |
DISPLAYS = [ | |
"HDMI2", | |
"eDP1", | |
"DP1" | |
] | |
MODES = { | |
"nb": { | |
"eDP1": ["--auto"] | |
}, | |
"1ext": { | |
"eDP1": ["--auto"], | |
"HDMI2": ["--auto", "--left-of", "eDP1"] | |
}, | |
"ext": { | |
"DP1": ["--auto"], | |
"HDMI2": ["--auto", "--left-of", "DP1"] | |
}, | |
} | |
AUTO = { | |
"eDP1": "nb", | |
"HDMI2, eDP1": "1ext", | |
"DP1, HDMI2, eDP1": "ext", | |
} | |
def get_displays(): | |
""" | |
Lists the names of the currently-connected displays | |
""" | |
proc = Popen(['xrandr', '-q'], stdout=PIPE) | |
output = proc.communicate()[0].decode(errors='replace') | |
connected_re = re.compile("^([a-zA-Z0-9]+) connected ") | |
connected = [] | |
for line in output.split('\n'): | |
match = connected_re.match(line) | |
if match is None: | |
continue | |
connected.append(match.groups()[0]) | |
yield connected[-1] | |
def get_automatic_mode(): | |
""" | |
Returns the suitable mode for the currently-connected displays | |
""" | |
displays = ", ".join(sorted(get_displays())) | |
print("connected displays: " + displays) | |
result = AUTO.get(displays) | |
print("automatic mode: %r" % result) | |
return result | |
def configure_displays(mode): | |
""" | |
Configures the displays to the given mode | |
""" | |
displays = OrderedDict() | |
displays["HDMI2"] = ["--off"] | |
displays["eDP1"] = ["--off"] | |
displays["DP1"] = ["--off"] | |
mode_info = MODES.get(mode) | |
if mode_info is None: | |
raise ValueError("unknown mode %r" % mode) | |
invocation = ["xrandr"] | |
for displayname in DISPLAYS: | |
invocation.append("--output") | |
invocation.append(displayname) | |
invocation.extend(mode_info.get(displayname, ["--off"])) | |
print(" ".join(quote(x) for x in invocation)) | |
call(invocation) | |
def main(): | |
""" CLI entry point """ | |
cli = ArgumentParser() | |
cli.add_argument("mode") | |
args = cli.parse_args() | |
mode = args.mode | |
if mode == "daemon": | |
context = pyudev.Context() | |
udev_monitor = pyudev.Monitor.from_netlink(context) | |
udev_monitor.filter_by(subsystem='drm') | |
while True: | |
try: | |
configure_displays(get_automatic_mode()) | |
except ValueError as exc: | |
print(exc) | |
# wait for next udev hotplug event | |
hotplug_device = udev_monitor.poll() | |
if hotplug_device is None: | |
return | |
print("hotplug: %r" % hotplug_device) | |
sleep(0.5) | |
else: | |
# run once | |
if mode == "auto": | |
mode = get_automatic_mode() | |
try: | |
configure_displays(mode) | |
except ValueError as exc: | |
cli.error(str(exc)) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment