]> git.donarmstrong.com Git - deb_pkgs/autorandr.git/blobdiff - autorandr.py
A git-buildpackage config file
[deb_pkgs/autorandr.git] / autorandr.py
index f62ad073b5b9ab118104a58d699df51debbf640e..27340b770571e4dc2224cef383e60a11b6411d46 100755 (executable)
@@ -50,6 +50,7 @@ except NameError:
 virtual_profiles = [
     # (name, description, callback)
     ("common", "Clone all connected outputs at the largest common resolution", None),
+    ("clone-largest", "Clone all connected outputs with the largest resolution (scaled down if necessary)", None),
     ("horizontal", "Stack all connected outputs horizontally at their largest resolution", None),
     ("vertical", "Stack all connected outputs vertically at their largest resolution", None),
 ]
@@ -345,6 +346,8 @@ class XrandrOutput(object):
         for line in configuration.split("\n"):
             if line:
                 line = line.split(None, 1)
+                if line and line[0].startswith("#"):
+                    continue
                 options[line[0]] = line[1] if len(line) > 1 else None
 
         edid = None
@@ -464,7 +467,7 @@ def load_profiles(profile_path):
         if not os.path.isfile(config_name) or not os.path.isfile(setup_name):
             continue
 
-        edids = dict([ x.strip().split() for x in open(setup_name).readlines() if x.strip() ])
+        edids = dict([ x.split() for x in (y.strip() for y in open(setup_name).readlines()) if x and x[0] != "#" ])
 
         config = {}
         buffer = []
@@ -599,6 +602,9 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False)
     #   at least.)
     # - Some implementations can not handle --transform at all, so avoid it unless
     #   necessary. (See https://github.com/phillipberndt/autorandr/issues/37)
+    # - Some implementations can not handle --panning without specifying --fb
+    #   explicitly, so avoid it unless necessary.
+    #   (See https://github.com/phillipberndt/autorandr/issues/72)
 
     auxiliary_changes_pre = []
     disable_outputs = []
@@ -613,15 +619,16 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False)
 
             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
+                for option in ("transform", "panning"):
+                    if option in current_configuration[output].options:
+                        auxiliary_changes_pre.append(["--output", output, "--%s" % option, "none"])
+                    else:
+                        try:
+                            option_index = option_vector.index("--%s" % option)
+                            if option_vector[option_index+1] == XrandrOutput.XRANDR_DEFAULTS[option]:
+                                option_vector = option_vector[:option_index] + option_vector[option_index+2:]
+                        except ValueError:
+                            pass
 
             enable_outputs.append(option_vector)
 
@@ -709,6 +716,21 @@ def generate_virtual_profile(configuration, modes, profile_name):
                 shift += int(mode[shift_index])
             else:
                 configuration[output].options["off"] = None
+    elif profile_name == "clone-largest":
+        biggest_resolution = sorted([output_modes[0] for output, output_modes in modes.items()], key=lambda x: int(x["width"])*int(x["height"]), reverse=True)[0]
+        for output in configuration:
+            configuration[output].options = {}
+            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"]
+                configuration[output].options["pos"] = "0x0"
+                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)
+            else:
+                configuration[output].options["off"] = None
     return configuration
 
 def print_profile_differences(one, another):
@@ -732,7 +754,15 @@ def exit_help():
     "Print help and exit"
     print(help_text)
     for profile in virtual_profiles:
-        print("  %-10s %s" % profile[:2])
+        name, description = profile[:2]
+        description = [ description ]
+        max_width = 78-18
+        while len(description[0]) > max_width + 1:
+            left_over = description[0][max_width:]
+            description[0] = description[0][:max_width] + "-"
+            description.insert(1, "  %-15s %s" % ("", left_over))
+        description = "\n".join(description)
+        print("  %-15s %s" % (name, description))
     sys.exit(0)
 
 def exec_scripts(profile_path, script_name, meta_information=None):
@@ -742,6 +772,8 @@ def exec_scripts(profile_path, script_name, meta_information=None):
     and system-wide configuration folders, named script_name or residing in
     subdirectories named script_name.d.
 
+    If profile_path is None, only global scripts will be invoked.
+
     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>.
 
@@ -761,8 +793,11 @@ def exec_scripts(profile_path, script_name, meta_information=None):
     if not os.path.isdir(user_profile_path):
         user_profile_path = os.path.join(os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), "autorandr")
 
-    for folder in chain((profile_path, os.path.dirname(profile_path), user_profile_path),
-                        (os.path.join(x, "autorandr") for x in os.environ.get("XDG_CONFIG_DIRS", "/etc/xdg").split(":"))):
+    candidate_directories = chain((user_profile_path,), (os.path.join(x, "autorandr") for x in os.environ.get("XDG_CONFIG_DIRS", "/etc/xdg").split(":")))
+    if profile_path:
+        candidate_directories = chain((profile_path,), candidate_directories)
+
+    for folder in candidate_directories:
 
         if script_name not in ran_scripts:
             script = os.path.join(folder, script_name)
@@ -836,6 +871,9 @@ def dispatch_call_to_sessions(argv):
                 process_environ[name] = value
         display = process_environ["DISPLAY"] if "DISPLAY" in process_environ else None
 
+        # To allow scripts to detect batch invocation (especially useful for predetect)
+        process_environ["AUTORANDR_BATCH_PID"] = str(os.getpid())
+
         if display and display not in X11_displays_done:
             try:
                 pwent = pwd.getpwuid(uid)
@@ -871,6 +909,9 @@ def main(argv):
               file=sys.stderr)
         sys.exit(posix.EX_USAGE)
 
+    if "-h" in options or "--help" in options:
+        exit_help()
+
     # Batch mode
     if "--batch" in options:
         if ("DISPLAY" not in os.environ or not os.environ["DISPLAY"]) and os.getuid() == 0:
@@ -905,6 +946,7 @@ def main(argv):
 
     profile_symlinks = { k: v for k, v in profile_symlinks.items() if v in (x[0] for x in virtual_profiles) or v in profiles }
 
+    exec_scripts(None, "predetect")
     config, modes = parse_xrandr_output()
 
     if "--fingerprint" in options:
@@ -964,9 +1006,6 @@ def main(argv):
             raise AutorandrException("Failed to remove profile '%s'" % (options["--remove"],), e)
         sys.exit(0)
 
-    if "-h" in options or "--help" in options:
-        exit_help()
-
     detected_profiles = find_profiles(config, profiles)
     load_profile = False