"""Run on the box with a focused, live Ubuntu stream and clear wallpaper.""" import ctypes as C, json, subprocess, time, os os.environ.update(DISPLAY=':0',XAUTHORITY='/home/armbian/.Xauthority') import gi gi.require_version('Gdk','3.0') from gi.repository import Gdk x=C.CDLL('libX11.so.6'); f=C.CDLL('libXfixes.so.3') class Cursor(C.Structure): _fields_=[('x',C.c_short),('y',C.c_short),('w',C.c_ushort),('h',C.c_ushort), ('hx',C.c_ushort),('hy',C.c_ushort),('serial',C.c_ulong), ('pixels',C.POINTER(C.c_ulong)),('atom',C.c_ulong),('name',C.c_char_p)] x.XOpenDisplay.argtypes=[C.c_char_p]; x.XOpenDisplay.restype=C.c_void_p x.XFree.argtypes=[C.c_void_p]; x.XCloseDisplay.argtypes=[C.c_void_p] f.XFixesGetCursorImage.argtypes=[C.c_void_p];f.XFixesGetCursorImage.restype=C.POINTER(Cursor) display=x.XOpenDisplay(None); assert display pid=int(subprocess.check_output(['pgrep','-x','moonlight']).strip()) def focused(): assert int(subprocess.check_output(['xdotool','getwindowfocus','getwindowpid']).strip())==pid def move(px,py): subprocess.run(['xdotool','mousemove',str(px),str(py)],check=True) time.sleep(.5) def patch(px,py): p=Gdk.pixbuf_get_from_window(Gdk.get_default_root_window(),px-8,py-8,64,64) assert p and p.get_n_channels()==3 return bytes(p.get_pixels()) results=[] try: focused() for px,py in [(900,900),(1300,900),(1800,900)]: move(400,1100); before=patch(px,py) move(px,py); after=patch(px,py) diffs=[max(abs(a-b) for a,b in zip(before[i:i+3],after[i:i+3])) for i in range(0,len(before)-2,3)] ptr=f.XFixesGetCursorImage(display); assert ptr try: c=ptr.contents opaque=sum(((c.pixels[i]>>24)&255)>0 for i in range(c.w*c.h)) assert opaque>0 and (c.x,c.y)==(px,py) results.append(dict(point=[px,py],video_pixels_changed_over_25=sum(v>25 for v in diffs), max_video_channel_difference=max(diffs),local_shape=[c.w,c.h,c.hx,c.hy], local_opaque_pixels=opaque)) finally:x.XFree(ptr) focused() print(json.dumps(dict(method='XGetImage-style root capture excludes local X11 cursor; compare clear wallpaper at identical coordinates',pid=pid,points=results))) finally:x.XCloseDisplay(display)