import sys
from distutils.version import LooseVersion as Version
+from functools import reduce
from itertools import chain
from collections import OrderedDict
+import posix
+
+
virtual_profiles = [
# (name, description, callback)
("common", "Clone all connected outputs at the largest common resolution", None),
-s, --save <profile> save your current setup to profile <profile>
-l, --load <profile> load profile <profile>
-d, --default <profile> make profile <profile> the default profile
+--skip-options <option> comma separated list of xrandr arguments (e.g. "gamma")
+ to skip both in detecting changes and applying a profile
--force force (re)loading of a profile
--fingerprint fingerprint your current hardware setup
--config dump your current xrandr setup
if xrandr_version() >= Version("1.2"):
options.update(self.XRANDR_12_DEFAULTS)
options.update(self.options)
- return options
+ return { a: b for a, b in options.items() if a not in self.ignored_options }
+
+ @property
+ def filtered_options(self):
+ "Return a dictionary of options without ignored options"
+ return { a: b for a, b in self.options.items() if a not in self.ignored_options }
@property
def option_vector(self):
@property
def option_string(self):
"Return the command line parameters in the configuration file format"
- return "\n".join([ " ".join(option) if option[1] else option[0] for option in chain((("output", self.output),), sorted(self.options.items()))])
+ return "\n".join([ " ".join(option) if option[1] else option[0] for option in chain((("output", self.output),), sorted(self.filtered_options.items()))])
@property
def sort_key(self):
self.output = output
self.edid = edid
self.options = options
+ self.ignored_options = []
self.remove_default_option_values()
+ def set_ignored_options(self, options):
+ "Set a list of xrandr options that are never used (neither when comparing configurations nor when applying them)"
+ self.ignored_options = list(options)
+
def remove_default_option_values(self):
"Remove values from the options dictionary that are superflous"
if "off" in self.options and len(self.options.keys()) > 1:
return hashlib.md5(binascii.unhexlify(self.edid)).hexdigest() == other.edid
return self.edid == other.edid
+ def __ne__(self, other):
+ return not (self == other)
+
def __eq__(self, other):
- return self.edid_equals(other) and self.output == other.output and self.options == other.options
+ return self.edid_equals(other) and self.output == other.output and self.filtered_options == other.filtered_options
def xrandr_version():
"Return the version of XRandR that this system uses"
if subprocess.call(argv) != 0:
raise AutorandrException("Command failed: %s" % " ".join(argv))
+def is_equal_configuration(source_configuration, target_configuration):
+ "Check if all outputs from target are already configured correctly in source"
+ for output in target_configuration.keys():
+ if (output not in source_configuration) or (source_configuration[output] != target_configuration[output]):
+ return False
+ return True
+
def add_unused_outputs(source_configuration, target_configuration):
"Add outputs that are missing in target to target, in 'off' state"
for output_name, output in source_configuration.items():
def main(argv):
try:
- options = dict(getopt.getopt(argv[1:], "s:l:d:cfh", [ "dry-run", "change", "default=", "save=", "load=", "force", "fingerprint", "config", "help" ])[0])
+ options = dict(getopt.getopt(argv[1:], "s:l:d:cfh", [ "dry-run", "change", "default=", "save=", "load=", "force", "fingerprint", "config", "skip-options=", "help" ])[0])
except getopt.GetoptError as e:
- print(str(e))
- options = { "--help": True }
+ print("Failed to parse options: {0}.\n"
+ "Use --help to get usage information.".format(str(e)),
+ file=sys.stderr)
+ sys.exit(posix.EX_USAGE)
profiles = {}
try:
output_configuration(config, sys.stdout)
sys.exit(0)
+ if "--skip-options" in options:
+ skip_options = [ y[2:] if y[:2] == "--" else y for y in ( x.strip() for x in options["--skip-options"].split(",") ) ]
+ for profile in profiles.values():
+ for output in profile["config"].values():
+ output.set_ignored_options(skip_options)
+ for output in config.values():
+ output.set_ignored_options(skip_options)
+
if "-s" in options:
options["--save"] = options["-s"]
if "--save" in options:
if profile_blocked(os.path.join(profile_path, profile_name)):
print("%s (blocked)" % profile_name, file=sys.stderr)
continue
+ props = []
if profile_name in detected_profiles:
- print("%s (detected)" % profile_name, file=sys.stderr)
+ props.append("(detected)")
if ("-c" in options or "--change" in options) and not load_profile:
load_profile = profile_name
- else:
- print(profile_name, file=sys.stderr)
+ if is_equal_configuration(config, profiles[profile_name]["config"]):
+ props.append("(current)")
+ print("%s%s%s" % (profile_name, " " if props else "", " ".join(props)), file=sys.stderr)
if "-d" in options:
options["--default"] = options["-d"]