162 lines
4.5 KiB
Python
Executable File
162 lines
4.5 KiB
Python
Executable File
#!@python@/bin/python
|
|
import argparse
|
|
import json
|
|
import os
|
|
import random
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
|
|
import filelock
|
|
|
|
class Screensaver:
|
|
def __init__(self, cmd, env=None, weight=1):
|
|
self.cmd = cmd
|
|
self.weight = weight
|
|
|
|
if env is not None:
|
|
self.env = os.environ.copy()
|
|
for k, v in env.items():
|
|
self.env[k] = v
|
|
else:
|
|
self.env = None
|
|
self.proc = None
|
|
|
|
def start(self):
|
|
assert self.proc is None
|
|
self.proc = subprocess.Popen(self.cmd, env=self.env)
|
|
|
|
def wait(self):
|
|
assert self.proc is not None
|
|
self.proc.wait()
|
|
|
|
def stop(self, kill=False):
|
|
assert self.proc is not None
|
|
if kill:
|
|
self.proc.kill()
|
|
else:
|
|
self.proc.terminate()
|
|
|
|
class DoomSaver(Screensaver):
|
|
wad = '@doomWad@'
|
|
|
|
def __init__(self, demo_index, weight=3):
|
|
super().__init__(
|
|
['@chocoDoom@/bin/chocolate-doom',
|
|
'-iwad', self.wad,
|
|
'-demoloopi', str(demo_index)],
|
|
env={
|
|
'SDL_AUDIODRIVER': 'null',
|
|
'SDL_VIDEODRIVER': 'caca',
|
|
'CACA_DRIVER': 'ncurses',
|
|
},
|
|
weight=weight,
|
|
)
|
|
|
|
def stop(self):
|
|
super().stop(kill=True)
|
|
|
|
class MultiSaver:
|
|
savers = [
|
|
DoomSaver(0),
|
|
DoomSaver(1),
|
|
DoomSaver(2),
|
|
]
|
|
state_filename = 'screensaver.json'
|
|
|
|
def __init__(self):
|
|
self.state_path = os.path.join(f'/run/user/{os.geteuid()}', self.state_filename)
|
|
self.lock = filelock.FileLock(f'{self.state_path}.lock')
|
|
|
|
self.selected = None
|
|
self.cleaned_up = False
|
|
|
|
def select(self):
|
|
assert self.selected is None
|
|
with self.lock:
|
|
if not os.path.exists(self.state_path):
|
|
state = {'instances': []}
|
|
else:
|
|
with open(self.state_path) as f:
|
|
state = json.load(f)
|
|
|
|
available = set(range(len(self.savers)))
|
|
new_instances = []
|
|
for instance in state['instances']:
|
|
if not os.path.exists(f"/proc/{instance['pid']}"):
|
|
continue
|
|
|
|
new_instances.append(instance)
|
|
i = instance['saver']
|
|
assert i in available
|
|
available.remove(i)
|
|
assert available, 'No screensavers left'
|
|
available = list(available)
|
|
|
|
weights = []
|
|
for i in available:
|
|
weights.append(self.savers[i].weight)
|
|
selected_i = random.choices(available, weights=weights)[0]
|
|
|
|
new_instances.append({'pid': os.getpid(), 'saver': selected_i})
|
|
state['instances'] = new_instances
|
|
|
|
with open(self.state_path, 'w') as f:
|
|
json.dump(state, f)
|
|
|
|
print(f'Selected saver {selected_i}')
|
|
self.selected = self.savers[selected_i]
|
|
|
|
def cleanup(self):
|
|
if self.cleaned_up:
|
|
return
|
|
self.cleaned_up = True
|
|
|
|
with self.lock:
|
|
with open(self.state_path) as f:
|
|
state = json.load(f)
|
|
|
|
for i, instance in enumerate(state['instances']):
|
|
if instance['pid'] == os.getpid():
|
|
del state['instances'][i]
|
|
|
|
with open(self.state_path, 'w') as f:
|
|
json.dump(state, f)
|
|
|
|
def run(self):
|
|
assert self.selected is not None
|
|
self.selected.start()
|
|
|
|
signal.signal(signal.SIGINT, self._sighandler)
|
|
signal.signal(signal.SIGTERM, self._sighandler)
|
|
signal.signal(signal.SIGHUP, self._sighandler)
|
|
self.selected.wait()
|
|
self.cleanup()
|
|
|
|
def stop(self):
|
|
assert self.selected is not None
|
|
print('Shutting down')
|
|
self.selected.stop()
|
|
self.cleanup()
|
|
def _sighandler(self, signum, frame):
|
|
self.stop()
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Wayland terminal-based lock screen')
|
|
parser.add_argument('-t', '--terminal', default='alacritty', help='Terminal emulator to use')
|
|
parser.add_argument('-i', '--instance', action='store_true', help='Run as instance')
|
|
|
|
args = parser.parse_args()
|
|
if not args.instance:
|
|
subprocess.check_call([
|
|
'swaylock-plugin', '--command-each',
|
|
f'@windowtolayer@/bin/windowtolayer -- {args.terminal} -e {sys.argv[0]} --instance'])
|
|
return
|
|
|
|
ms = MultiSaver()
|
|
ms.select()
|
|
ms.run()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|