X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=autorandr.py;h=596d3ffb504d20f04d3cbad2dba2956efe660ddf;hb=7d127f5879a929868d0a69ff6cd37a0876f9bac2;hp=315f62c86119c4c7f04ddd8dbb2a63682cdd8fb1;hpb=b606952b22ebfdb2e71dad18a53807ed1c9ab5c5;p=deb_pkgs%2Fautorandr.git diff --git a/autorandr.py b/autorandr.py index 315f62c..596d3ff 100755 --- a/autorandr.py +++ b/autorandr.py @@ -484,12 +484,13 @@ 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" - 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 +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_. + """ + return not exec_scripts(profile_path, "block", meta_information) def output_configuration(configuration, config): "Write a configuration file" @@ -543,6 +544,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 = [] @@ -554,9 +557,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: @@ -668,11 +682,52 @@ def exit_help(): print(" %-10s %s" % profile[:2]) sys.exit(0) -def exec_scripts(profile_path, script_name): - "Run userscripts" - for script in (os.path.join(profile_path, script_name), os.path.join(os.path.dirname(profile_path), script_name)): - if os.access(script, os.X_OK | os.F_OK): - subprocess.call(script) +def exec_scripts(profile_path, script_name, meta_information=None): + """"Run userscripts + + This will run all executables from the profile folder, and global per-user + and system-wide configuration folders, named script_name or residing in + subdirectories named script_name.d. + + meta_information is expected to be an dictionary. It will be passed to the block scripts + in the environment, as variables called AUTORANDR_. + + Returns True unless any of the scripts exited with non-zero exit status. + """ + all_ok = True + 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() + + # If there are multiple candidates, the XDG spec tells to only use the first one. + ran_scripts = set() + + user_profile_path = os.path.expanduser("~/.autorandr") + 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", "").split(":"))): + + if script_name not in ran_scripts: + script = os.path.join(folder, script_name) + if os.access(script, os.X_OK | os.F_OK): + all_ok &= subprocess.call(script, env=env) != 0 + ran_scripts.add(script_name) + + script_folder = os.path.join(folder, "%s.d" % script_name) + if os.access(script_folder, os.R_OK | os.X_OK) and os.path.isdir(script_folder): + for file_name in os.listdir(script_folder): + check_name = "d/%s" % (file_name,) + if check_name not in ran_scripts: + script = os.path.join(script_folder, file_name) + if os.access(script, os.X_OK | os.F_OK): + all_ok &= subprocess.call(script, env=env) != 0 + ran_scripts.add(check_name) + + return all_ok def main(argv): try: @@ -686,7 +741,8 @@ def main(argv): profiles = {} try: # Load profiles from each XDG config directory - for directory in os.environ.get("XDG_CONFIG_DIRS", "").split(":"): + # The XDG spec says that earlier entries should take precedence, so reverse the order + for directory in reversed(os.environ.get("XDG_CONFIG_DIRS", "").split(":")): system_profile_path = os.path.join(directory, "autorandr") if os.path.isdir(system_profile_path): profiles.update(load_profiles(system_profile_path)) @@ -727,7 +783,9 @@ def main(argv): 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"]) try: - save_configuration(os.path.join(profile_path, options["--save"]), config) + profile_folder = os.path.join(profile_path, options["--save"]) + save_configuration(profile_folder, config) + exec_scripts(profile_folder, "postsave", {"CURRENT_PROFILE": options["--save"], "PROFILE_FOLDER": profile_folder}) except Exception as e: raise AutorandrException("Failed to save current configuration as profile '%s'" % (options["--save"],), e) print("Saved current configuration as profile '%s'" % options["--save"]) @@ -744,8 +802,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(): - if profile_blocked(os.path.join(profile_path, profile_name)): + 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), block_script_metadata): print("%s (blocked)" % profile_name, file=sys.stderr) continue props = [] @@ -753,8 +822,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: @@ -792,9 +860,13 @@ def main(argv): if "--dry-run" in options: apply_configuration(load_config, config, True) else: - exec_scripts(scripts_path, "preswitch") + script_metadata = { + "CURRENT_PROFILE": load_profile, + "PROFILE_FOLDER": scripts_path, + } + exec_scripts(scripts_path, "preswitch", script_metadata) apply_configuration(load_config, config, False) - exec_scripts(scripts_path, "postswitch") + exec_scripts(scripts_path, "postswitch", script_metadata) except Exception as e: raise AutorandrException("Failed to apply profile '%s'" % load_profile, e, True)