vdi-cloud/docs/evidence/linux-gpu-20261111/response-probe-50.py

68 lines
3.2 KiB
Python

"""Run on box: XTest input to decoded marker. Excludes USB and scanout."""
import ctypes as C
import os
import json
import statistics
import subprocess
import time
os.environ.update(DISPLAY=':0',XAUTHORITY='/home/armbian/.Xauthority')
x=C.CDLL('libX11.so.6'); t=C.CDLL('libXtst.so.6')
x.XOpenDisplay.restype=C.c_void_p
x.XDefaultRootWindow.argtypes=[C.c_void_p]; x.XDefaultRootWindow.restype=C.c_ulong
x.XSync.argtypes=[C.c_void_p,C.c_int]
x.XGetImage.argtypes=[C.c_void_p,C.c_ulong,C.c_int,C.c_int,C.c_uint,C.c_uint,C.c_ulong,C.c_int]; x.XGetImage.restype=C.c_void_p
x.XGetPixel.argtypes=[C.c_void_p,C.c_int,C.c_int]; x.XGetPixel.restype=C.c_ulong
x.XDestroyImage.argtypes=[C.c_void_p]
x.XStringToKeysym.argtypes=[C.c_char_p]; x.XStringToKeysym.restype=C.c_ulong
x.XKeysymToKeycode.argtypes=[C.c_void_p,C.c_ulong];x.XKeysymToKeycode.restype=C.c_uint
t.XTestFakeMotionEvent.argtypes=[C.c_void_p,C.c_int,C.c_int,C.c_int,C.c_ulong]
t.XTestFakeButtonEvent.argtypes=[C.c_void_p,C.c_uint,C.c_int,C.c_ulong]
t.XTestFakeKeyEvent.argtypes=[C.c_void_p,C.c_uint,C.c_int,C.c_ulong]
d=x.XOpenDisplay(None);assert d;root=x.XDefaultRootWindow(d)
pid=int(subprocess.check_output(['pgrep','-x','moonlight']).strip())
assert int(subprocess.check_output(['xdotool','getwindowfocus','getwindowpid']).strip())==pid
def key(name):
code=x.XKeysymToKeycode(d,x.XStringToKeysym(name.encode()));assert code
t.XTestFakeKeyEvent(d,code,1,0);t.XTestFakeKeyEvent(d,code,0,0);x.XSync(d,0)
def move(px,py):
t.XTestFakeMotionEvent(d,-1,px,py,0);x.XSync(d,0)
def read():
im=x.XGetImage(d,root,40,1380,1320,1,0xffffffff,2);assert im
try:
def val(offset,n):
pixels=[x.XGetPixel(im,offset+20+i*40,0)&0xffffff for i in range(n)]
if any(0x404040<v<0xc0c0c0 for v in pixels):return -1
return sum((1<<i) for i,v in enumerate(pixels) if v>0xc0c0c0)
return val(0,16),val(760,8),val(1120,4)
finally:x.XDestroyImage(im)
def await_marker(m,s):
deadline=time.monotonic()+4
while time.monotonic()<deadline:
seen,magic,mode=read()
if (seen,magic,mode)==(s,0x5a,m):return
time.sleep(.002)
raise AssertionError(('marker timeout',m,s,read()))
def sample(m,s,action):
start=time.monotonic();action();await_marker(m,s);return (time.monotonic()-start)*1000
def stats(a):
b=sorted(a);return dict(samples_ms=a,p50=statistics.median(b),p95=b[int(len(b)*.95)],maximum=max(b))
result={'pid':pid,'method':'XTest to decoded unique GTK marker; actual buttons/masked entry; submit has no backend; no USB/scanout'}
for m in [1,2,3]:
move(300,300);time.sleep(.4);key('F'+str(m));await_marker(m,0)
values=[]
for i in range(1,51):
px=750 if i%2 else 1750
if m==1: action=lambda:move(px,550)
elif m==2:
move(px,550);time.sleep(.12)
def action():
t.XTestFakeButtonEvent(d,1,1,0);x.XSync(d,0);time.sleep(.04)
t.XTestFakeButtonEvent(d,1,0,0);x.XSync(d,0)
else:action=lambda:key('a')
values.append(sample(m,i,action));time.sleep(.12)
result[{1:'hover',2:'click_release',3:'masked_character'}[m]]=stats(values)
if m==3:result['synthetic_submit_ms']=sample(m,1050,lambda:key('Return'))
print(json.dumps(result))