from functools import reduce
from itertools import chain
-try:
- from packaging.version import Version
-except ModuleNotFoundError:
- from distutils.version import LooseVersion as Version
if sys.version_info.major == 2:
import ConfigParser as configparser
else:
import configparser
-__version__ = "1.12.1"
+__version__ = "1.13.3"
try:
input = raw_input
("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),
+ ("horizontal-reverse", "Stack all connected outputs horizontally at their largest resolution in reverse order", None),
+ ("vertical-reverse", "Stack all connected outputs vertically at their largest resolution in reverse order", None),
]
properties = [
--dry-run don't change anything, only print the xrandr commands
--fingerprint fingerprint your current hardware setup
--ignore-lid treat outputs as connected even if their lids are closed
---match-edid match diplays based on edid instead of name
+--match-edid match displays based on edid instead of name
--force force (re)loading of a profile / overwrite exiting files
--list list configurations
--skip-options <option> comma separated list of xrandr arguments (e.g. "gamma")
""".strip()
+class Version(object):
+ def __init__(self, version):
+ self._version = version
+ self._version_parts = re.split("([0-9]+)", version)
+
+ def __eq__(self, other):
+ return self._version_parts == other._version_parts
+
+ def __lt__(self, other):
+ for my, theirs in zip(self._version_parts, other._version_parts):
+ if my.isnumeric() and theirs.isnumeric():
+ my = int(my)
+ theirs = int(theirs)
+ if my < theirs:
+ return True
+ return len(theirs) > len(my)
+
+ def __ge__(self, other):
+ return not (self < other)
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __le__(self, other):
+ return (self < other) or (self == other)
+
+ def __gt__(self, other):
+ return self >= other and not (self == other)
+
def is_closed_lid(output):
if not re.match(r'(eDP(-?[0-9]\+)*|LVDS(-?[0-9]\+)*)', output):
return False
return x + 10000 * y
def __init__(self, output, edid, options):
- "Instanciate using output name, edid and a dictionary of XRandR command line parameters"
+ "Instantiate using output name, edid and a dictionary of XRandR command line parameters"
self.output = output
self.edid = edid
self.options = options
if self.edid:
if self.EDID_UNAVAILABLE in self.edid:
return
+ if "*" in self.edid:
+ return
# Thx to pyedid project, the following code was
# copied (and modified) from pyedid/__init__py:21 [parse_edid()]
raw = bytes.fromhex(self.edid)
self.ignored_options = list(options)
def remove_default_option_values(self):
- "Remove values from the options dictionary that are superflous"
+ "Remove values from the options dictionary that are superfluous"
if "off" in self.options and len(self.options.keys()) > 1:
self.options = {"off": None}
return
@classmethod
def from_xrandr_output(cls, xrandr_output):
- """Instanciate an XrandrOutput from the output of `xrandr --verbose'
+ """Instantiate an XrandrOutput from the output of `xrandr --verbose'
This method also returns a list of modes supported by the output.
"""
@classmethod
def from_config_file(cls, profile, edid_map, configuration):
- "Instanciate an XrandrOutput from the contents of a configuration file"
+ "Instantiate an XrandrOutput from the contents of a configuration file"
options = {}
for line in configuration.split("\n"):
if line:
# We are not interested in screens
xrandr_output = re.sub("(?m)^Screen [0-9].+", "", xrandr_output).strip()
- # Split at output boundaries and instanciate an XrandrOutput per output
+ # Split at output boundaries and instantiate an XrandrOutput per output
split_xrandr_output = re.split("(?m)^([^ ]+ (?:(?:dis)?connected|unknown connection).*)$", xrandr_output)
if len(split_xrandr_output) < 2:
raise AutorandrException("No output boundaries found", report_bug=True)
# 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 the call to 0x and x0 is split, inject one of them
if found_top_monitor and found_left_monitor:
enable_outputs.insert(0, enable_outputs[0])
if call_and_retry(argv, dry_run=dry_run) != 0:
raise AutorandrException("Command failed: %s" % " ".join(map(shlex.quote, argv)))
+ # Adjust the frame buffer to match (see #319)
+ if fb_args:
+ argv = base_argv
+ if call_and_retry(argv, dry_run=dry_run) != 0:
+ raise AutorandrException("Command failed: %s" % " ".join(map(shlex.quote, argv)))
+
+
def is_equal_configuration(source_configuration, target_configuration):
"""
configuration[output].options["pos"] = "0x0"
else:
configuration[output].options["off"] = None
- elif profile_name in ("horizontal", "vertical"):
+ elif profile_name in ("horizontal", "vertical", "horizontal-reverse", "vertical-reverse"):
shift = 0
- if profile_name == "horizontal":
+ if profile_name.startswith("horizontal"):
shift_index = "width"
pos_specifier = "%sx0"
else:
shift_index = "height"
pos_specifier = "0x%s"
-
- for output in configuration:
+
+ config_iter = reversed(configuration) if "reverse" in profile_name else iter(configuration)
+
+ for output in config_iter:
configuration[output].options = {}
if output in modes and configuration[output].edid:
def key(a):
if os.access(script, os.X_OK | os.F_OK):
try:
all_ok &= subprocess.call(script, env=env) != 0
- except:
- raise AutorandrException("Failed to execute user command: %s" % (script,))
+ except Exception as e:
+ raise AutorandrException("Failed to execute user command: %s. Error: %s" % (script, str(e)))
ran_scripts.add(script_name)
script_folder = os.path.join(folder, "%s.d" % script_name)
if os.access(script, os.X_OK | os.F_OK):
try:
all_ok &= subprocess.call(script, env=env) != 0
- except:
- raise AutorandrException("Failed to execute user command: %s" % (script,))
+ except Exception as e:
+ raise AutorandrException("Failed to execute user command: %s. Error: %s" % (script, str(e)))
ran_scripts.add(check_name)
return all_ok