41 lines
2.8 KiB
Python
41 lines
2.8 KiB
Python
"""Synthetic X11 input to decoded video; excludes physical USB and scanout."""
|
|
import ctypes, ctypes.util, subprocess, os, time, json, statistics
|
|
os.environ.update(DISPLAY=':0',XAUTHORITY='/home/armbian/.Xauthority')
|
|
x=ctypes.CDLL(ctypes.util.find_library('X11'))
|
|
x.XOpenDisplay.argtypes=[ctypes.c_char_p];x.XOpenDisplay.restype=ctypes.c_void_p
|
|
x.XDefaultRootWindow.argtypes=[ctypes.c_void_p];x.XDefaultRootWindow.restype=ctypes.c_ulong
|
|
x.XGetImage.argtypes=[ctypes.c_void_p,ctypes.c_ulong,ctypes.c_int,ctypes.c_int,ctypes.c_uint,ctypes.c_uint,ctypes.c_ulong,ctypes.c_int];x.XGetImage.restype=ctypes.c_void_p
|
|
x.XGetPixel.argtypes=[ctypes.c_void_p,ctypes.c_int,ctypes.c_int];x.XGetPixel.restype=ctypes.c_ulong
|
|
x.XDestroyImage.argtypes=[ctypes.c_void_p];x.XCloseDisplay.argtypes=[ctypes.c_void_p]
|
|
prefix='/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream 192.168.1.72 '
|
|
items=[l.split(None,1) for l in subprocess.check_output(['ps','-eo','pid,args'],text=True).splitlines()[1:] if len(l.split(None,1))==2 and l.split(None,1)[1].startswith(prefix)]
|
|
assert len(items)==1,items
|
|
pid=items[0][0]
|
|
ids=subprocess.check_output(['xdotool','search','--onlyvisible','--pid',pid,'--class','Moonlight'],text=True).split();assert ids
|
|
subprocess.run(['xdotool','windowactivate','--sync',ids[-1]],check=True)
|
|
assert subprocess.check_output(['xdotool','getwindowfocus','getwindowpid'],text=True).strip()==pid
|
|
subprocess.run(['xdotool','key','F8'],check=True)
|
|
for i in range(24):
|
|
px,py=((300,850) if i%2==0 else (1800,950))
|
|
subprocess.run(['xdotool','mousemove','--sync',str(px),str(py),'click','1'],check=True)
|
|
time.sleep(.06)
|
|
d=x.XOpenDisplay(b':0');assert d;root=x.XDefaultRootWindow(d)
|
|
def pixel():
|
|
im=x.XGetImage(d,root,2250,130,1,1,0xffffffff,2);assert im
|
|
v=x.XGetPixel(im,0,0);x.XDestroyImage(im);return v&0xffffff
|
|
samples=[];errors=[]
|
|
for i in range(24):
|
|
v=pixel();assert v<0x202020 or v>0xe0e0e0,hex(v)
|
|
assert subprocess.check_output(['xdotool','getwindowfocus','getwindowpid'],text=True).strip()==pid
|
|
start=time.monotonic_ns()
|
|
subprocess.run(['xdotool','key','--clearmodifiers','--delay','0','F9'],check=True,stdout=subprocess.DEVNULL)
|
|
while time.monotonic_ns()-start<2000000000:
|
|
cur=pixel()
|
|
if (v<0x202020 and cur>0xe0e0e0) or (v>0xe0e0e0 and cur<0x202020):
|
|
samples.append((time.monotonic_ns()-start)/1e6);break
|
|
time.sleep(.002)
|
|
else:errors.append(i)
|
|
time.sleep(.12)
|
|
x.XCloseDisplay(d);vals=sorted(samples)
|
|
print(json.dumps({'method':'synthetic F9 injection to decoded marker; includes xdotool startup/polling; excludes physical USB and panel scanout','samples_ms':samples,'timeouts':errors,'p50_ms':statistics.median(vals) if vals else None,'p95_ms':vals[min(len(vals)-1,int(len(vals)*.95))] if vals else None}))
|