"""Real-board injected-touch Home acceptance; uses an isolated layout.""" import os,fcntl,struct,time,socket,json,subprocess from pathlib import Path ROOT=Path('/run/k230-home-proof');ROOT.mkdir(mode=0o700,exist_ok=True);os.chown(ROOT,1000,995) state=ROOT/'state/k230-shell';state.mkdir(parents=True,exist_ok=True) for p in [ROOT/'state',state]:os.chown(p,1000,995) layout_file=state/'home.json';checks={};captures=[] def app(name):return {'kind':'app','id':name} apps=['foot.desktop','htop.desktop','dev.tchx84.Portfolio.desktop'] def layout(): p=[[None]*20 for _ in range(2)];p[0][0]=app(apps[0]);p[0][1]=app(apps[1]);p[0][2]=app(apps[2]);p[1][0]=app('k230-clock.desktop') return {'schema':2,'columns':4,'pages':p,'dock':[None]*4} def save(v):layout_file.write_text(json.dumps(v));os.chown(layout_file,1000,995) def current():return json.loads(layout_file.read_text()) save(layout()) def ipc(command,kind=0): with socket.socket(socket.AF_UNIX) as s: s.settimeout(5);s.connect('/run/shell/sway-ipc.sock');b=command.encode();s.sendall(b'i3-ipc'+struct.pack('=II',len(b),kind)+b) def read(n): b=b'' while len(b) Widgets -> drag Clock row into requested footprint. down(*slot(12));time.sleep(.7);up();capture('widget-picker-menu');tap(284,186);capture('widget-picker');drag((284,292),slot(8));wait(lambda:current()['pages'][0][8]=={'kind':'widget','widget':'clock'});checks['picker_places_clock_at_requested_cell']=True;done();capture('clock-placed') before=current();restart();checks['layout_survives_shell_restart']=current()==before;capture('persisted-home') # Repeat edge turns while one contact remains held; continue past the last page. v=layout();v['pages'].append([None]*20);save(v);restart() down(*slot(0));time.sleep(.7) for n in range(1,15):move(slot(0)[0]+(558-slot(0)[0])*n/14,slot(0)[1]);time.sleep(.04) time.sleep(.15);capture('edge-indicator');time.sleep(1.9);move(*slot(4));time.sleep(.2);up() wait(lambda:any(app(apps[0]) in page for page in current()['pages'][3:]));checks['edge_repeat_creates_new_page_and_keeps_drop']=True;done();capture('new-page-drop') # Fast interior displacement, never reaching the 40px edge band. save(layout());restart();down(*slot(0));time.sleep(.7);move(400,203);time.sleep(.03);move(410,203);time.sleep(.3);move(*slot(4));time.sleep(.12);up() wait(lambda:app(apps[0]) in current()['pages'][1]);checks['interior_fling_pages_without_edge_dwell']=True;done();capture('fling-drop') # Native production rendering of every clock style and absent-battery/weather. for name,widget in [('bubble','clock'),('thin','clock_minimal'),('dot-matrix','clock_dot_matrix'),('battery','clock')]: v=layout();v['pages']=[[None]*20];v['pages'][0][0]={'kind':'widget','widget':'clock_analog'};v['pages'][0][2]={'kind':'widget','widget':'weather'};v['pages'][0][8]={'kind':'widget','widget':widget};v['pages'][0][0]={'kind':'widget','widget':'battery' if name=='battery' else 'clock_analog'};save(v);restart();time.sleep(2);capture('widgets-'+name) checks['battery_attachment_absent']=not any(Path('/sys/class/power_supply').iterdir()) cache=Path('/home/shell/.cache/k230-shell/weather.json');checks['weather_disk_cache_present']=cache.is_file() pid=subprocess.check_output(['systemctl','show','shell-ui','-p','MainPID','--value'],text=True).strip() Path('/root/tmp/k230-deployment/home-proof-result.json').write_text(json.dumps({'result':'PASS','evidence_class':'physical-board-uinput-injection-and-native-captures','checks':checks,'rust':os.path.realpath('/proc/'+pid+'/exe'),'system':os.path.realpath('/run/current-system'),'captures':captures,'limits':['Not real-finger acceptance or a daylight contrast measurement. No battery attachment exists. Test layout is isolated; original user layout is untouched.']},indent=2)) print('HOME_BOARD_CHECK_COMPLETE') finally: fcntl.ioctl(f,0x5502);os.close(f);override.unlink(missing_ok=True);subprocess.run(['systemctl','daemon-reload'],check=True);subprocess.run(['systemctl','restart','shell-ui'],check=True);time.sleep(3);ipc('card_shell home')