84 lines
2.2 KiB
Python
84 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Keep one DropWall GTK target attached to a single Noctalia stream."""
|
|
|
|
import argparse
|
|
import ctypes
|
|
import os
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
|
|
# Exit immediately when Noctalia closes runStream's stdout pipe.
|
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
|
|
|
|
def arm_parent_death_signal():
|
|
"""Ask Linux to terminate us if Noctalia disappears."""
|
|
parent = os.getppid()
|
|
try:
|
|
libc = ctypes.CDLL(None, use_errno=True)
|
|
if libc.prctl(1, signal.SIGTERM, 0, 0, 0) != 0: # PR_SET_PDEATHSIG
|
|
return
|
|
if os.getppid() != parent:
|
|
os.kill(os.getpid(), signal.SIGTERM)
|
|
except (AttributeError, OSError):
|
|
return
|
|
|
|
|
|
def emit(line):
|
|
print(line, flush=True)
|
|
|
|
|
|
def worker_command(args):
|
|
helper = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dropwall_helper.py")
|
|
return [sys.executable, "-B", helper, "--layer", args.layer]
|
|
|
|
|
|
def run_worker(command):
|
|
process = subprocess.Popen(
|
|
command,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
encoding="utf-8",
|
|
errors="replace",
|
|
bufsize=1,
|
|
)
|
|
assert process.stdout is not None
|
|
for line in process.stdout:
|
|
emit(line.rstrip("\n"))
|
|
return process.wait()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="DropWall helper supervisor")
|
|
parser.add_argument("--layer", choices=("background", "bottom"), default="background")
|
|
args = parser.parse_args()
|
|
|
|
arm_parent_death_signal()
|
|
command = worker_command(args)
|
|
lock_retry_seconds = 2
|
|
|
|
while True:
|
|
try:
|
|
exit_code = run_worker(command)
|
|
except OSError as error:
|
|
emit("ERR\tcould not start the GTK helper: %s" % str(error).replace("\n", " "))
|
|
exit_code = 127
|
|
|
|
emit("RESTART\t%d" % exit_code)
|
|
if exit_code == 3:
|
|
# A collision normally means the previous runtime is still
|
|
# shutting down. Back off if it is a genuinely persistent owner.
|
|
time.sleep(lock_retry_seconds)
|
|
lock_retry_seconds = min(lock_retry_seconds * 2, 60)
|
|
else:
|
|
lock_retry_seconds = 2
|
|
time.sleep(30)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|