]> git.donarmstrong.com Git - deb_pkgs/autorandr.git/commitdiff
Update upstream source from tag 'upstream/1.10'
authorDon Armstrong <don@donarmstrong.com>
Sat, 25 Apr 2020 23:39:22 +0000 (16:39 -0700)
committerDon Armstrong <don@donarmstrong.com>
Sat, 25 Apr 2020 23:39:22 +0000 (16:39 -0700)
Update to upstream version '1.10'
with Debian dir e60c41769d4e52b2a2bf9f34a838448ce7cc87c0

Makefile
README.md
autorandr.py
contrib/etc/xdg/autostart/autorandr-launcher.desktop [new file with mode: 0644]
contrib/zsh_completion/_autorandr [new file with mode: 0644]
setup.py

index d8b27c6e5eac1ce9627e2cb224c3c2aa6473180e..55596e2387a4dc8babb034d8c9192f49b3a328d7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,7 @@
 DESTDIR=/
 PREFIX=/usr/
 RPM_SPEC=contrib/packaging/rpm/autorandr.spec
+CFLAGS=-O2
 
 .PHONY: all install uninstall autorandr bash_completion autostart_config pmutils systemd udev
 
@@ -23,14 +24,14 @@ all:
        @echo
        @echo 'E.g. "make install TARGETS='autorandr pmutils' PM_UTILS_DIR=/etc/pm/sleep.d".'
        @echo
-       @echo "An additional TARGETS variable \"launcher\" is available. This"
-       @echo "installs a launcher called \"autorandr_launcher\". The launcher"
-       @echo "is able to be run by the user and calls autorandr automatically"
-       @echo "without using udev rules."
+       @echo "By default, if xcb libraries are available, autorandr prefers to"
+       @echo "install a launcher that listens for X11 randr events and runs"
+       @echo "autorandr whenever something changes, over udev/systemd rules."
        @echo
        @echo "The following additional targets are available:"
        @echo
        @echo "    make deb        creates a Debian package"
+       @echo "    make rpm        creates a RPM package"
 
 # Rules for autorandr itself
 DEFAULT_TARGETS=autorandr
@@ -110,7 +111,7 @@ uninstall_pmutils:
 
 # Rules for udev
 UDEV_RULES_DIR:=$(shell pkg-config --variable=udevdir udev 2>/dev/null)/rules.d
-ifneq (,$(UDEV_RULES_DIR),y)
+ifneq (/rules.d,$(UDEV_RULES_DIR))
 DEFAULT_TARGETS+=udev
 endif
 
@@ -139,12 +140,24 @@ uninstall_manpage:
        rm -f ${DESTDIR}/${MANDIR}/autorandr.1
 
 # Rules for launcher
+LAUNCHER_FLAGS=$(shell pkg-config --libs --cflags pkg-config xcb xcb-randr 2>/dev/null)
+ifneq (,$(LAUNCHER_FLAGS))
+DEFAULT_TARGETS+=launcher
+DEFAULT_TARGETS:=$(filter-out systemd udev,$(DEFAULT_TARGETS))
+endif
+
 install_launcher:
-       gcc -Wall contrib/autorandr_launcher/autorandr_launcher.c -o contrib/autorandr_launcher/autorandr_launcher -lxcb -lxcb-randr
-       install -D -m 755 contrib/autorandr_launcher/autorandr_launcher ${DESTDIR}${PREFIX}/bin/autorandr_launcher
+       gcc -Wall $(CFLAGS) contrib/autorandr_launcher/autorandr_launcher.c -o contrib/autorandr_launcher/autorandr-launcher $(LAUNCHER_FLAGS)
+       install -D -m 755 contrib/autorandr_launcher/autorandr-launcher ${DESTDIR}${PREFIX}/bin/autorandr-launcher
+
+       install -D -m 644 contrib/etc/xdg/autostart/autorandr-launcher.desktop ${DESTDIR}/${XDG_AUTOSTART_DIR}/autorandr-launcher.desktop
+ifneq ($(PREFIX),/usr/)
+       sed -i -re 's#/usr/bin/autorandr-launcher#$(subst #,\#,${PREFIX})/bin/autorandr-launcher#g' ${DESTDIR}/${XDG_AUTOSTART_DIR}/autorandr-launcher.desktop
+endif
 
 uninstall_launcher:
