#!/usr/bin/env python3 """Opt-in bounded private XTest keyboard and optional relative-look experiment. Not a launcher or gameplay proof.""" import argparse import copy import ctypes as C import hashlib import json import multiprocessing import math import os from pathlib import Path import re import signal import stat import subprocess import sys import time import headless_display_probe as display NOTIFY = None def notify(value): if NOTIFY is not None: NOTIFY.send(value) class Refused(ValueError): pass class KeyStateBusy(Refused): pass def option(args, name): if args.count(name) != 1: raise Refused('Missing or repeated native option: ' + name) index = args.index(name) if index + 1 >= len(args): raise Refused('Missing native option value') return args[index + 1] def gate_options(native, args): # Two exact read-only options; a prefix match must never admit an alias, # duplicate, foreign nonce or extra diagnostic in keyboard-only mode. held = getattr(args, 'enable_private_held_action_proof', False) held_tokens = [value for value in native if value.startswith('-errOrdinaryHeld')] if type(held) is not bool or (held and not (getattr(args, 'enable_private_coarse_facing_proof', False) and getattr(args, 'enable_private_selection_proof', False))): raise Refused('Held action requires explicit coarse facing and selection') if held_tokens != (['-errOrdinaryHeldAction'] if held else []): raise Refused('Unexpected, missing or repeated native held observer option') if held and option(native, '-errOrdinaryHeldAction') != args.nonce: raise Refused('Native held observer nonce mismatch') tokens = [value for value in native if value.startswith('-errOrdinaryGate')] if any(value not in {'-errOrdinaryGateObserve', '-errOrdinaryGateChoices'} for value in tokens): raise Refused('Unknown native gate observer option') if getattr(args, 'enable_private_coarse_facing_proof', False) and not (getattr(args, 'enable_private_coarse_approach_proof', False) and getattr(args, 'enable_private_selection_proof', False)): raise Refused('Coarse facing requires coarse approach and selection') approach = getattr(args, 'enable_private_approach_proof', False) if getattr(args, 'enable_private_coarse_approach_proof', False) and not approach: raise Refused('Coarse approach requires explicit approach proof') if getattr(args, 'enable_private_selection_proof', False) and '-errOrdinaryGateChoices' not in tokens: raise Refused('Selection requires matching native gate choices') if approach and '-errOrdinaryGateObserve' not in tokens: raise Refused('Missing native gate observer nonce') if '-errOrdinaryGateChoices' in tokens and not approach: raise Refused('Native gate choices require explicit approach proof') for name in set(tokens): if option(native, name) != args.nonce: raise Refused('Native gate observer nonce mismatch') def game_arguments(owner, args): native = display.process(owner['pid']) if display.identity(native) != owner['process']: raise Refused('Game process changed') if option(native['args'], '-errOrdinaryInputObserve') != args.nonce: raise Refused('Native observer nonce mismatch') if getattr(args, 'enable_private_look_proof', False) and option(native['args'], '-errOrdinaryLookObserve') != args.nonce: raise Refused('Native look observer nonce mismatch') gate_options(native['args'], args) if option(native['args'], '-errOrdinaryScenario') not in ('core', 'chat'): raise Refused('Not an ordinary successor scenario') if option(native['args'], '-profile') != 'Z:' + str(args.profile): raise Refused('Native profile mismatch') NUMBER = r'[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?' VECTOR = r'<\s*(' + NUMBER + r')\s*,\s*(' + NUMBER + r')\s*,\s*(' + NUMBER + r')\s*>' LOOK_HEADER = re.compile(r'ERR LOOK OBSERVE: run=([A-Za-z0-9]{1,32}) sample=(\d+) real=(\d+) (.+)$') LOOK_SAMPLE = re.compile('SAMPLE direction=' + VECTOR + ' camera=' + VECTOR + ' body=' + VECTOR + ' bodyForward=' + VECTOR + ' turn=' + VECTOR) INPUT_SAMPLE = re.compile('SAMPLE origin=' + VECTOR + ' forward=(' + NUMBER + ') input=' + VECTOR + ' velocity=' + VECTOR + r' inventory=(0|1|true|false)') def parse_look_record(line, nonce): """Strict live handshake parser; final qualification independently reparses logs.""" if 'ERR LOOK OBSERVE:' not in line: return None match = LOOK_HEADER.search(line) if not match or match[1] != nonce or len(line) > 2048: raise Refused('Malformed or foreign look observation') sample, real, detail = int(match[2]), int(match[3]), match[4] if not 0 <= sample <= 450 or not 0 <= real <= 2147483647: raise Refused('Look observation bounds invalid') record = {'sample': sample, 'real_ms': real, 'raw': line} if detail == 'BASIS native-player-camera-world-transform': return {**record, 'kind': 'basis'} if re.fullmatch(r'CANCELLED [a-z-]+', detail): return {**record, 'kind': 'cancelled', 'reason': detail.split(' ', 1)[1]} values = LOOK_SAMPLE.fullmatch(detail) if not values: raise Refused('Malformed native look sample') numbers = [float(value) for value in values.groups()] if not all(math.isfinite(value) for value in numbers): raise Refused('Nonfinite native look sample') result = {**record, 'kind': 'sample'} for index, name in enumerate(('direction', 'camera', 'body', 'bodyForward', 'turn')): result[name] = numbers[index*3:index*3+3] for name in ('direction', 'bodyForward'): length = math.sqrt(sum(value*value for value in result[name])) if not .95 <= length <= 1.05 or math.hypot(result[name][0], result[name][2]) <= .1: raise Refused('Native look direction is degenerate or not normalized') return result def parse_stationary_sample(sample, real, detail): match = INPUT_SAMPLE.fullmatch(detail) if not match: raise Refused('Malformed input sample during look observation') values = [float(value) for value in match.groups()[:-1]] if not all(math.isfinite(value) for value in values): raise Refused('Nonfinite input sample during look observation') return {'sample': sample, 'real_ms': real, 'origin': values[:3], 'forward': values[3], 'input': values[4:7], 'velocity': values[7:10], 'inventory': match.groups()[-1] in ('1', 'true')} def yaw(direction): return math.degrees(math.atan2(direction[0], direction[2])) def yaw_delta(first, second): return (yaw(second) - yaw(first) + 180) % 360 - 180 class RelativeMouse: def __init__(self, query, keyboard, owner, enable_approach=False, enable_alignment=False, enable_coarse_approach=False, enable_coarse_facing=False, enable_selection=False): if type(enable_coarse_approach) is not bool or (enable_coarse_approach and not enable_approach): raise Refused("Coarse approach requires explicit approach capability") if type(enable_coarse_facing) is not bool or (enable_coarse_facing and not (enable_coarse_approach is True and enable_alignment is True and enable_selection is True)): raise Refused('Coarse facing requires explicit selection and coarse approach capability') self.enable_coarse_facing = enable_coarse_facing self.alignment_pixels = 0 self.alignment_requests = 0 self.enable_coarse_approach = enable_coarse_approach self.query, self.keyboard, self.owner = query, keyboard, owner self.enable_approach = enable_approach self.enable_alignment = enable_alignment self.xt = keyboard.xt self.xt.XTestFakeRelativeMotionEvent.restype = C.c_int self.xt.XTestFakeRelativeMotionEvent.argtypes = [C.c_void_p, C.c_int, C.c_int, C.c_ulong] query.x.XQueryPointer.restype = C.c_int query.x.XQueryPointer.argtypes = [C.c_void_p, C.c_ulong, C.POINTER(C.c_ulong), C.POINTER(C.c_ulong), C.POINTER(C.c_int), C.POINTER(C.c_int), C.POINTER(C.c_int), C.POINTER(C.c_int), C.POINTER(C.c_uint)] def pointer(self): root, child = C.c_ulong(), C.c_ulong() rx, ry, wx, wy = C.c_int(), C.c_int(), C.c_int(), C.c_int() mask = C.c_uint() if not self.query.x.XQueryPointer(self.query.d, self.owner['window'], C.byref(root), C.byref(child), C.byref(rx), C.byref(ry), C.byref(wx), C.byref(wy), C.byref(mask)): raise Refused('Private pointer is unavailable or on another screen') return {'root': root.value, 'child': child.value, 'root_x': rx.value, 'root_y': ry.value, 'mask': mask.value} def validate_pointer(self, target, dx, dy, allowed_mask=0): position = self.pointer() geometry = target['geometry']; left, top, right, bottom = target['focused_clipped_bounds'] if position['root'] != geometry['root'] or position['mask'] & ~allowed_mask != 0: raise Refused('Private pointer root or button/modifier state invalid') for x, y in ((position['root_x'], position['root_y']), (position['root_x']+dx, position['root_y']+dy)): if not left <= x < right or not top <= y < bottom: raise Refused('Relative pointer step leaves owned clipped bounds') child = position['child'] if child and self.query.server_pid(child) != self.owner['pid']: raise Refused('Pointer child belongs to a foreign native client') return position def move(self, dx, dy, guard): if type(dx) is not int or dx not in (-24, 24) or dy != 0 or type(dy) is not int: raise Refused('Unsupported relative look step') self._move(dx, dy, guard) def approach_move(self, dx, dy, guard, *, deadline, clock=time.monotonic): if (self.enable_approach is not True or type(dx) is not int or not 1 <= abs(dx) <= (384 if self.enable_coarse_approach else 128) or type(dy) is not int or dy != 0): raise Refused('Unsupported or unenabled approach relative step') def within_lease(): if clock() >= deadline: raise Refused("Original approach deadline exceeded before relative input") within_lease() self._move(dx, dy, guard, before_send=within_lease) def alignment_move(self, dx, dy, guard, *, deadline, clock=time.monotonic): if (self.enable_alignment is not True or self.enable_approach is not True or type(dx) is not int or not 1 <= abs(dx) <= (384 if self.enable_coarse_facing else 128) or type(dy) is not int or dy != 0): raise Refused('Unsupported or unenabled alignment relative step') def within_lease(): if type(deadline) not in (int, float) or not math.isfinite(deadline) or not 0 <= clock() < deadline: raise Refused('Original alignment deadline exceeded before relative input') within_lease() if self.alignment_requests >= 6 or self.alignment_pixels+abs(dx) > 768: raise Refused('Alignment request or cumulative pixel budget exhausted') def commit(): within_lease() self.alignment_requests += 1 self.alignment_pixels += abs(dx) self._move(dx, dy, guard, before_send=commit) def _move(self, dx, dy, guard, before_send=None): target = guard.check() self.keyboard.require_idle() self.validate_pointer(target, dx, dy) # Recheck process/focus after the pointer roundtrip, with no focus changes. target = guard.check() self.validate_pointer(target, dx, dy) if before_send is not None: before_send() if not self.xt.XTestFakeRelativeMotionEvent(self.query.d, dx, dy, 0): raise Refused('Native relative motion request failed') self.query.x.XFlush(self.query.d) def perform_look(guard, keyboard, observer, mouse, report, clock=time.monotonic, sleep=time.sleep): proof = {'requested': True, 'completed': False, 'started_monotonic': clock(), 'finished_monotonic': None, 'baseline_samples': [], 'response_sample': None, 'return_sample': None, 'motions': []} report['look_proof'] = proof deadline = proof['started_monotonic'] + 4 notify({'kind': 'look-pending'}) ready_sample = observer.ready_sample baseline = None def current(): if clock() >= deadline: raise Refused('Native look proof exceeded four seconds') observer.fresh(); guard.check(); keyboard.require_idle() if observer.events != ['READY'] or any(r['kind'] == 'cancelled' for r in observer.look_records): raise Refused('Native look context ended or inventory changed') if sum(r['kind'] == 'basis' for r in observer.look_records) != 1: return [] poses = [r for r in observer.look_records if r['kind'] == 'sample' and r['sample'] >= ready_sample] inputs = {r['sample']: r for r in observer.input_samples} joined = [] for pose in poses: sample = inputs.get(pose['sample']) if sample is None: continue if sample['real_ms'] != pose['real_ms'] or math.dist(sample['origin'], pose['body']) > .01: raise Refused('Native look/input body or clock mismatch') ready_input = inputs.get(ready_sample) if ready_input is None: raise Refused('Missing native READY body sample') origin = ready_input['origin'] if (sample['inventory'] or abs(sample['forward']) > .01 or math.sqrt(sum(x*x for x in sample['input'])) > .01 or math.sqrt(sum(x*x for x in sample['velocity'])) > .05 or math.dist(pose['body'], origin) > .05): raise Refused('Player moved before look proof completed') joined.append(pose) for previous, following in zip(joined, joined[1:]): if following['sample'] != previous['sample'] + 1 or not 0 < following['real_ms'] - previous['real_ms'] <= 500: raise Refused('Native look sampling gap or repeated clock') if joined and observer.input_samples[-1]['real_ms'] - joined[-1]['real_ms'] > 500: raise Refused('Native look pose is stale relative to input') return joined def wait(predicate): while clock() < deadline: poses = current() if poses and predicate(poses): return poses[-1] sleep(.025) raise Refused('Native view rotation acknowledgement missing') def motion(dx, after): poses = current() if not poses: raise Refused('Native look sample unavailable before motion') watermark = poses[-1]['sample'] report['input_attempted'] = True mouse.move(dx, 0, guard) proof['motions'].append({'dx': dx, 'dy': 0, 'monotonic': clock(), 'after_sample': after['sample'], 'after_real_ms': after['real_ms']}) report['input_sent'] = True return watermark try: def stable(poses): return len(poses) >= 2 and poses[-1]['sample'] == poses[-2]['sample']+1 and abs(yaw_delta(poses[-2]['direction'], poses[-1]['direction'])) <= .1 wait(stable) poses = current() if not stable(poses): raise Refused('Initial native view no longer stable') baseline = poses[-1] proof['baseline_samples'] = [poses[-2]['sample'], baseline['sample']] outbound_after = motion(24, baseline) response = wait(lambda p: p[-1]['sample'] > outbound_after and .25 <= abs(yaw_delta(baseline['direction'], p[-1]['direction'])) <= 30) proof['response_sample'] = response['sample'] displacement = abs(yaw_delta(baseline['direction'], response['direction'])) return_after = motion(-24, response) returned = wait(lambda p: p[-1]['sample'] > return_after and abs(yaw_delta(baseline['direction'], p[-1]['direction'])) <= .35 and abs(yaw_delta(baseline['direction'], p[-1]['direction'])) < displacement/2) proof['return_sample'] = returned['sample'] current() if clock() >= deadline: raise Refused('Native look proof exceeded four seconds') proof['completed'] = True finally: proof['finished_monotonic'] = clock() notify({'kind': 'look-finished'}) class Observer: """Only records appended after this exact owned console was opened count.""" pattern = re.compile(r'ERR INPUT OBSERVE: run=([A-Za-z0-9]{1,32}) sample=(\d+) real=(\d+) world=(\d+(?:\.\d+)?) (.+)$') def __init__(self, path, nonce, clock=time.monotonic, enable_look=False, enable_approach=False, enable_selection=False, enable_held=False): if type(enable_held) is not bool or (enable_held and not (enable_selection and enable_approach and enable_look)): raise Refused('Held observation requires all original observation capabilities') self.enable_held = enable_held self.held_previous = None; self.held_anchor = None self.path, self.nonce, self.clock = path, nonce, clock self.fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) info = os.fstat(self.fd) if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid(): os.close(self.fd) raise Refused('Console is not a regular same-user file') self.identity = info.st_dev, info.st_ino self.start_offset = os.lseek(self.fd, 0, os.SEEK_END) self.offset = self.start_offset self.pending = b'' self.sample = self.real = self.world = -1 self.last_sample_at = None self.events = [] self.raw = [] self.bytes = 0 self.terminal = False if enable_selection and not (enable_approach and enable_look): os.close(self.fd) raise Refused("Selection observation requires approach and look") if enable_approach and not enable_look: os.close(self.fd) raise Refused("Approach observation requires native look") self.enable_look = enable_look self.enable_approach = enable_approach self.enable_selection = enable_selection self.choice_previous = 0; self.choice_real = -1; self.selection_previous = None self.approach_lines = []; self.gate_records = []; self.gate_received = {} self.gate_complete_end = 0; self.approach_previous = None; self.alignment_previous = None self.ready_at = self.ready_real = self.queued_real = None self.look_records = []; self.input_samples = []; self.ready_sample = None def close(self): os.close(self.fd) def poll(self): info = self.path.lstat() if (info.st_dev, info.st_ino) != self.identity or not stat.S_ISREG(info.st_mode) or info.st_size < self.offset: raise Refused('Console replaced or truncated') data = os.read(self.fd, 65536) self.offset += len(data); self.bytes += len(data) if self.bytes > 8 * 1024 * 1024: raise Refused('Console observation byte budget exceeded') self.pending += data if len(self.pending) > 131072: raise Refused('Console line budget exceeded') lines = self.pending.split(b'\n'); self.pending = lines.pop() for raw in lines: line = raw.decode('utf-8', 'strict').rstrip('\r') if self.enable_approach and any(prefix in line for prefix in (('ERR INPUT OBSERVE:', 'ERR LOOK OBSERVE:', 'ERR GATE OBSERVE:', 'ERR GATE CHOICE:') if self.enable_selection else ('ERR INPUT OBSERVE:', 'ERR LOOK OBSERVE:', 'ERR GATE OBSERVE:'))): if len(self.approach_lines) >= (2400 if self.enable_selection else 1900): raise Refused('Native approach record bound exceeded') self.approach_lines.append(line) if self.enable_selection and 'ERR GATE CHOICE:' in line: from ordinary_gate_choices import parse_choice_record record = parse_choice_record(line, self.nonce) if record['sample'] != self.choice_previous + 1 or record['real_ms'] <= self.choice_real: raise Refused('Native choice records missing, repeated or reversed') if not self.gate_records or self.gate_records[-1]['kind'] != 'geometry' or any(record[k] != self.gate_records[-1][k] for k in ('sample', 'real_ms')): raise Refused('Native choices lack exact preceding gate geometry') self.choice_previous = record['sample']; self.choice_real = record['real_ms'] self.gate_complete_end = len(self.approach_lines) self.gate_received.setdefault(record['sample'], self.clock()) continue if self.enable_approach and 'ERR GATE OBSERVE:' in line: from ordinary_gate_evidence import parse_gate_record record = parse_gate_record(line, self.nonce) self.gate_records.append(record) if len(self.gate_records) > 902: raise Refused('Native gate record bound exceeded') if record['kind'] == 'geometry' and not self.enable_selection: self.gate_complete_end = len(self.approach_lines) self.gate_received.setdefault(record['sample'], self.clock()) continue if self.enable_look and 'ERR LOOK OBSERVE:' in line: record = parse_look_record(line, self.nonce) if len(self.look_records) >= 453: raise Refused('Native look record bound exceeded') if self.look_records and (record['sample'] < self.look_records[-1]['sample'] or record['real_ms'] < self.look_records[-1]['real_ms']): raise Refused('Native look records reversed') if any(r['kind'] == 'cancelled' for r in self.look_records): raise Refused('Native look records after cancellation') if record['kind'] == 'basis' and (self.look_records or record['sample'] != 1): raise Refused('Repeated or misplaced native look basis') if record['kind'] == 'sample' and not self.look_records: raise Refused('Missing native look basis') if record['kind'] == 'sample' and any(r['kind'] == 'sample' and r['sample'] == record['sample'] for r in self.look_records): raise Refused('Duplicate native look sample') self.look_records.append(record) continue if 'ERR INPUT OBSERVE: run=' + self.nonce + ' ' not in line: continue match = self.pattern.search(line) if not match or match[1] != self.nonce: raise Refused('Malformed matching observer record') sample, real, world = int(match[2]), int(match[3]), float(match[4]) if not math.isfinite(world) or real > 2147483647: raise Refused('Invalid native observer clock') if sample < self.sample or real < self.real or world < self.world: raise Refused('Native observer clock/sample reversed') if sample > 450 or len(self.raw) >= 512: raise Refused('Native observer record budget exceeded') detail = match[5] if detail.startswith('SAMPLE '): if sample <= self.sample: raise Refused('Repeated native sample') self.last_sample_at = self.clock() if self.enable_look: self.input_samples.append(parse_stationary_sample(sample, real, detail)) self.sample, self.real, self.world = sample, real, world self.raw.append(line) if self.enable_approach and detail == 'QUEUED': if self.queued_real is not None: raise Refused('Repeated native queued clock') self.queued_real = real if detail == 'READY': if self.events or self.last_sample_at is None: raise Refused('Repeated or unbound READY') self.events.append('READY') self.ready_sample = sample self.ready_at = self.clock(); self.ready_real = real elif detail in ('INVENTORY_OPEN', 'INVENTORY_CLOSED'): expected = ['READY'] if detail == 'INVENTORY_OPEN' else ['READY', 'INVENTORY_OPEN'] if self.events != expected: raise Refused('Unexpected native inventory transition') self.events.append(detail) elif detail.startswith(('CANCELLED ', 'FINISHED ')): self.terminal = True if self.terminal: raise Refused('Native observer ended') def fresh(self): self.poll() if self.last_sample_at is None or self.clock() - self.last_sample_at > 1: raise Refused('Native observer samples are stale') def approach_data(self): """Reparse complete raw triples, retaining incomplete final pairs for next poll.""" from ordinary_gate_evidence import join_gate_snapshots, validate_gate_geometry self.fresh() if (not self.enable_approach or self.events != ['READY'] or not self.gate_complete_end or any(r['kind'] == 'cancelled' for r in self.gate_records + self.look_records)): raise Refused('Native approach context is unavailable or ended') content = '\n'.join(self.approach_lines[:self.gate_complete_end]) if self.enable_selection: from ordinary_gate_choices import join_choice_snapshots data = join_choice_snapshots(content, self.nonce) else: data = join_gate_snapshots(content, self.nonce) index = max(data['snapshots']) geometry = validate_gate_geometry(content, self.nonce, data['ready']['sample'], index) if not geometry['verified']: raise Refused('Native gate geometry refused: ' + '; '.join(geometry['errors'])) for item in data['snapshots'].values(): action = item['action'] if item['sample'] >= data['ready']['sample'] and (action['pressed'] != 0 or action['performing'] or action['progress'] != 0): raise Refused('Gate action input or progress changed during approach-only observation') if (self.clock() - self.gate_received[index] > .5 or self.input_samples[-1]['real_ms'] - data['snapshots'][index]['real_ms'] > 500): raise Refused('Complete native gate snapshot is stale') return content, data def approach_snapshots(self): return self._snapshot_batch('approach_previous') def alignment_snapshots(self): return self._snapshot_batch('alignment_previous') def selection_snapshots(self): return self._snapshot_batch('selection_previous') def begin_held(self, final_sample): if self.enable_held is not True or self.held_anchor is not None or type(final_sample) is not int: raise Refused('Held observation cannot be started or re-adopted') _, data = self.approach_data() if final_sample not in data['snapshots'] or final_sample < data['ready']['sample']: raise Refused('Held observation lacks original selection anchor') self.held_anchor = final_sample; self.held_previous = final_sample-1 def held_data(self): # Native observer retains original world/player/action/door objects; the # same acknowledged stream may show their moving geometry after phase2. # Do not run the closed-yard geometry validator on that moving door. from ordinary_gate_choices import join_choice_snapshots self.fresh() if (self.enable_held is not True or self.held_anchor is None or self.events != ['READY'] or not self.gate_complete_end or any(r['kind'] == 'cancelled' for r in self.gate_records+self.look_records)): raise Refused('Original held observation context ended or was not adopted') content = '\n'.join(self.approach_lines[:self.gate_complete_end]) data = join_choice_snapshots(content, self.nonce) latest = max(data['snapshots']) if (latest < self.held_anchor or self.clock()-self.gate_received[latest] > .5 or self.input_samples[-1]['real_ms']-data['snapshots'][latest]['real_ms'] > 500): raise Refused('Original held native snapshot is stale or reversed') phase = 1 for index in range(self.held_anchor, latest+1): item = data['snapshots'][index] if item['action']['phase'] < phase or item['input']['inventory']: raise Refused('Original held phase or menu context changed') phase = item['action']['phase'] return content, data def held_snapshots(self): return self._snapshot_batch('held_previous') def _snapshot_batch(self, cursor): _, data = self.held_data() if cursor == 'held_previous' else self.approach_data() snapshots = data['snapshots']; latest = max(snapshots) previous = getattr(self, cursor) first = latest if previous is None else min(previous + 1, latest) if previous is not None and latest < previous: raise Refused('Native approach snapshot index reversed') result = [] for index in range(first, latest + 1): item = snapshots[index]; geo, action, native, pose = item['geometry'], item['action'], item['input'], item['look'] result.append({'sample': index, 'real_ms': item['real_ms'], 'received_monotonic': self.gate_received[index], 'origin': native['origin'], 'direction': pose['direction'], 'body_forward': pose['body_forward'], 'camera': pose['camera'], **{key: action[key] for key in ('selected', 'context_selected', 'cached_performable', 'progress_bound', 'pressed', 'progress', 'performing', 'duration')}, 'input': native['input'], 'forward': native['forward'], 'velocity': native['velocity'], 'inventory': native['inventory'], 'phase': action['phase'], 'door_state': geo['door_state'], 'door_control': geo['door_control'], 'geometry': {'nonce': self.nonce, 'basis': 'registered-prison-lock-native-interaction', **{key: geo[key] for key in ('prison', 'gate', 'gate_forward', 'context', 'radius')}}}) if self.enable_held: result[-1].update(world_ms=native['world_ms'], turn=pose['turn']) if self.enable_selection: choice = item['choices'] if cursor != 'approach_previous' and action['phase'] == 1 and action['context_selected']: from ordinary_gate_choices import exact_prison_choices choice = exact_prison_choices(item) result[-1]['choices'] = {key: choice[key] for key in ('nonce','sample','real_ms','selected_id','selected_class','actions')} setattr(self, cursor, latest) return result class Keyboard: """Only W/Tab key events on the pre-existing explicit owned connection.""" def __init__(self, query): self.query = query self.xt = C.CDLL('libXtst.so.6') self.xt.XTestQueryExtension.restype = C.c_int self.xt.XTestQueryExtension.argtypes = [C.c_void_p] + [C.POINTER(C.c_int)] * 4 self.xt.XTestFakeKeyEvent.restype = C.c_int self.xt.XTestFakeKeyEvent.argtypes = [C.c_void_p, C.c_uint, C.c_int, C.c_ulong] for name, restype, argtypes in ( ('XKeysymToKeycode', C.c_ubyte, [C.c_void_p, C.c_ulong]), ('XKeycodeToKeysym', C.c_ulong, [C.c_void_p, C.c_ubyte, C.c_int]), ('XQueryKeymap', C.c_int, [C.c_void_p, C.POINTER(C.c_char)]), ('XFlush', C.c_int, [C.c_void_p])): fn = getattr(query.x, name); fn.restype = restype; fn.argtypes = argtypes values = [C.c_int() for _ in range(4)] if not self.xt.XTestQueryExtension(query.d, *(C.byref(v) for v in values)): raise Refused('Private XTest extension unavailable') self.codes = {} for key, keysym in (('W', 0x77), ('Tab', 0xff09)): code = int(query.x.XKeysymToKeycode(query.d, keysym)) if not 8 <= code <= 255 or query.x.XKeycodeToKeysym(query.d, code, 0) != keysym: raise Refused('Default unmodified key mapping unavailable') self.codes[key] = code if len(set(self.codes.values())) != 2: raise Refused('Ambiguous key mapping') def require_idle(self): state = C.create_string_buffer(32) if not self.query.x.XQueryKeymap(self.query.d, state): raise Refused('Native private keyboard state query failed') if any(state.raw): raise KeyStateBusy('A private keyboard key is still held') def send(self, key, down): if key not in self.codes or type(down) is not bool: raise Refused('Unsupported input event') if not self.xt.XTestFakeKeyEvent(self.query.d, self.codes[key], int(down), 0): raise Refused('Native XTest key request failed') self.query.x.XFlush(self.query.d) class HeldFKey: """One F-only attempt on the captured private connection; never creates one.""" def __init__(self, guard, keyboard, mouse, report, clock, sleep, original_deadline, *, enabled=False): if enabled is not True or type(original_deadline) not in (int,float) or not math.isfinite(original_deadline): raise Refused('Held F requires explicit capability and original deadline') self.guard, self.keyboard, self.mouse = guard, keyboard, mouse self.query, self.xt = keyboard.query, keyboard.xt self.owner = copy.deepcopy(mouse.owner) self.report, self.clock, self.sleep = report, clock, sleep self.original_deadline = original_deadline self.attempted = False; self.pending = False; self.lease = None; self.up_attempts = 0 self._context(guard) self.code = int(self.query.x.XKeysymToKeycode(self.query.d, 0x66)) if (not 8 <= self.code <= 255 or self.query.x.XKeycodeToKeysym(self.query.d, self.code, 0) != 0x66 or self.code in keyboard.codes.values()): raise Refused('Default distinct unmodified F mapping unavailable') report.setdefault('held_events', []); report.setdefault('held_release_errors', []) def _context(self, guard): if (guard is not self.guard or self.guard.query is not self.query or self.keyboard.query is not self.query or self.mouse.query is not self.query or self.mouse.keyboard is not self.keyboard or self.keyboard.xt is not self.xt or self.mouse.owner != self.owner or self.guard.owner != self.owner): raise Refused('Captured F owner or connection changed') def _owned(self, guard, *, idle): self._context(guard) target = guard.check() if idle: self.keyboard.require_idle() self.mouse.validate_pointer(target,0,0) target = guard.check() if idle: self.keyboard.require_idle() self.mouse.validate_pointer(target,0,0) self._context(guard) def down_f(self, guard, *, deadline, not_after): if (self.attempted or type(deadline) not in (int,float) or not math.isfinite(deadline) or type(not_after) not in (int,float) or not math.isfinite(not_after) or not 0 <= self.clock() <= not_after <= self.original_deadline-6 or not self.clock() < deadline <= min(self.original_deadline,self.clock()+5)): raise Refused('Repeated, expired or unqualified held F admission') # The numeric original keycode is retained for cleanup even if its # mapping later changes; never redirect key-up to a new F keycode. self.attempted = True; self.pending = True; self.lease = deadline self.report['unconfirmed_held_key'] = 'F' self._owned(guard,idle=True) if self.query.x.XKeycodeToKeysym(self.query.d,self.code,0) != 0x66: raise Refused('Original F mapping changed before down') now = self.clock() if not 0 <= now <= not_after or now >= self.lease: raise Refused('Six-second admission or native pose freshness expired before F') event = {'key':'F','down':True,'monotonic':now,'sent':False} self.report['held_events'].append(event); self.report['input_attempted'] = True if not self.xt.XTestFakeKeyEvent(self.query.d,self.code,1,0): raise Refused('Native held F down request uncertain') self.query.x.XFlush(self.query.d) event['sent'] = True; self.report['input_sent'] = True return {'key':'F','down_monotonic':now} def up_f(self, guard, *, deadline, cleanup=False): try: if (type(cleanup) is not bool or not self.attempted or not self.pending or self.up_attempts >= 2 or type(deadline) not in (int,float) or not math.isfinite(deadline) or deadline != self.lease): raise Refused('Unowned, repeated or renewed held F release') self.up_attempts += 1 self._owned(guard,idle=False) now = self.clock() if not math.isfinite(now) or now < 0 or (not cleanup and now >= min(self.lease,self.original_deadline)): raise Refused('Original F release deadline expired') event = {'key':'F','down':False,'monotonic':now,'sent':False} if cleanup: event['finally'] = True self.report['held_events'].append(event); self.report['input_attempted'] = True if not self.xt.XTestFakeKeyEvent(self.query.d,self.code,0,0): raise Refused('Native held F up request uncertain') self.query.x.XFlush(self.query.d) event['sent'] = True; self.report['input_sent'] = True confirm_release(guard,self.keyboard,self.report,'F',self.clock,self.sleep) self.pending = False; self.report['unconfirmed_held_key'] = None return {'key':'F','up_monotonic':now,'released':True} except Exception as error: self.report['held_release_errors'].append(str(error)) raise class Guard: def __init__(self, args, bound, query, owner): self.args, self.bound, self.query, self.owner = args, bound, query, owner def check(self): if display.binding(self.args.host_display, self.args.prefix) != self.bound: raise Refused('Original display binding changed') # A new per-check read budget, still bounded by the experiment deadline. self.query.query_budget = 128 current = {'window': self.owner['window'], 'pid': self.query.prop(self.owner['window'], '_NET_WM_PID'), 'server_pid': self.query.server_pid(self.owner['window'])} verified = display.owned_window(current, self.bound, self.args.prefix, self.args.game_executable) if verified is None or verified['process'] != self.owner['process'] or verified['pid'] != self.owner['pid']: raise Refused('Original game window or process changed') game_arguments(verified, self.args) target = display.input_target(self.query, verified) if target.get('verified') is not True: raise Refused('Owned existing focus/mapping unavailable: ' + target.get('reason', 'unknown')) if display.binding(self.args.host_display, self.args.prefix) != self.bound: raise Refused('Display binding changed during target check') return target def confirm_release(guard, keyboard, report, key, clock=time.monotonic, sleep=time.sleep): """Bounded read-only state polling; never sends/retries an input event.""" start = clock(); deadline = start + .35 observation = {'key': key, 'start': start, 'attempts': 0, 'confirmed': False} report.setdefault('release_observations', []).append(observation) try: while True: guard.check() if clock() >= deadline: raise Refused('Private key release state did not settle within 350ms') observation['attempts'] += 1 try: keyboard.require_idle() if clock() >= deadline: raise Refused('Private key release state query exceeded 350ms bound') observation['confirmed'] = True return except KeyStateBusy as error: observation['last_busy_error'] = str(error) remaining = deadline - clock() if remaining <= 0: raise Refused('Private key release state did not settle within 350ms') from error sleep(min(.025, remaining)) finally: observation['end'] = clock() def sequence(guard, keyboard, observer, report, clock=time.monotonic, sleep=time.sleep, mouse=None, approach=False, alignment=False, selection=False, coarse_approach=False, coarse_facing=False, held_action=False): if type(coarse_approach) is not bool or (coarse_approach and not approach) or report.get("coarse_approach_requested", False) is not coarse_approach: raise Refused("Coarse approach requires matching explicit capability") if type(coarse_facing) is not bool or (coarse_facing and not (selection and coarse_approach)) or report.get('coarse_facing_requested', False) is not coarse_facing: raise Refused('Coarse facing requires matching explicit selection capability') if type(held_action) is not bool or report.get('held_action_requested', False) is not held_action or (held_action and not (selection and coarse_facing and getattr(observer, 'enable_held', False))): raise Refused('Held action requires matching explicit selection and observer capabilities') deadline = clock() + 70 held = None approach_completed = False if selection and (not alignment or not approach or mouse is None or not getattr(observer, "enable_selection", False)): raise Refused("Selection requires explicit alignment and choice capabilities") if alignment and (not approach or mouse is None): raise Refused("Alignment requires approach and look capabilities") report.update(events=[], paired_release_errors=[], sequence_completed=False) def check(native=True): if clock() >= deadline: raise Refused('Input sequence deadline exceeded') if native: observer.fresh() return guard.check() def wait_for(event, seconds): end = min(deadline, clock() + seconds) while clock() < end: observer.poll() guard.check() if event in observer.events: observer.fresh() return sleep(0.05) raise Refused('Native transition timeout: ' + event) def key(key, down): nonlocal held check() if down: keyboard.require_idle() # Mark uncertain commit before the FFI call, so an exception also releases. held = key notify({'kind': 'press-pending', 'key': key}) report['input_attempted'] = True keyboard.send(key, down) report['events'].append({'key': key, 'down': down, 'monotonic': clock()}) report['input_sent'] = True if not down: confirm_release(guard, keyboard, report, key, clock, sleep) held = None notify({'kind': 'release-confirmed', 'key': key}) def pulse(key_name, duration): key(key_name, True) end = clock() + duration while clock() < end: sleep(min(0.05, end - clock())) # The key-up path performs a full fresh guard immediately before # sending. Avoid duplicating that guard once the hold has elapsed. if clock() < end: check() key(key_name, False) try: wait_for('READY', 45) deadline = clock() + 12 if approach: if mouse is None or not observer.enable_approach or observer.ready_at is None: raise Refused('Approach requires explicit look and gate observation') deadline = observer.ready_at + 35 if clock() >= deadline: raise Refused('Original approach deadline exceeded before calibration') notify({'kind': 'approach-pending', 'deadline_monotonic': deadline}) if observer.events != ['READY']: raise Refused('Inventory was already changed before input') if held_action: original_end = observer.ready_at + (45000-(observer.ready_real-observer.queued_real))/1000-.5 notify({'kind':'held-observer-deadline','deadline_monotonic':original_end}) if mouse is not None: perform_look(guard, keyboard, observer, mouse, report, clock, sleep) if not approach: deadline = clock() + 12 if approach: run_approach(guard, keyboard, observer, mouse, report, clock, sleep) if clock() >= deadline: raise Refused("Original approach deadline exceeded") keyboard.require_idle() approach_completed = True if alignment: notify({'kind': 'approach-finished', 'successful': True}) if selection: run_selection(guard, keyboard, observer, mouse, report, clock, sleep) if held_action: run_held_action(guard, keyboard, observer, mouse, report, clock, sleep) else: run_alignment(guard, keyboard, observer, mouse, report, clock, sleep) report["sequence_completed"] = True return pulse('W', 0.30) check() pulse('Tab', 0.05) wait_for('INVENTORY_OPEN', 4) open_until = clock() + 3.0 while clock() < open_until: sleep(min(.05, open_until - clock())) check() if observer.events != ['READY', 'INVENTORY_OPEN']: raise Refused('Inventory closed before the observed open interval') pulse('Tab', 0.05) wait_for('INVENTORY_CLOSED', 4) check() keyboard.require_idle() report['sequence_completed'] = True finally: if held is not None: try: # Release needs live display/window/focus authority, but not a live # observer; cancellation must not suppress an otherwise safe key-up. guard.check() keyboard.send(held, False) confirm_release(guard, keyboard, report, held, clock, sleep) notify({'kind': 'release-confirmed', 'key': held}) report['events'].append({'key': held, 'down': False, 'finally': True, 'monotonic': clock()}) except (OSError, ValueError) as error: report['paired_release_errors'].append(str(error)) report['unconfirmed_held_key'] = held if report['paired_release_errors'] else None if approach: route = report.get('approach_proof', {}) report['paired_release_errors'].extend(route.get('paired_release_errors', [])) report['unconfirmed_held_key'] = report.get('unconfirmed_held_key') or route.get('unconfirmed_held_key') notify({'kind': 'approach-finished', 'successful': approach_completed if alignment else report['sequence_completed']}) report['all_attempted_presses_released'] = not report.get('unconfirmed_held_key') and not report['paired_release_errors'] and not report.get('unconfirmed_held_button') and not report.get('wheel_release_errors') and not report.get('held_release_errors') class ApproachKeyboard: """W-only capability for the separate approach module; never F/Tab.""" def __init__(self, keyboard, report, clock, deadline): self.keyboard, self.report, self.clock, self.deadline = keyboard, report, clock, deadline def require_idle(self): self.keyboard.require_idle() def send(self, key, down): if key != 'W' or type(down) is not bool: raise Refused('Approach permits W key pairs only') if down and self.clock() >= self.deadline: raise Refused('Original approach deadline exceeded before W press') self.report['input_attempted'] = True self.keyboard.send(key, down) self.report['events'].append({'key': key, 'down': down, 'monotonic': self.clock()}) self.report['input_sent'] = True class ApproachMouse: def __init__(self, mouse, report, clock, deadline): self.mouse, self.report, self.clock, self.deadline = mouse, report, clock, deadline def move(self, dx, dy, guard): self.report['input_attempted'] = True self.mouse.approach_move(dx, dy, guard, deadline=self.deadline, clock=self.clock) self.report['input_sent'] = True def run_approach(guard, keyboard, observer, mouse, report, clock, sleep): from ordinary_look_evidence import validate_look_evidence from prison_approach_proof import perform_approach content, data = observer.approach_data() validated = validate_look_evidence(content, observer.nonce, report['look_proof']) if not validated['verified']: raise Refused('Native approach calibration refused: ' + '; '.join(validated['errors'])) poses = {r['sample']: r for r in observer.look_records if r['kind'] == 'sample'} proof = report['look_proof'] if observer.queued_real is None or observer.ready_at is None or observer.ready_real is None: raise Refused('Original native observation clocks missing') calibration = {'nonce': observer.nonce, 'completed': True, 'pixels': 24, 'baseline_direction': poses[proof['baseline_samples'][-1]]['direction'], 'response_direction': poses[proof['response_sample']]['direction'], 'ready_real_ms': observer.ready_real, 'ready_monotonic': observer.ready_at, 'observer_start_real_ms': observer.queued_real} geometry = observer.approach_snapshots()[-1]['geometry'] # The adapter has only read here. The module's initial snapshot can repeat # this latest sample; every subsequent native sample is returned in order. def route_notify(event): # Only the sequence owner can mark completion after its final checks. if event.get("kind") != "approach-finished": notify(event) deadline = observer.ready_at + 35 perform_approach(guard, ApproachKeyboard(keyboard, report, clock, deadline), ApproachMouse(mouse, report, clock, deadline), observer.approach_snapshots, geometry, calibration, report, clock=clock, sleep=sleep, notify=route_notify, key_state_busy=KeyStateBusy, profile="coarse" if report.get("coarse_approach_requested", False) else "standard") if not report.get('approach_proof', {}).get('completed'): raise Refused('Approach module did not complete') class AlignmentMouse: """Explicit yaw-only capability. Caller cannot extend its original lease.""" def __init__(self, mouse, report, clock, deadline): self.mouse, self.report, self.clock, self.deadline = mouse, report, clock, deadline def move(self, dx, dy, guard, *, deadline): if type(deadline) not in (int, float) or not math.isfinite(deadline) or deadline > self.deadline: raise Refused('Alignment module attempted to extend original deadline') self.report['input_attempted'] = True self.mouse.alignment_move(dx, dy, guard, deadline=deadline, clock=self.clock) self.report['input_sent'] = True class SelectionWheel: """One button5 pair on the existing private connection; no other input API.""" def __init__(self, mouse, report, clock, sleep, deadline, enabled=False): self.mouse, self.report, self.clock, self.sleep = mouse, report, clock, sleep self.deadline, self.enabled, self.attempted = deadline, enabled, False if enabled is not True: raise Refused('Selection wheel capability is not enabled') self.xt = mouse.xt self.xt.XTestFakeButtonEvent.restype = C.c_int self.xt.XTestFakeButtonEvent.argtypes = [C.c_void_p, C.c_uint, C.c_int, C.c_ulong] def pulse(self, button, guard, *, deadline): if (self.enabled is not True or self.attempted or type(button) is not int or button != 5 or not math.isfinite(deadline) or deadline > self.deadline or self.clock() >= deadline): raise Refused('Unsupported, repeated or expired wheel trial') self.attempted = True report = self.report report.setdefault('wheel_events', []); report.setdefault('wheel_release_errors', []) up_attempted = False; pending = False; down_at = up_at = None mask = 1 << 12 # X11 Button5Mask; no other modifier/button may be held. def owned(releasing=False): target = guard.check(); self.mouse.keyboard.require_idle() self.mouse.validate_pointer(target, 0, 0, allowed_mask=mask if releasing else 0) target = guard.check() self.mouse.validate_pointer(target, 0, 0, allowed_mask=mask if releasing else 0) def send(down, cleanup=False): nonlocal up_attempted, pending, down_at, up_at owned(not down) if not cleanup and self.clock() >= deadline: raise Refused('Original facing lease expired before wheel request') if down: pending = True; report['unconfirmed_held_button'] = button notify({'kind':'button-pending','button':button}) else: up_attempted = True event = {'button':button,'down':down,'monotonic':self.clock(),'sent':False} if cleanup: event['finally'] = True report['wheel_events'].append(event) report['input_attempted'] = True if not self.xt.XTestFakeButtonEvent(self.mouse.query.d, button, int(down), 0): raise Refused('Native wheel request refused') self.mouse.query.x.XFlush(self.mouse.query.d) event['sent'] = True; report['input_sent'] = True if down: down_at = event['monotonic'] else: up_at = event['monotonic'] def confirm(): nonlocal pending end = self.clock() + .35 while True: target = guard.check(); self.mouse.keyboard.require_idle() position = self.mouse.validate_pointer(target, 0, 0, allowed_mask=mask) if self.clock() >= end: raise Refused('Private wheel release confirmation exceeded bound') if position['mask'] == 0: pending = False; report['unconfirmed_held_button'] = None notify({'kind':'button-release-confirmed','button':button}) return if position['mask'] != mask or self.clock() >= end: raise Refused('Private wheel release was not confirmed') self.sleep(min(.025, end-self.clock())) try: send(True) send(False) confirm() if self.clock() >= deadline: raise Refused('Original facing lease expired before wheel confirmation') return {'button':button,'down_monotonic':down_at,'up_monotonic':up_at,'released':True} except Exception as error: report['wheel_release_errors'].append(str(error)) raise finally: if pending and not up_attempted: try: # Expiry permits only cleanup of our already attempted button; # current private ownership remains mandatory, without native state. send(False, cleanup=True); confirm() except Exception as error: report['wheel_release_errors'].append(str(error)) if pending: report['unconfirmed_held_button'] = button def run_alignment(guard, keyboard, observer, mouse, report, clock, sleep): from prison_alignment_proof import perform_alignment from prison_approach_evidence import validate_approach_evidence if observer.ready_at is None or observer.ready_real is None or observer.queued_real is None: raise Refused('Original native alignment clocks missing') original_end = observer.ready_at + (45000 - (observer.ready_real - observer.queued_real))/1000 - .5 started = clock() deadline = min(started + 8, original_end) if deadline <= started: raise Refused('Original observation lifetime unavailable for alignment') notify({'kind': 'alignment-pending', 'deadline_monotonic': deadline}) successful = False try: content, data = observer.approach_data() route = report.get('approach_proof') evidence = validate_approach_evidence(content, observer.nonce, report['look_proof'], route) if not evidence['verified']: raise Refused('Native approach prerequisite refused: ' + '; '.join(evidence['errors'])) if clock() >= deadline: raise Refused('Original alignment deadline exceeded during prerequisite validation') poses = {r['sample']: r for r in observer.look_records if r['kind'] == 'sample'} look = report['look_proof'] calibration = {'nonce': observer.nonce, 'completed': True, 'pixels': 24, 'baseline_direction': poses[look['baseline_samples'][-1]]['direction'], 'response_direction': poses[look['response_sample']]['direction'], 'ready_real_ms': observer.ready_real, 'ready_monotonic': observer.ready_at, 'observer_start_real_ms': observer.queued_real, 'observer_deadline_monotonic': original_end, 'alignment_deadline_monotonic': deadline} observer.alignment_previous = evidence['last_sample'] - 1 def alignment_notify(event): if event.get('kind') != 'alignment-finished': notify(event) # Native ownership checks remain live, but no keyboard-sending capability # is handed to this stage. It may only assert that all keys are idle. perform_alignment(guard, keyboard.require_idle, AlignmentMouse(mouse, report, clock, deadline), observer.alignment_snapshots, route['geometry'], calibration, evidence, report, clock=clock, sleep=sleep, notify=alignment_notify) if not report.get('alignment_proof', {}).get('completed'): raise Refused('Alignment module did not complete') guard.check(); keyboard.require_idle() observer.approach_data() if clock() >= deadline: raise Refused('Original alignment deadline exceeded at final observation') successful = True finally: notify({'kind': 'alignment-finished', 'successful': successful}) def run_selection(guard, keyboard, observer, mouse, report, clock, sleep): from prison_alignment_proof import perform_facing from prison_alignment_evidence import validate_facing_evidence from prison_action_selection_proof import perform_selection coarse_facing = report.get('coarse_facing_requested', False) if type(coarse_facing) is not bool or coarse_facing is not getattr(mouse, 'enable_coarse_facing', False): raise Refused('Facing profile differs from owned mouse capability') facing_profile = 'coarse' if coarse_facing else 'standard' from prison_approach_evidence import validate_approach_evidence if observer.ready_at is None or observer.ready_real is None or observer.queued_real is None: raise Refused('Original native alignment clocks missing') original_end = observer.ready_at + (45000 - (observer.ready_real - observer.queued_real))/1000 - .5 started = clock() deadline = min(started + 8, original_end) if deadline <= started: raise Refused('Original observation lifetime unavailable for alignment') notify({'kind': 'alignment-pending', 'deadline_monotonic': deadline}) successful = False try: content, data = observer.approach_data() route = report.get('approach_proof') evidence = validate_approach_evidence(content, observer.nonce, report['look_proof'], route) if not evidence['verified']: raise Refused('Native approach prerequisite refused: ' + '; '.join(evidence['errors'])) if clock() >= deadline: raise Refused('Original alignment deadline exceeded during prerequisite validation') poses = {r['sample']: r for r in observer.look_records if r['kind'] == 'sample'} look = report['look_proof'] calibration = {'nonce': observer.nonce, 'completed': True, 'pixels': 24, 'baseline_direction': poses[look['baseline_samples'][-1]]['direction'], 'response_direction': poses[look['response_sample']]['direction'], 'ready_real_ms': observer.ready_real, 'ready_monotonic': observer.ready_at, 'observer_start_real_ms': observer.queued_real, 'observer_deadline_monotonic': original_end, 'alignment_deadline_monotonic': deadline} observer.alignment_previous = evidence['last_sample'] - 1 def alignment_notify(event): if event.get('kind') in ('facing-pending', 'action-selection-pending'): notify({'kind':'alignment-pending','deadline_monotonic':event['deadline_monotonic']}) # Native ownership checks remain live, but no keyboard-sending capability # is handed to this stage. It may only assert that all keys are idle. perform_facing(guard, keyboard.require_idle, AlignmentMouse(mouse, report, clock, deadline), observer.alignment_snapshots, route['geometry'], calibration, evidence, report, clock=clock, sleep=sleep, notify=alignment_notify, profile=facing_profile) facing = report.get('facing_proof', {}) if not facing.get('completed'): raise Refused('Facing module did not complete') deadline = min(deadline, facing['deadline_monotonic']) content, data = observer.approach_data() facing_evidence = validate_facing_evidence(content, observer.nonce, look, route, facing, profile=facing_profile) if not facing_evidence.get('verified'): raise Refused('Independent native facing prerequisite refused') if clock() >= deadline: raise Refused('Original facing lease expired during validation') observer.selection_previous = facing_evidence['last_sample'] - 1 wheel = SelectionWheel(mouse, report, clock, sleep, deadline, enabled=True) perform_selection(guard, keyboard.require_idle, wheel, observer.selection_snapshots, route['geometry'], calibration, facing_evidence, report, clock=clock, sleep=sleep, notify=alignment_notify) if not report.get('action_selection_proof', {}).get('completed'): raise Refused('Selection module did not complete') guard.check(); keyboard.require_idle() observer.approach_data() if clock() >= deadline: raise Refused('Original alignment deadline exceeded at final observation') successful = True finally: notify({'kind': 'alignment-finished', 'successful': successful}) def run_held_action(guard, keyboard, observer, mouse, report, clock, sleep): from prison_action_selection_evidence import validate_selection_evidence from prison_held_action_proof import perform_held_action from prison_held_action_evidence import validate_held_action_evidence if (report.get('held_action_requested') is not True or report.get('coarse_facing_requested') is not True or observer.enable_held is not True or mouse.enable_coarse_facing is not True): raise Refused('Held action lacks explicit native/input capabilities') content, _ = observer.approach_data() selected = report.get('action_selection_proof') accepted = validate_selection_evidence(content,observer.nonce,report.get('look_proof'),report.get('approach_proof'), report.get('facing_proof'),selected,facing_profile='coarse') if accepted.get('verified') is not True: raise Refused('Independent native one-wheel selection prerequisite refused') if observer.ready_at is None or observer.ready_real is None or observer.queued_real is None: raise Refused('Original held observation clock missing') original_end = observer.ready_at+(45000-(observer.ready_real-observer.queued_real))/1000-.5 calibration = {'nonce':observer.nonce,'ready_real_ms':observer.ready_real,'ready_monotonic':observer.ready_at, 'observer_start_real_ms':observer.queued_real,'observer_deadline_monotonic':original_end} observer.begin_held(accepted['last_sample']) key = HeldFKey(guard,keyboard,mouse,report,clock,sleep,original_end,enabled=True) try: perform_held_action(guard,keyboard.require_idle,key,observer.held_snapshots,selected['geometry'],calibration,accepted,report, clock=clock,sleep=sleep,notify=notify) finally: held = report.get('held_action_proof',{}) report['paired_release_errors'].extend(held.get('paired_release_errors',[])) if held.get('unconfirmed_held_key') is not None: report['unconfirmed_held_key'] = held['unconfirmed_held_key'] # Live success still belongs to the independent raw parser. No action/input # can follow this final read-only observation and full policy verification. content, _ = observer.held_data() verified = validate_held_action_evidence(content,observer.nonce,report.get('look_proof'),report.get('approach_proof'), report.get('facing_proof'),selected,report.get('held_action_proof'),facing_profile='coarse') if verified.get('verified') is not True: raise Refused('Independent native held-action evidence refused') guard.check(); keyboard.require_idle() if clock() >= original_end: raise Refused('Original held observation lifetime expired before completion') def arguments(argv=None): p = argparse.ArgumentParser(description=__doc__) p.add_argument('--enable-private-keyboard-proof', action='store_true', required=True) p.add_argument('--enable-private-held-action-proof', action='store_true', help='One bounded F hold after independently accepted selection; requires explicit coarse facing') p.add_argument('--enable-private-selection-proof', action='store_true', help='After facing, try one button5 wheel selection; never perform the action') p.add_argument('--enable-private-look-proof', action='store_true') p.add_argument('--enable-private-alignment-proof', action='store_true', help='After successful approach, attempt stationary native lock selection using horizontal relative mouse only') p.add_argument('--enable-private-coarse-facing-proof', action='store_true', help='Explicit coarse facing only; requires coarse approach and selection, no renewed clocks') p.add_argument('--enable-private-coarse-approach-proof', action='store_true', help='Explicit larger stationary-yaw profile; requires approach, original clocks unchanged') p.add_argument('--enable-private-approach-proof', action='store_true', help='After look calibration, replace W/Tab cycle with bounded interior approach only') for name in ('prefix', 'game-executable', 'profile', 'report'): p.add_argument('--' + name, type=Path, required=True) p.add_argument('--console', type=Path, help='Existing exact session; omit only in wrapper mode') p.add_argument('--host-display', required=True) p.add_argument('--nonce', required=True) p.add_argument('command', nargs=argparse.REMAINDER) args = p.parse_args(argv) if args.enable_private_held_action_proof and not (args.enable_private_selection_proof and args.enable_private_coarse_facing_proof): p.error('Held action requires selection and coarse facing') if args.enable_private_coarse_facing_proof and not (args.enable_private_coarse_approach_proof and args.enable_private_selection_proof): p.error('Coarse facing requires coarse approach and selection') if args.enable_private_coarse_approach_proof and not args.enable_private_approach_proof: p.error('Coarse approach proof requires explicit approach proof') if args.enable_private_selection_proof and not (args.enable_private_alignment_proof and args.enable_private_approach_proof and args.enable_private_look_proof): p.error('Selection proof requires explicit alignment, approach and look proof') if args.enable_private_alignment_proof and not (args.enable_private_approach_proof and args.enable_private_look_proof): p.error("Alignment proof requires explicit approach and look proof") if args.enable_private_approach_proof and not args.enable_private_look_proof: p.error("Approach proof requires explicit private look proof") return args def wrapper_command(args): command = args.command[1:] if args.command[:1] == ['--'] else args.command if not command: if args.console is None: raise Refused('Sidecar requires an exact existing console') return [] if args.console is not None: raise Refused('Wrapper discovers a new session; do not supply console') if (len(command) < 3 or Path(command[0]).resolve() != Path(sys.executable).resolve() or Path(command[1]) != Path(display.__file__).resolve()): raise Refused('Only the unchanged sibling read-only display wrapper is allowed') wrapped = display.parse_args(command[2:]) if (wrapped.query_worker or wrapped.prefix != args.prefix or wrapped.profile != args.profile or wrapped.game_executable != args.game_executable or wrapped.host_display != args.host_display or wrapped.report == args.report): raise Refused('Read-only wrapper context differs') display.validate_paths(wrapped) native = wrapped.command[1:] if wrapped.command[:1] == ['--'] else wrapped.command proton = Path.home() / '.local/share/Steam/steamapps/common/Proton - Experimental/proton' if len(native) < 3 or native[:3] != [str(proton), 'run', str(args.game_executable)]: raise Refused('Expected exact installed Proton/game command') if (option(native, '-errOrdinaryInputObserve') != args.nonce or option(native, '-profile') != 'Z:' + str(args.profile) or option(native, '-errOrdinaryScenario') not in ('core', 'chat')): raise Refused('Wrapped native observer context differs') if args.enable_private_look_proof and option(native, '-errOrdinaryLookObserve') != args.nonce: raise Refused('Wrapped native look observer context differs') gate_options(native, args) return command def sessions(profile): folder = profile / 'logs' if not folder.exists(): return set() if folder.is_symlink() or not folder.is_dir(): raise Refused('Invalid private native logs directory') return {path for path in folder.iterdir() if re.fullmatch(r'logs_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}', path.name)} def console_path(args): path = args.console if (path != path.resolve() or path.name != 'console.log' or path.parent.parent != args.profile / 'logs' or not re.fullmatch(r'logs_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}', path.parent.name)): raise Refused('Expected exact canonical native session console') APPROACH_MODULES = ('prison_approach_proof.py', 'ordinary_gate_evidence.py', 'ordinary_look_evidence.py', 'prison_approach_evidence.py') ALIGNMENT_MODULES = ('prison_alignment_proof.py', 'prison_alignment_evidence.py') SELECTION_MODULES = ('ordinary_gate_choices.py', 'prison_action_selection_proof.py', 'prison_action_selection_evidence.py') HELD_MODULES = ('prison_held_action_proof.py', 'prison_held_action_evidence.py') def approach_module_hashes(): return module_hashes(APPROACH_MODULES) def module_hashes(names): folder = Path(__file__).resolve().parent return {name: hashlib.sha256((folder / name).read_bytes()).hexdigest() for name in names} def report_for_write(report): """Keep raw console as pose authority; archive bounded references, not duplicate poses.""" if report.get('approach_requested') is not True: return report result = dict(report) names = ['approach_proof'] if report.get('alignment_requested') is True: names.append('alignment_proof') if report.get('selection_requested') is True: names += ['facing_proof', 'action_selection_proof'] if report.get('held_action_requested') is True: names.append('held_action_proof') for name in names: route = report.get(name) if isinstance(route, dict): result[name] = dict(route) samples = route.get('samples') if isinstance(samples, list): result[name]['samples'] = [ {key: sample.get(key) for key in (('sample', 'real_ms', 'world_ms', 'received_monotonic') if name == 'held_action_proof' else ('sample', 'real_ms', 'received_monotonic'))} if isinstance(sample, dict) else {'invalid_reference': True} for sample in samples] return result def main(argv=None): args = arguments(argv) args.observe_seconds = 25; args.query_worker = False display.validate_paths(args) if not re.fullmatch(r'[A-Za-z0-9]{1,32}', args.nonce): raise Refused('Invalid observer nonce') command = wrapper_command(args) if args.console is not None: console_path(args) bound = display.binding(args.host_display, args.prefix) report = {'schema': 1, 'kind': 'headless-private-keyboard-experiment', 'input_sent': False, 'held_action_requested': getattr(args, 'enable_private_held_action_proof', False), 'look_requested': args.enable_private_look_proof, 'coarse_facing_requested': getattr(args, 'enable_private_coarse_facing_proof', False), 'coarse_approach_requested': getattr(args, 'enable_private_coarse_approach_proof', False), 'approach_requested': getattr(args, 'enable_private_approach_proof', False), 'alignment_requested': getattr(args, 'enable_private_alignment_proof', False), 'selection_requested': getattr(args, 'enable_private_selection_proof', False), 'sequence_completed': False, 'release_qualified': False, 'gameplay_qualified': False, 'hard_supervision': NOTIFY is not None, 'nonce': args.nonce, 'binding': bound, 'command': command, 'report': str(args.report), 'input_attempted': False, 'helper_sha256': hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), 'ownership_helper_sha256': hashlib.sha256(Path(display.__file__).read_bytes()).hexdigest(), 'prefix': str(args.prefix), 'profile': str(args.profile), 'game_executable': str(args.game_executable)} if report['approach_requested']: report['approach_module_sha256'] = approach_module_hashes() if report['alignment_requested']: report['alignment_module_sha256'] = module_hashes(ALIGNMENT_MODULES) if report['selection_requested']: report['selection_module_sha256'] = module_hashes(SELECTION_MODULES) report.update(wheel_events=[], wheel_release_errors=[], unconfirmed_held_button=None) if report['held_action_requested']: report['held_module_sha256'] = module_hashes(HELD_MODULES) report.update(held_events=[], held_release_errors=[]) fd = os.open(args.report, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) info = os.fstat(fd) notify({'kind': 'report', 'path': str(args.report), 'device': info.st_dev, 'inode': info.st_ino, 'metadata': report}) query = observer = child = None before_sessions = sessions(args.profile) if command else set() deadline = time.monotonic() + 180 previous = {} def interrupted(signum, frame): if report.get('sequence_completed') and fd is None and child is not None: if child.poll() is None: child.send_signal(signum) return raise Refused('Input experiment interrupted by signal ' + str(signum)) try: for signum in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP): previous[signum] = signal.signal(signum, interrupted) if command: child = subprocess.Popen(command) # inherit the existing private process group notify({'kind': 'child', 'identity': display.identity(display.process(child.pid)), 'command': command}) while time.monotonic() < deadline: if child.poll() is not None: raise Refused('Read-only wrapper exited before native session') if display.binding(args.host_display, args.prefix) != bound: raise Refused('Display binding changed during startup') new = sessions(args.profile) - before_sessions if len(new) > 1: raise Refused('Ambiguous new native sessions') if new: candidate = next(iter(new)) / 'console.log' if candidate.exists(): args.console = candidate; console_path(args); break time.sleep(.1) if args.console is None: raise Refused('New native console deadline exceeded') report['console'] = str(args.console) observer = Observer(args.console, args.nonce, enable_look=args.enable_private_look_proof, enable_approach=getattr(args, "enable_private_approach_proof", False), enable_selection=getattr(args, "enable_private_selection_proof", False), enable_held=getattr(args, "enable_private_held_action_proof", False)) query = display.X11(bound['display']) while time.monotonic() < deadline: if child is not None and child.poll() is not None: raise Refused('Read-only wrapper exited during startup') observer.poll() if display.binding(args.host_display, args.prefix) != bound: raise Refused('Display binding changed during window startup') # Bind only after the native mission's original player is ready. # Startup/splash windows may legitimately disappear before then; # after binding, the existing guard still forbids any replacement. if observer.events != ['READY']: time.sleep(.1) continue query.query_budget = display.MAX_WINDOWS owners = [owner for window in query.windows() if (owner := display.owned_window(window, bound, args.prefix, args.game_executable))] eligible = [owner for owner in owners[:16] if display.input_target(query, owner).get('verified') is True] exact = [owner for owner in eligible if query.focus()[0] == owner['window']] selected = exact or eligible if len(selected) == 1: break if len(selected) > 1: raise Refused('Ambiguous mapped focused owned game windows') time.sleep(.1) else: raise Refused('Mapped owned game window deadline exceeded') owner = selected[0] report['owner'] = {key: owner[key] for key in ('window', 'pid', 'server_pid', 'process')} guard = Guard(args, bound, query, owner) guard.check() keyboard = Keyboard(query) keyboard.require_idle() mouse = RelativeMouse(query, keyboard, owner, enable_approach=getattr(args, "enable_private_approach_proof", False), enable_alignment=getattr(args, "enable_private_alignment_proof", False), enable_coarse_approach=getattr(args, "enable_private_coarse_approach_proof", False), enable_coarse_facing=getattr(args, 'enable_private_coarse_facing_proof', False), enable_selection=getattr(args, 'enable_private_selection_proof', False)) if args.enable_private_look_proof else None sequence(guard, keyboard, observer, report, mouse=mouse, approach=getattr(args, "enable_private_approach_proof", False), alignment=getattr(args, "enable_private_alignment_proof", False), selection=getattr(args, "enable_private_selection_proof", False), coarse_approach=getattr(args, "enable_private_coarse_approach_proof", False), coarse_facing=getattr(args, 'enable_private_coarse_facing_proof', False), held_action=getattr(args, 'enable_private_held_action_proof', False)) except (OSError, ValueError, UnicodeError, subprocess.SubprocessError) as error: report['error'] = str(error) finally: report['whole_process_group_cleanup_verified'] = False # outer runner owns it if observer: report['observer'] = {'inode': observer.identity, 'start_offset': observer.start_offset, 'end_offset': observer.offset, 'records': observer.raw, 'events': observer.events} if args.enable_private_look_proof: report['look_observation'] = observer.look_records if getattr(args, "enable_private_approach_proof", False): report["gate_observation"] = observer.gate_records observer.close() if query: query.close() with os.fdopen(fd, 'w') as stream: fd = None json.dump(report_for_write(report), stream, indent=2); stream.write('\n'); stream.flush(); os.fsync(stream.fileno()) notify({'kind': 'proof-finished', 'successful': report['sequence_completed'] and not report.get('error')}) child_code = 0 try: if child is not None: if report.get('error'): child.terminate() child_code = child.wait(timeout=max(.1, deadline - time.monotonic()) if not report.get('error') else 5) except subprocess.TimeoutExpired: child.terminate() try: child.wait(timeout=5) except subprocess.TimeoutExpired: child.kill(); child.wait(timeout=2) child_code = 1 finally: for signum, handler in previous.items(): signal.signal(signum, handler) return 0 if report['sequence_completed'] and not report.get('error') and child_code == 0 else 1 def worker_entry(argv, pipe): global NOTIFY NOTIFY = pipe try: code = main(argv) finally: pipe.close() raise SystemExit(code) def stop_exact_child(record, read=display.process, kill=os.kill): if record is None: return False try: current = read(record['identity']['pid']) if (display.identity(current) != record['identity'] or current['args'] != record['command'] or current['uid'] != os.getuid()): return False # Only the exact display wrapper created by our worker; no group signalling. kill(current['pid'], signal.SIGTERM) return True except (OSError, ValueError, KeyError): return False def supervision_failure(record, key, reason, button=None): """Amend only the exact exclusive report created by our now-stopped worker.""" if record is None: return fd = os.open(record['path'], os.O_RDWR | os.O_NOFOLLOW) try: info = os.fstat(fd) if (info.st_dev, info.st_ino) != (record['device'], record['inode']) or info.st_uid != os.getuid() or not stat.S_ISREG(info.st_mode): raise Refused('Worker report changed before timeout recording') raw = os.read(fd, 2 * 1024 * 1024) try: report = json.loads(raw) if raw else dict(record['metadata']) except (ValueError, UnicodeError): report = dict(record['metadata']) # Keep the worker's actual refusal; supervisor lifecycle is separate evidence. key = key or report.get('unconfirmed_held_key') button = button or report.get('unconfirmed_held_button') report.setdefault('error', reason) report.update(supervision_failed=True, supervision_error=reason, sequence_completed=False, all_attempted_presses_released=key is None and button is None and report.get('all_attempted_presses_released', True), unconfirmed_held_key=key) if key is not None: report['input_attempted'] = True report.setdefault('paired_release_errors', []).append('Supervisor could not confirm paired key release') if button is not None: report['unconfirmed_held_button'] = button report['input_attempted'] = True report.setdefault('wheel_release_errors', []).append('Supervisor could not confirm wheel button release') payload = (json.dumps(report, indent=2) + '\n').encode() os.lseek(fd, 0, os.SEEK_SET); os.write(fd, payload); os.ftruncate(fd, len(payload)); os.fsync(fd) finally: os.close(fd) class HeldSupervisor: """Independent absolute observer/F deadlines; other key leases stay1.5s.""" def __init__(self, enabled): self.enabled = enabled; self.observer_deadline = None self.key_deadline = None; self.key = None; self.attempted = False; self.failed = False def consume(self, event, now, *, other_pending=False): kind = event.get('kind') if not isinstance(kind,str) or not kind.startswith('held-'): return False if self.enabled is not True or type(now) not in (int,float) or not math.isfinite(now): self.failed = True; return True value = event.get('deadline_monotonic') numeric = type(value) in (int,float) and math.isfinite(value) if kind == 'held-observer-deadline': if self.observer_deadline is not None or not numeric or not now < value <= now+45: self.failed = True else:self.observer_deadline = value elif kind == 'held-action-pending': if self.observer_deadline is None or not numeric or not now < value <= self.observer_deadline: self.failed = True else:self.observer_deadline = value elif kind == 'held-key-pending': if (event.get('key') != 'F' or self.attempted or other_pending or self.observer_deadline is None or not numeric or not now < value <= min(now+5,self.observer_deadline)): self.failed = True else: self.attempted = True; self.key = 'F'; self.key_deadline = value elif kind == 'held-key-release-confirmed': if event.get('key') != 'F' or self.key != 'F':self.failed = True else: if now >= self.key_deadline:self.failed = True self.key = None; self.key_deadline = None elif kind != 'held-action-finished':self.failed = True return True def expired(self, now, proof_finished=False): return (self.failed or (self.key_deadline is not None and now >= self.key_deadline) or (not proof_finished and self.observer_deadline is not None and now >= self.observer_deadline)) def cleanup_ack(self, event): if event.get('kind') == 'held-key-release-confirmed' and event.get('key') == self.key == 'F': self.key = None; self.key_deadline = None def supervise(argv=None): # Fork before loading Xlib or opening any display. The worker owns the one # connection. Parent never opens a second connection or retries an event. context = multiprocessing.get_context('fork') parent, child_pipe = context.Pipe(duplex=False) child = context.Process(target=worker_entry, args=(argv, child_pipe)) previous = {}; received = [] def forward(signum, frame): received.append(signum) if child.pid and child.is_alive(): os.kill(child.pid, signum) for signum in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP): previous[signum] = signal.signal(signum, forward) alignment_deadline = None; alignment_finished = False approach_deadline = None; approach_finished = False button_at = None; held_button = None look_at = None; held_at = None; held_key = None; owned_child = None; report_record = None; proof_finished = False; expired = False raw_args = sys.argv[1:] if argv is None else argv held_scope = HeldSupervisor(raw_args.count('--enable-private-held-action-proof') == 1) deadline = time.monotonic() + 190 try: child.start(); child_pipe.close() while child.is_alive(): if parent.poll(.05): try: event = parent.recv() except EOFError: event = None if event and str(event.get('kind','')).startswith('held-') and held_scope.consume(event,time.monotonic(),other_pending=held_key is not None or held_button is not None or look_at is not None): pass elif event and event['kind'] == 'alignment-pending': proposed = event.get('deadline_monotonic') current = time.monotonic() if type(proposed) not in (int, float) or not math.isfinite(proposed) or proposed > current + 8: proposed = current alignment_deadline = proposed if alignment_deadline is None else min(alignment_deadline, proposed) elif event and event['kind'] == 'alignment-finished': alignment_finished = bool(event.get('successful')) elif event and event['kind'] == 'approach-pending': proposed = event.get('deadline_monotonic') current = time.monotonic() if type(proposed) not in (int, float) or not math.isfinite(proposed) or proposed > current + 35: proposed = current # malformed lease fails closed approach_deadline = proposed if approach_deadline is None else min(approach_deadline, proposed) elif event and event['kind'] == 'approach-finished': approach_finished = bool(event.get('successful')) elif event and event['kind'] == 'look-pending': look_at = time.monotonic() elif event and event['kind'] == 'look-finished': look_at = None elif event and event['kind'] == 'button-pending': if button_at is None: button_at = time.monotonic() held_button = event['button'] elif event and event['kind'] == 'button-release-confirmed': if event['button'] == held_button: button_at = None; held_button = None elif event and event['kind'] == 'press-pending': held_at = time.monotonic(); held_key = event['key'] elif event and event['kind'] == 'release-confirmed': held_at = None; held_key = None elif event and event['kind'] == 'child': owned_child = event elif event and event['kind'] == 'report': report_record = event elif event and event['kind'] == 'proof-finished': proof_finished = event['successful'] now = time.monotonic() if received or now >= deadline or held_scope.expired(now,proof_finished) or (alignment_deadline is not None and not alignment_finished and now >= alignment_deadline) or (approach_deadline is not None and not approach_finished and now >= approach_deadline) or (button_at is not None and now - button_at > 1.5) or (held_at is not None and now - held_at > 1.5) or (look_at is not None and now - look_at > 4): expired = True child.terminate() # asks worker finally to release if still provable child.join(1) stop_exact_child(owned_child) if child.is_alive(): child.kill(); child.join(2) # Drain release acknowledgements emitted during cooperative finally. while parent.poll(): try: last = parent.recv() except EOFError: break held_scope.cleanup_ack(last) if last['kind'] == 'release-confirmed': held_key = None elif last['kind'] == 'button-release-confirmed' and last['button'] == held_button: held_button = None elif last['kind'] == 'report': report_record = last elif last['kind'] == 'proof-finished': proof_finished = last['successful'] if not (received and proof_finished and held_key is None and held_scope.key is None and held_button is None): supervision_failure(report_record, held_scope.key or held_key, 'Private input worker timed out or was interrupted', button=held_button) break child.join(2) if child.exitcode != 0 and not expired: stop_exact_child(owned_child) if not (received and proof_finished and held_key is None and held_scope.key is None and held_button is None): supervision_failure(report_record, held_scope.key or held_key, 'Private input worker exited unsuccessfully', button=held_button) return 1 if expired or child.exitcode != 0 else 0 finally: if child.pid and child.is_alive(): child.terminate(); child.join(1) stop_exact_child(owned_child) if child.is_alive(): child.kill(); child.join(2) parent.close() for signum, handler in previous.items(): signal.signal(signum, handler) if __name__ == '__main__': raise SystemExit(supervise())