feat: add whyoolw/dropwall (#6)
Co-authored-by: whyoolw <whyoolw@gmail.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user