-       rm -f ${DESTDIR}${PREFIX}/bin/autorandr_launcher
+       rm -f ${DESTDIR}${PREFIX}/bin/autorandr-launcher
+       rm -f ${DESTDIR}/${XDG_AUTOSTART_DIR}/autorandr-launcher.desktop
 
 TARGETS=$(DEFAULT_TARGETS)
 install: $(patsubst %,install_%,$(TARGETS))
index 6c57b104575e5a245d84e4f2d7ff346c3e3ed8c1..0f2012e2ffb1f7ff345f748857c01c2c9bf95eb2 100644 (file)
--- a/README.md
+++ b/README.md
@@ -191,7 +191,7 @@ configuration takes precedence, and
 it has a unique name.
 
 If you switch back from `docked` to `mobile`, `~/.config/autorandr/postswitch`
-is executed instead of the `mobile` specific `postswitch`.
+is executed instead of the `docked` specific `postswitch`.
 
 In these scripts, some of autorandr's state is exposed as environment variables
 prefixed with `AUTORANDR_`, such as:
@@ -214,6 +214,15 @@ profiles matching multiple (or any) monitors.
 
 ## Changelog
 
+**autorandr 1.10**
+* *2020-04-23* Fix hook script execution order to match description from readme
+* *2020-04-11* Handle negative gamma values (fixes #188)
+* *2020-04-11* Sort approximate matches in detected profiles by quality of match
+* *2020-01-31* Handle non-ASCII environment variables (fixes #180)
+* *2019-12-31* Fix output positioning if the top-left output is not the first
+* *2019-12-31* Accept negative gamma values (and interpret them as 0)
+* *2019-12-31* Prefer the X11 launcher over systemd/udev configuration
+
 **autorandr 1.9**
 
 * *2019-11-10* Count closed lids as disconnected outputs
index aa1be0b28d04f340504687898e2511139b06d33b..f7356a08a56bd99c67c8076ec6e4d56ed0cb62a5 100755 (executable)
@@ -26,7 +26,6 @@ from __future__ import print_function
 
 import binascii
 import copy
-import fnmatch
 import getopt
 import hashlib
 import os
@@ -49,7 +48,7 @@ if sys.version_info.major == 2:
 else:
     import configparser
 
-__version__ = "1.9"
+__version__ = "1.10"
 
 try:
     input = raw_input
@@ -173,7 +172,7 @@ class XrandrOutput(object):
         (?:[\ \t]*tracking\ (?P<tracking>[0-9]+x[0-9]+\+[0-9]+\+[0-9]+))?               # Tracking information
         (?:[\ \t]*border\ (?P<border>(?:[0-9]+/){3}[0-9]+))?                            # Border information
         (?:\s*(?:                                                                       # Properties of the output
-            Gamma: (?P<gamma>(?:inf|[0-9\.: e])+) |                                     # Gamma value
+            Gamma: (?P<gamma>(?:inf|-?[0-9\.\-: e])+) |                                 # Gamma value
             CRTC:\s*(?P<crtc>[0-9]) |                                                   # CRTC value
             Transform: (?P<transform>(?:[\-0-9\. ]+\s+){3}) |                           # Transformation matrix
             EDID: (?P<edid>\s*?(?:\\n\\t\\t[0-9a-f]+)+) |                               # EDID of the output
@@ -428,9 +427,9 @@ class XrandrOutput(object):
             if len(self.edid) != 32 and len(other.edid) == 32 and not self.edid.startswith(XrandrOutput.EDID_UNAVAILABLE):
                 return hashlib.md5(binascii.unhexlify(self.edid)).hexdigest() == other.edid
             if "*" in self.edid:
-                return fnmatch.fnmatch(other.edid, self.edid)
+                return match_asterisk(self.edid, other.edid) > 0
             elif "*" in other.edid:
-                return fnmatch.fnmatch(self.edid, other.edid)
+                return match_asterisk(other.edid, self.edid) > 0
         return self.edid == other.edid
 
     def __ne__(self, other):
@@ -573,8 +572,29 @@ def get_symlinks(profile_path):
     return symlinks
 
 
+def match_asterisk(pattern, data):
+    """Match data against a pattern
+
+    The difference to fnmatch is that this function only accepts patterns with a single
+    asterisk and that it returns a "closeness" number, which is larger the better the match.
+    Zero indicates no match at all.
+    """
+    if "*" not in pattern:
+        return 1 if pattern == data else 0
+    parts = pattern.split("*")
+    if len(parts) > 2:
+        raise ValueError("Only patterns with a single asterisk are supported, %s is invalid" % pattern)
+    if not data.startswith(parts[0]):
+        return 0
+    if not data.endswith(parts[1]):
+        return 0
+    matched = len(pattern)
+    total = len(data) + 1
+    return matched * 1. / total
+
+
 def find_profiles(current_config, profiles):
-    "Find profiles matching the currently connected outputs"
+    "Find profiles matching the currently connected outputs, sorting asterisk matches to the back"
     detected_profiles = []
     for profile_name, profile in profiles.items():
         config = profile["config"]
@@ -588,7 +608,9 @@ def find_profiles(current_config, profiles):
         if not matches or any((name not in config.keys() for name in current_config.keys() if current_config[name].edid)):
             continue
         if matches:
-            detected_profiles.append(profile_name)
+            closeness = max(match_asterisk(output.edid, current_config[name].edid), match_asterisk(current_config[name].edid, output.edid))
+            detected_profiles.append((closeness, profile_name))
+    detected_profiles = [o[1] for o in sorted(detected_profiles, key=lambda x: -x[0])]
     return detected_profiles
 
 
@@ -717,6 +739,9 @@ def get_fb_dimensions(configuration):
 
 def apply_configuration(new_configuration, current_configuration, dry_run=False):
     "Apply a configuration"
+    found_top_left_monitor = False
+    found_left_monitor = False
+    found_top_monitor = False
     outputs = sorted(new_configuration.keys(), key=lambda x: new_configuration[x].sort_key)
     if dry_run:
         base_argv = ["echo", "xrandr"]
@@ -776,8 +801,21 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False)
                                 option_vector = option_vector[:option_index] + option_vector[option_index + 2:]
                         except ValueError:
                             pass
-
-            enable_outputs.append(option_vector)
+            if not found_top_left_monitor:
+                position = new_configuration[output].options.get("pos", "0x0")
+                if position == "0x0":
+                    found_top_left_monitor = True
+                    enable_outputs.insert(0, option_vector)
+                elif not found_left_monitor and position.startswith("0x"):
+                    found_left_monitor = True
+                    enable_outputs.insert(0, option_vector)
+                elif not found_top_monitor and position.endswith("x0"):
+                    found_top_monitor = True
+                    enable_outputs.insert(0, option_vector)
+                else:
+                    enable_outputs.append(option_vector)
+            else:
+                enable_outputs.append(option_vector)
 
     # Perform pe-change auxiliary changes
     if auxiliary_changes_pre:
@@ -804,6 +842,13 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False)
         # In the context of a xrandr call that changes the display state, `--query' should do nothing
         disable_outputs.insert(0, ['--query'])
 
+    # If we did not find a candidate, we might need to inject a call
+    # If there is no output to disable, we will enable 0x and x0 at the same time
+    if not found_top_left_monitor and len(disable_outputs) > 0:
+        # If the call to 0x and x0 is splitted, inject one of them
+        if found_top_monitor and found_left_monitor:
+            enable_outputs.insert(0, enable_outputs[0])
+
     # Enable the remaining outputs in pairs of two operations
     operations = disable_outputs + enable_outputs
     for index in range(0, len(operations), 2):
@@ -992,11 +1037,12 @@ 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")
 
-    candidate_directories = [user_profile_path]
-    for config_dir in os.environ.get("XDG_CONFIG_DIRS", "/etc/xdg").split(":"):
-        candidate_directories.append(os.path.join(config_dir, "autorandr"))
+    candidate_directories = []
     if profile_path:
         candidate_directories.append(profile_path)
+    candidate_directories.append(user_profile_path)
+    for config_dir in os.environ.get("XDG_CONFIG_DIRS", "/etc/xdg").split(":"):
+        candidate_directories.append(os.path.join(config_dir, "autorandr"))
 
     for folder in candidate_directories:
         if script_name not in ran_scripts:
@@ -1061,7 +1107,7 @@ def dispatch_call_to_sessions(argv):
             os.chdir(pwent.pw_dir)
             os.environ.clear()
             os.environ.update(process_environ)
-            os.execl(autorandr_binary, autorandr_binary, *argv[1:])
+            os.execl(sys.executable, sys.executable, autorandr_binary, *argv[1:])
             os.exit(1)
         os.waitpid(child_pid, 0)
 
@@ -1084,7 +1130,11 @@ def dispatch_call_to_sessions(argv):
             continue
 
         process_environ = {}
-        for environ_entry in open(environ_file).read().split("\0"):
+        for environ_entry in open(environ_file, 'rb').read().split(b"\0"):
+            try:
+                environ_entry = environ_entry.decode("ascii")
+            except UnicodeDecodeError:
+                continue
             name, sep, value = environ_entry.partition("=")
             if name and sep:
                 if name == "DISPLAY" and "." in value:
@@ -1310,6 +1360,7 @@ def main(argv):
             "CURRENT_PROFILES": ":".join(current_profiles)
         }
 
+        best_index = 9999
         for profile_name in profiles.keys():
             if profile_blocked(os.path.join(profile_path, profile_name), block_script_metadata):
                 if "--current" not in options and "--detected" not in options:
@@ -1317,9 +1368,15 @@ def main(argv):
                 continue
             props = []
             if profile_name in detected_profiles:
-                props.append("(detected)")
-                if ("-c" in options or "--change" in options) and not load_profile:
+                if len(detected_profiles) == 1:
+                    index = 1
+                    props.append("(detected)")
+                else:
+                    index = detected_profiles.index(profile_name) + 1
+                    props.append("(detected) (%d%s match)" % (index, ["st", "nd", "rd"][index - 1] if index < 4 else "th"))
+                if ("-c" in options or "--change" in options) and index < best_index:
                     load_profile = profile_name
+                    best_index = index
             elif "--detected" in options:
                 continue
             if profile_name in current_profiles:
diff --git a/contrib/etc/xdg/autostart/autorandr-launcher.desktop b/contrib/etc/xdg/autostart/autorandr-launcher.desktop
new file mode 100644 (file)
index 0000000..1a5bff0
--- /dev/null
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Name=Autorandr Launcher
+Comment=Automatically run autorandr whenever the configuration changes
+Type=Application
+Exec=/usr/bin/autorandr-launcher
+X-GNOME-Autostart-Phase=Initialization
diff --git a/contrib/zsh_completion/_autorandr b/contrib/zsh_completion/_autorandr
new file mode 100644 (file)
index 0000000..b6cacbb
--- /dev/null
@@ -0,0 +1,40 @@
+#compdef autorandr
+
+__autorandr_profile () {
+    declare -a virtual
+    virtual=("off":"disable all outputs"
+             "common":"clone at the largest common resolution"
+             "clone-largest":"clone with the largest resolution"
+             "horizontal":"stack all connected outputs horizontally"
+             "vertical":"stack all connected outputs vertically")
+    _describe -t virtual-profiles "virtual profiles" virtual
+    __autorandr_saved_profile
+}
+__autorandr_saved_profile () {
+    declare -a saved
+    saved=(${${(f)${:-"$(autorandr)"}}/ /:})
+    _describe -t profiles "saved profiles" saved
+}
+
+_autorandr () {
+    local curcontext="$curcontext" state line exclude="-s --save -l --load -r --remove -c --change"
+
+    _arguments -C \
+       "(: -)"{-h,--help}"[get help]" \
+       "($exclude)"{-c,--change}"[automatically load the first detected profile]" \
+       "($exclude)"{-d,--default}"[set default profile]:profile:__autorandr_profile" \
+       "($exclude)"{-l,--load}"[load profile]:profile:__autorandr_profile" \
+       "($exclude)"{-s,--save}"[save current setup to a profile]:profile: " \
+       "($exclude)"{-r,--remove}"[remove profile]:profile:__autorandr_saved_profile" \
+       --batch"[run autorandr for all users]" \
+       --current"[list current active configurations]" \
+       --config"[dump current xrandr setup]" \
+       --debug"[enable verbose output]" \
+       --dry-run"[don't change anything]" \
+       --fingerprint"[fingerprint current hardware]" \
+       --force"[force loading of a profile]" \
+       --skip-options"[skip xrandr options]:xrandr options:_values -s , options gamma brightness panning transform primary mode pos rate" \
+       --version"[show version]"
+}
+
+_autorandr "$@"
index 55b35412be951fd9a6b1341d2f5a63b9ff0de518..75713a8fc68bb05dcb89adec154d19d9227f21be 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -9,7 +9,7 @@ except:
 setup(
     name='autorandr',
 
-    version='1.9.post1',
+    version='1.10.post1',
 
     description='Automatically select a display configuration based on connected devices',
     long_description=long_description,