#
from __future__ import print_function
-import copy
-import getopt
import binascii
+import copy
+import getopt
import hashlib
import os
+import posix
import re
import subprocess
import sys
-from distutils.version import LooseVersion as Version
+from collections import OrderedDict
+from distutils.version import LooseVersion as Version
from functools import reduce
from itertools import chain
-from collections import OrderedDict
-
-import posix
virtual_profiles = [
detected_profiles.append(profile_name)
return detected_profiles
-def profile_blocked(profile_path):
- "Check if a profile is blocked"
+def profile_blocked(profile_path, meta_information=None):
+ """Check if a profile is blocked.
+
+ meta_information is expected to be an dictionary. It will be passed to the block scripts
+ in the environment, as variables called AUTORANDR_<CAPITALIZED_KEY_HERE>.
+ """
script = os.path.join(profile_path, "block")
if not os.access(script, os.X_OK | os.F_OK):
return False
- return subprocess.call(script) == 0
+ if meta_information:
+ env = os.environ.copy()
+ env.update({ "AUTORANDR_%s" % str(key).upper(): str(value) for (key, value) in meta_information.items() })
+ else:
+ env = os.environ.copy()
+ return subprocess.call(script, env=env) == 0
def output_configuration(configuration, config):
"Write a configuration file"
# the xrandr call fails with an invalid RRSetScreenSize parameter error.
# Update the configuration in 3 passes in that case. (On Haswell graphics,
# at least.)
+ # - Some implementations can not handle --transform at all, so avoid it unless
+ # necessary. (See https://github.com/phillipberndt/autorandr/issues/37)
auxiliary_changes_pre = []
disable_outputs = []
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"])
+
+ option_vector = new_configuration[output].option_vector
+ if xrandr_version() >= Version("1.3.0"):
+ if "transform" in current_configuration[output].options:
+ auxiliary_changes_pre.append(["--output", output, "--transform", "none"])
+ else:
+ try:
+ transform_index = option_vector.index("--transform")
+ if option_vector[transform_index+1] == XrandrOutput.XRANDR_DEFAULTS["transform"]:
+ option_vector = option_vector[:transform_index] + option_vector[transform_index+2:]
+ except ValueError:
+ pass
+
+ enable_outputs.append(option_vector)
# Perform pe-change auxiliary changes
if auxiliary_changes_pre:
"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:
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"]
if "--load" in options:
load_profile = options["--load"]
else:
+ # Find the active profile(s) first, for the block script (See #42)
+ current_profiles = []
+ for profile_name in profiles.keys():
+ configs_are_equal = is_equal_configuration(config, profiles[profile_name]["config"])
+ if configs_are_equal:
+ current_profiles.append(profile_name)
+ block_script_metadata = {
+ "CURRENT_PROFILE": "".join(current_profiles[:1]),
+ "CURRENT_PROFILES": ":".join(current_profiles)
+ }
+
for profile_name in profiles.keys():
- if profile_blocked(os.path.join(profile_path, profile_name)):
+ if profile_blocked(os.path.join(profile_path, profile_name), block_script_metadata):
print("%s (blocked)" % profile_name, file=sys.stderr)
continue
props = []
props.append("(detected)")
if ("-c" in options or "--change" in options) and not load_profile:
load_profile = profile_name
- configs_are_equal = is_equal_configuration(config, profiles[profile_name]["config"])
- if configs_are_equal:
+ if profile_name in current_profiles:
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:
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__':