Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ default values in the form `option-name=option-argument`.
A common and effective use of this is to specify default `skip-options`, for
instance skipping the `gamma` setting if using
[`redshift`](https://github.com/jonls/redshift) as a daemon. To implement
the equivalent of `--skip-options gamma`, your `settings.ini` file should look
like this:

```
Expand Down Expand Up @@ -238,6 +237,57 @@ notify-send -i display "Display profile" "$AUTORANDR_CURRENT_PROFILE"
The one kink is that during `preswitch`, `AUTORANDR_CURRENT_PROFILE` is
reporting the *upcoming* profile rather than the *current* one.

#### Exit status

If a hook script exits with a non-zero exit status, a warning will be output, but the
operation continue.

#### Canceling operation from within a hook script

A hook script can send a `SIGUSR1` signal to its parent process to indicate that the
current operation should halt. The script is allowed to finish in any case.
Only scripts with names starting with `pre` support this feature.

Bash example:

```bash
#!/usr/bin/env bash

should_continue() {
# No
return 1
}

if ! should_continue; then
# Send SIGUSR1 signal to parent process to cancel operation
kill -s USR1 "$PPID"
exit
fi

# Continue ...
```

Python example:

```python
#!/usr/bin/env python
import os
import sys
import signal

def should_continue():
# No
return False

if __name__ == "__main__":
if not should_continue():
# Send SIGUSR1 signal to parent process to cancel operation
os.kill(os.getppid(), signal.SIGUSR1)
sys.exit()

# Continue ...
```

### Wildcard EDID matching

The EDID strings in the `~/.config/autorandr/*/setup` files may contain an
Expand Down
47 changes: 39 additions & 8 deletions autorandr.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

from __future__ import print_function

from signal import signal, SIGUSR1, SIG_DFL

import binascii
import copy
import getopt
Expand Down Expand Up @@ -61,6 +63,9 @@
except NameError:
pass


cancel_signal = SIGUSR1

virtual_profiles = [
# (name, description, callback)
("off", "Disable all outputs", None),
Expand Down Expand Up @@ -1179,10 +1184,7 @@ def exec_scripts(profile_path, script_name, meta_information=None):
if script_name not in ran_scripts:
script = os.path.join(folder, 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,))
all_ok &= exec_one_script(script, script_name, env) != 0
ran_scripts.add(script_name)

script_folder = os.path.join(folder, "%s.d" % script_name)
Expand All @@ -1192,15 +1194,44 @@ def exec_scripts(profile_path, script_name, meta_information=None):
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):
try:
all_ok &= subprocess.call(script, env=env) != 0
except:
raise AutorandrException("Failed to execute user command: %s" % (script,))
all_ok &= exec_one_script(script, script_name, env) != 0
ran_scripts.add(check_name)

return all_ok


def exec_one_script(script, script_name, env):
""""Run a userscript and return the exit code.

If the script exits with a non-zero exit status, a warning is sent to stderr but the operation continues.

If the script sends a SIGUSR1 signal to the parent, the parent process will
wait for the child process to exit, then halt with a zero status.
"""
def handle_cancel_signal(signum, frame):
if not script_name.startswith('pre'):
print("Script %s issued %s signal, but it is ignored for %s scripts." % (
script, cancel_signal.name, script_name), file=sys.stderr)
return

main_operation_name = script_name[3:]

# Clean up, inform, exit
print("Script %s issued %s signal. Cancelling %s operation." % (script, cancel_signal.name, main_operation_name))
sys.exit()

try:
signal(cancel_signal, handle_cancel_signal)
return subprocess.check_call(script, env=env)
except subprocess.CalledProcessError as e:
print("Warning: Script %s returned exit code %d." % (script, e.returncode), file=sys.stderr)
except Exception as e:
raise AutorandrException("Failed to execute user command: %s" % (script,), original_exception=e)
finally:
# Revert to default handler
signal(cancel_signal, SIG_DFL)


def dispatch_call_to_sessions(argv):
"""Invoke autorandr for each open local X11 session with the given options.

Expand Down