# This regular expression is used to parse an output in `xrandr --verbose'
XRANDR_OUTPUT_REGEXP = """(?x)
^(?P<output>[^ ]+)\s+ # Line starts with output name
- (?: # Differentiate disconnected and connected in first line
- disconnected |
+ (?: # Differentiate disconnected and connected
+ disconnected | # in first line
unknown\ connection |
(?P<connected>connected)
)
(?:\(0x[0-9a-fA-F]+\)\s+)? # XID
(?P<rotate>(?:normal|left|right|inverted))\s+ # Rotation
(?:(?P<reflect>X\ and\ Y|X|Y)\ axis)? # Reflection
- )? # .. but everything of the above only if the screen is in use.
+ )? # .. but only if the screen is in use.
(?:[\ \t]*\([^\)]+\))(?:\s*[0-9]+mm\sx\s[0-9]+mm)?
(?:[\ \t]*panning\ (?P<panning>[0-9]+x[0-9]+\+[0-9]+\+[0-9]+))? # Panning information
(?:[\ \t]*tracking\ (?P<tracking>[0-9]+x[0-9]+\+[0-9]+\+[0-9]+))? # Tracking information
))+
\s*
(?P<modes>(?:
- (?P<mode_name>\S+).+?\*current.*\s+ # Interesting (current) resolution: Extract rate
- h:\s+width\s+(?P<mode_width>[0-9]+).+\s+
+ (?P<mode_name>\S+).+?\*current.*\s+ # Interesting (current) resolution:
+ h:\s+width\s+(?P<mode_width>[0-9]+).+\s+ # Extract rate
v:\s+height\s+(?P<mode_height>[0-9]+).+clock\s+(?P<rate>[0-9\.]+)Hz\s* |
\S+(?:(?!\*current).)+\s+h:.+\s+v:.+\s* # Other resolutions
)*)
xrandr_output = xrandr_output.replace("\r\n", "\n")
match_object = re.search(XrandrOutput.XRANDR_OUTPUT_REGEXP, xrandr_output)
except:
- raise AutorandrException("Parsing XRandR output failed, there is an error in the regular expression.", report_bug=True)
+ raise AutorandrException("Parsing XRandR output failed, there is an error in the regular expression.",
+ report_bug=True)
if not match_object:
debug = debug_regexp(XrandrOutput.XRANDR_OUTPUT_REGEXP, xrandr_output)
- raise AutorandrException("Parsing XRandR output failed, the regular expression did not match: %s" % debug, report_bug=True)
+ raise AutorandrException("Parsing XRandR output failed, the regular expression did not match: %s" % debug,
+ report_bug=True)
remainder = xrandr_output[len(match_object.group(0)):]
if remainder:
- raise AutorandrException(("Parsing XRandR output failed, %d bytes left unmatched after regular expression, "
- "starting at byte %d with ..'%s'.") % (len(remainder), len(match_object.group(0)), remainder[:10]), report_bug=True)
+ raise AutorandrException("Parsing XRandR output failed, %d bytes left unmatched after "
+ "regular expression, starting at byte %d with ..'%s'." %
+ (len(remainder), len(match_object.group(0)), remainder[:10]),
+ report_bug=True)
match = match_object.groupdict()
if transformation != "1.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,1.000000":
options["transform"] = transformation
if not match["mode_name"]:
- # TODO We'd need to apply the reverse transformation here. Let's see if someone complains, I doubt that this
- # special case is actually required.
- print("Warning: Output %s has a transformation applied. Could not determine correct mode! Using `%s'." % (match["output"], options["mode"]), file=sys.stderr)
+ # TODO We'd need to apply the reverse transformation here. Let's see if someone complains,
+ # I doubt that this special case is actually required.
+ print("Warning: Output %s has a transformation applied. Could not determine correct mode! "
+ "Using `%s'." % (match["output"], options["mode"]), file=sys.stderr)
if match["gamma"]:
gamma = match["gamma"].strip()
# xrandr prints different values in --verbose than it accepts as a parameter value for --gamma
if fuzzy_output in fuzzy_edid_map:
edid = edid_map[list(edid_map.keys())[fuzzy_edid_map.index(fuzzy_output)]]
elif "off" not in options:
- raise AutorandrException("Failed to find an EDID for output `%s' in setup file, required as `%s' is not off in config file." % (options["output"], options["output"]))
+ raise AutorandrException("Failed to find an EDID for output `%s' in setup file, required as `%s' "
+ "is not off in config file." % (options["output"], options["output"]))
output = options["output"]
del options["output"]
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 ""))
+ 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]))
+ 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]))
+ 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
break
bounds = (half, bounds[1]) if regex.search(pattern, string[:half], partial=True) else (bounds[0], half - 1)
partial_length = bounds[0]
- return ("Regular expression matched until position "
- "%d, ..'%s', and did not match from '%s'.." % (partial_length, string[max(0, partial_length - 20):partial_length],
- string[partial_length:partial_length + 10]))
+ return ("Regular expression matched until position %d, ..'%s', and did not match from '%s'.." %
+ (partial_length, string[max(0, partial_length - 20):partial_length],
+ string[partial_length:partial_length + 10]))
except ImportError:
pass
return "Debug information would be available if the `regex' module was installed."
# Disable unused outputs, but make sure that there always is at least one active screen
disable_keep = 0 if remain_active_count else 1
if len(disable_outputs) > disable_keep:
- if call_and_retry(base_argv + list(chain.from_iterable(disable_outputs[:-1] if disable_keep else disable_outputs)), dry_run=dry_run) != 0:
+ argv = base_argv + list(chain.from_iterable(disable_outputs[:-1] if disable_keep else disable_outputs))
+ if call_and_retry(argv, dry_run=dry_run) != 0:
# Disabling the outputs failed. Retry with the next command:
# Sometimes disabling of outputs fails due to an invalid RRSetScreenSize.
# This does not occur if simultaneously the primary screen is reset.
def remove_irrelevant_outputs(source_configuration, target_configuration):
"Remove outputs from target that ought to be 'off' and already are"
for output_name, output in source_configuration.items():
- if "off" in output.options and output_name in target_configuration and "off" in target_configuration[output_name].options:
- del target_configuration[output_name]
+ if "off" in output.options:
+ if output_name in target_configuration:
+ if "off" in target_configuration[output_name].options:
+ del target_configuration[output_name]
def generate_virtual_profile(configuration, modes, profile_name):
configuration[output].options["mode"] = mode["name"]
configuration[output].options["rate"] = mode["rate"]
configuration[output].options["pos"] = "0x0"
- scale = max(float(biggest_resolution["width"]) / float(mode["width"]), float(biggest_resolution["height"]) / float(mode["height"]))
+ scale = max(float(biggest_resolution["width"]) / float(mode["width"]),
+ float(biggest_resolution["height"]) / float(mode["height"]))
mov_x = (float(mode["width"]) * scale - float(biggest_resolution["width"])) / -2
mov_y = (float(mode["height"]) * scale - float(biggest_resolution["height"])) / -2
configuration[output].options["transform"] = "{},0,{},0,{},{},0,0,1".format(scale, mov_x, scale, mov_y)
def main(argv):
try:
- opts, args = getopt.getopt(argv[1:], "s:r:l:d:cfh", ["batch", "dry-run", "change", "default=", "save=", "remove=", "load=", "force", "fingerprint", "config", "debug", "skip-options=", "help"])
+ opts, args = getopt.getopt(argv[1:], "s:r:l:d:cfh",
+ ["batch", "dry-run", "change", "default=", "save=", "remove=", "load=",
+ "force", "fingerprint", "config", "debug", "skip-options=", "help"])
except getopt.GetoptError as e:
print("Failed to parse options: {0}.\n"
"Use --help to get usage information.".format(str(e)),
options["--save"] = options["-s"]
if "--save" in options:
if options["--save"] in (x[0] for x in virtual_profiles):
- raise AutorandrException("Cannot save current configuration as profile '%s':\nThis configuration name is a reserved virtual configuration." % options["--save"])
+ raise AutorandrException("Cannot save current configuration as profile '%s':\n"
+ "This configuration name is a reserved virtual configuration." % options["--save"])
try:
profile_folder = os.path.join(profile_path, options["--save"])
save_configuration(profile_folder, config)
options["--remove"] = options["-r"]
if "--remove" in options:
if options["--remove"] in (x[0] for x in virtual_profiles):
- raise AutorandrException("Cannot remove profile '%s':\nThis configuration name is a reserved virtual configuration." % options["--remove"])
+ raise AutorandrException("Cannot remove profile '%s':\n"
+ "This configuration name is a reserved virtual configuration." % options["--remove"])
if options["--remove"] not in profiles.keys():
- raise AutorandrException("Cannot remove profile '%s':\nThis profile does not exist." % options["--remove"])
+ raise AutorandrException("Cannot remove profile '%s':\n"
+ "This profile does not exist." % options["--remove"])
try:
remove = True
profile_folder = os.path.join(profile_path, options["--remove"])
profile_dirlist.remove("config")
profile_dirlist.remove("setup")
if profile_dirlist:
- print("Profile folder '%s' contains the following additional files:\n---\n%s\n---" % (options["--remove"], "\n".join(profile_dirlist)))
+ print("Profile folder '%s' contains the following additional files:\n"
+ "---\n%s\n---" % (options["--remove"], "\n".join(profile_dirlist)))
response = input("Do you really want to remove profile '%s'? If so, type 'yes': " % options["--remove"]).strip()
if response != "yes":
remove = False
print("Exception: {0}".format(e.__class__.__name__))
sys.exit(2)
- print("Unhandled exception ({0}). Please report this as a bug at https://github.com/phillipberndt/autorandr/issues.".format(e), file=sys.stderr)
+ print("Unhandled exception ({0}). Please report this as a bug at "
+ "https://github.com/phillipberndt/autorandr/issues.".format(e),
+ file=sys.stderr)
raise