]> git.donarmstrong.com Git - deb_pkgs/autorandr.git/blobdiff - autorandr.py
Pass meta-information to block scripts
[deb_pkgs/autorandr.git] / autorandr.py
index adeaa6099ecd2917b6f437fc3f0987502b01e8f1..2fac25de3735417069ca77291156f9a1dc98e661 100755 (executable)
 #
 
 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 = [
@@ -485,12 +484,21 @@ def find_profiles(current_config, 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"
@@ -544,6 +552,8 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False)
     #   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 = []
@@ -555,9 +565,20 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False)
         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:
@@ -613,13 +634,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:
@@ -635,7 +656,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"]
@@ -745,8 +766,19 @@ def main(argv):
     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 = []
@@ -754,8 +786,7 @@ def main(argv):
                 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:
@@ -799,6 +830,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__':