swayfx-mosaic/tooling/probes/step20c-effects-damage/run.py

125 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Bounded headless FX0–FX4 and camera-state source fixture."""
import argparse
import fcntl
import json
from pathlib import Path
import subprocess
import sys
from PIL import Image
ap = argparse.ArgumentParser()
ap.add_argument('lab', type=Path)
ap.add_argument('mode', choices=('control', 'private_standard', 'private_mosaic'))
ap.add_argument('--camera-pairs', type=int, choices=range(4), default=3)
ap.add_argument('--reference-resize', action='store_true',
help='replay the retained late-resize reference variant')
args = ap.parse_args()
lab = args.lab.resolve()
repo = Path(__file__).resolve().parents[3]
# WHY: reproduction and trace controls must not multiply camera repetitions.
# CONTRACT: one active runner per lab; camera pairs are counted across runs.
# INVARIANT: at most three new hide/return pairs in a fresh lab campaign.
campaign_lock = (lab / 'effects-campaign.lock').open('a')
fcntl.flock(campaign_lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
prior_pairs = sum(len(list(run.glob('left-T4-hidden-*.png')))
for run in (lab / 'runs').glob('private_mosaic-effects-*'))
budget_file = lab / 'effects-camera-budget.json'
if budget_file.exists():
consumed = json.loads(budget_file.read_text())['consumed']
if type(consumed) is not int or consumed < 0:
ap.error('invalid persisted camera budget')
prior_pairs = max(prior_pairs, consumed)
if args.mode == 'private_mosaic' and args.camera_pairs and prior_pairs + args.camera_pairs > 3:
ap.error(f'campaign camera budget exceeded: {prior_pairs} already recorded; use --camera-pairs 0')
if args.mode == 'private_mosaic' and args.camera_pairs:
# Reserve before launch: failed attempts also consume the finite budget.
temporary_budget = budget_file.with_suffix('.tmp')
temporary_budget.write_text(json.dumps({'consumed': prior_pairs + args.camera_pairs}) + '\n')
temporary_budget.replace(budget_file)
pattern = Image.new('RGB', (64, 64))
for y in range(64):
for x in range(64):
pattern.putpixel((x, y), (245, 245, 245) if (x // 8) % 2 == 0
else (15, 25, 45))
pattern.save(lab / 'fx-checker.png')
source = (repo / 'tooling/probes/step20c-applied-state-input/run.py').read_text()
old = "choices=('cycles', 'buffer_cycles', 'identity', 'outside', 'hidden_click', 'toggle')"
assert source.count(old) == 1
adapted = source.replace(old, old[:-1] + ", 'effects')")
anchor = " if args.kind == 'cycles':\n"
assert adapted.count(anchor) == 1
scenario = ''' if args.kind == 'effects':
# CONTRACT: ordinary IPC creates effects; snapshots follow settled
# states. No compositor scheduler or rendering implementation changes.
spawn(['swaybg', '-i', str(lab / 'fx-checker.png'), '-m', 'tile'],
'swaybg.log')
time.sleep(.3)
command('[app_id="wev"] floating enable')
if EFFECT_REFERENCE_RESIZE:
command('[app_id="wev"] resize set width 300 height 250')
command('[app_id="wev"] move position 250 250')
command('gaps inner 24')
if args.mode != 'private_mosaic':
command('[app_id="org.mosaic.NestedGrabProbe"] focus')
command('move right')
command('move right')
command('shadows disable')
command('blur disable')
command('shadow_blur_radius 16')
command('shadow_offset 30 0')
command('shadow_color #000000A0')
command('blur_radius 5')
command('blur_passes 3')
if args.mode == 'private_mosaic': scroll(400)
record('effect_owner', owner=owner()['rect'], mode=args.mode)
time.sleep(.2)
snapshot('FX0-off')
command('shadows enable')
time.sleep(.2)
snapshot('FX1-shadow')
command('shadows disable')
command('blur enable')
time.sleep(.2)
snapshot('FX2-blur')
command('blur_xray enable')
time.sleep(.2)
snapshot('FX3-optimized-candidate')
command('shadows enable')
time.sleep(.2)
snapshot('FX4-combined')
if args.mode == 'private_mosaic':
for pair in range(1, EFFECT_CAMERA_PAIRS + 1):
scroll(1280)
time.sleep(.15)
snapshot(f'T4-hidden-{pair}')
scroll(400)
time.sleep(.15)
snapshot(f'T5-return-{pair}')
command('layout splith')
time.sleep(.2)
snapshot('T7-standard-return')
command('blur disable')
command('shadows disable')
time.sleep(.2)
snapshot('T6-disabled')
command('blur enable')
command('shadows enable')
time.sleep(.2)
snapshot('T6-restored')
client.stdin.write('close_owner\\n')
client.stdin.flush()
client.wait(timeout=8)
record('owner_closed', exit_code=client.returncode)
time.sleep(.2)
snapshot('T6-closed')
elif args.kind == 'cycles':
'''
adapted = adapted.replace(anchor, scenario).replace('EFFECT_CAMERA_PAIRS', str(args.camera_pairs))
adapted = adapted.replace('EFFECT_REFERENCE_RESIZE', str(args.reference_resize))
generated = lab / 'effects-damage-run-generated.py'
generated.write_text(adapted)
raise SystemExit(subprocess.call([sys.executable, str(generated), str(lab),
args.mode, 'effects', '--asi-mode', '0', '--max-seconds', '120',
'--max-captures', '40', '--max-bytes', '35000000']))