X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=autorandr.py;h=feee62b58bc4f2e220c9295b9e5b18f32c7194f7;hb=af4a3f82fde54436b5393682364c0340b661c554;hp=efa171b6a035dadf21f0c8580a67729f129a870a;hpb=98c93c20ff140757abdbe516a6ace4883be41373;p=deb_pkgs%2Fautorandr.git diff --git a/autorandr.py b/autorandr.py index efa171b..feee62b 100755 --- a/autorandr.py +++ b/autorandr.py @@ -62,6 +62,7 @@ Usage: autorandr [options] --fingerprint fingerprint your current hardware setup --config dump your current xrandr setup --dry-run don't change anything, only print the xrandr commands +--debug enable verbose output To prevent a profile from being loaded, place a script call "block" in its directory. The script is evaluated before the screen setup is inspected, and @@ -171,7 +172,11 @@ class XrandrOutput(object): EDID_UNAVAILABLE = "--CONNECTED-BUT-EDID-UNAVAILABLE-" def __repr__(self): - return "<%s%s %s>" % (self.output, (" %s..%s" % (self.edid[:5], self.edid[-5:])) if self.edid else "", " ".join(self.option_vector)) + return "<%s%s %s>" % (self.output, self.short_edid, " ".join(self.option_vector)) + + @property + def short_edid(self): + return ("%s..%s" % (self.edid[:5], self.edid[-5:])) if self.edid else "" @property def options_with_defaults(self): @@ -352,9 +357,33 @@ class XrandrOutput(object): 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.filtered_options == other.filtered_options + def verbose_diff(self, other): + "Compare to another XrandrOutput and return a list of human readable differences" + diffs = [] + if not self.edid_equals(other): + diffs.append("EDID `%s' differs from `%s'" % (self.short_edid, other.short_edid)) + if self.output != other.output: + diffs.append("Output name `%s' differs from `%s'" % (self.output, other.output)) + if "off" in self.options and "off" not in other.options: + diffs.append("The output is disabled currently, but active in the new configuration") + elif "off" in other.options and "off" not in self.options: + diffs.append("The output is currently enabled, but inactive in the new configuration") + else: + for name in set(chain.from_iterable((self.options.keys(), other.options.keys()))): + if name not in other.options: + diffs.append("Option --%s %sis not present in the new configuration" % (name, "(= `%s') " % self.options[name] if self.options[name] else "")) + elif name not in self.options: + diffs.append("Option --%s (`%s' in the new configuration) is not present currently" % (name, other.options[name])) + elif self.options[name] != other.options[name]: + diffs.append("Option --%s %sis `%s' in the new configuration" % (name, "(= `%s') " % self.options[name] if self.options[name] else "", other.options[name])) + return diffs + def xrandr_version(): "Return the version of XRandR that this system uses" if getattr(xrandr_version, "version", False) is False: @@ -561,6 +590,13 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False) 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(): @@ -577,13 +613,13 @@ def generate_virtual_profile(configuration, modes, profile_name): "Generate one of the virtual profiles" configuration = copy.deepcopy(configuration) if profile_name == "common": - common_resolution = [ set(( ( mode["width"], mode["height"] ) for mode in output )) for output in modes.values() ] + common_resolution = [ set(( ( mode["width"], mode["height"] ) for mode in output_modes )) for output, output_modes in modes.items() if configuration[output].edid ] common_resolution = reduce(lambda a, b: a & b, common_resolution[1:], common_resolution[0]) common_resolution = sorted(common_resolution, key=lambda a: int(a[0])*int(a[1])) if common_resolution: for output in configuration: configuration[output].options = {} - if output in modes: + if output in modes and configuration[output].edid: configuration[output].options["mode"] = [ x["name"] for x in sorted(modes[output], key=lambda x: 0 if x["preferred"] else 1) if x["width"] == common_resolution[-1][0] and x["height"] == common_resolution[-1][1] ][0] configuration[output].options["pos"] = "0x0" else: @@ -599,7 +635,7 @@ def generate_virtual_profile(configuration, modes, profile_name): for output in configuration: configuration[output].options = {} - if output in modes: + if output in modes and configuration[output].edid: mode = sorted(modes[output], key=lambda a: int(a["width"])*int(a["height"]) + (10**6 if a["preferred"] else 0))[-1] configuration[output].options["mode"] = mode["name"] configuration[output].options["rate"] = mode["rate"] @@ -609,6 +645,23 @@ def generate_virtual_profile(configuration, modes, profile_name): configuration[output].options["off"] = None return configuration +def print_profile_differences(one, another): + "Print the differences between two profiles for debugging" + if one == another: + return + print("| Differences between the two profiles:", file=sys.stderr) + for output in set(chain.from_iterable((one.keys(), another.keys()))): + if output not in one: + if "off" not in another[output].options: + print("| Output `%s' is missing from the active configuration" % output, file=sys.stderr) + elif output not in another: + if "off" not in one[output].options: + print("| Output `%s' is missing from the new configuration" % output, file=sys.stderr) + else: + for line in one[output].verbose_diff(another[output]): + print("| [Output %s] %s" % (output, line), file=sys.stderr) + print ("\\-", file=sys.stderr) + def exit_help(): "Print help and exit" print(help_text) @@ -624,7 +677,7 @@ def exec_scripts(profile_path, script_name): def main(argv): try: - options = dict(getopt.getopt(argv[1:], "s:l:d:cfh", [ "dry-run", "change", "default=", "save=", "load=", "force", "fingerprint", "config", "skip-options=", "help" ])[0]) + options = dict(getopt.getopt(argv[1:], "s:l:d:cfh", [ "dry-run", "change", "default=", "save=", "load=", "force", "fingerprint", "config", "debug", "skip-options=", "help" ])[0]) except getopt.GetoptError as e: print("Failed to parse options: {0}.\n" "Use --help to get usage information.".format(str(e)), @@ -696,12 +749,17 @@ def main(argv): 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) + configs_are_equal = is_equal_configuration(config, profiles[profile_name]["config"]) + if configs_are_equal: + props.append("(current)") + print("%s%s%s" % (profile_name, " " if props else "", " ".join(props)), file=sys.stderr) + if not configs_are_equal and "--debug" in options and profile_name in detected_profiles: + print_profile_differences(config, profiles[profile_name]["config"]) if "-d" in options: options["--default"] = options["-d"] @@ -725,6 +783,10 @@ def main(argv): if load_config == dict(config) and not "-f" in options and not "--force" in options: print("Config already loaded", file=sys.stderr) sys.exit(0) + if "--debug" in options and load_config != dict(config): + print("Loading profile '%s'" % load_profile) + print_profile_differences(config, load_config) + remove_irrelevant_outputs(config, load_config) try: @@ -737,6 +799,12 @@ def main(argv): except Exception as e: raise AutorandrException("Failed to apply profile '%s'" % load_profile, e, True) + if "--dry-run" not in options and "--debug" in options: + new_config, _ = parse_xrandr_output() + if not is_equal_configuration(new_config, load_config): + print("The configuration change did not go as expected:") + print_profile_differences(new_config, load_config) + sys.exit(0) if __name__ == '__main__':