+ if not new_configuration[output].edid or "off" in new_configuration[output].options:
+ disable_outputs.append(new_configuration[output].option_vector)
+ else:
+ if "off" not in current_configuration[output].options:
+ remain_active_count += 1
+ enable_outputs.append(new_configuration[output].option_vector)
+ if xrandr_version() >= Version("1.3.0") and "transform" in current_configuration[output].options:
+ auxiliary_changes_pre.append(["--output", output, "--transform", "none"])
+
+ # Perform pe-change auxiliary changes
+ if auxiliary_changes_pre:
+ argv = base_argv + list(chain.from_iterable(auxiliary_changes_pre))
+ if subprocess.call(argv) != 0:
+ raise RuntimeError("Command failed: %s" % " ".join(argv))
+
+ # 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 subprocess.call(base_argv + list(chain.from_iterable(disable_outputs[:-1] if disable_keep else disable_outputs))) != 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.
+ pass
+ else:
+ disable_outputs = disable_outputs[-1:] if disable_keep else []
+
+ # If disable_outputs still has more than one output in it, one of the xrandr-calls below would
+ # disable the last two screens. This is a problem, so if this would happen, instead disable only
+ # one screen in the first call below.
+ if len(disable_outputs) > 0 and len(disable_outputs) % 2 == 0:
+ # In the context of a xrandr call that changes the display state, `--query' should do nothing
+ disable_outputs.insert(0, ['--query'])
+
+ # Enable the remaining outputs in pairs of two operations
+ operations = disable_outputs + enable_outputs
+ for index in range(0, len(operations), 2):
+ argv = base_argv + list(chain.from_iterable(operations[index:index+2]))
+ if subprocess.call(argv) != 0:
+ raise RuntimeError("Command failed: %s" % " ".join(argv))
+
+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():
+ if output_name not in target_configuration:
+ target_configuration[output_name] = XrandrOutput(output_name, output.edid, { "off": None })
+
+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]
+
+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 = 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:
+ configuration[output].options["mode"] = "%sx%s" % common_resolution[-1]
+ configuration[output].options["pos"] = "0x0"
+ else:
+ configuration[output].options["off"] = None
+ elif profile_name in ("horizontal", "vertical"):
+ shift = 0
+ if profile_name == "horizontal":
+ shift_index = "width"
+ pos_specifier = "%sx0"
+ else:
+ shift_index = "height"
+ pos_specifier = "0x%s"
+
+ for output in configuration:
+ configuration[output].options = {}
+ if output in modes:
+ 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"] = "%sx%s" % (mode["width"], mode["height"])
+ configuration[output].options["rate"] = mode["rate"]
+ configuration[output].options["pos"] = pos_specifier % shift
+ shift += int(mode[shift_index])
+ else:
+ configuration[output].options["off"] = None
+ return configuration