功能:接入Linux原生光标并完善设备接入工具

This commit is contained in:
songyiming committed 2026-11-05 19:18:54 +08:00
1 parent a14f028402
commit a47968496e
92 files changed
+3090 -22

No files matched your search

@@ -0,0 +1,28 @@
[
{
"host": "192.168.1.72",
"normal": {
"width": 24,
"height": 24,
"visible_pixels": 240
},
"adapter": {
"width": 24,
"height": 24,
"visible_pixels": 0
}
},
{
"host": "192.168.1.71",
"normal": {
"width": 16,
"height": 16,
"visible_pixels": 0
},
"adapter": {
"width": 16,
"height": 16,
"visible_pixels": 0
}
}
]
@@ -0,0 +1,16 @@
{
"ubuntu": {
"measured_clicks": 28,
"matched_source_events": 28,
"max_coordinate_error_pixels": 0.9814453125,
"p50_ms": 188.9504005,
"p95_ms": 468.920931
},
"kylin": {
"measured_clicks": 28,
"matched_source_events": 28,
"max_coordinate_error_pixels": 0.74267578125,
"p50_ms": 127.61443249999999,
"p95_ms": 168.630841
}
}
@@ -0,0 +1 @@
5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03
@@ -0,0 +1,13 @@
{
"192.168.1.71": {
"host_py_sha256": "41f6a9a7dcbe3b916864eb3a4bb1b2cbaf669c779256f913fbeef4f53cf09b65",
"client_sha256": "5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03",
"status": "600\nActiveState=inactive\n"
},
"192.168.1.72": {
"host_py_sha256": "41f6a9a7dcbe3b916864eb3a4bb1b2cbaf669c779256f913fbeef4f53cf09b65",
"client_sha256": "5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03",
"status": "600\nActiveState=active\n39181 /usr/bin/python3 /home/test2/vdi-terminal/native-cursor-v1/host.py stream\n"
},
"box_helper_sha256": "44b3e8a0ca591fbde42cf3372d343ab4cb603c089e7805bbccf6d261b2e173ac"
}
@@ -0,0 +1,103 @@
"""Run only with the controlled GTK acceptance window filling the stream."""
import ctypes as C
import json
import os
from pathlib import Path
import signal
import statistics
import struct
import subprocess
import time
os.environ.update(DISPLAY=':0', XAUTHORITY='/home/armbian/.Xauthority')
x = C.CDLL('libX11.so.6')
fix = C.CDLL('libXfixes.so.3')
test = C.CDLL('libXtst.so.6')
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.XDefaultRootWindow.argtypes = [C.c_void_p]
x.XDefaultRootWindow.restype = C.c_ulong
x.XFree.argtypes = [C.c_void_p]
x.XSync.argtypes = [C.c_void_p, C.c_int]
x.XWarpPointer.argtypes = [C.c_void_p, C.c_ulong, C.c_ulong, C.c_int,
C.c_int, C.c_uint, C.c_uint, C.c_int, 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]
fix.XFixesGetCursorImage.argtypes = [C.c_void_p]
fix.XFixesGetCursorImage.restype = C.POINTER(Cursor)
test.XTestFakeButtonEvent.argtypes = [C.c_void_p, C.c_uint, C.c_int, C.c_ulong]
d = x.XOpenDisplay(None)
assert d
root = x.XDefaultRootWindow(d)
def native():
p = fix.XFixesGetCursorImage(d)
assert p
try:
c = p.contents
return dict(x=c.x, y=c.y, w=c.w, h=c.h, hx=c.hx, hy=c.hy,
pixels=[int(c.pixels[i]) & 0xffffffff for i in range(c.w*c.h)])
finally:
x.XFree(p)
def warp(px, py):
x.XWarpPointer(d, 0, root, 0, 0, 0, 0, px, py)
x.XSync(d, 0)
def decoded_sequence():
im = x.XGetImage(d, root, 40, 1370, 640, 1, 0xffffffff, 2)
assert im
try:
values = [x.XGetPixel(im, i*40+20, 0) & 0xffffff for i in range(16)]
if any(0x202020 <= v <= 0xe0e0e0 for v in values):
return -1
return sum((1 << i) for i, v in enumerate(values) if v > 0xe0e0e0)
finally:
x.XDestroyImage(im)
def flat_video_crop(px, py):
im = x.XGetImage(d, root, px-40, py-40, 100, 100, 0xffffffff, 2)
assert im
try:
pixels = [x.XGetPixel(im, a, b) & 0xffffff for b in range(100) for a in range(100)]
# No baked-in pointer: uniform acceptance-window background at this point.
ref = pixels[0]
bad = sum(any(abs(((v >> s) & 255) - ((ref >> s) & 255)) > 8
for s in (0, 8, 16)) for v in pixels)
return dict(reference=ref, nonuniform_pixels=bad)
finally:
x.XDestroyImage(im)
start_sequence=decoded_sequence()
results=[]
for i in range(16):
px,py=((300,800) if i%2==0 else (1500,900))
warp(px,py);time.sleep(.18)
start=time.monotonic_ns();test.XTestFakeButtonEvent(d,1,1,0);x.XSync(d,0)
time.sleep(.04);test.XTestFakeButtonEvent(d,1,0,0);x.XSync(d,0)
target=start_sequence+i+1
while decoded_sequence()!=target and time.monotonic_ns()-start<2000000000:time.sleep(.002)
seen=decoded_sequence();results.append(dict(seq=target,seen=seen,x=px,y=py));assert seen==target,results[-1]
time.sleep(.08)
# Two quick clicks before waiting for their combined numbered video update.
start=decoded_sequence()
for i in range(2):
test.XTestFakeButtonEvent(d,1,1,0);x.XSync(d,0);time.sleep(.04);test.XTestFakeButtonEvent(d,1,0,0);x.XSync(d,0);time.sleep(.07)
end=time.monotonic()+2
while decoded_sequence()!=start+2 and time.monotonic()<end:time.sleep(.002)
print(json.dumps(dict(clicks=results,double_click_start=start,double_click_end=decoded_sequence())))
@@ -0,0 +1,10 @@
{
"192.168.1.71": {
"status": "MainPID=194186\nExecStart={ path=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine ; argv[]=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine /home/test1/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 12:28:12 CST] ; stop_time=[n/a] ; pid=194186 ; code=(null) ; status=0/0 }\nId=vdi-sunshine.service\nActiveState=active\n\nMainPID=0\nExecStart={ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 /home/test1/vdi-terminal/native-cursor-v1/host.py guard ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }\nId=vdi-native-cursor-guard.service\nActiveState=inactive\n\nMainPID=0\nId=vdi-native-cursor-source.service\nActiveState=inactive\nScreen 0: minimum 320 x 200, current 2560 x 1440, maximum 8192 x 8192\nVirtual-1 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 325mm x 203mm\n'Adwaita'\n24\n'default'\n1c3099bf924db3aee759b3d7c05e012569ef8650c400d1479144398795decc71 /opt/vdi-sunshine-0.21.0/usr/bin/sunshine\nno-runtime-dropin\n",
"stderr": ""
},
"192.168.1.72": {
"status": "MainPID=42901\nExecStart={ path=/home/test2/vdi-terminal/native-cursor-v1/sunshine ; argv[]=/home/test2/vdi-terminal/native-cursor-v1/sunshine /home/test2/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 12:29:41 CST] ; stop_time=[n/a] ; pid=42901 ; code=(null) ; status=0/0 }\nId=vdi-sunshine.service\nActiveState=active\n\nMainPID=42870\nExecStart={ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 /home/test2/vdi-terminal/native-cursor-v1/host.py guard ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }\nId=vdi-native-cursor-guard.service\nActiveState=active\n\nMainPID=0\nId=vdi-native-cursor-source.service\nActiveState=inactive\nScreen 0: minimum 320 x 200, current 2560 x 1440, maximum 8192 x 8192\nVirtual-1 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 325mm x 203mm\n'Adwaita'\n24\n'default'\n61e1ecbc8db2df786b9760c652aa4b9885dff36c57d4af09616344314cb7c34c /usr/bin/sunshine\n/usr/bin/sunshine cap_sys_admin,cap_sys_nice=p\n",
"stderr": ""
}
}
@@ -0,0 +1,4 @@
{
"192.168.1.72": "MainPID=42703\nExecStart={ path=/usr/bin/sunshine ; argv[]=/usr/bin/sunshine /home/test2/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 12:25:39 CST] ; stop_time=[n/a] ; pid=42703 ; code=(null) ; status=0/0 }\nId=vdi-sunshine.service\nActiveState=active\n\nMainPID=0\nExecStart={ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 /home/test2/vdi-terminal/native-cursor-v1/host.py guard ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }\nId=vdi-native-cursor-guard.service\nActiveState=inactive\n",
"192.168.1.71": "MainPID=193092\nExecStart={ path=/home/test1/vdi-terminal/native-cursor-v1/sunshine ; argv[]=/home/test1/vdi-terminal/native-cursor-v1/sunshine /home/test1/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 12:26:14 CST] ; stop_time=[n/a] ; pid=193092 ; code=(null) ; status=0/0 }\nId=vdi-sunshine.service\nActiveState=active\n\nMainPID=193023\nExecStart={ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 /home/test1/vdi-terminal/native-cursor-v1/host.py guard ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }\nId=vdi-native-cursor-guard.service\nActiveState=active\n"
}
@@ -0,0 +1,96 @@
"""Run only with the controlled GTK acceptance window filling the stream."""
import ctypes as C
import json
import os
from pathlib import Path
import signal
import statistics
import struct
import subprocess
import time
os.environ.update(DISPLAY=':0', XAUTHORITY='/home/armbian/.Xauthority')
x = C.CDLL('libX11.so.6')
fix = C.CDLL('libXfixes.so.3')
test = C.CDLL('libXtst.so.6')
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.XDefaultRootWindow.argtypes = [C.c_void_p]
x.XDefaultRootWindow.restype = C.c_ulong
x.XFree.argtypes = [C.c_void_p]
x.XSync.argtypes = [C.c_void_p, C.c_int]
x.XWarpPointer.argtypes = [C.c_void_p, C.c_ulong, C.c_ulong, C.c_int,
C.c_int, C.c_uint, C.c_uint, C.c_int, 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]
fix.XFixesGetCursorImage.argtypes = [C.c_void_p]
fix.XFixesGetCursorImage.restype = C.POINTER(Cursor)
test.XTestFakeButtonEvent.argtypes = [C.c_void_p, C.c_uint, C.c_int, C.c_ulong]
d = x.XOpenDisplay(None)
assert d
root = x.XDefaultRootWindow(d)
def native():
p = fix.XFixesGetCursorImage(d)
assert p
try:
c = p.contents
return dict(x=c.x, y=c.y, w=c.w, h=c.h, hx=c.hx, hy=c.hy,
pixels=[int(c.pixels[i]) & 0xffffffff for i in range(c.w*c.h)])
finally:
x.XFree(p)
def warp(px, py):
x.XWarpPointer(d, 0, root, 0, 0, 0, 0, px, py)
x.XSync(d, 0)
def decoded_sequence():
im = x.XGetImage(d, root, 40, 1370, 640, 1, 0xffffffff, 2)
assert im
try:
values = [x.XGetPixel(im, i*40+20, 0) & 0xffffff for i in range(16)]
if any(0x202020 <= v <= 0xe0e0e0 for v in values):
return -1
return sum((1 << i) for i, v in enumerate(values) if v > 0xe0e0e0)
finally:
x.XDestroyImage(im)
def flat_video_crop(px, py):
im = x.XGetImage(d, root, px-40, py-40, 100, 100, 0xffffffff, 2)
assert im
try:
pixels = [x.XGetPixel(im, a, b) & 0xffffff for b in range(100) for a in range(100)]
# No baked-in pointer: uniform acceptance-window background at this point.
ref = pixels[0]
bad = sum(any(abs(((v >> s) & 255) - ((ref >> s) & 255)) > 8
for s in (0, 8, 16)) for v in pixels)
return dict(reference=ref, nonuniform_pixels=bad)
finally:
x.XDestroyImage(im)
focus=C.c_ulong();revert=C.c_int()
x.XGetInputFocus.argtypes=[C.c_void_p,C.POINTER(C.c_ulong),C.POINTER(C.c_int)]
x.XSetInputFocus.argtypes=[C.c_void_p,C.c_ulong,C.c_int,C.c_ulong]
x.XGetInputFocus(d,C.byref(focus),C.byref(revert))
before=native()
try:
x.XSetInputFocus(d,0,1,0);x.XSync(d,0);time.sleep(.25);unfocused=native()
finally:
x.XSetInputFocus(d,focus.value,revert.value,0);x.XSync(d,0)
time.sleep(.3);after=native()
print(json.dumps(dict(original_focus=focus.value,unfocused_shape_changed=before['pixels']!=unfocused['pixels'],native_restored=before['pixels']==after['pixels'],position=[after['x'],after['y']])))
@@ -0,0 +1,10 @@
{
"192.168.1.71": {
"output": "MainPID=184769\nExecStart={ path=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine ; argv[]=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine /home/test1/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 11:33:00 CST] ; stop_time=[n/a] ; pid=184769 ; code=(null) ; status=0/0 }\nId=vdi-sunshine.service\nActiveState=active\n\nMainPID=0\nExecStart={ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 /home/test1/vdi-terminal/native-cursor-v1/host.py guard ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }\nId=vdi-native-cursor-guard.service\nActiveState=inactive\n\nMainPID=0\nId=vdi-native-cursor-source.service\nActiveState=inactive\n",
"stderr": "sha256sum: /usr/bin/sunshine: 没有那个文件或目录\n/usr/bin/sunshine (No such file or directory)\n"
},
"192.168.1.72": {
"output": "MainPID=32278\nExecStart={ path=/usr/bin/sunshine ; argv[]=/usr/bin/sunshine /home/test2/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 11:43:42 CST] ; stop_time=[n/a] ; pid=32278 ; code=(null) ; status=0/0 }\nId=vdi-sunshine.service\nActiveState=active\n\nMainPID=0\nExecStart={ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 /home/test2/vdi-terminal/native-cursor-v1/host.py guard ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }\nId=vdi-native-cursor-guard.service\nActiveState=inactive\n\nMainPID=0\nId=vdi-native-cursor-source.service\nActiveState=inactive\n61e1ecbc8db2df786b9760c652aa4b9885dff36c57d4af09616344314cb7c34c /usr/bin/sunshine\n/usr/bin/sunshine cap_sys_admin,cap_sys_nice=p\n",
"stderr": ""
}
}
@@ -0,0 +1 @@
{"clicks": [{"seq": 29, "seen": 29, "x": 300, "y": 800}, {"seq": 30, "seen": 30, "x": 1500, "y": 900}, {"seq": 31, "seen": 31, "x": 300, "y": 800}, {"seq": 32, "seen": 32, "x": 1500, "y": 900}, {"seq": 33, "seen": 33, "x": 300, "y": 800}, {"seq": 34, "seen": 34, "x": 1500, "y": 900}, {"seq": 35, "seen": 35, "x": 300, "y": 800}, {"seq": 36, "seen": 36, "x": 1500, "y": 900}, {"seq": 37, "seen": 37, "x": 300, "y": 800}, {"seq": 38, "seen": 38, "x": 1500, "y": 900}, {"seq": 39, "seen": 39, "x": 300, "y": 800}, {"seq": 40, "seen": 40, "x": 1500, "y": 900}, {"seq": 41, "seen": 41, "x": 300, "y": 800}, {"seq": 42, "seen": 42, "x": 1500, "y": 900}, {"seq": 43, "seen": 43, "x": 300, "y": 800}, {"seq": 44, "seen": 44, "x": 1500, "y": 900}], "double_click_start": 44, "double_click_end": 46}
@@ -0,0 +1 @@
{"host": "192.168.1.71", "pid": 267646, "shapes": [{"name": "default", "source": {"w": 24, "h": 24, "hx": 4, "hy": 0, "x": 182, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 182, "y": 600, "w": 24, "h": 24, "hx": 4, "hy": 0}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 300, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "text", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 548, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 548, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 216, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "ew-resize", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 914, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 914, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 204, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "wait", "source": {"w": 24, "h": 24, "hx": 12, "hy": 12, "x": 1280, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 1280, "y": 600, "w": 24, "h": 24, "hx": 12, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 290, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "none", "source": {"w": 1, "h": 1, "hx": 0, "hy": 0, "x": 1645, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 1645, "y": 600, "w": 1, "h": 1, "hx": 0, "hy": 0}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 0, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "crosshair", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 2011, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 2011, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 252, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "custom", "source": {"w": 24, "h": 24, "hx": 7, "hy": 9, "x": 2377, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 2377, "y": 600, "w": 24, "h": 24, "hx": 7, "hy": 9}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 576, "video": {"reference": 15726074, "nonuniform_pixels": 0}}], "paused_client": {"position": [350, 700], "elapsed_ms": 249.580297, "same_shape": true, "steps": [{"target": [250, 650], "position": [250, 650], "query_ms": 2.364751}, {"target": [280, 670], "position": [280, 670], "query_ms": 2.234379}, {"target": [300, 690], "position": [300, 690], "query_ms": 2.128216}, {"target": [350, 700], "position": [350, 700], "query_ms": 2.286878}]}, "clicks": [{"seq": 1, "x": 182, "y": 750, "seen": 1, "decoded_ms": 108.930461}, {"seq": 2, "x": 548, "y": 850, "seen": 2, "decoded_ms": 98.691574}, {"seq": 3, "x": 914, "y": 750, "seen": 3, "decoded_ms": 152.320189}, {"seq": 4, "x": 1280, "y": 850, "seen": 4, "decoded_ms": 125.352183}, {"seq": 5, "x": 1645, "y": 750, "seen": 5, "decoded_ms": 135.816524}, {"seq": 6, "x": 2011, "y": 850, "seen": 6, "decoded_ms": 111.517755}, {"seq": 7, "x": 2377, "y": 750, "seen": 7, "decoded_ms": 108.898971}, {"seq": 8, "x": 182, "y": 850, "seen": 8, "decoded_ms": 119.075451}, {"seq": 9, "x": 548, "y": 750, "seen": 9, "decoded_ms": 134.943313}, {"seq": 10, "x": 914, "y": 850, "seen": 10, "decoded_ms": 146.081384}, {"seq": 11, "x": 1280, "y": 750, "seen": 11, "decoded_ms": 117.025403}, {"seq": 12, "x": 1645, "y": 850, "seen": 12, "decoded_ms": 152.719197}, {"seq": 13, "x": 2011, "y": 750, "seen": 13, "decoded_ms": 141.342555}, {"seq": 14, "x": 2377, "y": 850, "seen": 14, "decoded_ms": 130.95113}, {"seq": 15, "x": 182, "y": 750, "seen": 15, "decoded_ms": 143.698269}, {"seq": 16, "x": 548, "y": 850, "seen": 16, "decoded_ms": 114.9634}, {"seq": 17, "x": 914, "y": 750, "seen": 17, "decoded_ms": 159.173858}, {"seq": 18, "x": 1280, "y": 850, "seen": 18, "decoded_ms": 175.329882}, {"seq": 19, "x": 1645, "y": 750, "seen": 19, "decoded_ms": 119.051263}, {"seq": 20, "x": 2011, "y": 850, "seen": 20, "decoded_ms": 124.040341}, {"seq": 21, "x": 2377, "y": 750, "seen": 21, "decoded_ms": 121.588678}, {"seq": 22, "x": 182, "y": 850, "seen": 22, "decoded_ms": 129.876682}, {"seq": 23, "x": 548, "y": 750, "seen": 23, "decoded_ms": 124.467041}, {"seq": 24, "x": 914, "y": 850, "seen": 24, "decoded_ms": 153.052003}, {"seq": 25, "x": 1280, "y": 750, "seen": 25, "decoded_ms": 136.166846}, {"seq": 26, "x": 1645, "y": 850, "seen": 26, "decoded_ms": 168.630841}, {"seq": 27, "x": 2011, "y": 750, "seen": 27, "decoded_ms": 119.541845}, {"seq": 28, "x": 2377, "y": 850, "seen": 28, "decoded_ms": 110.41562}], "p50_ms": 127.61443249999999, "p95_ms": 168.630841, "method": "XTest click-down to unique decoded sequence; excludes USB and scanout"}
Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

@@ -0,0 +1 @@
{"original_focus": 48234511, "unfocused_shape_changed": false, "native_restored": true, "position": [400, 600]}
@@ -0,0 +1 @@
{"pid": 297530, "args": "/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream 192.168.1.71 Desktop --resolution 2560x1440 --fps 30 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --capture-system-keys always --keep-awake --no-vsync --no-frame-pacing --audio-on-host ", "sha256": "5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03", "native": true, "shape": [24, 24, 4, 0]}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

@@ -0,0 +1 @@
{"host": "192.168.1.71", "pid": 297530, "shapes": [{"name": "default", "source": {"w": 24, "h": 24, "hx": 4, "hy": 0, "x": 182, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 182, "y": 600, "w": 24, "h": 24, "hx": 4, "hy": 0}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 300, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "text", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 548, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 548, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 216, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "ew-resize", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 914, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 914, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 204, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "wait", "source": {"w": 24, "h": 24, "hx": 12, "hy": 12, "x": 1280, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 1280, "y": 600, "w": 24, "h": 24, "hx": 12, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 290, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "none", "source": {"w": 1, "h": 1, "hx": 0, "hy": 0, "x": 1645, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 1645, "y": 600, "w": 1, "h": 1, "hx": 0, "hy": 0}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 0, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "crosshair", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 2011, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 2011, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 252, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "custom", "source": {"w": 24, "h": 24, "hx": 7, "hy": 9, "x": 2377, "y": 600, "sw": 2560, "sh": 1440}, "local": {"x": 2377, "y": 600, "w": 24, "h": 24, "hx": 7, "hy": 9}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 576, "video": {"reference": 15726074, "nonuniform_pixels": 0}}], "paused_client": {"position": [350, 700], "elapsed_ms": 249.614056, "same_shape": true, "steps": [{"target": [250, 650], "position": [250, 650], "query_ms": 2.255936}, {"target": [280, 670], "position": [280, 670], "query_ms": 2.398262}, {"target": [300, 690], "position": [300, 690], "query_ms": 2.188272}, {"target": [350, 700], "position": [350, 700], "query_ms": 2.242227}]}, "clicks": [{"seq": 1, "x": 182, "y": 750, "seen": 1, "decoded_ms": 149.895203}, {"seq": 2, "x": 548, "y": 850, "seen": 2, "decoded_ms": 120.380242}, {"seq": 3, "x": 914, "y": 750, "seen": 3, "decoded_ms": 119.660739}, {"seq": 4, "x": 1280, "y": 850, "seen": 4, "decoded_ms": 147.265671}, {"seq": 5, "x": 1645, "y": 750, "seen": 5, "decoded_ms": 145.663335}, {"seq": 6, "x": 2011, "y": 850, "seen": 6, "decoded_ms": 129.562066}, {"seq": 7, "x": 2377, "y": 750, "seen": 7, "decoded_ms": 134.743571}, {"seq": 8, "x": 182, "y": 850, "seen": 8, "decoded_ms": 140.630877}, {"seq": 9, "x": 548, "y": 750, "seen": 9, "decoded_ms": 130.420116}, {"seq": 10, "x": 914, "y": 850, "seen": 10, "decoded_ms": 107.155271}, {"seq": 11, "x": 1280, "y": 750, "seen": 11, "decoded_ms": 117.124838}, {"seq": 12, "x": 1645, "y": 850, "seen": 12, "decoded_ms": 130.236678}, {"seq": 13, "x": 2011, "y": 750, "seen": 13, "decoded_ms": 111.917383}, {"seq": 14, "x": 2377, "y": 850, "seen": 14, "decoded_ms": 136.811123}, {"seq": 15, "x": 182, "y": 750, "seen": 15, "decoded_ms": 106.746093}, {"seq": 16, "x": 548, "y": 850, "seen": 16, "decoded_ms": 108.595469}, {"seq": 17, "x": 914, "y": 750, "seen": 17, "decoded_ms": 123.440014}, {"seq": 18, "x": 1280, "y": 850, "seen": 18, "decoded_ms": 150.429282}, {"seq": 19, "x": 1645, "y": 750, "seen": 19, "decoded_ms": 117.354684}, {"seq": 20, "x": 2011, "y": 850, "seen": 20, "decoded_ms": 147.688919}, {"seq": 21, "x": 2377, "y": 750, "seen": 21, "decoded_ms": 135.505122}, {"seq": 22, "x": 182, "y": 850, "seen": 22, "decoded_ms": 142.014237}, {"seq": 23, "x": 548, "y": 750, "seen": 23, "decoded_ms": 195.466041}, {"seq": 24, "x": 914, "y": 850, "seen": 24, "decoded_ms": 165.559673}, {"seq": 25, "x": 1280, "y": 750, "seen": 25, "decoded_ms": 142.88512}, {"seq": 26, "x": 1645, "y": 850, "seen": 26, "decoded_ms": 127.731718}, {"seq": 27, "x": 2011, "y": 750, "seen": 27, "decoded_ms": 146.832648}, {"seq": 28, "x": 2377, "y": 850, "seen": 28, "decoded_ms": 133.995845}], "p50_ms": 134.369708, "p95_ms": 165.559673, "method": "XTest click-down to unique decoded sequence; excludes USB and scanout"}
@@ -0,0 +1,5 @@
{
"recovered": true,
"seconds": 3.555,
"state": "ExecStart={ path=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine ; argv[]=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine /home/test1/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 11:33:00 CST] ; stop_time=[n/a] ; pid=184769 ; code=(null) ; status=0/0 }\nActiveState=active\nScreen 0: minimum 320 x 200, current 2560 x 1440, maximum 8192 x 8192\nVirtual-1 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 325mm x 203mm\n 1280x800 74.99 +\n 3840x2160 60.00 50.00 59.94 \n 1920x1440 60.00 \ninactive\n"
}
@@ -0,0 +1,9 @@
{
"host": "192.168.1.71",
"target": [
400,
600
],
"readback": "X=400\nY=600\nSCREEN=0\nWINDOW=48234511\n",
"passed": true
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.
@@ -0,0 +1,12 @@
{
"192.168.1.71": {
"status": "MainPID=186038\nId=vdi-sunshine.service\nActiveState=active\n\nMainPID=185981\nId=vdi-native-cursor-guard.service\nActiveState=active\n\nMainPID=0\nId=vdi-native-cursor-source.service\nActiveState=inactive\nScreen 0: minimum 320 x 200, current 2560 x 1440, maximum 8192 x 8192\nVirtual-1 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 325mm x 203mm\n 1280x800 74.99 +\n 3840x2160 60.00 50.00 59.94 \n 1920x1440 60.00 \n'Adwaita'\n24\n'default'\ntrue\n1c3099bf924db3aee759b3d7c05e012569ef8650c400d1479144398795decc71 /opt/vdi-sunshine-0.21.0/usr/bin/sunshine\n",
"stderr": "",
"profile_client_sha": "1da552e4855d1562fdd8d6f04f1553382db4ccd364c66fe7486b1688cc584f15"
},
"192.168.1.72": {
"status": "MainPID=32278\nId=vdi-sunshine.service\nActiveState=active\n\nMainPID=0\nId=vdi-native-cursor-guard.service\nActiveState=inactive\n\nMainPID=0\nId=vdi-native-cursor-source.service\nActiveState=inactive\nScreen 0: minimum 320 x 200, current 2560 x 1440, maximum 8192 x 8192\nVirtual-1 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 325mm x 203mm\n 1280x800 74.99 +\n 1920x1440 60.00 \n 2560x1080 50.00 \n'Adwaita'\n24\n'default'\nfalse\n61e1ecbc8db2df786b9760c652aa4b9885dff36c57d4af09616344314cb7c34c /usr/bin/sunshine\n/usr/bin/sunshine cap_sys_admin,cap_sys_nice=p\n",
"stderr": "",
"profile_client_sha": "1da552e4855d1562fdd8d6f04f1553382db4ccd364c66fe7486b1688cc584f15"
}
}
@@ -0,0 +1,14 @@
{
"baseline": "de2467e433821664cdd2224aad8c89a625be1ad9",
"patches": [
"0001-vdi-input-and-latest-frame.patch",
"0003-vdi-input-priority.patch",
"0004-vdi-power-key-policy.patch",
"0005-vdi-native-linux-cursor.patch",
"0002-vdi-absolute-click-barrier.patch"
],
"native_patch_sha256": "6a5879901ae0d2986271df7441a38d0f1813c0d7fb3f616af5e1212c75eaaa02",
"returncode": 0,
"stdout": "PATCHES_OK\n",
"stderr": ""
}
@@ -0,0 +1 @@
{"patches_apply_cleanly": ["0001-vdi-input-and-latest-frame.patch", "0003-vdi-input-priority.patch", "0004-vdi-power-key-policy.patch", "0005-vdi-native-linux-cursor.patch", "0002-vdi-absolute-click-barrier.patch"], "baseline": "de2467e433821664cdd2224aad8c89a625be1ad9"}
@@ -0,0 +1,188 @@
"""Run only with the controlled GTK acceptance window filling the stream."""
import ctypes as C
import json
import os
from pathlib import Path
import signal
import statistics
import struct
import subprocess
import time
os.environ.update(DISPLAY=':0', XAUTHORITY='/home/armbian/.Xauthority')
x = C.CDLL('libX11.so.6')
fix = C.CDLL('libXfixes.so.3')
test = C.CDLL('libXtst.so.6')
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.XDefaultRootWindow.argtypes = [C.c_void_p]
x.XDefaultRootWindow.restype = C.c_ulong
x.XFree.argtypes = [C.c_void_p]
x.XSync.argtypes = [C.c_void_p, C.c_int]
x.XWarpPointer.argtypes = [C.c_void_p, C.c_ulong, C.c_ulong, C.c_int,
C.c_int, C.c_uint, C.c_uint, C.c_int, 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]
fix.XFixesGetCursorImage.argtypes = [C.c_void_p]
fix.XFixesGetCursorImage.restype = C.POINTER(Cursor)
test.XTestFakeButtonEvent.argtypes = [C.c_void_p, C.c_uint, C.c_int, C.c_ulong]
d = x.XOpenDisplay(None)
assert d
root = x.XDefaultRootWindow(d)
def native():
p = fix.XFixesGetCursorImage(d)
assert p
try:
c = p.contents
return dict(x=c.x, y=c.y, w=c.w, h=c.h, hx=c.hx, hy=c.hy,
pixels=[int(c.pixels[i]) & 0xffffffff for i in range(c.w*c.h)])
finally:
x.XFree(p)
def warp(px, py):
x.XWarpPointer(d, 0, root, 0, 0, 0, 0, px, py)
x.XSync(d, 0)
def decoded_sequence():
im = x.XGetImage(d, root, 40, 1370, 640, 1, 0xffffffff, 2)
assert im
try:
values = [x.XGetPixel(im, i*40+20, 0) & 0xffffff for i in range(16)]
if any(0x202020 <= v <= 0xe0e0e0 for v in values):
return -1
return sum((1 << i) for i, v in enumerate(values) if v > 0xe0e0e0)
finally:
x.XDestroyImage(im)
def flat_video_crop(px, py):
im = x.XGetImage(d, root, px-40, py-40, 100, 100, 0xffffffff, 2)
assert im
try:
pixels = [x.XGetPixel(im, a, b) & 0xffffff for b in range(100) for a in range(100)]
# No baked-in pointer: uniform acceptance-window background at this point.
ref = pixels[0]
bad = sum(any(abs(((v >> s) & 255) - ((ref >> s) & 255)) > 8
for s in (0, 8, 16)) for v in pixels)
return dict(reference=ref, nonuniform_pixels=bad)
finally:
x.XDestroyImage(im)
host = os.sys.argv[1]
prefix = '/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream ' + host + ' '
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 = int(items[0][0])
focus = subprocess.check_output(['xdotool','getwindowfocus','getwindowpid'], text=True).strip()
assert focus == str(pid), (focus, pid)
env = dict(item.split('=', 1) for item in Path('/proc/%d/environ' % pid).read_bytes().decode().split('\0') if '=' in item)
statepath = Path(env['VDI_NATIVE_CURSOR_STATE'])
initial_sequence = decoded_sequence()
assert 0 <= initial_sequence < 60000, 'test window not ready'
reference = flat_video_crop(180, 600)['reference']
assert all(abs(((reference >> s) & 255)-v) < 10 for s,v in [(16,240),(8,245),(0,250)]), 'acceptance window not visible'
shapes = []
for i, name in enumerate(['default','text','ew-resize','wait','none','crosshair','custom']):
px = int((i+.5)*2560/7)
warp(px, 600)
time.sleep(.6)
state = statepath.read_bytes()
header = struct.unpack('<8sQQQIIIIiiII', state[:64])
local = native()
source = dict(zip(['w','h','hx','hy','x','y','sw','sh'], header[4:]))
# Convert expected straight RGBA to XRender premultiplied ARGB.
expected = []
for j in range(64, len(state)-8, 4):
r,g,b,a = state[j:j+4]
expected.append((a << 24) | ((r*a+127)//255 << 16) |
((g*a+127)//255 << 8) | ((b*a+127)//255))
same_geometry = all(local[k] == source[k] for k in ['w','h','hx','hy'])
# Busy frames animate; these snapshots can legitimately be adjacent frames.
diff = max((abs(((a >> s) & 255)-((b >> s) & 255))
for a,b in zip(local['pixels'],expected) for s in [0,8,16,24]), default=0)
shapes.append(dict(name=name, source=source,
local={k:v for k,v in local.items() if k != 'pixels'},
geometry_matches=same_geometry, max_pixel_difference=diff,
visible_pixels=sum(p >> 24 != 0 for p in local['pixels']),
video=flat_video_crop(px, 600)))
Path('/tmp/native-%s-shapes.json' % host).write_text(json.dumps(shapes))
samples = []
for i in range(28):
px = int(((i % 7)+.5)*2560/7)
py = 750 if i % 2 == 0 else 850
warp(px, py)
time.sleep(.25)
start = time.monotonic_ns()
test.XTestFakeButtonEvent(d, 1, 1, 0)
x.XSync(d, 0)
expected = initial_sequence+i+1
seen = -1
released = False
try:
while time.monotonic_ns()-start < 3000000000:
if not released and time.monotonic_ns()-start >= 80000000:
test.XTestFakeButtonEvent(d, 1, 0, 0)
x.XSync(d, 0)
released = True
seen = decoded_sequence()
if seen == expected:
break
if seen > expected:
raise RuntimeError('unexpected duplicate click %d instead of %d' % (seen,expected))
time.sleep(.002)
finally:
if not released:
time.sleep(max(0, .08-(time.monotonic_ns()-start)/1e9))
test.XTestFakeButtonEvent(d, 1, 0, 0)
x.XSync(d, 0)
samples.append(dict(seq=expected, x=px, y=py, seen=seen,
decoded_ms=(time.monotonic_ns()-start)/1e6))
if seen != expected:
Path('/tmp/native-%s-failed-clicks.json' % host).write_text(json.dumps(samples))
raise AssertionError(samples[-1])
time.sleep(.1)
times = sorted(s['decoded_ms'] for s in samples)
# A stopped Moonlight cannot capture input, receive video, or draw frames.
# X11's native pointer must still move and keep its shape while it is stopped.
warp(200, 600)
time.sleep(.3)
before = native()
try:
os.kill(pid, signal.SIGSTOP)
start = time.monotonic_ns()
steps = []
for px,py in [(250,650),(280,670),(300,690),(350,700)]:
step_start = time.monotonic_ns()
warp(px, py)
after = native()
steps.append(dict(target=[px,py],position=[after['x'],after['y']],
query_ms=(time.monotonic_ns()-step_start)/1e6))
time.sleep(.06)
paused_ms = (time.monotonic_ns()-start)/1e6
finally:
os.kill(pid, signal.SIGCONT)
pause = dict(position=[after['x'],after['y']], elapsed_ms=paused_ms,
same_shape=before['pixels']==after['pixels'], steps=steps)
assert all(s['target']==s['position'] for s in steps) and pause['same_shape']
time.sleep(.4)
print(json.dumps(dict(host=host,pid=pid,shapes=shapes,paused_client=pause,
clicks=samples,p50_ms=statistics.median(times),
p95_ms=times[int(len(times)*.95)],
method='XTest click-down to unique decoded sequence; excludes USB and scanout')))
@@ -0,0 +1,17 @@
{
"client_sha256": "5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03",
"ubuntu": {
"measured_clicks": 28,
"matched_source_events": 28,
"max_coordinate_error_pixels": 1.0546875,
"p50_ms": 188.3848435,
"p95_ms": 522.420223
},
"kylin": {
"measured_clicks": 28,
"matched_source_events": 28,
"max_coordinate_error_pixels": 0.82855224609375,
"p50_ms": 134.369708,
"p95_ms": 165.559673
}
}
@@ -0,0 +1,53 @@
"""Observe actual deployed state/receiver; keep the test seat awake with motion.
Run on the box only while a controlled native-cursor session is active.
No clicks, protocol injection, or changes to the state producer are made.
"""
import json
import os
from pathlib import Path
import struct
import subprocess
import sys
import time
duration = float(sys.argv[1])
output = Path(sys.argv[2])
pid = int(subprocess.check_output(['pgrep', '-x', 'moonlight']).strip())
proc = Path('/proc') / str(pid)
env = dict(x.split('=', 1) for x in (proc / 'environ').read_bytes().decode().split('\0') if '=' in x)
state = Path(env['VDI_NATIVE_CURSOR_STATE'])
start = time.monotonic()
last_motion = 0
result = dict(pid=pid, requested_seconds=duration, samples=0,
timestamp_after_pre_read_clock=0, timestamp_after_post_read_clock=0,
max_age_ms=0, survived=False)
try:
while time.monotonic() - start < duration:
if not proc.exists() or (proc / 'comm').read_text().strip() != 'moonlight':
raise RuntimeError('receiver exited')
before = time.monotonic_ns()
packet = state.read_bytes()
after = time.monotonic_ns()
stamp = struct.unpack_from('<Q', packet, 24)[0]
result['samples'] += 1
result['timestamp_after_pre_read_clock'] += stamp > before
result['timestamp_after_post_read_clock'] += stamp > after
result['max_age_ms'] = max(result['max_age_ms'], (after - stamp) / 1e6)
result['last_sequence'] = struct.unpack_from('<Q', packet, 16)[0]
if stamp > after or after - stamp > 2_000_000_000:
raise RuntimeError('invalid post-read timestamp')
now = time.monotonic()
if now - last_motion > 10:
px = 1800 + (int(now - start) // 10 % 2) * 50
motion_env = dict(os.environ, DISPLAY=':0', XAUTHORITY='/home/armbian/.Xauthority')
subprocess.run(['xdotool', 'mousemove', str(px), '1100'], env=motion_env, check=True)
last_motion = now
time.sleep(.004)
result['survived'] = True
except Exception as error:
result['error'] = str(error)
finally:
result['elapsed_seconds'] = round(time.monotonic() - start, 3)
output.write_text(json.dumps(result, indent=2) + '\n')
print(json.dumps(result), flush=True)
@@ -0,0 +1,92 @@
"""Controlled GTK test window: real native cursors and unique click sequences."""
import gi
import json
import time
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib
win = Gtk.Window(title='VDI native cursor acceptance')
area = Gtk.DrawingArea()
win.add(area)
area.set_can_focus(True)
area.set_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.BUTTON_PRESS_MASK |
Gdk.EventMask.BUTTON_RELEASE_MASK)
display = Gdk.Display.get_default()
names = ['default', 'text', 'ew-resize', 'wait', 'none', 'crosshair', 'custom']
cursors = {name: Gdk.Cursor.new_from_name(display, name) for name in names[:4] + ['crosshair']}
cursors['none'] = Gdk.Cursor.new_for_display(display, Gdk.CursorType.BLANK_CURSOR)
pix = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, 24, 24)
pix.fill(0xff5030ff)
cursors['custom'] = Gdk.Cursor.new_from_pixbuf(display, pix, 7, 9)
current = None
sequence = 0
def report(**kw):
kw['t'] = time.monotonic()
print(json.dumps(kw), flush=True)
def motion(widget, event):
global current
index = min(6, int(event.x // (2560/7)))
name = names[index]
if name != current:
area.get_window().set_cursor(cursors[name])
current = name
report(event='shape', name=name)
return False
def button(widget, event):
global sequence
if event.type in (Gdk.EventType.BUTTON_PRESS, Gdk.EventType.DOUBLE_BUTTON_PRESS,
Gdk.EventType.TRIPLE_BUTTON_PRESS):
# GTK emits an extra double/triple notification, not another input.
if event.type == Gdk.EventType.BUTTON_PRESS:
sequence += 1
area.queue_draw_area(0, 1300, 1000, 140)
report(event=event.type.value_nick, seq=sequence, x=event.x_root,
y=event.y_root, button=event.button)
return False
def draw(widget, cr):
cr.set_source_rgb(.94, .96, .98)
cr.paint()
cr.select_font_face('sans')
cr.set_font_size(25)
for i, name in enumerate(names):
cr.set_source_rgb(.1, .15, .2)
cr.move_to(40 + i*2560/7, 100)
cr.show_text(name)
cr.set_source_rgb(.65, .7, .8)
cr.rectangle(i*2560/7, 120, 2, 1100)
cr.fill()
# Unique monotonic sequence, read from the actual decoded video.
for bit in range(16):
v = 1 if sequence & (1 << bit) else 0
cr.set_source_rgb(v, v, v)
cr.rectangle(40 + bit*40, 1340, 40, 60)
cr.fill()
return False
def key(widget, event):
if event.keyval == Gdk.KEY_Escape:
Gtk.main_quit()
return False
win.connect('destroy', Gtk.main_quit)
win.connect('key-press-event', key)
area.connect('motion-notify-event', motion)
area.connect('button-press-event', button)
area.connect('button-release-event', button)
area.connect('draw', draw)
win.fullscreen()
win.show_all()
win.present()
area.grab_focus()
report(event='ready')
Gtk.main()
@@ -0,0 +1,4 @@
{
"sunshine": "1c3099bf924db3aee759b3d7c05e012569ef8650c400d1479144398795decc71",
"capture-no-cursor.so": "cff4b7532aee69f0f205fa42946f45a89c1330486adc6358b90d13d2ccf1a0ea"
}
@@ -0,0 +1,302 @@
{
"host": "192.168.1.71",
"ready": true,
"samples": [
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 1
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 2
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 3
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 4
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 5
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 6
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 7
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 8
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 9
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 10
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 11
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 12
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 13
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 14
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 15
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 16
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 17
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 18
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 19
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 20
},
{
"w": 16,
"h": 16,
"hx": 8,
"hy": 8,
"x": 1814,
"y": 554,
"sw": 2560,
"sh": 1440,
"shape": 166,
"type": "cursor",
"version": 1,
"seq": 21
}
],
"stderr": "",
"exit": 0
}
@@ -0,0 +1,7 @@
{
"host": "192.168.1.71",
"ready": false,
"samples": [],
"stderr": "RuntimeError: Permission denied by kysec\n",
"exit": 1
}
@@ -0,0 +1,16 @@
[
{
"case": "agent-sigkill",
"host": "192.168.1.71",
"recovered": true,
"recovery_seconds": 3.22,
"state": "ExecStart={ path=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine ; argv[]=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine /home/test1/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 11:11:57 CST] ; stop_time=[n/a] ; pid=180535 ; code=(null) ; status=0/0 }\nActiveState=active\n"
},
{
"case": "guard-sigkill",
"host": "192.168.1.71",
"recovered": true,
"recovery_seconds": 0.893,
"state": "ExecStart={ path=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine ; argv[]=/opt/vdi-sunshine-0.21.0/usr/bin/sunshine /home/test1/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 11:12:00 CST] ; stop_time=[n/a] ; pid=180701 ; code=(null) ; status=0/0 }\nActiveState=active\n"
}
]
@@ -0,0 +1,4 @@
{
"sunshine": "61e1ecbc8db2df786b9760c652aa4b9885dff36c57d4af09616344314cb7c34c",
"capture-no-cursor.so": "cff4b7532aee69f0f205fa42946f45a89c1330486adc6358b90d13d2ccf1a0ea"
}
@@ -0,0 +1,316 @@
{
"host": "192.168.1.72",
"ready": true,
"samples": [
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 1
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 2
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 3
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 4
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 5
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 6
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 7
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 8
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 9
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 10
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 11
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 12
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 13
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 14
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 15
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 16
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 17
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 18
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 19
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 20
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 21
},
{
"w": 24,
"h": 24,
"hx": 3,
"hy": 1,
"x": 2548,
"y": 1,
"sw": 2560,
"sh": 1440,
"shape": 124,
"type": "cursor",
"version": 1,
"seq": 22
}
],
"stderr": "",
"exit": 0
}
@@ -0,0 +1,7 @@
{
"host": "192.168.1.72",
"ready": false,
"samples": [],
"stderr": "Traceback (most recent call last):\n File \"/home/test2/vdi-terminal/native-cursor-v1/host.py\", line 211, in <module>\n main()\n File \"/home/test2/vdi-terminal/native-cursor-v1/host.py\", line 205, in main\n stream()\n File \"/home/test2/vdi-terminal/native-cursor-v1/host.py\", line 154, in stream\n raise RuntimeError('guard not ready')\nRuntimeError: guard not ready\n",
"exit": 1
}
@@ -0,0 +1,16 @@
[
{
"case": "agent-sigkill",
"host": "192.168.1.72",
"recovered": true,
"recovery_seconds": 2.724,
"state": "ExecStart={ path=/usr/bin/sunshine ; argv[]=/usr/bin/sunshine /home/test2/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 11:11:56 CST] ; stop_time=[n/a] ; pid=24296 ; code=(null) ; status=0/0 }\nActiveState=active\n"
},
{
"case": "guard-sigkill",
"host": "192.168.1.72",
"recovered": true,
"recovery_seconds": 0.771,
"state": "ExecStart={ path=/usr/bin/sunshine ; argv[]=/usr/bin/sunshine /home/test2/.config/sunshine/sunshine.conf ; ignore_errors=no ; start_time=[Thu 2026-11-05 11:12:14 CST] ; stop_time=[n/a] ; pid=24470 ; code=(null) ; status=0/0 }\nActiveState=active\n"
}
]
@@ -0,0 +1,11 @@
{
"pid": 285520,
"requested_seconds": 360.0,
"samples": 65658,
"timestamp_after_pre_read_clock": 0,
"timestamp_after_post_read_clock": 0,
"max_age_ms": 308.103546,
"survived": true,
"last_sequence": 7956,
"elapsed_seconds": 360.005
}
@@ -0,0 +1 @@
[{"file": "_usr_bin_sunshine.1000.crash", "ProblemType": "Crash", "Date": "Thu Nov 5 11:12:07 2026", "ExecutablePath": "/usr/bin/sunshine", "Package": "sunshine 2026.914.233613-1+ubuntu24.04 [origin: unknown]", "Signal": "5"}]
@@ -0,0 +1 @@
{"clicks": [{"seq": 29, "seen": 29, "x": 300, "y": 800}, {"seq": 30, "seen": 30, "x": 1500, "y": 900}, {"seq": 31, "seen": 31, "x": 300, "y": 800}, {"seq": 32, "seen": 32, "x": 1500, "y": 900}, {"seq": 33, "seen": 33, "x": 300, "y": 800}, {"seq": 34, "seen": 34, "x": 1500, "y": 900}, {"seq": 35, "seen": 35, "x": 300, "y": 800}, {"seq": 36, "seen": 36, "x": 1500, "y": 900}, {"seq": 37, "seen": 37, "x": 300, "y": 800}, {"seq": 38, "seen": 38, "x": 1500, "y": 900}, {"seq": 39, "seen": 39, "x": 300, "y": 800}, {"seq": 40, "seen": 40, "x": 1500, "y": 900}, {"seq": 41, "seen": 41, "x": 300, "y": 800}, {"seq": 42, "seen": 42, "x": 1500, "y": 900}, {"seq": 43, "seen": 43, "x": 300, "y": 800}, {"seq": 44, "seen": 44, "x": 1500, "y": 900}], "double_click_start": 44, "double_click_end": 46}
@@ -0,0 +1 @@
{"host": "192.168.1.72", "pid": 263384, "shapes": [{"name": "default", "source": {"w": 24, "h": 24, "hx": 3, "hy": 1, "x": 181, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 182, "y": 600, "w": 24, "h": 24, "hx": 3, "hy": 1}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 254, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "text", "source": {"w": 24, "h": 24, "hx": 11, "hy": 12, "x": 547, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 548, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 240, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "ew-resize", "source": {"w": 24, "h": 24, "hx": 12, "hy": 12, "x": 913, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 914, "y": 600, "w": 24, "h": 24, "hx": 12, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 301, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "wait", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 1280, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 1280, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 433, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "none", "source": {"w": 1, "h": 1, "hx": 0, "hy": 0, "x": 1645, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 1645, "y": 600, "w": 1, "h": 1, "hx": 0, "hy": 0}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 0, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "crosshair", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 2011, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 2011, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 281, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "custom", "source": {"w": 24, "h": 24, "hx": 7, "hy": 9, "x": 2377, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 2377, "y": 600, "w": 24, "h": 24, "hx": 7, "hy": 9}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 576, "video": {"reference": 15726074, "nonuniform_pixels": 0}}], "paused_client": {"position": [250, 650], "elapsed_ms": 2.232537, "same_shape": true}, "clicks": [{"seq": 1, "x": 182, "y": 750, "seen": 1, "decoded_ms": 183.136301}, {"seq": 2, "x": 548, "y": 850, "seen": 2, "decoded_ms": 150.601919}, {"seq": 3, "x": 914, "y": 750, "seen": 3, "decoded_ms": 162.979614}, {"seq": 4, "x": 1280, "y": 850, "seen": 4, "decoded_ms": 562.228218}, {"seq": 5, "x": 1645, "y": 750, "seen": 5, "decoded_ms": 149.661983}, {"seq": 6, "x": 2011, "y": 850, "seen": 6, "decoded_ms": 187.689807}, {"seq": 7, "x": 2377, "y": 750, "seen": 7, "decoded_ms": 186.617722}, {"seq": 8, "x": 182, "y": 850, "seen": 8, "decoded_ms": 167.201514}, {"seq": 9, "x": 548, "y": 750, "seen": 9, "decoded_ms": 143.805796}, {"seq": 10, "x": 914, "y": 850, "seen": 10, "decoded_ms": 145.803285}, {"seq": 11, "x": 1280, "y": 750, "seen": 11, "decoded_ms": 505.147227}, {"seq": 12, "x": 1645, "y": 850, "seen": 12, "decoded_ms": 156.874419}, {"seq": 13, "x": 2011, "y": 750, "seen": 13, "decoded_ms": 164.724633}, {"seq": 14, "x": 2377, "y": 850, "seen": 14, "decoded_ms": 172.592036}, {"seq": 15, "x": 182, "y": 750, "seen": 15, "decoded_ms": 195.775758}, {"seq": 16, "x": 548, "y": 850, "seen": 16, "decoded_ms": 151.145679}, {"seq": 17, "x": 914, "y": 750, "seen": 17, "decoded_ms": 173.941806}, {"seq": 18, "x": 1280, "y": 850, "seen": 18, "decoded_ms": 352.426864}, {"seq": 19, "x": 1645, "y": 750, "seen": 19, "decoded_ms": 156.050885}, {"seq": 20, "x": 2011, "y": 850, "seen": 20, "decoded_ms": 158.266234}, {"seq": 21, "x": 2377, "y": 750, "seen": 21, "decoded_ms": 126.999009}, {"seq": 22, "x": 182, "y": 850, "seen": 22, "decoded_ms": 194.209712}, {"seq": 23, "x": 548, "y": 750, "seen": 23, "decoded_ms": 183.157837}, {"seq": 24, "x": 914, "y": 850, "seen": 24, "decoded_ms": 186.545894}, {"seq": 25, "x": 1280, "y": 750, "seen": 25, "decoded_ms": 506.013562}, {"seq": 26, "x": 1645, "y": 850, "seen": 26, "decoded_ms": 164.698513}, {"seq": 27, "x": 2011, "y": 750, "seen": 27, "decoded_ms": 177.684307}, {"seq": 28, "x": 2377, "y": 850, "seen": 28, "decoded_ms": 174.644202}], "p50_ms": 173.26692100000002, "p95_ms": 506.013562, "method": "XTest click-down to unique decoded sequence; excludes USB and scanout"}
@@ -0,0 +1,11 @@
{
"pid": 293859,
"requested_seconds": 360.0,
"samples": 65960,
"timestamp_after_pre_read_clock": 1,
"timestamp_after_post_read_clock": 0,
"max_age_ms": 1109.787402,
"survived": true,
"last_sequence": 6538,
"elapsed_seconds": 360.003
}
@@ -0,0 +1 @@
{"pid": 293859, "worker": 293861, "pause_seconds": 2.6, "exited": true, "check_seconds": 2.9042484689998673}
Binary file not shown.

After

Width:  |  Height:  |  Size: 804 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

@@ -0,0 +1 @@
{"pid": 299860, "ppid": 235482, "args": "/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream 192.168.1.72 Desktop --resolution 2560x1440 --fps 30 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --capture-system-keys always --keep-awake --no-vsync --no-frame-pacing --audio-on-host ", "sha256": "5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03", "native": true, "shape": [24, 24, 3, 1]}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Whitespace-only changes.
@@ -0,0 +1 @@
{"host": "192.168.1.72", "pid": 256179, "shapes": [{"name": "default", "source": {"w": 24, "h": 24, "hx": 3, "hy": 1, "x": 181, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 182, "y": 600, "w": 24, "h": 24, "hx": 3, "hy": 1}, "geometry_matches": true, "max_pixel_difference": 162, "visible_pixels": 254, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "text", "source": {"w": 24, "h": 24, "hx": 11, "hy": 12, "x": 547, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 548, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 101, "visible_pixels": 240, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "ew-resize", "source": {"w": 24, "h": 24, "hx": 12, "hy": 12, "x": 913, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 914, "y": 600, "w": 24, "h": 24, "hx": 12, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 175, "visible_pixels": 301, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "wait", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 1280, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 1280, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 129, "visible_pixels": 433, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "none", "source": {"w": 1, "h": 1, "hx": 0, "hy": 0, "x": 1645, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 1645, "y": 600, "w": 1, "h": 1, "hx": 0, "hy": 0}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 0, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "crosshair", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 2011, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 2011, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 78, "visible_pixels": 281, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "custom", "source": {"w": 24, "h": 24, "hx": 7, "hy": 9, "x": 2377, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 2377, "y": 600, "w": 24, "h": 24, "hx": 7, "hy": 9}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 576, "video": {"reference": 15726074, "nonuniform_pixels": 0}}], "paused_client": {"position": [250, 650], "elapsed_ms": 2.727055, "same_shape": true}, "clicks": [{"seq": 3, "x": 182, "y": 750, "seen": 3, "decoded_ms": 177.207219}, {"seq": 4, "x": 548, "y": 850, "seen": 4, "decoded_ms": 177.766631}, {"seq": 5, "x": 914, "y": 750, "seen": 5, "decoded_ms": 199.373737}, {"seq": 6, "x": 1280, "y": 850, "seen": 6, "decoded_ms": 182.446665}, {"seq": 7, "x": 1645, "y": 750, "seen": 7, "decoded_ms": 174.159044}, {"seq": 8, "x": 2011, "y": 850, "seen": 8, "decoded_ms": 211.839356}, {"seq": 9, "x": 2377, "y": 750, "seen": 9, "decoded_ms": 172.112732}, {"seq": 10, "x": 182, "y": 850, "seen": 10, "decoded_ms": 218.076291}, {"seq": 11, "x": 548, "y": 750, "seen": 11, "decoded_ms": 158.319669}, {"seq": 12, "x": 914, "y": 850, "seen": 12, "decoded_ms": 190.78387}, {"seq": 13, "x": 1280, "y": 750, "seen": 13, "decoded_ms": 475.373962}, {"seq": 14, "x": 1645, "y": 850, "seen": 14, "decoded_ms": 183.265657}, {"seq": 15, "x": 2011, "y": 750, "seen": 15, "decoded_ms": 190.493082}, {"seq": 16, "x": 2377, "y": 850, "seen": 16, "decoded_ms": 174.658874}, {"seq": 17, "x": 182, "y": 750, "seen": 17, "decoded_ms": 202.290292}, {"seq": 18, "x": 548, "y": 850, "seen": 18, "decoded_ms": 201.035264}, {"seq": 19, "x": 914, "y": 750, "seen": 19, "decoded_ms": 169.113849}, {"seq": 20, "x": 1280, "y": 850, "seen": 20, "decoded_ms": 389.791156}, {"seq": 21, "x": 1645, "y": 750, "seen": 21, "decoded_ms": 145.709303}, {"seq": 22, "x": 2011, "y": 850, "seen": 22, "decoded_ms": 142.136716}, {"seq": 23, "x": 2377, "y": 750, "seen": 23, "decoded_ms": 150.834416}, {"seq": 24, "x": 182, "y": 850, "seen": 24, "decoded_ms": 218.919785}, {"seq": 25, "x": 548, "y": 750, "seen": 25, "decoded_ms": 145.115185}, {"seq": 26, "x": 914, "y": 850, "seen": 26, "decoded_ms": 174.238963}, {"seq": 27, "x": 1280, "y": 750, "seen": 27, "decoded_ms": 476.883703}, {"seq": 28, "x": 1645, "y": 850, "seen": 28, "decoded_ms": 165.254265}, {"seq": 29, "x": 2011, "y": 750, "seen": 29, "decoded_ms": 182.507335}, {"seq": 30, "x": 2377, "y": 850, "seen": 30, "decoded_ms": 168.10207}], "p50_ms": 180.106648, "p95_ms": 475.373962, "method": "XTest click-down to unique decoded sequence; excludes USB and scanout"}
Whitespace-only changes.
@@ -0,0 +1 @@
{"host": "192.168.1.72", "pid": 293859, "shapes": [{"name": "default", "source": {"w": 24, "h": 24, "hx": 3, "hy": 1, "x": 181, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 182, "y": 600, "w": 24, "h": 24, "hx": 3, "hy": 1}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 254, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "text", "source": {"w": 24, "h": 24, "hx": 11, "hy": 12, "x": 547, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 548, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 240, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "ew-resize", "source": {"w": 24, "h": 24, "hx": 12, "hy": 12, "x": 913, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 914, "y": 600, "w": 24, "h": 24, "hx": 12, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 301, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "wait", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 1280, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 1280, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 433, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "none", "source": {"w": 1, "h": 1, "hx": 0, "hy": 0, "x": 1645, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 1645, "y": 600, "w": 1, "h": 1, "hx": 0, "hy": 0}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 0, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "crosshair", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 2011, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 2011, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 281, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "custom", "source": {"w": 24, "h": 24, "hx": 7, "hy": 9, "x": 2377, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 2377, "y": 600, "w": 24, "h": 24, "hx": 7, "hy": 9}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 576, "video": {"reference": 15726074, "nonuniform_pixels": 0}}], "paused_client": {"position": [350, 700], "elapsed_ms": 249.623897, "same_shape": true, "steps": [{"target": [250, 650], "position": [250, 650], "query_ms": 2.255624}, {"target": [280, 670], "position": [280, 670], "query_ms": 2.353619}, {"target": [300, 690], "position": [300, 690], "query_ms": 2.202252}, {"target": [350, 700], "position": [350, 700], "query_ms": 2.288873}]}, "clicks": [{"seq": 1, "x": 182, "y": 750, "seen": 1, "decoded_ms": 186.428878}, {"seq": 2, "x": 548, "y": 850, "seen": 2, "decoded_ms": 181.712898}, {"seq": 3, "x": 914, "y": 750, "seen": 3, "decoded_ms": 207.490698}, {"seq": 4, "x": 1280, "y": 850, "seen": 4, "decoded_ms": 533.830025}, {"seq": 5, "x": 1645, "y": 750, "seen": 5, "decoded_ms": 190.340809}, {"seq": 6, "x": 2011, "y": 850, "seen": 6, "decoded_ms": 204.401567}, {"seq": 7, "x": 2377, "y": 750, "seen": 7, "decoded_ms": 166.595545}, {"seq": 8, "x": 182, "y": 850, "seen": 8, "decoded_ms": 195.011611}, {"seq": 9, "x": 548, "y": 750, "seen": 9, "decoded_ms": 181.070161}, {"seq": 10, "x": 914, "y": 850, "seen": 10, "decoded_ms": 191.989545}, {"seq": 11, "x": 1280, "y": 750, "seen": 11, "decoded_ms": 450.587655}, {"seq": 12, "x": 1645, "y": 850, "seen": 12, "decoded_ms": 166.558247}, {"seq": 13, "x": 2011, "y": 750, "seen": 13, "decoded_ms": 174.993944}, {"seq": 14, "x": 2377, "y": 850, "seen": 14, "decoded_ms": 195.413266}, {"seq": 15, "x": 182, "y": 750, "seen": 15, "decoded_ms": 242.226344}, {"seq": 16, "x": 548, "y": 850, "seen": 16, "decoded_ms": 206.124429}, {"seq": 17, "x": 914, "y": 750, "seen": 17, "decoded_ms": 181.78767}, {"seq": 18, "x": 1280, "y": 850, "seen": 18, "decoded_ms": 463.825494}, {"seq": 19, "x": 1645, "y": 750, "seen": 19, "decoded_ms": 194.360157}, {"seq": 20, "x": 2011, "y": 850, "seen": 20, "decoded_ms": 177.975533}, {"seq": 21, "x": 2377, "y": 750, "seen": 21, "decoded_ms": 229.660945}, {"seq": 22, "x": 182, "y": 850, "seen": 22, "decoded_ms": 159.611781}, {"seq": 23, "x": 548, "y": 750, "seen": 23, "decoded_ms": 149.18704}, {"seq": 24, "x": 914, "y": 850, "seen": 24, "decoded_ms": 179.366438}, {"seq": 25, "x": 1280, "y": 750, "seen": 25, "decoded_ms": 522.420223}, {"seq": 26, "x": 1645, "y": 850, "seen": 26, "decoded_ms": 154.151825}, {"seq": 27, "x": 2011, "y": 750, "seen": 27, "decoded_ms": 154.669796}, {"seq": 28, "x": 2377, "y": 850, "seen": 28, "decoded_ms": 145.580813}], "p50_ms": 188.3848435, "p95_ms": 522.420223, "method": "XTest click-down to unique decoded sequence; excludes USB and scanout"}
Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

@@ -0,0 +1,7 @@
{
"agent": 39181,
"client": 293859,
"pause_seconds": 1.1,
"local_pointer": "x:650 y:900 screen:0 window:48234511",
"survived": true
}
@@ -0,0 +1,5 @@
{
"bridge_frozen_seconds": 1.2,
"checked_after_seconds": 3.289,
"remaining_clients": []
}
Whitespace-only changes.
@@ -0,0 +1 @@
{"host": "192.168.1.72", "pid": 272099, "shapes": [{"name": "default", "source": {"w": 24, "h": 24, "hx": 3, "hy": 1, "x": 181, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 182, "y": 600, "w": 24, "h": 24, "hx": 3, "hy": 1}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 254, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "text", "source": {"w": 24, "h": 24, "hx": 11, "hy": 12, "x": 547, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 548, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 240, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "ew-resize", "source": {"w": 24, "h": 24, "hx": 12, "hy": 12, "x": 913, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 914, "y": 600, "w": 24, "h": 24, "hx": 12, "hy": 12}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 301, "video": {"reference": 15726330, "nonuniform_pixels": 0}}, {"name": "wait", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 1280, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 1280, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 433, "video": {"reference": 15791866, "nonuniform_pixels": 0}}, {"name": "none", "source": {"w": 1, "h": 1, "hx": 0, "hy": 0, "x": 1645, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 1645, "y": 600, "w": 1, "h": 1, "hx": 0, "hy": 0}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 0, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "crosshair", "source": {"w": 24, "h": 24, "hx": 11, "hy": 11, "x": 2011, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 2011, "y": 600, "w": 24, "h": 24, "hx": 11, "hy": 11}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 281, "video": {"reference": 15726074, "nonuniform_pixels": 0}}, {"name": "custom", "source": {"w": 24, "h": 24, "hx": 7, "hy": 9, "x": 2377, "y": 599, "sw": 2560, "sh": 1440}, "local": {"x": 2377, "y": 600, "w": 24, "h": 24, "hx": 7, "hy": 9}, "geometry_matches": true, "max_pixel_difference": 0, "visible_pixels": 576, "video": {"reference": 15726074, "nonuniform_pixels": 0}}], "paused_client": {"position": [350, 700], "elapsed_ms": 263.611753, "same_shape": true, "steps": [{"target": [250, 650], "position": [250, 650], "query_ms": 16.220678}, {"target": [280, 670], "position": [280, 670], "query_ms": 2.214575}, {"target": [300, 690], "position": [300, 690], "query_ms": 2.215451}, {"target": [350, 700], "position": [350, 700], "query_ms": 2.380822}]}, "clicks": [{"seq": 2, "x": 182, "y": 750, "seen": 2, "decoded_ms": 192.980398}, {"seq": 3, "x": 548, "y": 850, "seen": 3, "decoded_ms": 249.189423}, {"seq": 4, "x": 914, "y": 750, "seen": 4, "decoded_ms": 193.088898}, {"seq": 5, "x": 1280, "y": 850, "seen": 5, "decoded_ms": 374.099277}, {"seq": 6, "x": 1645, "y": 750, "seen": 6, "decoded_ms": 166.521582}, {"seq": 7, "x": 2011, "y": 850, "seen": 7, "decoded_ms": 207.861192}, {"seq": 8, "x": 2377, "y": 750, "seen": 8, "decoded_ms": 246.000709}, {"seq": 9, "x": 182, "y": 850, "seen": 9, "decoded_ms": 181.998527}, {"seq": 10, "x": 548, "y": 750, "seen": 10, "decoded_ms": 181.410831}, {"seq": 11, "x": 914, "y": 850, "seen": 11, "decoded_ms": 221.952754}, {"seq": 12, "x": 1280, "y": 750, "seen": 12, "decoded_ms": 507.168335}, {"seq": 13, "x": 1645, "y": 850, "seen": 13, "decoded_ms": 182.568433}, {"seq": 14, "x": 2011, "y": 750, "seen": 14, "decoded_ms": 164.879833}, {"seq": 15, "x": 2377, "y": 850, "seen": 15, "decoded_ms": 212.375805}, {"seq": 16, "x": 182, "y": 750, "seen": 16, "decoded_ms": 188.116394}, {"seq": 17, "x": 548, "y": 850, "seen": 17, "decoded_ms": 203.54084}, {"seq": 18, "x": 914, "y": 750, "seen": 18, "decoded_ms": 183.044427}, {"seq": 19, "x": 1280, "y": 850, "seen": 19, "decoded_ms": 316.917585}, {"seq": 20, "x": 1645, "y": 750, "seen": 20, "decoded_ms": 161.788239}, {"seq": 21, "x": 2011, "y": 850, "seen": 21, "decoded_ms": 180.879436}, {"seq": 22, "x": 2377, "y": 750, "seen": 22, "decoded_ms": 172.71762}, {"seq": 23, "x": 182, "y": 850, "seen": 23, "decoded_ms": 175.79901}, {"seq": 24, "x": 548, "y": 750, "seen": 24, "decoded_ms": 185.907366}, {"seq": 25, "x": 914, "y": 850, "seen": 25, "decoded_ms": 207.759418}, {"seq": 26, "x": 1280, "y": 750, "seen": 26, "decoded_ms": 468.920931}, {"seq": 27, "x": 1645, "y": 850, "seen": 27, "decoded_ms": 189.784407}, {"seq": 28, "x": 2011, "y": 750, "seen": 28, "decoded_ms": 186.12174}, {"seq": 29, "x": 2377, "y": 850, "seen": 29, "decoded_ms": 182.798273}], "p50_ms": 188.9504005, "p95_ms": 468.920931, "method": "XTest click-down to unique decoded sequence; excludes USB and scanout"}
@@ -0,0 +1,9 @@
{
"host": "192.168.1.72",
"target": [
400,
600
],
"readback": "X=400\nY=600\nSCREEN=0\nWINDOW=48234511\n",
"passed": true
}
@@ -0,0 +1,8 @@
{
"host": "192.168.1.72",
"target": [
400,
600
],
"readback": "X=1500\nY=900\nSCREEN=0\nWINDOW=48234511\n"
}
@@ -0,0 +1 @@
{"pid": 299142, "args": "/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream 192.168.1.70 Desktop --resolution 2560x1440 --fps 45 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --capture-system-keys always --keep-awake --no-vsync --no-frame-pacing --audio-on-host ", "sha256": "5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03", "native": false}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB